comparison Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua @ 0:98c6f55e6619

First commit
author Xiiph
date Sat, 05 Feb 2011 16:45:02 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:98c6f55e6619
1 --[[-----------------------------------------------------------------------------
2 TreeGroup Container
3 Container that uses a tree control to switch between groups.
4 -------------------------------------------------------------------------------]]
5 local Type, Version = "TreeGroup", 32
6 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
7 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
8
9 -- Lua APIs
10 local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
11 local math_min, math_max, floor = math.min, math.max, floor
12 local select, tremove, unpack = select, table.remove, unpack
13
14 -- WoW APIs
15 local CreateFrame, UIParent = CreateFrame, UIParent
16
17 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
18 -- List them here for Mikk's FindGlobals script
19 -- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE
20
21 -- Recycling functions
22 local new, del
23 do
24 local pool = setmetatable({},{__mode='k'})
25 function new()
26 local t = next(pool)
27 if t then
28 pool[t] = nil
29 return t
30 else
31 return {}
32 end
33 end
34 function del(t)
35 for k in pairs(t) do
36 t[k] = nil
37 end
38 pool[t] = true
39 end
40 end
41
42 local DEFAULT_TREE_WIDTH = 175
43 local DEFAULT_TREE_SIZABLE = true
44
45 --[[-----------------------------------------------------------------------------
46 Support functions
47 -------------------------------------------------------------------------------]]
48 local function GetButtonUniqueValue(line)
49 local parent = line.parent
50 if parent and parent.value then
51 return GetButtonUniqueValue(parent).."\001"..line.value
52 else
53 return line.value
54 end
55 end
56
57 local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
58 local self = button.obj
59 local toggle = button.toggle
60 local frame = self.frame
61 local text = treeline.text or ""
62 local icon = treeline.icon
63 local iconCoords = treeline.iconCoords
64 local level = treeline.level
65 local value = treeline.value
66 local uniquevalue = treeline.uniquevalue
67 local disabled = treeline.disabled
68
69 button.treeline = treeline
70 button.value = value
71 button.uniquevalue = uniquevalue
72 if selected then
73 button:LockHighlight()
74 button.selected = true
75 else
76 button:UnlockHighlight()
77 button.selected = false
78 end
79 local normalTexture = button:GetNormalTexture()
80 local line = button.line
81 button.level = level
82 if ( level == 1 ) then
83 button:SetNormalFontObject("GameFontNormal")
84 button:SetHighlightFontObject("GameFontHighlight")
85 button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
86 else
87 button:SetNormalFontObject("GameFontHighlightSmall")
88 button:SetHighlightFontObject("GameFontHighlightSmall")
89 button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2)
90 end
91
92 if disabled then
93 button:EnableMouse(false)
94 button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
95 else
96 button.text:SetText(text)
97 button:EnableMouse(true)
98 end
99
100 if icon then
101 button.icon:SetTexture(icon)
102 button.icon:SetPoint("LEFT", 8 * level, (level == 1) and 0 or 1)
103 else
104 button.icon:SetTexture(nil)
105 end
106
107 if iconCoords then
108 button.icon:SetTexCoord(unpack(iconCoords))
109 else
110 button.icon:SetTexCoord(0, 1, 0, 1)
111 end
112
113 if canExpand then
114 if not isExpanded then
115 toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
116 toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
117 else
118 toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
119 toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
120 end
121 toggle:Show()
122 else
123 toggle:Hide()
124 end
125 end
126
127 local function ShouldDisplayLevel(tree)
128 local result = false
129 for k, v in ipairs(tree) do
130 if v.children == nil and v.visible ~= false then
131 result = true
132 elseif v.children then
133 result = result or ShouldDisplayLevel(v.children)
134 end
135 if result then return result end
136 end
137 return false
138 end
139
140 local function addLine(self, v, tree, level, parent)
141 local line = new()
142 line.value = v.value
143 line.text = v.text
144 line.icon = v.icon
145 line.iconCoords = v.iconCoords
146 line.disabled = v.disabled
147 line.tree = tree
148 line.level = level
149 line.parent = parent
150 line.visible = v.visible
151 line.uniquevalue = GetButtonUniqueValue(line)
152 if v.children then
153 line.hasChildren = true
154 else
155 line.hasChildren = nil
156 end
157 self.lines[#self.lines+1] = line
158 return line
159 end
160
161 --fire an update after one frame to catch the treeframes height
162 local function FirstFrameUpdate(frame)
163 local self = frame.obj
164 frame:SetScript("OnUpdate", nil)
165 self:RefreshTree()
166 end
167
168 local function BuildUniqueValue(...)
169 local n = select('#', ...)
170 if n == 1 then
171 return ...
172 else
173 return (...).."\001"..BuildUniqueValue(select(2,...))
174 end
175 end
176
177 --[[-----------------------------------------------------------------------------
178 Scripts
179 -------------------------------------------------------------------------------]]
180 local function Expand_OnClick(frame)
181 local button = frame.button
182 local self = button.obj
183 local status = (self.status or self.localstatus).groups
184 status[button.uniquevalue] = not status[button.uniquevalue]
185 self:RefreshTree()
186 end
187
188 local function Button_OnClick(frame)
189 local self = frame.obj
190 self:Fire("OnClick", frame.uniquevalue, frame.selected)
191 if not frame.selected then
192 self:SetSelected(frame.uniquevalue)
193 frame.selected = true
194 frame:LockHighlight()
195 self:RefreshTree()
196 end
197 AceGUI:ClearFocus()
198 end
199
200 local function Button_OnDoubleClick(button)
201 local self = button.obj
202 local status = self.status or self.localstatus
203 local status = (self.status or self.localstatus).groups
204 status[button.uniquevalue] = not status[button.uniquevalue]
205 self:RefreshTree()
206 end
207
208 local function Button_OnEnter(frame)
209 local self = frame.obj
210 self:Fire("OnButtonEnter", frame.uniquevalue, frame)
211
212 if self.enabletooltips then
213 GameTooltip:SetOwner(frame, "ANCHOR_NONE")
214 GameTooltip:SetPoint("LEFT",frame,"RIGHT")
215 GameTooltip:SetText(frame.text:GetText() or "", 1, .82, 0, 1)
216
217 GameTooltip:Show()
218 end
219 end
220
221 local function Button_OnLeave(frame)
222 local self = frame.obj
223 self:Fire("OnButtonLeave", frame.uniquevalue, frame)
224
225 if self.enabletooltips then
226 GameTooltip:Hide()
227 end
228 end
229
230 local function OnScrollValueChanged(frame, value)
231 if frame.obj.noupdate then return end
232 local self = frame.obj
233 local status = self.status or self.localstatus
234 status.scrollvalue = value
235 self:RefreshTree()
236 AceGUI:ClearFocus()
237 end
238
239 local function Tree_OnSizeChanged(frame)
240 frame.obj:RefreshTree()
241 end
242
243 local function Tree_OnMouseWheel(frame, delta)
244 local self = frame.obj
245 if self.showscroll then
246 local scrollbar = self.scrollbar
247 local min, max = scrollbar:GetMinMaxValues()
248 local value = scrollbar:GetValue()
249 local newvalue = math_min(max,math_max(min,value - delta))
250 if value ~= newvalue then
251 scrollbar:SetValue(newvalue)
252 end
253 end
254 end
255
256 local function Dragger_OnLeave(frame)
257 frame:SetBackdropColor(1, 1, 1, 0)
258 end
259
260 local function Dragger_OnEnter(frame)
261 frame:SetBackdropColor(1, 1, 1, 0.8)
262 end
263
264 local function Dragger_OnMouseDown(frame)
265 local treeframe = frame:GetParent()
266 treeframe:StartSizing("RIGHT")
267 end
268
269 local function Dragger_OnMouseUp(frame)
270 local treeframe = frame:GetParent()
271 local self = treeframe.obj
272 local frame = treeframe:GetParent()
273 treeframe:StopMovingOrSizing()
274 --treeframe:SetScript("OnUpdate", nil)
275 treeframe:SetUserPlaced(false)
276 --Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
277 treeframe:SetHeight(0)
278 treeframe:SetPoint("TOPLEFT", frame, "TOPLEFT",0,0)
279 treeframe:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT",0,0)
280
281 local status = self.status or self.localstatus
282 status.treewidth = treeframe:GetWidth()
283
284 treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth())
285 -- recalculate the content width
286 treeframe.obj:OnWidthSet(status.fullwidth)
287 -- update the layout of the content
288 treeframe.obj:DoLayout()
289 end
290
291 --[[-----------------------------------------------------------------------------
292 Methods
293 -------------------------------------------------------------------------------]]
294 local methods = {
295 ["OnAcquire"] = function(self)
296 self:SetTreeWidth(DEFAULT_TREE_WIDTH, DEFAULT_TREE_SIZABLE)
297 self:EnableButtonTooltips(true)
298 end,
299
300 ["OnRelease"] = function(self)
301 self.status = nil
302 for k, v in pairs(self.localstatus) do
303 if k == "groups" then
304 for k2 in pairs(v) do
305 v[k2] = nil
306 end
307 else
308 self.localstatus[k] = nil
309 end
310 end
311 self.localstatus.scrollvalue = 0
312 self.localstatus.treewidth = DEFAULT_TREE_WIDTH
313 self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
314 end,
315
316 ["EnableButtonTooltips"] = function(self, enable)
317 self.enabletooltips = enable
318 end,
319
320 ["CreateButton"] = function(self)
321 local num = AceGUI:GetNextWidgetNum("TreeGroupButton")
322 local button = CreateFrame("Button", ("AceGUI30TreeButton%d"):format(num), self.treeframe, "OptionsListButtonTemplate")
323 button.obj = self
324
325 local icon = button:CreateTexture(nil, "OVERLAY")
326 icon:SetWidth(14)
327 icon:SetHeight(14)
328 button.icon = icon
329
330 button:SetScript("OnClick",Button_OnClick)
331 button:SetScript("OnDoubleClick", Button_OnDoubleClick)
332 button:SetScript("OnEnter",Button_OnEnter)
333 button:SetScript("OnLeave",Button_OnLeave)
334
335 button.toggle.button = button
336 button.toggle:SetScript("OnClick",Expand_OnClick)
337
338 return button
339 end,
340
341 ["SetStatusTable"] = function(self, status)
342 assert(type(status) == "table")
343 self.status = status
344 if not status.groups then
345 status.groups = {}
346 end
347 if not status.scrollvalue then
348 status.scrollvalue = 0
349 end
350 if not status.treewidth then
351 status.treewidth = DEFAULT_TREE_WIDTH
352 end
353 if status.treesizable == nil then
354 status.treesizable = DEFAULT_TREE_SIZABLE
355 end
356 self:SetTreeWidth(status.treewidth,status.treesizable)
357 self:RefreshTree()
358 end,
359
360 --sets the tree to be displayed
361 ["SetTree"] = function(self, tree, filter)
362 self.filter = filter
363 if tree then
364 assert(type(tree) == "table")
365 end
366 self.tree = tree
367 self:RefreshTree()
368 end,
369
370 ["BuildLevel"] = function(self, tree, level, parent)
371 local groups = (self.status or self.localstatus).groups
372 local hasChildren = self.hasChildren
373
374 for i, v in ipairs(tree) do
375 if v.children then
376 if not self.filter or ShouldDisplayLevel(v.children) then
377 local line = addLine(self, v, tree, level, parent)
378 if groups[line.uniquevalue] then
379 self:BuildLevel(v.children, level+1, line)
380 end
381 end
382 elseif v.visible ~= false or not self.filter then
383 addLine(self, v, tree, level, parent)
384 end
385 end
386 end,
387
388 ["RefreshTree"] = function(self)
389 local buttons = self.buttons
390 local lines = self.lines
391
392 for i, v in ipairs(buttons) do
393 v:Hide()
394 end
395 while lines[1] do
396 local t = tremove(lines)
397 for k in pairs(t) do
398 t[k] = nil
399 end
400 del(t)
401 end
402
403 if not self.tree then return end
404 --Build the list of visible entries from the tree and status tables
405 local status = self.status or self.localstatus
406 local groupstatus = status.groups
407 local tree = self.tree
408
409 local treeframe = self.treeframe
410
411 self:BuildLevel(tree, 1)
412
413 local numlines = #lines
414
415 local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
416 if maxlines <= 0 then return end
417
418 local first, last
419
420 if numlines <= maxlines then
421 --the whole tree fits in the frame
422 status.scrollvalue = 0
423 self:ShowScroll(false)
424 first, last = 1, numlines
425 else
426 self:ShowScroll(true)
427 --scrolling will be needed
428 self.noupdate = true
429 self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
430 --check if we are scrolled down too far
431 if numlines - status.scrollvalue < maxlines then
432 status.scrollvalue = numlines - maxlines
433 end
434 if self.scrollbar:GetValue() ~= status.scrollvalue then
435 self.scrollbar:SetValue(status.scrollvalue)
436 end
437 self.noupdate = nil
438 first, last = status.scrollvalue+1, status.scrollvalue + maxlines
439 end
440
441 local buttonnum = 1
442 for i = first, last do
443 local line = lines[i]
444 local button = buttons[buttonnum]
445 if not button then
446 button = self:CreateButton()
447
448 buttons[buttonnum] = button
449 button:SetParent(treeframe)
450 button:SetFrameLevel(treeframe:GetFrameLevel()+1)
451 button:ClearAllPoints()
452 if buttonnum == 1 then
453 if self.showscroll then
454 button:SetPoint("TOPRIGHT", -22, -10)
455 button:SetPoint("TOPLEFT", 0, -10)
456 else
457 button:SetPoint("TOPRIGHT", 0, -10)
458 button:SetPoint("TOPLEFT", 0, -10)
459 end
460 else
461 button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0)
462 button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0)
463 end
464 end
465
466 UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] )
467 button:Show()
468 buttonnum = buttonnum + 1
469 end
470 end,
471
472 ["SetSelected"] = function(self, value)
473 local status = self.status or self.localstatus
474 if status.selected ~= value then
475 status.selected = value
476 self:Fire("OnGroupSelected", value)
477 end
478 end,
479
480 ["Select"] = function(self, uniquevalue, ...)
481 self.filter = false
482 local status = self.status or self.localstatus
483 local groups = status.groups
484 for i = 1, select('#', ...) do
485 groups[BuildUniqueValue(select(i, ...))] = true
486 end
487 status.selected = uniquevalue
488 self:RefreshTree()
489 self:Fire("OnGroupSelected", uniquevalue)
490 end,
491
492 ["SelectByPath"] = function(self, ...)
493 self:Select(BuildUniqueValue(...), ...)
494 end,
495
496 ["SelectByValue"] = function(self, uniquevalue)
497 self:Select(uniquevalue, ("\001"):split(uniquevalue))
498 end,
499
500 ["ShowScroll"] = function(self, show)
501 self.showscroll = show
502 if show then
503 self.scrollbar:Show()
504 if self.buttons[1] then
505 self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
506 end
507 else
508 self.scrollbar:Hide()
509 if self.buttons[1] then
510 self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
511 end
512 end
513 end,
514
515 ["OnWidthSet"] = function(self, width)
516 local content = self.content
517 local treeframe = self.treeframe
518 local status = self.status or self.localstatus
519 status.fullwidth = width
520
521 local contentwidth = width - status.treewidth - 20
522 if contentwidth < 0 then
523 contentwidth = 0
524 end
525 content:SetWidth(contentwidth)
526 content.width = contentwidth
527
528 local maxtreewidth = math_min(400, width - 50)
529
530 if maxtreewidth > 100 and status.treewidth > maxtreewidth then
531 self:SetTreeWidth(maxtreewidth, status.treesizable)
532 end
533 treeframe:SetMaxResize(maxtreewidth, 1600)
534 end,
535
536 ["OnHeightSet"] = function(self, height)
537 local content = self.content
538 local contentheight = height - 20
539 if contentheight < 0 then
540 contentheight = 0
541 end
542 content:SetHeight(contentheight)
543 content.height = contentheight
544 end,
545
546 ["SetTreeWidth"] = function(self, treewidth, resizable)
547 if not resizable then
548 if type(treewidth) == 'number' then
549 resizable = false
550 elseif type(treewidth) == 'boolean' then
551 resizable = treewidth
552 treewidth = DEFAULT_TREE_WIDTH
553 else
554 resizable = false
555 treewidth = DEFAULT_TREE_WIDTH
556 end
557 end
558 self.treeframe:SetWidth(treewidth)
559 self.dragger:EnableMouse(resizable)
560
561 local status = self.status or self.localstatus
562 status.treewidth = treewidth
563 status.treesizable = resizable
564
565 -- recalculate the content width
566 if status.fullwidth then
567 self:OnWidthSet(status.fullwidth)
568 end
569 end,
570
571 ["LayoutFinished"] = function(self, width, height)
572 if self.noAutoHeight then return end
573 self:SetHeight((height or 0) + 20)
574 end
575 }
576
577 --[[-----------------------------------------------------------------------------
578 Constructor
579 -------------------------------------------------------------------------------]]
580 local PaneBackdrop = {
581 bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
582 edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
583 tile = true, tileSize = 16, edgeSize = 16,
584 insets = { left = 3, right = 3, top = 5, bottom = 3 }
585 }
586
587 local DraggerBackdrop = {
588 bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
589 edgeFile = nil,
590 tile = true, tileSize = 16, edgeSize = 0,
591 insets = { left = 3, right = 3, top = 7, bottom = 7 }
592 }
593
594 local function Constructor()
595 local num = AceGUI:GetNextWidgetNum(Type)
596 local frame = CreateFrame("Frame", nil, UIParent)
597
598 local treeframe = CreateFrame("Frame", nil, frame)
599 treeframe:SetPoint("TOPLEFT")
600 treeframe:SetPoint("BOTTOMLEFT")
601 treeframe:SetWidth(DEFAULT_TREE_WIDTH)
602 treeframe:EnableMouseWheel(true)
603 treeframe:SetBackdrop(PaneBackdrop)
604 treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
605 treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4)
606 treeframe:SetResizable(true)
607 treeframe:SetMinResize(100, 1)
608 treeframe:SetMaxResize(400, 1600)
609 treeframe:SetScript("OnUpdate", FirstFrameUpdate)
610 treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged)
611 treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel)
612
613 local dragger = CreateFrame("Frame", nil, treeframe)
614 dragger:SetWidth(8)
615 dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
616 dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
617 dragger:SetBackdrop(DraggerBackdrop)
618 dragger:SetBackdropColor(1, 1, 1, 0)
619 dragger:SetScript("OnEnter", Dragger_OnEnter)
620 dragger:SetScript("OnLeave", Dragger_OnLeave)
621 dragger:SetScript("OnMouseDown", Dragger_OnMouseDown)
622 dragger:SetScript("OnMouseUp", Dragger_OnMouseUp)
623
624 local scrollbar = CreateFrame("Slider", ("AceConfigDialogTreeGroup%dScrollBar"):format(num), treeframe, "UIPanelScrollBarTemplate")
625 scrollbar:SetScript("OnValueChanged", nil)
626 scrollbar:SetPoint("TOPRIGHT", -10, -26)
627 scrollbar:SetPoint("BOTTOMRIGHT", -10, 26)
628 scrollbar:SetMinMaxValues(0,0)
629 scrollbar:SetValueStep(1)
630 scrollbar:SetValue(0)
631 scrollbar:SetWidth(16)
632 scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
633
634 local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
635 scrollbg:SetAllPoints(scrollbar)
636 scrollbg:SetTexture(0,0,0,0.4)
637
638 local border = CreateFrame("Frame",nil,frame)
639 border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT")
640 border:SetPoint("BOTTOMRIGHT")
641 border:SetBackdrop(PaneBackdrop)
642 border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
643 border:SetBackdropBorderColor(0.4, 0.4, 0.4)
644
645 --Container Support
646 local content = CreateFrame("Frame", nil, border)
647 content:SetPoint("TOPLEFT", 10, -10)
648 content:SetPoint("BOTTOMRIGHT", -10, 10)
649
650 local widget = {
651 frame = frame,
652 lines = {},
653 levels = {},
654 buttons = {},
655 hasChildren = {},
656 localstatus = { groups = {}, scrollvalue = 0 },
657 filter = false,
658 treeframe = treeframe,
659 dragger = dragger,
660 scrollbar = scrollbar,
661 border = border,
662 content = content,
663 type = Type
664 }
665 for method, func in pairs(methods) do
666 widget[method] = func
667 end
668 treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget
669
670 return AceGUI:RegisterAsContainer(widget)
671 end
672
673 AceGUI:RegisterWidgetType(Type, Constructor, Version)