Asa@0: local AceGUI = LibStub("AceGUI-3.0") Asa@0: Asa@0: -- Lua APIs Asa@0: local pairs, ipairs, assert, type = pairs, ipairs, assert, type Asa@0: Asa@0: -- WoW APIs Asa@0: local CreateFrame, UIParent = CreateFrame, UIParent Asa@0: local _G = _G Asa@0: Asa@0: -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded Asa@0: -- List them here for Mikk's FindGlobals script Asa@0: -- GLOBALS: PanelTemplates_TabResize, PanelTemplates_SetDisabledTabState, PanelTemplates_SelectTab, PanelTemplates_DeselectTab Asa@0: Asa@0: ------------- Asa@0: -- Widgets -- Asa@0: ------------- Asa@0: --[[ Asa@0: Widgets must provide the following functions Asa@0: Acquire() - Called when the object is aquired, should set everything to a default hidden state Asa@0: Release() - Called when the object is Released, should remove any anchors and hide the Widget Asa@0: Asa@0: And the following members Asa@0: frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes Asa@0: type - the type of the object, same as the name given to :RegisterWidget() Asa@0: Asa@0: Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet Asa@0: It will be cleared automatically when a widget is released Asa@0: Placing values directly into a widget object should be avoided Asa@0: Asa@0: If the Widget can act as a container for other Widgets the following Asa@0: content - frame or derivitive that children will be anchored to Asa@0: Asa@0: The Widget can supply the following Optional Members Asa@0: Asa@0: Asa@0: ]] Asa@0: Asa@0: -------------------------- Asa@0: -- Tab Group -- Asa@0: -------------------------- Asa@0: Asa@0: do Asa@0: local Type = "TabGroup" Asa@0: local Version = 24 Asa@0: Asa@0: local PaneBackdrop = { Asa@0: bgFile = "Interface\\ChatFrame\\ChatFrameBackground", Asa@0: edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", Asa@0: tile = true, tileSize = 16, edgeSize = 16, Asa@0: insets = { left = 3, right = 3, top = 5, bottom = 3 } Asa@0: } Asa@0: Asa@0: local function OnAcquire(self) Asa@0: Asa@0: end Asa@0: Asa@0: local function OnRelease(self) Asa@0: self.frame:ClearAllPoints() Asa@0: self.frame:Hide() Asa@0: self.status = nil Asa@0: for k in pairs(self.localstatus) do Asa@0: self.localstatus[k] = nil Asa@0: end Asa@0: self.tablist = nil Asa@0: for _, tab in pairs(self.tabs) do Asa@0: tab:Hide() Asa@0: end Asa@0: self:SetTitle() Asa@0: end Asa@0: Asa@0: local function Tab_SetText(self, text) Asa@0: self:_SetText(text) Asa@0: local width = self.obj.frame.width or self.obj.frame:GetWidth() or 0 Asa@0: PanelTemplates_TabResize(self, 0, nil, width) Asa@0: end Asa@0: Asa@0: local function UpdateTabLook(self) Asa@0: if self.disabled then Asa@0: PanelTemplates_SetDisabledTabState(self) Asa@0: elseif self.selected then Asa@0: PanelTemplates_SelectTab(self) Asa@0: else Asa@0: PanelTemplates_DeselectTab(self) Asa@0: end Asa@0: end Asa@0: Asa@0: local function Tab_SetSelected(self, selected) Asa@0: self.selected = selected Asa@0: UpdateTabLook(self) Asa@0: end Asa@0: Asa@0: local function Tab_OnClick(self) Asa@0: if not (self.selected or self.disabled) then Asa@0: self.obj:SelectTab(self.value) Asa@0: end Asa@0: end Asa@0: Asa@0: local function Tab_SetDisabled(self, disabled) Asa@0: self.disabled = disabled Asa@0: UpdateTabLook(self) Asa@0: end Asa@0: Asa@0: local function Tab_OnEnter(this) Asa@0: local self = this.obj Asa@0: self:Fire("OnTabEnter", self.tabs[this.id].value, this) Asa@0: end Asa@0: Asa@0: local function Tab_OnLeave(this) Asa@0: local self = this.obj Asa@0: self:Fire("OnTabLeave", self.tabs[this.id].value, this) Asa@0: end Asa@0: Asa@0: local function Tab_OnShow(this) Asa@0: _G[this:GetName().."HighlightTexture"]:SetWidth(this:GetTextWidth() + 30) Asa@0: end Asa@0: Asa@0: local function CreateTab(self, id) Asa@0: local tabname = "AceGUITabGroup"..self.num.."Tab"..id Asa@0: local tab = CreateFrame("Button",tabname,self.border,"OptionsFrameTabButtonTemplate") Asa@0: tab.obj = self Asa@0: tab.id = id Asa@0: Asa@0: tab.text = _G[tabname .. "Text"] Asa@0: tab.text:ClearAllPoints() Asa@0: tab.text:SetPoint("LEFT", tab, "LEFT", 14, -3) Asa@0: tab.text:SetPoint("RIGHT", tab, "RIGHT", -12, -3) Asa@0: Asa@0: tab:SetScript("OnClick",Tab_OnClick) Asa@0: tab:SetScript("OnEnter",Tab_OnEnter) Asa@0: tab:SetScript("OnLeave",Tab_OnLeave) Asa@0: tab:SetScript("OnShow", Tab_OnShow) Asa@0: Asa@0: tab._SetText = tab.SetText Asa@0: tab.SetText = Tab_SetText Asa@0: tab.SetSelected = Tab_SetSelected Asa@0: tab.SetDisabled = Tab_SetDisabled Asa@0: Asa@0: return tab Asa@0: end Asa@0: Asa@0: local function SetTitle(self, text) Asa@0: self.titletext:SetText(text or "") Asa@0: if text and text ~= "" then Asa@0: self.alignoffset = 25 Asa@0: else Asa@0: self.alignoffset = 18 Asa@0: end Asa@0: self:BuildTabs() Asa@0: end Asa@0: Asa@0: -- called to set an external table to store status in Asa@0: local function SetStatusTable(self, status) Asa@0: assert(type(status) == "table") Asa@0: self.status = status Asa@0: end Asa@0: Asa@0: local function SelectTab(self, value) Asa@0: local status = self.status or self.localstatus Asa@0: Asa@0: local found Asa@0: for i, v in ipairs(self.tabs) do Asa@0: if v.value == value then Asa@0: v:SetSelected(true) Asa@0: found = true Asa@0: else Asa@0: v:SetSelected(false) Asa@0: end Asa@0: end Asa@0: status.selected = value Asa@0: if found then Asa@0: self:Fire("OnGroupSelected",value) Asa@0: end Asa@0: end Asa@0: Asa@0: local function SetTabs(self, tabs) Asa@0: self.tablist = tabs Asa@0: self:BuildTabs() Asa@0: end Asa@0: Asa@0: Asa@0: local widths = {} Asa@0: local rowwidths = {} Asa@0: local rowends = {} Asa@0: local function BuildTabs(self) Asa@0: local hastitle = (self.titletext:GetText() and self.titletext:GetText() ~= "") Asa@0: local status = self.status or self.localstatus Asa@0: local tablist = self.tablist Asa@0: local tabs = self.tabs Asa@0: Asa@0: if not tablist then return end Asa@0: Asa@0: local width = self.frame.width or self.frame:GetWidth() or 0 Asa@0: Asa@0: for i = #widths, 1, -1 do Asa@0: widths[i] = nil Asa@0: end Asa@0: for i = #rowwidths, 1, -1 do Asa@0: rowwidths[i] = nil Asa@0: end Asa@0: for i = #rowends, 1, -1 do Asa@0: rowends[i] = nil Asa@0: end Asa@0: Asa@0: --Place Text into tabs and get thier initial width Asa@0: for i, v in ipairs(tablist) do Asa@0: local tab = tabs[i] Asa@0: if not tab then Asa@0: tab = self:CreateTab(i) Asa@0: tabs[i] = tab Asa@0: end Asa@0: Asa@0: tab:Show() Asa@0: tab:SetText(v.text) Asa@0: tab:SetDisabled(v.disabled) Asa@0: tab.value = v.value Asa@0: Asa@0: widths[i] = tab:GetWidth() - 6 --tabs are anchored 10 pixels from the right side of the previous one to reduce spacing, but add a fixed 4px padding for the text Asa@0: end Asa@0: Asa@0: for i = (#tablist)+1, #tabs, 1 do Asa@0: tabs[i]:Hide() Asa@0: end Asa@0: Asa@0: --First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout Asa@0: local numtabs = #tablist Asa@0: local numrows = 1 Asa@0: local usedwidth = 0 Asa@0: Asa@0: for i = 1, #tablist do Asa@0: --If this is not the first tab of a row and there isn't room for it Asa@0: if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then Asa@0: rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px Asa@0: rowends[numrows] = i - 1 Asa@0: numrows = numrows + 1 Asa@0: usedwidth = 0 Asa@0: end Asa@0: usedwidth = usedwidth + widths[i] Asa@0: end Asa@0: rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px Asa@0: rowends[numrows] = #tablist Asa@0: Asa@0: --Fix for single tabs being left on the last row, move a tab from the row above if applicable Asa@0: if numrows > 1 then Asa@0: --if the last row has only one tab Asa@0: if rowends[numrows-1] == numtabs-1 then Asa@0: --if there are more than 2 tabs in the 2nd last row Asa@0: if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then Asa@0: --move 1 tab from the second last row to the last, if there is enough space Asa@0: if (rowwidths[numrows] + widths[numtabs-1]) <= width then Asa@0: rowends[numrows-1] = rowends[numrows-1] - 1 Asa@0: rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1] Asa@0: rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1] Asa@0: end Asa@0: end Asa@0: end Asa@0: end Asa@0: Asa@0: --anchor the rows as defined and resize tabs to fill thier row Asa@0: local starttab = 1 Asa@0: for row, endtab in ipairs(rowends) do Asa@0: local first = true Asa@0: for tabno = starttab, endtab do Asa@0: local tab = tabs[tabno] Asa@0: tab:ClearAllPoints() Asa@0: if first then Asa@0: tab:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 0, -(hastitle and 14 or 7)-(row-1)*20 ) Asa@0: first = false Asa@0: else Asa@0: tab:SetPoint("LEFT", tabs[tabno-1], "RIGHT", -10, 0) Asa@0: end Asa@0: end Asa@0: Asa@0: -- equal padding for each tab to fill the available width, Asa@0: -- if the used space is above 75% already Asa@0: local padding = 0 Asa@0: if not (numrows == 1 and rowwidths[1] < width*0.75) then Asa@0: padding = (width - rowwidths[row]) / (endtab - starttab+1) Asa@0: end Asa@0: Asa@0: for i = starttab, endtab do Asa@0: PanelTemplates_TabResize(tabs[i], padding + 4, nil, width) Asa@0: end Asa@0: starttab = endtab + 1 Asa@0: end Asa@0: Asa@0: self.borderoffset = (hastitle and 17 or 10)+((numrows)*20) Asa@0: self.border:SetPoint("TOPLEFT",self.frame,"TOPLEFT",1,-self.borderoffset) Asa@0: end Asa@0: Asa@0: local function BuildTabsOnUpdate(this) Asa@0: BuildTabs(this.obj) Asa@0: this:SetScript("OnUpdate", nil) Asa@0: end Asa@0: Asa@0: local function OnWidthSet(self, width) Asa@0: local content = self.content Asa@0: local contentwidth = width - 60 Asa@0: if contentwidth < 0 then Asa@0: contentwidth = 0 Asa@0: end Asa@0: content:SetWidth(contentwidth) Asa@0: content.width = contentwidth Asa@0: BuildTabs(self) Asa@0: self.frame:SetScript("OnUpdate", BuildTabsOnUpdate) Asa@0: end Asa@0: Asa@0: Asa@0: local function OnHeightSet(self, height) Asa@0: local content = self.content Asa@0: local contentheight = height - (self.borderoffset + 23) Asa@0: if contentheight < 0 then Asa@0: contentheight = 0 Asa@0: end Asa@0: content:SetHeight(contentheight) Asa@0: content.height = contentheight Asa@0: end Asa@0: Asa@0: local function LayoutFinished(self, width, height) Asa@0: if self.noAutoHeight then return end Asa@0: self:SetHeight((height or 0) + (self.borderoffset + 23)) Asa@0: end Asa@0: Asa@0: local function Constructor() Asa@0: local frame = CreateFrame("Frame",nil,UIParent) Asa@0: local self = {} Asa@0: self.type = Type Asa@0: Asa@0: self.num = AceGUI:GetNextWidgetNum(Type) Asa@0: Asa@0: self.localstatus = {} Asa@0: Asa@0: self.OnRelease = OnRelease Asa@0: self.OnAcquire = OnAcquire Asa@0: self.SetTitle = SetTitle Asa@0: self.CreateTab = CreateTab Asa@0: self.SelectTab = SelectTab Asa@0: self.BuildTabs = BuildTabs Asa@0: self.SetStatusTable = SetStatusTable Asa@0: self.SetTabs = SetTabs Asa@0: self.LayoutFinished = LayoutFinished Asa@0: self.frame = frame Asa@0: Asa@0: self.OnWidthSet = OnWidthSet Asa@0: self.OnHeightSet = OnHeightSet Asa@0: Asa@0: frame.obj = self Asa@0: Asa@0: frame:SetHeight(100) Asa@0: frame:SetWidth(100) Asa@0: frame:SetFrameStrata("FULLSCREEN_DIALOG") Asa@0: Asa@0: self.alignoffset = 18 Asa@0: Asa@0: local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") Asa@0: titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) Asa@0: titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) Asa@0: titletext:SetJustifyH("LEFT") Asa@0: titletext:SetHeight(18) Asa@0: titletext:SetText("") Asa@0: Asa@0: self.titletext = titletext Asa@0: Asa@0: local border = CreateFrame("Frame",nil,frame) Asa@0: self.border = border Asa@0: self.borderoffset = 27 Asa@0: border:SetPoint("TOPLEFT",frame,"TOPLEFT",1,-27) Asa@0: border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-1,3) Asa@0: Asa@0: border:SetBackdrop(PaneBackdrop) Asa@0: border:SetBackdropColor(0.1,0.1,0.1,0.5) Asa@0: border:SetBackdropBorderColor(0.4,0.4,0.4) Asa@0: Asa@0: self.tabs = {} Asa@0: Asa@0: --Container Support Asa@0: local content = CreateFrame("Frame",nil,border) Asa@0: self.content = content Asa@0: content.obj = self Asa@0: content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-7) Asa@0: content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,7) Asa@0: Asa@0: AceGUI:RegisterAsContainer(self) Asa@0: return self Asa@0: end Asa@0: Asa@0: AceGUI:RegisterWidgetType(Type,Constructor,Version) Asa@0: end