/libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua

http://calcute-bedb.googlecode.com/ · Lua · 687 lines · 532 code · 112 blank · 43 comment · 63 complexity · 5361cbd2713039ac9f0c6f8785418d8a MD5 · raw file

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