Automated update from: http://smariot.no-ip.org/translate
[QuestHelper.git] / libs / AceGUI-3.0 / widgets / AceGUIWidget-TreeGroup.lua
blobcd5ecbcc422fbf68f6dc3e7875100a1d35cd6683
1 local AceGUI = LibStub("AceGUI-3.0")
3 -- Lua APIs
4 local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
5 local math_min, math_max, floor = math.min, math.max, floor
6 local select, tremove, unpack = select, table.remove, unpack
8 -- WoW APIs
9 local CreateFrame, UIParent = CreateFrame, UIParent
11 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
12 -- List them here for Mikk's FindGlobals script
13 -- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE
15 -- Recycling functions
16 local new, del
18 local pool = setmetatable({},{__mode='k'})
19 function new()
20 local t = next(pool)
21 if t then
22 pool[t] = nil
23 return t
24 else
25 return {}
26 end
27 end
28 function del(t)
29 for k in pairs(t) do
30 t[k] = nil
31 end
32 pool[t] = true
33 end
34 end
36 --------------
37 -- TreeView --
38 --------------
41 local Type = "TreeGroup"
42 local Version = 23
44 local DEFAULT_TREE_WIDTH = 175
45 local DEFAULT_TREE_SIZABLE = true
47 local PaneBackdrop = {
48 bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
49 edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
50 tile = true, tileSize = 16, edgeSize = 16,
51 insets = { left = 3, right = 3, top = 5, bottom = 3 }
54 local DraggerBackdrop = {
55 bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
56 edgeFile = nil,
57 tile = true, tileSize = 16, edgeSize = 0,
58 insets = { left = 3, right = 3, top = 7, bottom = 7 }
61 local function OnAcquire(self)
62 self:SetTreeWidth(DEFAULT_TREE_WIDTH,DEFAULT_TREE_SIZABLE)
63 self:EnableButtonTooltips(true)
64 end
66 local function OnRelease(self)
68 self.frame:ClearAllPoints()
69 self.frame:Hide()
70 self.status = nil
71 for k, v in pairs(self.localstatus) do
72 if k == "groups" then
73 for k2 in pairs(v) do
74 v[k2] = nil
75 end
76 else
77 self.localstatus[k] = nil
78 end
79 end
80 self.localstatus.scrollvalue = 0
81 self.localstatus.treewidth = DEFAULT_TREE_WIDTH
82 self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
83 end
85 local function GetButtonParents(line)
86 local parent = line.parent
87 if parent and parent.value then
88 return parent.value, GetButtonParents(parent)
89 end
90 end
92 local function GetButtonUniqueValue(line)
93 local parent = line.parent
94 if parent and parent.value then
95 return GetButtonUniqueValue(parent).."\001"..line.value
96 else
97 return line.value
98 end
99 end
101 local function ButtonOnClick(this)
102 local self = this.obj
103 self:Fire("OnClick",this.uniquevalue, this.selected)
104 if not this.selected then
105 self:SetSelected(this.uniquevalue)
106 this.selected = true
107 this:LockHighlight()
108 self:RefreshTree()
110 AceGUI:ClearFocus()
113 local function ExpandOnClick(this)
114 local button = this.button
115 local self = button.obj
116 local status = (self.status or self.localstatus).groups
117 status[button.uniquevalue] = not status[button.uniquevalue]
118 self:RefreshTree()
121 local function ButtonOnDoubleClick(button)
122 local self = button.obj
123 local status = self.status or self.localstatus
124 local status = (self.status or self.localstatus).groups
125 status[button.uniquevalue] = not status[button.uniquevalue]
126 self:RefreshTree()
129 local function EnableButtonTooltips(self, enable)
130 self.enabletooltips = enable
133 local function Button_OnEnter(this)
134 local self = this.obj
135 self:Fire("OnButtonEnter", this.uniquevalue, this)
137 if self.enabletooltips then
138 GameTooltip:SetOwner(this, "ANCHOR_NONE")
139 GameTooltip:SetPoint("LEFT",this,"RIGHT")
140 GameTooltip:SetText(this.text:GetText() or "", 1, .82, 0, 1)
142 GameTooltip:Show()
146 local function Button_OnLeave(this)
147 local self = this.obj
148 self:Fire("OnButtonLeave", this.uniquevalue, this)
150 if self.enabletooltips then
151 GameTooltip:Hide()
156 local buttoncount = 1
157 local function CreateButton(self)
158 local button = CreateFrame("Button",("AceGUI30TreeButton%d"):format(buttoncount),self.treeframe, "OptionsListButtonTemplate")
159 buttoncount = buttoncount + 1
160 button.obj = self
162 local icon = button:CreateTexture(nil, "OVERLAY")
163 icon:SetWidth(14)
164 icon:SetHeight(14)
165 button.icon = icon
167 button:SetScript("OnClick",ButtonOnClick)
168 button:SetScript("OnDoubleClick", ButtonOnDoubleClick)
169 button:SetScript("OnEnter",Button_OnEnter)
170 button:SetScript("OnLeave",Button_OnLeave)
172 button.toggle.button = button
173 button.toggle:SetScript("OnClick",ExpandOnClick)
175 return button
178 local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
179 local self = button.obj
180 local toggle = button.toggle
181 local frame = self.frame
182 local text = treeline.text or ""
183 local icon = treeline.icon
184 local iconCoords = treeline.iconCoords
185 local level = treeline.level
186 local value = treeline.value
187 local uniquevalue = treeline.uniquevalue
188 local disabled = treeline.disabled
190 button.treeline = treeline
191 button.value = value
192 button.uniquevalue = uniquevalue
193 if selected then
194 button:LockHighlight()
195 button.selected = true
196 else
197 button:UnlockHighlight()
198 button.selected = false
200 local normalTexture = button:GetNormalTexture()
201 local line = button.line
202 button.level = level
203 if ( level == 1 ) then
204 button:SetNormalFontObject("GameFontNormal")
205 button:SetHighlightFontObject("GameFontHighlight")
206 button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
207 else
208 button:SetNormalFontObject("GameFontHighlightSmall")
209 button:SetHighlightFontObject("GameFontHighlightSmall")
210 button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2)
213 if disabled then
214 button:EnableMouse(false)
215 button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
216 else
217 button.text:SetText(text)
218 button:EnableMouse(true)
221 if icon then
222 button.icon:SetTexture(icon)
223 button.icon:SetPoint("LEFT", button, "LEFT", 8 * level, (level == 1) and 0 or 1)
224 else
225 button.icon:SetTexture(nil)
228 if iconCoords then
229 button.icon:SetTexCoord(unpack(iconCoords))
230 else
231 button.icon:SetTexCoord(0, 1, 0, 1)
234 if canExpand then
235 if not isExpanded then
236 toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
237 toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
238 else
239 toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
240 toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
242 toggle:Show()
243 else
244 toggle:Hide()
249 local function OnScrollValueChanged(this, value)
250 if this.obj.noupdate then return end
251 local self = this.obj
252 local status = self.status or self.localstatus
253 status.scrollvalue = value
254 self:RefreshTree()
255 AceGUI:ClearFocus()
258 -- called to set an external table to store status in
259 local function SetStatusTable(self, status)
260 assert(type(status) == "table")
261 self.status = status
262 if not status.groups then
263 status.groups = {}
265 if not status.scrollvalue then
266 status.scrollvalue = 0
268 if not status.treewidth then
269 status.treewidth = DEFAULT_TREE_WIDTH
271 if not status.treesizable then
272 status.treesizable = DEFAULT_TREE_SIZABLE
274 self:SetTreeWidth(status.treewidth,status.treesizable)
275 self:RefreshTree()
278 --sets the tree to be displayed
279 --[[
280 example tree
282 Alpha
283 Bravo
284 -Charlie
285 -Delta
286 -Echo
287 Foxtrot
289 tree = {
291 value = "A",
292 text = "Alpha"
295 value = "B",
296 text = "Bravo",
297 children = {
299 value = "C",
300 text = "Charlie"
303 value = "D",
304 text = "Delta"
305 children = {
307 value = "E",
308 text = "Echo"
315 value = "F",
316 text = "Foxtrot"
320 local function SetTree(self, tree, filter)
321 self.filter = filter
322 if tree then
323 assert(type(tree) == "table")
325 self.tree = tree
326 self:RefreshTree()
329 local function ShouldDisplayLevel(tree)
330 local result = false
331 for k, v in ipairs(tree) do
332 if v.children == nil and v.visible ~= false then
333 result = true
334 elseif v.children then
335 result = result or ShouldDisplayLevel(v.children)
337 if result then return result end
339 return false
342 local function addLine(self, v, tree, level, parent)
343 local line = new()
344 line.value = v.value
345 line.text = v.text
346 line.icon = v.icon
347 line.iconCoords = v.iconCoords
348 line.disabled = v.disabled
349 line.tree = tree
350 line.level = level
351 line.parent = parent
352 line.visible = v.visible
353 line.uniquevalue = GetButtonUniqueValue(line)
354 if v.children then
355 line.hasChildren = true
356 else
357 line.hasChildren = nil
358 end
359 self.lines[#self.lines+1] = line
360 return line
363 local function BuildLevel(self, tree, level, parent)
364 local groups = (self.status or self.localstatus).groups
365 local hasChildren = self.hasChildren
367 for i, v in ipairs(tree) do
368 if v.children then
369 if not self.filter or ShouldDisplayLevel(v.children) then
370 local line = addLine(self, v, tree, level, parent)
371 if groups[line.uniquevalue] then
372 self:BuildLevel(v.children, level+1, line)
375 elseif v.visible ~= false or not self.filter then
376 addLine(self, v, tree, level, parent)
381 --fire an update after one frame to catch the treeframes height
382 local function FirstFrameUpdate(this)
383 local self = this.obj
384 this:SetScript("OnUpdate",nil)
385 self:RefreshTree()
388 local function ResizeUpdate(this)
389 this.obj:RefreshTree()
392 local function RefreshTree(self)
393 local buttons = self.buttons
394 local lines = self.lines
396 for i, v in ipairs(buttons) do
397 v:Hide()
399 while lines[1] do
400 local t = tremove(lines)
401 for k in pairs(t) do
402 t[k] = nil
404 del(t)
405 end
407 if not self.tree then return end
408 --Build the list of visible entries from the tree and status tables
409 local status = self.status or self.localstatus
410 local groupstatus = status.groups
411 local tree = self.tree
413 local treeframe = self.treeframe
415 self:BuildLevel(tree, 1)
417 local numlines = #lines
419 local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
421 local first, last
423 if numlines <= maxlines then
424 --the whole tree fits in the frame
425 status.scrollvalue = 0
426 self:ShowScroll(false)
427 first, last = 1, numlines
428 else
429 self:ShowScroll(true)
430 --scrolling will be needed
431 self.noupdate = true
432 self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
433 --check if we are scrolled down too far
434 if numlines - status.scrollvalue < maxlines then
435 status.scrollvalue = numlines - maxlines
436 self.scrollbar:SetValue(status.scrollvalue)
438 self.noupdate = nil
439 first, last = status.scrollvalue+1, status.scrollvalue + maxlines
442 local buttonnum = 1
443 for i = first, last do
444 local line = lines[i]
445 local button = buttons[buttonnum]
446 if not button then
447 button = self:CreateButton()
449 buttons[buttonnum] = button
450 button:SetParent(treeframe)
451 button:SetFrameLevel(treeframe:GetFrameLevel()+1)
452 button:ClearAllPoints()
453 if i == 1 then
454 if self.showscroll then
455 button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
456 button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10)
457 else
458 button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
459 button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10)
461 else
462 button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0)
463 button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0)
467 UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] )
468 button:Show()
469 buttonnum = buttonnum + 1
474 local function SetSelected(self, value)
475 local status = self.status or self.localstatus
476 if status.selected ~= value then
477 status.selected = value
478 self:Fire("OnGroupSelected", value)
482 local function BuildUniqueValue(...)
483 local n = select('#', ...)
484 if n == 1 then
485 return ...
486 else
487 return (...).."\001"..BuildUniqueValue(select(2,...))
491 local function Select(self, uniquevalue, ...)
492 self.filter = false
493 local status = self.status or self.localstatus
494 local groups = status.groups
495 for i = 1, select('#', ...) do
496 groups[BuildUniqueValue(select(i, ...))] = true
498 status.selected = uniquevalue
499 self:RefreshTree()
500 self:Fire("OnGroupSelected", uniquevalue)
503 local function SelectByPath(self, ...)
504 self:Select(BuildUniqueValue(...), ...)
507 --Selects a tree node by UniqueValue
508 local function SelectByValue(self, uniquevalue)
509 self:Select(uniquevalue, ("\001"):split(uniquevalue))
513 local function ShowScroll(self, show)
514 self.showscroll = show
515 if show then
516 self.scrollbar:Show()
517 if self.buttons[1] then
518 self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
520 else
521 self.scrollbar:Hide()
522 if self.buttons[1] then
523 self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
528 local function OnWidthSet(self, width)
529 local content = self.content
530 local treeframe = self.treeframe
531 local status = self.status or self.localstatus
532 status.fullwidth = width
534 local contentwidth = width - status.treewidth - 20
535 if contentwidth < 0 then
536 contentwidth = 0
538 content:SetWidth(contentwidth)
539 content.width = contentwidth
541 local maxtreewidth = math_min(400, width - 50)
543 if maxtreewidth > 100 and status.treewidth > maxtreewidth then
544 self:SetTreeWidth(maxtreewidth, status.treesizable)
546 treeframe:SetMaxResize(maxtreewidth,1600)
550 local function OnHeightSet(self, height)
551 local content = self.content
552 local contentheight = height - 20
553 if contentheight < 0 then
554 contentheight = 0
556 content:SetHeight(contentheight)
557 content.height = contentheight
561 local function TreeOnMouseWheel(this, delta)
562 local self = this.obj
563 if self.showscroll then
564 local scrollbar = self.scrollbar
565 local min, max = scrollbar:GetMinMaxValues()
566 local value = scrollbar:GetValue()
567 local newvalue = math_min(max,math_max(min,value - delta))
568 if value ~= newvalue then
569 scrollbar:SetValue(newvalue)
574 local function SetTreeWidth(self, treewidth, resizable)
575 if not resizable then
576 if type(treewidth) == 'number' then
577 resizable = false
578 elseif type(treewidth) == 'boolean' then
579 resizable = treewidth
580 treewidth = DEFAULT_TREE_WIDTH
581 else
582 resizable = false
583 treewidth = DEFAULT_TREE_WIDTH
586 self.treeframe:SetWidth(treewidth)
587 self.dragger:EnableMouse(resizable)
589 local status = self.status or self.localstatus
590 status.treewidth = treewidth
591 status.treesizable = resizable
593 -- recalculate the content width
594 if status.fullwidth then
595 self:OnWidthSet(status.fullwidth)
599 local function draggerLeave(this)
600 this:SetBackdropColor(1, 1, 1, 0)
603 local function draggerEnter(this)
604 this:SetBackdropColor(1, 1, 1, 0.8)
607 local function draggerDown(this)
608 local treeframe = this:GetParent()
609 treeframe:StartSizing("RIGHT")
612 local function draggerUp(this)
613 local treeframe = this:GetParent()
614 local self = treeframe.obj
615 local frame = treeframe:GetParent()
616 treeframe:StopMovingOrSizing()
617 --treeframe:SetScript("OnUpdate", nil)
618 treeframe:SetUserPlaced(false)
619 --Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
620 treeframe:SetHeight(0)
621 treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
622 treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
624 local status = self.status or self.localstatus
625 status.treewidth = treeframe:GetWidth()
627 treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth())
628 -- recalculate the content width
629 treeframe.obj:OnWidthSet(status.fullwidth)
630 -- update the layout of the content
631 treeframe.obj:DoLayout()
634 local function LayoutFinished(self, width, height)
635 if self.noAutoHeight then return end
636 self:SetHeight((height or 0) + 20)
639 local createdcount = 0
640 local function Constructor()
641 local frame = CreateFrame("Frame",nil,UIParent)
642 local self = {}
643 self.type = Type
644 self.lines = {}
645 self.levels = {}
646 self.buttons = {}
647 self.hasChildren = {}
648 self.localstatus = {}
649 self.localstatus.groups = {}
650 self.filter = false
652 local treeframe = CreateFrame("Frame",nil,frame)
653 treeframe.obj = self
654 treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
655 treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
656 treeframe:SetWidth(DEFAULT_TREE_WIDTH)
657 treeframe:SetScript("OnUpdate",FirstFrameUpdate)
658 treeframe:SetScript("OnSizeChanged",ResizeUpdate)
660 treeframe:EnableMouseWheel(true)
661 treeframe:SetScript("OnMouseWheel", TreeOnMouseWheel)
662 treeframe:SetBackdrop(PaneBackdrop)
663 treeframe:SetBackdropColor(0.1,0.1,0.1,0.5)
664 treeframe:SetBackdropBorderColor(0.4,0.4,0.4)
666 treeframe:SetResizable(true)
667 treeframe:SetMinResize(100, 1)
668 treeframe:SetMaxResize(400,1600)
669 local dragger = CreateFrame("Frame", nil, treeframe)
670 dragger:SetWidth(8)
671 dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
672 dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
673 dragger:SetBackdrop(DraggerBackdrop)
674 dragger:SetBackdropColor(1, 1, 1, 0)
675 dragger:SetScript("OnMouseDown", draggerDown)
676 dragger:SetScript("OnMouseUp", draggerUp)
677 dragger:SetScript("OnEnter", draggerEnter)
678 dragger:SetScript("OnLeave", draggerLeave)
680 self.dragger = dragger
681 self.treeframe = treeframe
682 self.OnRelease = OnRelease
683 self.OnAcquire = OnAcquire
685 self.SetTree = SetTree
686 self.SetTreeWidth = SetTreeWidth
687 self.RefreshTree = RefreshTree
688 self.SetStatusTable = SetStatusTable
689 self.BuildLevel = BuildLevel
690 self.CreateButton = CreateButton
691 self.SetSelected = SetSelected
692 self.ShowScroll = ShowScroll
693 self.SetStatusTable = SetStatusTable
694 self.Select = Select
695 self.SelectByValue = SelectByValue
696 self.SelectByPath = SelectByPath
697 self.OnWidthSet = OnWidthSet
698 self.OnHeightSet = OnHeightSet
699 self.EnableButtonTooltips = EnableButtonTooltips
700 --self.Filter = Filter
701 self.LayoutFinished = LayoutFinished
703 self.frame = frame
704 frame.obj = self
706 createdcount = createdcount + 1
707 local scrollbar = CreateFrame("Slider",("AceConfigDialogTreeGroup%dScrollBar"):format(createdcount),treeframe,"UIPanelScrollBarTemplate")
708 self.scrollbar = scrollbar
709 local scrollbg = scrollbar:CreateTexture(nil,"BACKGROUND")
710 scrollbg:SetAllPoints(scrollbar)
711 scrollbg:SetTexture(0,0,0,0.4)
712 scrollbar.obj = self
713 self.noupdate = true
714 scrollbar:SetPoint("TOPRIGHT",treeframe,"TOPRIGHT",-10,-26)
715 scrollbar:SetPoint("BOTTOMRIGHT",treeframe,"BOTTOMRIGHT",-10,26)
716 scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
717 scrollbar:SetMinMaxValues(0,0)
718 self.localstatus.scrollvalue = 0
719 scrollbar:SetValueStep(1)
720 scrollbar:SetValue(0)
721 scrollbar:SetWidth(16)
722 self.noupdate = nil
724 local border = CreateFrame("Frame",nil,frame)
725 self.border = border
726 border:SetPoint("TOPLEFT",treeframe,"TOPRIGHT", 0,0)
727 border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
729 border:SetBackdrop(PaneBackdrop)
730 border:SetBackdropColor(0.1,0.1,0.1,0.5)
731 border:SetBackdropBorderColor(0.4,0.4,0.4)
733 --Container Support
734 local content = CreateFrame("Frame",nil,border)
735 self.content = content
736 content.obj = self
737 content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10)
738 content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10)
740 AceGUI:RegisterAsContainer(self)
741 --AceGUI:RegisterAsWidget(self)
742 return self
745 AceGUI:RegisterWidgetType(Type,Constructor,Version)