yellowfive@57: --[[----------------------------------------------------------------------------- yellowfive@57: TabGroup Container yellowfive@57: Container that uses tabs on top to switch between groups. yellowfive@57: -------------------------------------------------------------------------------]] yellowfive@57: local Type, Version = "TabGroup", 35 yellowfive@57: local AceGUI = LibStub and LibStub("AceGUI-3.0", true) yellowfive@57: if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end yellowfive@57: yellowfive@57: -- Lua APIs yellowfive@57: local pairs, ipairs, assert, type, wipe = pairs, ipairs, assert, type, wipe yellowfive@57: yellowfive@57: -- WoW APIs yellowfive@57: local PlaySound = PlaySound yellowfive@57: local CreateFrame, UIParent = CreateFrame, UIParent yellowfive@57: local _G = _G yellowfive@57: yellowfive@57: -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded yellowfive@57: -- List them here for Mikk's FindGlobals script yellowfive@57: -- GLOBALS: PanelTemplates_TabResize, PanelTemplates_SetDisabledTabState, PanelTemplates_SelectTab, PanelTemplates_DeselectTab yellowfive@57: yellowfive@57: -- local upvalue storage used by BuildTabs yellowfive@57: local widths = {} yellowfive@57: local rowwidths = {} yellowfive@57: local rowends = {} yellowfive@57: yellowfive@57: --[[----------------------------------------------------------------------------- yellowfive@57: Support functions yellowfive@57: -------------------------------------------------------------------------------]] yellowfive@57: local function UpdateTabLook(frame) yellowfive@57: if frame.disabled then yellowfive@57: PanelTemplates_SetDisabledTabState(frame) yellowfive@57: elseif frame.selected then yellowfive@57: PanelTemplates_SelectTab(frame) yellowfive@57: else yellowfive@57: PanelTemplates_DeselectTab(frame) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: local function Tab_SetText(frame, text) yellowfive@57: frame:_SetText(text) yellowfive@57: local width = frame.obj.frame.width or frame.obj.frame:GetWidth() or 0 yellowfive@57: PanelTemplates_TabResize(frame, 0, nil, nil, width, frame:GetFontString():GetStringWidth()) yellowfive@57: end yellowfive@57: yellowfive@57: local function Tab_SetSelected(frame, selected) yellowfive@57: frame.selected = selected yellowfive@57: UpdateTabLook(frame) yellowfive@57: end yellowfive@57: yellowfive@57: local function Tab_SetDisabled(frame, disabled) yellowfive@57: frame.disabled = disabled yellowfive@57: UpdateTabLook(frame) yellowfive@57: end yellowfive@57: yellowfive@57: local function BuildTabsOnUpdate(frame) yellowfive@57: local self = frame.obj yellowfive@57: self:BuildTabs() yellowfive@57: frame:SetScript("OnUpdate", nil) yellowfive@57: end yellowfive@57: yellowfive@57: --[[----------------------------------------------------------------------------- yellowfive@57: Scripts yellowfive@57: -------------------------------------------------------------------------------]] yellowfive@57: local function Tab_OnClick(frame) yellowfive@57: if not (frame.selected or frame.disabled) then yellowfive@57: PlaySound("igCharacterInfoTab") yellowfive@57: frame.obj:SelectTab(frame.value) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: local function Tab_OnEnter(frame) yellowfive@57: local self = frame.obj yellowfive@57: self:Fire("OnTabEnter", self.tabs[frame.id].value, frame) yellowfive@57: end yellowfive@57: yellowfive@57: local function Tab_OnLeave(frame) yellowfive@57: local self = frame.obj yellowfive@57: self:Fire("OnTabLeave", self.tabs[frame.id].value, frame) yellowfive@57: end yellowfive@57: yellowfive@57: local function Tab_OnShow(frame) yellowfive@57: _G[frame:GetName().."HighlightTexture"]:SetWidth(frame:GetTextWidth() + 30) yellowfive@57: end yellowfive@57: yellowfive@57: --[[----------------------------------------------------------------------------- yellowfive@57: Methods yellowfive@57: -------------------------------------------------------------------------------]] yellowfive@57: local methods = { yellowfive@57: ["OnAcquire"] = function(self) yellowfive@57: self:SetTitle() yellowfive@57: end, yellowfive@57: yellowfive@57: ["OnRelease"] = function(self) yellowfive@57: self.status = nil yellowfive@57: for k in pairs(self.localstatus) do yellowfive@57: self.localstatus[k] = nil yellowfive@57: end yellowfive@57: self.tablist = nil yellowfive@57: for _, tab in pairs(self.tabs) do yellowfive@57: tab:Hide() yellowfive@57: end yellowfive@57: end, yellowfive@57: yellowfive@57: ["CreateTab"] = function(self, id) yellowfive@57: local tabname = ("AceGUITabGroup%dTab%d"):format(self.num, id) yellowfive@57: local tab = CreateFrame("Button", tabname, self.border, "OptionsFrameTabButtonTemplate") yellowfive@57: tab.obj = self yellowfive@57: tab.id = id yellowfive@57: yellowfive@57: tab.text = _G[tabname .. "Text"] yellowfive@57: tab.text:ClearAllPoints() yellowfive@57: tab.text:SetPoint("LEFT", 14, -3) yellowfive@57: tab.text:SetPoint("RIGHT", -12, -3) yellowfive@57: yellowfive@57: tab:SetScript("OnClick", Tab_OnClick) yellowfive@57: tab:SetScript("OnEnter", Tab_OnEnter) yellowfive@57: tab:SetScript("OnLeave", Tab_OnLeave) yellowfive@57: tab:SetScript("OnShow", Tab_OnShow) yellowfive@57: yellowfive@57: tab._SetText = tab.SetText yellowfive@57: tab.SetText = Tab_SetText yellowfive@57: tab.SetSelected = Tab_SetSelected yellowfive@57: tab.SetDisabled = Tab_SetDisabled yellowfive@57: yellowfive@57: return tab yellowfive@57: end, yellowfive@57: yellowfive@57: ["SetTitle"] = function(self, text) yellowfive@57: self.titletext:SetText(text or "") yellowfive@57: if text and text ~= "" then yellowfive@57: self.alignoffset = 25 yellowfive@57: else yellowfive@57: self.alignoffset = 18 yellowfive@57: end yellowfive@57: self:BuildTabs() yellowfive@57: end, yellowfive@57: yellowfive@57: ["SetStatusTable"] = function(self, status) yellowfive@57: assert(type(status) == "table") yellowfive@57: self.status = status yellowfive@57: end, yellowfive@57: yellowfive@57: ["SelectTab"] = function(self, value) yellowfive@57: local status = self.status or self.localstatus yellowfive@57: local found yellowfive@57: for i, v in ipairs(self.tabs) do yellowfive@57: if v.value == value then yellowfive@57: v:SetSelected(true) yellowfive@57: found = true yellowfive@57: else yellowfive@57: v:SetSelected(false) yellowfive@57: end yellowfive@57: end yellowfive@57: status.selected = value yellowfive@57: if found then yellowfive@57: self:Fire("OnGroupSelected",value) yellowfive@57: end yellowfive@57: end, yellowfive@57: yellowfive@57: ["SetTabs"] = function(self, tabs) yellowfive@57: self.tablist = tabs yellowfive@57: self:BuildTabs() yellowfive@57: end, yellowfive@57: yellowfive@57: yellowfive@57: ["BuildTabs"] = function(self) yellowfive@57: local hastitle = (self.titletext:GetText() and self.titletext:GetText() ~= "") yellowfive@57: local status = self.status or self.localstatus yellowfive@57: local tablist = self.tablist yellowfive@57: local tabs = self.tabs yellowfive@57: yellowfive@57: if not tablist then return end yellowfive@57: yellowfive@57: local width = self.frame.width or self.frame:GetWidth() or 0 yellowfive@57: yellowfive@57: wipe(widths) yellowfive@57: wipe(rowwidths) yellowfive@57: wipe(rowends) yellowfive@57: yellowfive@57: --Place Text into tabs and get thier initial width yellowfive@57: for i, v in ipairs(tablist) do yellowfive@57: local tab = tabs[i] yellowfive@57: if not tab then yellowfive@57: tab = self:CreateTab(i) yellowfive@57: tabs[i] = tab yellowfive@57: end yellowfive@57: yellowfive@57: tab:Show() yellowfive@57: tab:SetText(v.text) yellowfive@57: tab:SetDisabled(v.disabled) yellowfive@57: tab.value = v.value yellowfive@57: yellowfive@57: 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 yellowfive@57: end yellowfive@57: yellowfive@57: for i = (#tablist)+1, #tabs, 1 do yellowfive@57: tabs[i]:Hide() yellowfive@57: end yellowfive@57: yellowfive@57: --First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout yellowfive@57: local numtabs = #tablist yellowfive@57: local numrows = 1 yellowfive@57: local usedwidth = 0 yellowfive@57: yellowfive@57: for i = 1, #tablist do yellowfive@57: --If this is not the first tab of a row and there isn't room for it yellowfive@57: if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then yellowfive@57: rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px yellowfive@57: rowends[numrows] = i - 1 yellowfive@57: numrows = numrows + 1 yellowfive@57: usedwidth = 0 yellowfive@57: end yellowfive@57: usedwidth = usedwidth + widths[i] yellowfive@57: end yellowfive@57: rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px yellowfive@57: rowends[numrows] = #tablist yellowfive@57: yellowfive@57: --Fix for single tabs being left on the last row, move a tab from the row above if applicable yellowfive@57: if numrows > 1 then yellowfive@57: --if the last row has only one tab yellowfive@57: if rowends[numrows-1] == numtabs-1 then yellowfive@57: --if there are more than 2 tabs in the 2nd last row yellowfive@57: if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then yellowfive@57: --move 1 tab from the second last row to the last, if there is enough space yellowfive@57: if (rowwidths[numrows] + widths[numtabs-1]) <= width then yellowfive@57: rowends[numrows-1] = rowends[numrows-1] - 1 yellowfive@57: rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1] yellowfive@57: rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1] yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: --anchor the rows as defined and resize tabs to fill thier row yellowfive@57: local starttab = 1 yellowfive@57: for row, endtab in ipairs(rowends) do yellowfive@57: local first = true yellowfive@57: for tabno = starttab, endtab do yellowfive@57: local tab = tabs[tabno] yellowfive@57: tab:ClearAllPoints() yellowfive@57: if first then yellowfive@57: tab:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 0, -(hastitle and 14 or 7)-(row-1)*20 ) yellowfive@57: first = false yellowfive@57: else yellowfive@57: tab:SetPoint("LEFT", tabs[tabno-1], "RIGHT", -10, 0) yellowfive@57: end yellowfive@57: end yellowfive@57: yellowfive@57: -- equal padding for each tab to fill the available width, yellowfive@57: -- if the used space is above 75% already yellowfive@57: -- the 18 pixel is the typical width of a scrollbar, so we can have a tab group inside a scrolling frame, yellowfive@57: -- and not have the tabs jump around funny when switching between tabs that need scrolling and those that don't yellowfive@57: local padding = 0 yellowfive@57: if not (numrows == 1 and rowwidths[1] < width*0.75 - 18) then yellowfive@57: padding = (width - rowwidths[row]) / (endtab - starttab+1) yellowfive@57: end yellowfive@57: yellowfive@57: for i = starttab, endtab do yellowfive@57: PanelTemplates_TabResize(tabs[i], padding + 4, nil, nil, width, tabs[i]:GetFontString():GetStringWidth()) yellowfive@57: end yellowfive@57: starttab = endtab + 1 yellowfive@57: end yellowfive@57: yellowfive@57: self.borderoffset = (hastitle and 17 or 10)+((numrows)*20) yellowfive@57: self.border:SetPoint("TOPLEFT", 1, -self.borderoffset) yellowfive@57: end, yellowfive@57: yellowfive@57: ["OnWidthSet"] = function(self, width) yellowfive@57: local content = self.content yellowfive@57: local contentwidth = width - 60 yellowfive@57: if contentwidth < 0 then yellowfive@57: contentwidth = 0 yellowfive@57: end yellowfive@57: content:SetWidth(contentwidth) yellowfive@57: content.width = contentwidth yellowfive@57: self:BuildTabs(self) yellowfive@57: self.frame:SetScript("OnUpdate", BuildTabsOnUpdate) yellowfive@57: end, yellowfive@57: yellowfive@57: ["OnHeightSet"] = function(self, height) yellowfive@57: local content = self.content yellowfive@57: local contentheight = height - (self.borderoffset + 23) yellowfive@57: if contentheight < 0 then yellowfive@57: contentheight = 0 yellowfive@57: end yellowfive@57: content:SetHeight(contentheight) yellowfive@57: content.height = contentheight yellowfive@57: end, yellowfive@57: yellowfive@57: ["LayoutFinished"] = function(self, width, height) yellowfive@57: if self.noAutoHeight then return end yellowfive@57: self:SetHeight((height or 0) + (self.borderoffset + 23)) yellowfive@57: end yellowfive@57: } yellowfive@57: yellowfive@57: --[[----------------------------------------------------------------------------- yellowfive@57: Constructor yellowfive@57: -------------------------------------------------------------------------------]] yellowfive@57: local PaneBackdrop = { yellowfive@57: bgFile = "Interface\\ChatFrame\\ChatFrameBackground", yellowfive@57: edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", yellowfive@57: tile = true, tileSize = 16, edgeSize = 16, yellowfive@57: insets = { left = 3, right = 3, top = 5, bottom = 3 } yellowfive@57: } yellowfive@57: yellowfive@57: local function Constructor() yellowfive@57: local num = AceGUI:GetNextWidgetNum(Type) yellowfive@57: local frame = CreateFrame("Frame",nil,UIParent) yellowfive@57: frame:SetHeight(100) yellowfive@57: frame:SetWidth(100) yellowfive@57: frame:SetFrameStrata("FULLSCREEN_DIALOG") yellowfive@57: yellowfive@57: local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") yellowfive@57: titletext:SetPoint("TOPLEFT", 14, 0) yellowfive@57: titletext:SetPoint("TOPRIGHT", -14, 0) yellowfive@57: titletext:SetJustifyH("LEFT") yellowfive@57: titletext:SetHeight(18) yellowfive@57: titletext:SetText("") yellowfive@57: yellowfive@57: local border = CreateFrame("Frame", nil, frame) yellowfive@57: border:SetPoint("TOPLEFT", 1, -27) yellowfive@57: border:SetPoint("BOTTOMRIGHT", -1, 3) yellowfive@57: border:SetBackdrop(PaneBackdrop) yellowfive@57: border:SetBackdropColor(0.1, 0.1, 0.1, 0.5) yellowfive@57: border:SetBackdropBorderColor(0.4, 0.4, 0.4) yellowfive@57: yellowfive@57: local content = CreateFrame("Frame", nil, border) yellowfive@57: content:SetPoint("TOPLEFT", 10, -7) yellowfive@57: content:SetPoint("BOTTOMRIGHT", -10, 7) yellowfive@57: yellowfive@57: local widget = { yellowfive@57: num = num, yellowfive@57: frame = frame, yellowfive@57: localstatus = {}, yellowfive@57: alignoffset = 18, yellowfive@57: titletext = titletext, yellowfive@57: border = border, yellowfive@57: borderoffset = 27, yellowfive@57: tabs = {}, yellowfive@57: content = content, yellowfive@57: type = Type yellowfive@57: } yellowfive@57: for method, func in pairs(methods) do yellowfive@57: widget[method] = func yellowfive@57: end yellowfive@57: yellowfive@57: return AceGUI:RegisterAsContainer(widget) yellowfive@57: end yellowfive@57: yellowfive@57: AceGUI:RegisterWidgetType(Type, Constructor, Version)