WoWInterface SVN oUF_Smee2

[/] [trunk/] [oUF_Smee2_Config/] [Libs/] [AceGUI-3.0/] [widgets/] [AceGUIWidget-TreeGroup.lua] - Rev 4

Compare with Previous | Blame | View Log

local AceGUI = LibStub("AceGUI-3.0")

-- Recycling functions
local new, del
do
        local pool = setmetatable({},{__mode='k'})
        function new()
                local t = next(pool)
                if t then
                        pool[t] = nil
                        return t
                else
                        return {}
                end
        end
        function del(t)
                for k in pairs(t) do
                        t[k] = nil
                end     
                pool[t] = true
        end
end

--------------
-- TreeView --
--------------

do
        local Type = "TreeGroup"
        local Version = 16
        
        local DEFAULT_TREE_WIDTH = 175
        local DEFAULT_TREE_SIZABLE = true

        local PaneBackdrop  = {
                bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                tile = true, tileSize = 16, edgeSize = 16,
                insets = { left = 3, right = 3, top = 5, bottom = 3 }
        }

    local DraggerBackdrop  = {
                bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
                edgeFile = nil,
                tile = true, tileSize = 16, edgeSize = 0,
                insets = { left = 3, right = 3, top = 7, bottom = 7 }
        }
    
        local function OnAcquire(self)
                self:SetTreeWidth(DEFAULT_TREE_WIDTH,DEFAULT_TREE_SIZABLE)
                self:EnableButtonTooltips(true)
        end
        
        local function OnRelease(self)
        
                self.frame:ClearAllPoints()
                self.frame:Hide()
                self.status = nil
                for k, v in pairs(self.localstatus) do
                        if k == "groups" then
                                for k2 in pairs(v) do
                                        v[k2] = nil
                                end
                        else
                                self.localstatus[k] = nil
                        end
                end
                self.localstatus.scrollvalue = 0
                self.localstatus.treewidth = DEFAULT_TREE_WIDTH
                self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
        end
        
        local function GetButtonParents(line)
                local parent = line.parent
                if parent and parent.value then
                        return parent.value, GetButtonParents(parent)
                end
        end
        
        local function GetButtonUniqueValue(line)
                local parent = line.parent
                if parent and parent.value then
                        return GetButtonUniqueValue(parent).."\001"..line.value
                else
                        return line.value
                end             
        end
        
        local function ButtonOnClick(this)
                local self = this.obj
                self:Fire("OnClick",this.uniquevalue, this.selected)
                if not this.selected then
                        self:SetSelected(this.uniquevalue)
                        this.selected = true
                        this:LockHighlight()
                        self:RefreshTree()
                end
                AceGUI:ClearFocus()
        end
        
        local function ExpandOnClick(this)
                local button = this.button
                local self = button.obj
                local status = (self.status or self.localstatus).groups
                status[button.uniquevalue] = not status[button.uniquevalue]
                self:RefreshTree()
        end
        
        local function ButtonOnDoubleClick(button)
                local self = button.obj
                local status = self.status or self.localstatus
                local status = (self.status or self.localstatus).groups
                status[button.uniquevalue] = not status[button.uniquevalue]
                self:RefreshTree()
        end
    
    local function EnableButtonTooltips(self, enable)
        self.enabletooltips = enable
    end
    
    local function Button_OnEnter(this)
        local self = this.obj
        self:Fire("OnButtonEnter", this.uniquevalue, this)
        
        if self.enabletooltips then
                GameTooltip:SetOwner(this, "ANCHOR_NONE")
                GameTooltip:SetPoint("LEFT",this,"RIGHT")
                GameTooltip:SetText(this.text:GetText(), 1, .82, 0, 1)
            
                        GameTooltip:Show()
                end
        end
        
        local function Button_OnLeave(this)
                local self = this.obj
                self:Fire("OnButtonLeave", this.uniquevalue, this)
                
                if self.enabletooltips then
                        GameTooltip:Hide()
                end
        end
    
    
        local buttoncount = 1
        local function CreateButton(self)
                local button = CreateFrame("Button",("AceGUI30TreeButton%d"):format(buttoncount),self.treeframe, "OptionsListButtonTemplate")
                buttoncount = buttoncount + 1
                button.obj = self

                button:SetScript("OnClick",ButtonOnClick)
                button:SetScript("OnDoubleClick", ButtonOnDoubleClick)
                button:SetScript("OnEnter",Button_OnEnter)
                button:SetScript("OnLeave",Button_OnLeave)

                button.toggle.button = button
                button.toggle:SetScript("OnClick",ExpandOnClick)
                
                return button
        end

        local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
                local self = button.obj
                local toggle = button.toggle
                local frame = self.frame
                local text = treeline.text or ""
                local level = treeline.level
                local value = treeline.value
                local uniquevalue = treeline.uniquevalue
                local disabled = treeline.disabled
                
                button.treeline = treeline
                button.value = value
                button.uniquevalue = uniquevalue
                if selected then
                        button:LockHighlight()
                        button.selected = true
                else
                        button:UnlockHighlight()
                        button.selected = false
                end
                local normalText = button.text
                local normalTexture = button:GetNormalTexture()
                local line = button.line
                button.level = level
                if ( level == 1 ) then
                        button:SetNormalFontObject("GameFontNormal")
                        button:SetHighlightFontObject("GameFontHighlight")
                        button.text:SetPoint("LEFT", 8, 2)
                else
                        button:SetNormalFontObject("GameFontHighlightSmall")
                        button:SetHighlightFontObject("GameFontHighlightSmall")
                        button.text:SetPoint("LEFT", 8 * level, 2)
                end
                
                if disabled then
                        button:EnableMouse(false)
                        button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
                else
                        button.text:SetText(text)
                        button:EnableMouse(true)
                end
                
                if canExpand then
                        if not isExpanded then
                                toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
                                toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
                        else
                                toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
                                toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
                        end
                        toggle:Show()
                else
                        toggle:Hide()
                end
        end

        
        local function OnScrollValueChanged(this, value)
                if this.obj.noupdate then return end
                local self = this.obj
                local status = self.status or self.localstatus
                status.scrollvalue = value
                self:RefreshTree()
                AceGUI:ClearFocus()
        end
        
        -- called to set an external table to store status in
        local function SetStatusTable(self, status)
                assert(type(status) == "table")
                self.status = status
                if not status.groups then
                        status.groups = {}
                end
                if not status.scrollvalue then
                        status.scrollvalue = 0
                end
                if not status.treewidth then
                        status.treewidth = DEFAULT_TREE_WIDTH
                end
                if not status.treesizable then
                        status.treesizable = DEFAULT_TREE_SIZABLE
                end
                self:SetTreeWidth(status.treewidth,status.treesizable)
                self:RefreshTree()
        end

        --sets the tree to be displayed
        --[[
                example tree
                
                Alpha
                Bravo
                  -Charlie
                  -Delta
                        -Echo
                Foxtrot
                
                tree = { 
                        { 
                                value = "A",
                                text = "Alpha"
                        },
                        {
                                value = "B",
                                text = "Bravo",
                                children = {
                                        { 
                                                value = "C", 
                                                text = "Charlie"
                                        },
                                        {
                                                value = "D",    
                                                text = "Delta"
                                                children = { 
                                                        { 
                                                                value = "E",
                                                                text = "Echo"
                                                        } 
                                                } 
                                        }
                                }
                        },
                        { 
                                value = "F", 
                                text = "Foxtrot" 
                        },
                }
        ]]
        local function SetTree(self, tree)
                if tree then 
                        assert(type(tree) == "table") 
                end
                self.tree = tree
                self:RefreshTree()
        end
        
        local function BuildLevel(self, tree, level, parent)
                local lines = self.lines

                local status = (self.status or self.localstatus)
                local groups = status.groups
                local hasChildren = self.hasChildren
                
                for i, v in ipairs(tree) do
                        local line = new()
                        lines[#lines+1] = line
                        line.value = v.value
                        line.text = v.text
                        line.disabled = v.disabled
                        line.tree = tree
                        line.level = level
                        line.parent = parent
                        line.uniquevalue = GetButtonUniqueValue(line)
                        
                        if v.children then
                                line.hasChildren = true
                        else
                                line.hasChildren = nil
                        end
                        if v.children then
                                if groups[line.uniquevalue] then
                                        self:BuildLevel(v.children, level+1, line)
                                end
                        end
                end
        end
        
        --fire an update after one frame to catch the treeframes height
        local function FirstFrameUpdate(this)
                local self = this.obj
                this:SetScript("OnUpdate",nil)
                self:RefreshTree()
        end
        
        local function ResizeUpdate(this)
                this.obj:RefreshTree()
        end
        
        local function RefreshTree(self)
                local buttons = self.buttons
                local lines = self.lines
                
                for i, v in ipairs(buttons) do
                        v:Hide()
                end
                while lines[1] do
                        local t = tremove(lines)
                        for k in pairs(t) do
                                t[k] = nil
                        end
                        del(t)
                end             
                
                if not self.tree then return end
                --Build the list of visible entries from the tree and status tables
                local status = self.status or self.localstatus
                local groupstatus = status.groups
                local tree = self.tree

                local treeframe = self.treeframe

                self:BuildLevel(tree, 1)
                
                local numlines = #lines
                
                local maxlines = (math.floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
                
                local first, last
                
                if numlines <= maxlines then
                        --the whole tree fits in the frame
                        status.scrollvalue = 0
                        self:ShowScroll(false)
                        first, last = 1, numlines
                else
                        self:ShowScroll(true)
                        --scrolling will be needed
                        self.noupdate = true
                        self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
                        --check if we are scrolled down too far
                        if numlines - status.scrollvalue < maxlines then
                                status.scrollvalue = numlines - maxlines
                                self.scrollbar:SetValue(status.scrollvalue)
                        end
                        self.noupdate = nil
                        first, last = status.scrollvalue+1, status.scrollvalue + maxlines
                end
                
                local buttonnum = 1
                for i = first, last do
                        local line = lines[i]
                        local button = buttons[buttonnum]
                        if not button then
                                button = self:CreateButton()

                                buttons[buttonnum] = button
                                button:SetParent(treeframe)
                                button:SetFrameLevel(treeframe:GetFrameLevel()+1)
                                button:ClearAllPoints()
                                if i == 1 then
                                        if self.showscroll then
                                                button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
                                                button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10)
                                        else
                                                button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
                                                button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10)
                                        end
                                else
                                        button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0)
                                        button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0)
                                end
                        end

                        UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] )
                        button:Show()
                        buttonnum = buttonnum + 1
                end

        end
        
        local function SetSelected(self, value)
                local status = self.status or self.localstatus
                if status.selected ~= value then
                        status.selected = value
                        self:Fire("OnGroupSelected", value)
                end
        end
        
        local function BuildUniqueValue(...)
                local n = select('#', ...)
                if n == 1 then
                        return ...
                else
                        return (...).."\001"..BuildUniqueValue(select(2,...))
                end
        end
        
        local function Select(self, uniquevalue, ...)
                local status = self.status or self.localstatus
                local groups = status.groups
                for i = 1, select('#', ...) do
                        groups[BuildUniqueValue(select(i, ...))] = true
                end
                status.selected = uniquevalue
                self:RefreshTree()
                self:Fire("OnGroupSelected", uniquevalue)
        end
        
        local function SelectByPath(self, ...)
                 self:Select(BuildUniqueValue(...), ...)
        end
        
        --Selects a tree node by UniqueValue
        local function SelectByValue(self, uniquevalue)
                self:Select(uniquevalue,string.split("\001", uniquevalue))
        end
        

        local function ShowScroll(self, show)
                self.showscroll = show
                if show then
                        self.scrollbar:Show()
                        if self.buttons[1] then
                                self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
                        end
                else
                        self.scrollbar:Hide()
                        if self.buttons[1] then
                                self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
                        end
                end
        end
        
        local function OnWidthSet(self, width)
                local content = self.content
                local treeframe = self.treeframe
                local status = self.status or self.localstatus
                
                local contentwidth = width - status.treewidth - 20
                if contentwidth < 0 then
                        contentwidth = 0
                end
                content:SetWidth(contentwidth)
                content.width = contentwidth
                
                local maxtreewidth = math.min(400, width - 50)
                
                if maxtreewidth > 100 and status.treewidth > maxtreewidth then
                        self:SetTreeWidth(maxtreewidth, status.treesizable)
                end
                treeframe:SetMaxResize(maxtreewidth,1600)
        end
        
        
        local function OnHeightSet(self, height)
                local content = self.content
                local contentheight = height - 20
                if contentheight < 0 then
                        contentheight = 0
                end
                content:SetHeight(contentheight)
                content.height = contentheight
        end
        

        local function TreeOnMouseWheel(this, delta)
                local self = this.obj
                if self.showscroll then
                        local scrollbar = self.scrollbar
                        local min, max = scrollbar:GetMinMaxValues()
                        local value = scrollbar:GetValue()
                        local newvalue = math.min(max,math.max(min,value - delta))
                        if value ~= newvalue then
                                scrollbar:SetValue(newvalue)
                        end
                end
        end
        
        local function SetTreeWidth(self, treewidth, resizable)
                if not resizable then
                        if type(treewidth) == 'number' then
                                resizable = false
                        elseif type(treewidth) == 'boolean' then
                                resizable = treewidth
                                treewidth = DEFAULT_TREE_WIDTH
                        else
                                resizable = false
                                treewidth = DEFAULT_TREE_WIDTH 
                        end
                end
                self.treeframe:SetWidth(treewidth)
                self.dragger:EnableMouse(resizable)
                
                local status = self.status or self.localstatus
                status.treewidth = treewidth
                status.treesizable = resizable
        end
        
        local function draggerLeave(this)
                this:SetBackdropColor(1, 1, 1, 0)
        end
        
        local function draggerEnter(this)
                this:SetBackdropColor(1, 1, 1, 0.8)
        end
        
        local function draggerDown(this)
                local treeframe = this:GetParent()
                treeframe:StartSizing("RIGHT")
        end
        
        local function draggerUp(this)
                local treeframe = this:GetParent()
                local self = treeframe.obj
                local frame = treeframe:GetParent()
                treeframe:StopMovingOrSizing()
                --treeframe:SetScript("OnUpdate", nil)
                treeframe:SetUserPlaced(false)
                --Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
                treeframe:SetHeight(0)
                treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
                treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
                treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth())
                
                local status = self.status or self.localstatus
                status.treewidth = treeframe:GetWidth()
        end

        local createdcount = 0
        local function Constructor()
                local frame = CreateFrame("Frame",nil,UIParent)
                local self = {}
                self.type = Type
                self.lines = {}
                self.levels = {}
                self.buttons = {}
                self.hasChildren = {}
                self.localstatus = {}
                self.localstatus.groups = {}
                
                local treeframe = CreateFrame("Frame",nil,frame)
                treeframe.obj = self
                treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
                treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0)
                treeframe:SetWidth(DEFAULT_TREE_WIDTH)
                treeframe:SetScript("OnUpdate",FirstFrameUpdate)
                treeframe:SetScript("OnSizeChanged",ResizeUpdate)
                
                treeframe:EnableMouseWheel(true)
                treeframe:SetScript("OnMouseWheel", TreeOnMouseWheel)
                treeframe:SetBackdrop(PaneBackdrop)
                treeframe:SetBackdropColor(0.1,0.1,0.1,0.5)
                treeframe:SetBackdropBorderColor(0.4,0.4,0.4)
                
                treeframe:SetResizable(true)
                treeframe:SetMinResize(100, 1)
                treeframe:SetMaxResize(400,1600)
                local dragger = CreateFrame("Frame", nil, treeframe)
                dragger:SetWidth(8)
                dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
                dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
                dragger:SetBackdrop(DraggerBackdrop)
                dragger:SetBackdropColor(1, 1, 1, 0)
                dragger:SetScript("OnMouseDown", draggerDown)
                dragger:SetScript("OnMouseUp", draggerUp)
                dragger:SetScript("OnEnter", draggerEnter)
                dragger:SetScript("OnLeave", draggerLeave)
                
                self.dragger = dragger
                self.treeframe = treeframe
                self.OnRelease = OnRelease
                self.OnAcquire = OnAcquire
                
                self.SetTree = SetTree
                self.SetTreeWidth = SetTreeWidth
                self.RefreshTree = RefreshTree
                self.SetStatusTable = SetStatusTable
                self.BuildLevel = BuildLevel
                self.CreateButton = CreateButton
                self.SetSelected = SetSelected
                self.ShowScroll = ShowScroll
                self.SetStatusTable = SetStatusTable
                self.Select = Select
                self.SelectByValue = SelectByValue
                self.SelectByPath = SelectByPath
                self.OnWidthSet = OnWidthSet
                self.OnHeightSet = OnHeightSet          
                self.EnableButtonTooltips = EnableButtonTooltips
                
                self.frame = frame
                frame.obj = self

                createdcount = createdcount + 1
                local scrollbar = CreateFrame("Slider",("AceConfigDialogTreeGroup%dScrollBar"):format(createdcount),treeframe,"UIPanelScrollBarTemplate")
                self.scrollbar = scrollbar
                local scrollbg = scrollbar:CreateTexture(nil,"BACKGROUND")
                scrollbg:SetAllPoints(scrollbar)
                scrollbg:SetTexture(0,0,0,0.4)
                scrollbar.obj = self
                self.noupdate = true
                scrollbar:SetPoint("TOPRIGHT",treeframe,"TOPRIGHT",-10,-26)
                scrollbar:SetPoint("BOTTOMRIGHT",treeframe,"BOTTOMRIGHT",-10,26)
                scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
                scrollbar:SetMinMaxValues(0,0)
                self.localstatus.scrollvalue = 0
                scrollbar:SetValueStep(1)
                scrollbar:SetValue(0)
                scrollbar:SetWidth(16)
                self.noupdate = nil

                local border = CreateFrame("Frame",nil,frame)
                self.border = border
                border:SetPoint("TOPLEFT",treeframe,"TOPRIGHT", 0,0)
                border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0)
                
                border:SetBackdrop(PaneBackdrop)
                border:SetBackdropColor(0.1,0.1,0.1,0.5)
                border:SetBackdropBorderColor(0.4,0.4,0.4)
                
                --Container Support
                local content = CreateFrame("Frame",nil,border)
                self.content = content
                content.obj = self
                content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10)
                content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10)
                
                AceGUI:RegisterAsContainer(self)
                --AceGUI:RegisterAsWidget(self)
                return self
        end
        
        AceGUI:RegisterWidgetType(Type,Constructor,Version)
end

Compare with Previous | Blame