/libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua
Lua | 687 lines | 532 code | 112 blank | 43 comment | 48 complexity | 5361cbd2713039ac9f0c6f8785418d8a MD5 | raw file
1--[[ $Id: AceGUIWidget-DropDown.lua 793 2009-04-07 09:26:44Z nevcairiel $ ]]-- 2local min, max, floor = math.min, math.max, math.floor 3 4local AceGUI = LibStub("AceGUI-3.0") 5 6local function fixlevels(parent,...) 7 local i = 1 8 local child = select(i, ...) 9 while child do 10 child:SetFrameLevel(parent:GetFrameLevel()+1) 11 fixlevels(child, child:GetChildren()) 12 i = i + 1 13 child = select(i, ...) 14 end 15end 16 17local function fixstrata(strata, parent, ...) 18 local i = 1 19 local child = select(i, ...) 20 parent:SetFrameStrata(strata) 21 while child do 22 fixstrata(strata, child, child:GetChildren()) 23 i = i + 1 24 child = select(i, ...) 25 end 26end 27 28do 29 local widgetType = "Dropdown-Pullout" 30 local widgetVersion = 2 31 32 --[[ Static data ]]-- 33 34 local backdrop = { 35 bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", 36 edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", 37 edgeSize = 32, 38 tileSize = 32, 39 tile = true, 40 insets = { left = 11, right = 12, top = 12, bottom = 11 }, 41 } 42 local sliderBackdrop = { 43 bgFile = "Interface\\Buttons\\UI-SliderBar-Background", 44 edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", 45 tile = true, tileSize = 8, edgeSize = 8, 46 insets = { left = 3, right = 3, top = 3, bottom = 3 } 47 } 48 49 local defaultWidth = 200 50 local defaultMaxHeight = 600 51 52 --[[ UI Event Handlers ]]-- 53 54 -- HACK: This should be no part of the pullout, but there 55 -- is no other 'clean' way to response to any item-OnEnter 56 -- Used to close Submenus when an other item is entered 57 local function OnEnter(item) 58 local self = item.pullout 59 for k, v in ipairs(self.items) do 60 if v.CloseMenu and v ~= item then 61 v:CloseMenu() 62 end 63 end 64 end 65 66 -- See the note in Constructor() for each scroll related function 67 local function OnMouseWheel(this, value) 68 this.obj:MoveScroll(value) 69 end 70 71 local function OnScrollValueChanged(this, value) 72 this.obj:SetScroll(value) 73 end 74 75 local function OnSizeChanged(this) 76 this.obj:FixScroll() 77 end 78 79 --[[ Exported methods ]]-- 80 81 -- exported 82 local function SetScroll(self, value) 83 local status = self.scrollStatus 84 local frame, child = self.scrollFrame, self.itemFrame 85 local height, viewheight = frame:GetHeight(), child:GetHeight() 86 87 local offset 88 if height > viewheight then 89 offset = 0 90 else 91 offset = floor((viewheight - height) / 1000 * value) 92 end 93 child:ClearAllPoints() 94 child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset) 95 child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset) 96 status.offset = offset 97 status.scrollvalue = value 98 end 99 100 -- exported 101 local function MoveScroll(self, value) 102 local status = self.scrollStatus 103 local frame, child = self.scrollFrame, self.itemFrame 104 local height, viewheight = frame:GetHeight(), child:GetHeight() 105 106 if height > viewheight then 107 self.slider:Hide() 108 else 109 self.slider:Show() 110 local diff = height - viewheight 111 local delta = 1 112 if value < 0 then 113 delta = -1 114 end 115 self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000)) 116 end 117 end 118 119 -- exported 120 local function FixScroll(self) 121 local status = self.scrollStatus 122 local frame, child = self.scrollFrame, self.itemFrame 123 local height, viewheight = frame:GetHeight(), child:GetHeight() 124 local offset = status.offset or 0 125 126 if viewheight < height then 127 self.slider:Hide() 128 child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset) 129 self.slider:SetValue(0) 130 else 131 self.slider:Show() 132 local value = (offset / (viewheight - height) * 1000) 133 if value > 1000 then value = 1000 end 134 self.slider:SetValue(value) 135 self:SetScroll(value) 136 if value < 1000 then 137 child:ClearAllPoints() 138 child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset) 139 child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset) 140 status.offset = offset 141 end 142 end 143 end 144 145 -- exported, AceGUI callback 146 local function OnAcquire(self) 147 self.frame:SetParent(UIParent) 148 --self.itemFrame:SetToplevel(true) 149 end 150 151 -- exported, AceGUI callback 152 local function OnRelease(self) 153 self:Clear() 154 self.frame:ClearAllPoints() 155 self.frame:Hide() 156 end 157 158 -- exported 159 local function AddItem(self, item) 160 self.items[#self.items + 1] = item 161 162 local h = #self.items * 16 163 self.itemFrame:SetHeight(h) 164 self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement 165 166 item.frame:SetPoint("LEFT", self.itemFrame, "LEFT") 167 item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT") 168 169 item:SetPullout(self) 170 item:SetOnEnter(OnEnter) 171 end 172 173 -- exported 174 local function Open(self, point, relFrame, relPoint, x, y) 175 local items = self.items 176 local frame = self.frame 177 local itemFrame = self.itemFrame 178 179 frame:SetPoint(point, relFrame, relPoint, x, y) 180 181 182 local height = 8 183 for i, item in pairs(items) do 184 if i == 1 then 185 item:SetPoint("TOP", itemFrame, "TOP", 0, -2) 186 else 187 item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1) 188 end 189 190 item:Show() 191 192 height = height + 16 193 end 194 itemFrame:SetHeight(height) 195 fixstrata("TOOLTIP", frame, frame:GetChildren()) 196 frame:Show() 197 self:Fire("OnOpen") 198 end 199 200 -- exported 201 local function Close(self) 202 self.frame:Hide() 203 self:Fire("OnClose") 204 end 205 206 -- exported 207 local function Clear(self) 208 local items = self.items 209 for i, item in pairs(items) do 210 AceGUI:Release(item) 211 items[i] = nil 212 end 213 end 214 215 -- exported 216 local function IterateItems(self) 217 return ipairs(self.items) 218 end 219 220 -- exported 221 local function SetHideOnLeave(self, val) 222 self.hideOnLeave = val 223 end 224 225 -- exported 226 local function SetMaxHeight(self, height) 227 self.maxHeight = height or defaultMaxHeight 228 if self.frame:GetHeight() > height then 229 self.frame:SetHeight(height) 230 elseif (self.itemFrame:GetHeight() + 34) < height then 231 self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem 232 end 233 end 234 235 -- exported 236 local function GetRightBorderWidth(self) 237 return 6 + (self.slider:IsShown() and 12 or 0) 238 end 239 240 -- exported 241 local function GetLeftBorderWidth(self) 242 return 6 243 end 244 245 --[[ Constructor ]]-- 246 247 local function Constructor() 248 local count = AceGUI:GetNextWidgetNum(widgetType) 249 local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent) 250 local self = {} 251 self.count = count 252 self.type = widgetType 253 self.frame = frame 254 frame.obj = self 255 256 self.OnAcquire = OnAcquire 257 self.OnRelease = OnRelease 258 259 self.AddItem = AddItem 260 self.Open = Open 261 self.Close = Close 262 self.Clear = Clear 263 self.IterateItems = IterateItems 264 self.SetHideOnLeave = SetHideOnLeave 265 266 self.SetScroll = SetScroll 267 self.MoveScroll = MoveScroll 268 self.FixScroll = FixScroll 269 270 self.SetMaxHeight = SetMaxHeight 271 self.GetRightBorderWidth = GetRightBorderWidth 272 self.GetLeftBorderWidth = GetLeftBorderWidth 273 274 self.items = {} 275 276 self.scrollStatus = { 277 scrollvalue = 0, 278 } 279 280 self.maxHeight = defaultMaxHeight 281 282 frame:SetBackdrop(backdrop) 283 frame:SetBackdropColor(0, 0, 0) 284 frame:SetFrameStrata("FULLSCREEN_DIALOG") 285 frame:SetClampedToScreen(true) 286 frame:SetWidth(defaultWidth) 287 frame:SetHeight(self.maxHeight) 288 --frame:SetToplevel(true) 289 290 -- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame 291 local scrollFrame = CreateFrame("ScrollFrame", nil, frame) 292 local itemFrame = CreateFrame("Frame", nil, scrollFrame) 293 294 self.scrollFrame = scrollFrame 295 self.itemFrame = itemFrame 296 297 scrollFrame.obj = self 298 itemFrame.obj = self 299 300 local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame) 301 slider:SetOrientation("VERTICAL") 302 slider:SetHitRectInsets(0, 0, -10, 0) 303 slider:SetBackdrop(sliderBackdrop) 304 slider:SetWidth(8) 305 slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") 306 slider:SetFrameStrata("FULLSCREEN_DIALOG") 307 self.slider = slider 308 slider.obj = self 309 310 scrollFrame:SetScrollChild(itemFrame) 311 scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12) 312 scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12) 313 scrollFrame:EnableMouseWheel(true) 314 scrollFrame:SetScript("OnMouseWheel", OnMouseWheel) 315 scrollFrame:SetScript("OnSizeChanged", OnSizeChanged) 316 scrollFrame:SetToplevel(true) 317 scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG") 318 319 itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0) 320 itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0) 321 itemFrame:SetHeight(400) 322 itemFrame:SetToplevel(true) 323 itemFrame:SetFrameStrata("FULLSCREEN_DIALOG") 324 325 slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0) 326 slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0) 327 slider:SetScript("OnValueChanged", OnScrollValueChanged) 328 slider:SetMinMaxValues(0, 1000) 329 slider:SetValueStep(1) 330 slider:SetValue(0) 331 332 scrollFrame:Show() 333 itemFrame:Show() 334 slider:Hide() 335 336 self:FixScroll() 337 338 AceGUI:RegisterAsWidget(self) 339 return self 340 end 341 342 AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) 343end 344 345do 346 local widgetType = "Dropdown" 347 local widgetVersion = 19 348 349 --[[ Static data ]]-- 350 351 --[[ UI event handler ]]-- 352 353 local function Control_OnEnter(this) 354 this.obj:Fire("OnEnter") 355 end 356 357 local function Control_OnLeave(this) 358 this.obj:Fire("OnLeave") 359 end 360 361 local function Dropdown_OnHide(this) 362 local self = this.obj 363 if self.open then 364 self.pullout:Close() 365 end 366 end 367 368 local function Dropdown_TogglePullout(this) 369 local self = this.obj 370 if self.open then 371 self.open = nil 372 self.pullout:Close() 373 AceGUI:ClearFocus() 374 else 375 self.open = true 376 self.pullout:SetWidth(self.frame:GetWidth()) 377 self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0) 378 AceGUI:SetFocus(self) 379 end 380 end 381 382 local function OnPulloutOpen(this) 383 local self = this.userdata.obj 384 local value = self.value 385 386 if not self.multiselect then 387 for i, item in this:IterateItems() do 388 item:SetValue(item.userdata.value == value) 389 end 390 end 391 392 self.open = true 393 end 394 395 local function OnPulloutClose(this) 396 local self = this.userdata.obj 397 self.open = nil 398 self:Fire("OnClosed") 399 end 400 401 local function ShowMultiText(self) 402 local text 403 for i, widget in self.pullout:IterateItems() do 404 if widget.type == "Dropdown-Item-Toggle" then 405 if widget:GetValue() then 406 if text then 407 text = text..", "..widget:GetText() 408 else 409 text = widget:GetText() 410 end 411 end 412 end 413 end 414 self:SetText(text) 415 end 416 417 local function OnItemValueChanged(this, event, checked) 418 local self = this.userdata.obj 419 420 if self.multiselect then 421 self:Fire("OnValueChanged", this.userdata.value, checked) 422 ShowMultiText(self) 423 else 424 if checked then 425 self:SetValue(this.userdata.value) 426 self:Fire("OnValueChanged", this.userdata.value) 427 else 428 this:SetValue(true) 429 end 430 if self.open then 431 self.pullout:Close() 432 end 433 end 434 end 435 436 --[[ Exported methods ]]-- 437 438 -- exported, AceGUI callback 439 local function OnAcquire(self) 440 local pullout = AceGUI:Create("Dropdown-Pullout") 441 self.pullout = pullout 442 pullout.userdata.obj = self 443 pullout:SetCallback("OnClose", OnPulloutClose) 444 pullout:SetCallback("OnOpen", OnPulloutOpen) 445 self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1) 446 fixlevels(self.pullout.frame, self.pullout.frame:GetChildren()) 447 448 self:SetHeight(44) 449 self:SetWidth(200) 450 end 451 452 -- exported, AceGUI callback 453 local function OnRelease(self) 454 if self.open then 455 self.pullout:Close() 456 end 457 AceGUI:Release(self.pullout) 458 459 self:SetText("") 460 self:SetLabel("") 461 self:SetDisabled(false) 462 self:SetMultiselect(false) 463 464 self.value = nil 465 self.list = nil 466 self.open = nil 467 self.hasClose = nil 468 469 self.frame:ClearAllPoints() 470 self.frame:Hide() 471 end 472 473 -- exported 474 local function SetDisabled(self, disabled) 475 self.disabled = disabled 476 if disabled then 477 self.text:SetTextColor(0.5,0.5,0.5) 478 self.button:Disable() 479 self.label:SetTextColor(0.5,0.5,0.5) 480 else 481 self.button:Enable() 482 self.label:SetTextColor(1,.82,0) 483 self.text:SetTextColor(1,1,1) 484 end 485 end 486 487 -- exported 488 local function ClearFocus(self) 489 if self.open then 490 self.pullout:Close() 491 end 492 end 493 494 -- exported 495 local function SetText(self, text) 496 self.text:SetText(text or "") 497 end 498 499 -- exported 500 local function SetLabel(self, text) 501 if text and text ~= "" then 502 self.label:SetText(text) 503 self.label:Show() 504 self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-18) 505 self.frame:SetHeight(44) 506 else 507 self.label:SetText("") 508 self.label:Hide() 509 self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0) 510 self.frame:SetHeight(26) 511 end 512 end 513 514 -- exported 515 local function SetValue(self, value) 516 if self.list then 517 self:SetText(self.list[value] or "") 518 end 519 self.value = value 520 end 521 522 -- exported 523 local function SetItemValue(self, item, value) 524 if not self.multiselect then return end 525 for i, widget in self.pullout:IterateItems() do 526 if widget.userdata.value == item then 527 if widget.SetValue then 528 widget:SetValue(value) 529 end 530 end 531 end 532 ShowMultiText(self) 533 end 534 535 -- exported 536 local function SetItemDisabled(self, item, disabled) 537 for i, widget in self.pullout:IterateItems() do 538 if widget.userdata.value == item then 539 widget:SetDisabled(disabled) 540 end 541 end 542 end 543 544 local function AddListItem(self, value, text) 545 local item = AceGUI:Create("Dropdown-Item-Toggle") 546 item:SetText(text) 547 item.userdata.obj = self 548 item.userdata.value = value 549 item:SetCallback("OnValueChanged", OnItemValueChanged) 550 self.pullout:AddItem(item) 551 end 552 553 local function AddCloseButton(self) 554 if not self.hasClose then 555 local close = AceGUI:Create("Dropdown-Item-Execute") 556 close:SetText(CLOSE) 557 self.pullout:AddItem(close) 558 self.hasClose = true 559 end 560 end 561 562 -- exported 563 local sortlist = {} 564 local function SetList(self, list) 565 self.list = list 566 self.pullout:Clear() 567 self.hasClose = nil 568 if not list then return end 569 570 for v in pairs(list) do 571 sortlist[#sortlist + 1] = v 572 end 573 table.sort(sortlist) 574 575 for i, value in pairs(sortlist) do 576 AddListItem(self, value, list[value]) 577 sortlist[i] = nil 578 end 579 if self.multiselect then 580 ShowMultiText(self) 581 AddCloseButton(self) 582 end 583 end 584 585 -- exported 586 local function AddItem(self, value, text) 587 if self.list then 588 self.list[value] = text 589 AddListItem(self, value, text) 590 end 591 end 592 593 -- exported 594 local function SetMultiselect(self, multi) 595 self.multiselect = multi 596 if multi then 597 ShowMultiText(self) 598 AddCloseButton(self) 599 end 600 end 601 602 -- exported 603 local function GetMultiselect(self) 604 return self.multiselect 605 end 606 607 --[[ Constructor ]]-- 608 609 local function Constructor() 610 local count = AceGUI:GetNextWidgetNum(widgetType) 611 local frame = CreateFrame("Frame", nil, UIParent) 612 local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate") 613 614 local self = {} 615 self.type = widgetType 616 self.frame = frame 617 self.dropdown = dropdown 618 self.count = count 619 frame.obj = self 620 dropdown.obj = self 621 622 self.OnRelease = OnRelease 623 self.OnAcquire = OnAcquire 624 625 self.ClearFocus = ClearFocus 626 627 self.SetText = SetText 628 self.SetValue = SetValue 629 self.SetList = SetList 630 self.SetLabel = SetLabel 631 self.SetDisabled = SetDisabled 632 self.AddItem = AddItem 633 self.SetMultiselect = SetMultiselect 634 self.GetMultiselect = GetMultiselect 635 self.SetItemValue = SetItemValue 636 self.SetItemDisabled = SetItemDisabled 637 638 self.alignoffset = 31 639 640 frame:SetHeight(44) 641 frame:SetWidth(200) 642 frame:SetScript("OnHide",Dropdown_OnHide) 643 644 dropdown:ClearAllPoints() 645 dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0) 646 dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0) 647 dropdown:SetScript("OnHide", nil) 648 649 local left = _G[dropdown:GetName() .. "Left"] 650 local middle = _G[dropdown:GetName() .. "Middle"] 651 local right = _G[dropdown:GetName() .. "Right"] 652 653 middle:ClearAllPoints() 654 right:ClearAllPoints() 655 656 middle:SetPoint("LEFT", left, "RIGHT", 0, 0) 657 middle:SetPoint("RIGHT", right, "LEFT", 0, 0) 658 right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17) 659 660 local button = _G[dropdown:GetName() .. "Button"] 661 self.button = button 662 button.obj = self 663 button:SetScript("OnEnter",Control_OnEnter) 664 button:SetScript("OnLeave",Control_OnLeave) 665 button:SetScript("OnClick",Dropdown_TogglePullout) 666 667 local text = _G[dropdown:GetName() .. "Text"] 668 self.text = text 669 text.obj = self 670 text:ClearAllPoints() 671 text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2) 672 text:SetPoint("LEFT", left, "LEFT", 25, 2) 673 674 local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") 675 label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) 676 label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) 677 label:SetJustifyH("LEFT") 678 label:SetHeight(18) 679 label:Hide() 680 self.label = label 681 682 AceGUI:RegisterAsWidget(self) 683 return self 684 end 685 686 AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) 687end