-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- local _, TSM = ... local Settings = TSM.MainUI:NewPackage("Settings") local L = TSM.Include("Locale").GetTable() local Wow = TSM.Include("Util.Wow") local UIElements = TSM.Include("UI.UIElements") local private = { settingPages = { top = {}, middle = {}, bottom = {}, }, callback = {}, childSettingsPages = {}, sectionCollapsed = {}, } local SECTIONS = { "top", "middle" } local SETTING_PATH_SEP = "`" local SETTING_LABEL_WIDTH = 400 -- ============================================================================ -- Module Functions -- ============================================================================ function Settings.OnInitialize() TSM.MainUI.RegisterTopLevelPage(L["Settings"], private.GetSettingsFrame) end function Settings.RegisterSettingPage(name, section, callback) assert(tContains(SECTIONS, section)) tinsert(private.settingPages[section], name) private.callback[name] = callback end function Settings.RegisterChildSettingPage(parentName, childName, callback) local path = parentName..SETTING_PATH_SEP..childName private.childSettingsPages[parentName] = private.childSettingsPages[parentName] or {} tinsert(private.childSettingsPages[parentName], childName) private.callback[path] = callback end function Settings.CreateSettingLine(id, labelText, width) width = width or SETTING_LABEL_WIDTH return UIElements.New("Frame", id) :SetLayout("HORIZONTAL") :SetHeight(26) :SetMargin(0, 0, 0, 16) :AddChild(UIElements.New("Text", "label") :SetWidth(width) :SetFont("BODY_BODY2_MEDIUM") :SetTextColor("TEXT_ALT") :SetText(labelText) ) end function Settings.CreateHeading(id, text) return UIElements.New("Text", id) :SetHeight(19) :SetMargin(0, 0, 0, 4) :SetFont("BODY_BODY1_BOLD") :SetText(text) end function Settings.CreateInputWithReset(id, label, context, validate) local scope, namespace, key = strsplit(".", context) local validateFunc, validateContext = nil, nil if type(validate) == "table" then validateFunc = "CUSTOM_PRICE" validateContext = validate elseif type(validate) == "function" then validateFunc = validate elseif validate == nil then validateFunc = "CUSTOM_PRICE" else error("Invalid validate: "..tostring(validate)) end return UIElements.New("Frame", id) :SetLayout("VERTICAL") :AddChild(UIElements.New("Text", "label") :SetHeight(18) :SetMargin(0, 0, 0, 4) :SetFont("BODY_BODY2_MEDIUM") :SetTextColor("TEXT_ALT") :SetText(label) ) :AddChild(UIElements.New("Frame", "content") :SetLayout("HORIZONTAL") :SetHeight(24) :AddChild(UIElements.New("Input", "input") :SetMargin(0, 8, 0, 0) :SetBackgroundColor("ACTIVE_BG") :SetValidateFunc(validateFunc, validateContext) :SetSettingInfo(TSM.db[scope][namespace], key) :SetScript("OnValueChanged", private.InputOnValueChanged) ) :AddChild(UIElements.New("ActionButton", "resetButton") :SetWidth(108) :SetText(L["Reset"]) :SetDisabled(TSM.db[scope][namespace][key] == TSM.db:GetDefault(scope, namespace, key)) :SetScript("OnClick", private.ResetBtnOnClick) :SetContext(context) ) ) end function Settings.CreateExpandableSection(pageName, id, text, description, descriptionHeight) return UIElements.New("CollapsibleContainer", id) :SetLayout("VERTICAL") :SetMargin(0, 0, 0, 8) :SetContextTable(private.sectionCollapsed, pageName..text) :SetHeadingText(text) :AddChild(UIElements.New("Text", "description") :SetHeight(descriptionHeight or 20) :SetMargin(0, 0, 0, 12) :SetFont("BODY_BODY3") :SetText(description) ) end function Settings.PromptToReload() StaticPopupDialogs["TSMReloadPrompt"] = StaticPopupDialogs["TSMReloadPrompt"] or { text = L["You must reload your UI for these settings to take effect. Reload now?"], button1 = YES, button2 = NO, timeout = 0, OnAccept = ReloadUI, } Wow.ShowStaticPopupDialog("TSMReloadPrompt") end -- ============================================================================ -- Settings UI -- ============================================================================ function private.GetSettingsFrame() TSM.UI.AnalyticsRecordPathChange("main", "settings") local defaultPage = private.settingPages.top[1] local frame = UIElements.New("Frame", "settings") :SetLayout("HORIZONTAL") :SetBackgroundColor("PRIMARY_BG_ALT") :AddChild(UIElements.New("Frame", "settingNavigation") :SetLayout("VERTICAL") :SetWidth(160) :SetPadding(12, 12, 1, 9) :AddChild(UIElements.New("Frame", "top") :SetLayout("VERTICAL") ) :AddChild(UIElements.New("Texture", "vline") :SetHeight(1) :SetMargin(0, 0, 8, 8) :SetTexture("ACTIVE_BG_ALT") ) :AddChild(UIElements.New("Frame", "middle") :SetLayout("VERTICAL") ) :AddChild(UIElements.New("Spacer", "spacer") -- make all the navigation align to the top ) ) :AddChild(UIElements.New("Texture", "divider") :SetWidth(2) :SetTexture("ACTIVE_BG") ) :AddChild(UIElements.New("Frame", "contentFrame") :SetLayout("VERTICAL") :SetBackgroundColor("PRIMARY_BG") :AddChild(UIElements.New("ViewContainer", "content") :SetNavCallback(private.ContentNavCallback) ) ) local content = frame:GetElement("contentFrame.content") local settingNav = frame:GetElement("settingNavigation") for _, location in ipairs(SECTIONS) do local navFrame = settingNav:GetElement(location) for _, settingName in ipairs(private.settingPages[location]) do navFrame:AddChild(UIElements.New("Button", settingName) :SetHeight(20) :SetMargin(0, 0, 8, 0) :SetFont("BODY_BODY2_BOLD") :SetJustifyH("LEFT") :SetContext(settingName) :SetText(settingName) :SetScript("OnClick", private.NavButtonOnClick) ) content:AddPath(settingName, settingName == defaultPage) if private.childSettingsPages[settingName] then for _, childSettingName in ipairs(private.childSettingsPages[settingName]) do local path = settingName..SETTING_PATH_SEP..childSettingName navFrame:AddChild(UIElements.New("Button", path) :SetHeight(20) :SetMargin(9, 0, 8, 0) :SetFont("BODY_BODY3_MEDIUM") :SetJustifyH("LEFT") :SetContext(path) :SetText(strupper(childSettingName)) :SetScript("OnClick", private.NavButtonOnClick) ) content:AddPath(path, path == defaultPage) end end end end private.UpdateNavFrame(settingNav, defaultPage) return frame end function private.ContentNavCallback(content, path) return private.callback[path]() end -- ============================================================================ -- Local Script Handlers -- ============================================================================ function private.NavButtonOnClick(button) local path = button:GetContext() if private.childSettingsPages[path] then -- select the first child path = path..SETTING_PATH_SEP..private.childSettingsPages[path][1] end local contentFrame = button:GetElement("__parent.__parent.__parent.contentFrame") local navFrame = contentFrame:GetElement("__parent.settingNavigation") private.UpdateNavFrame(navFrame, path) navFrame:Draw() contentFrame:GetElement("content"):SetPath(path, true) end function private.InputOnValueChanged(input) local button = input:GetElement("__parent.resetButton") local scope, namespace, key = strsplit(".", button:GetContext()) button:SetDisabled(TSM.db[scope][namespace][key] == TSM.db:GetDefault(scope, namespace, key)) :Draw() end function private.ResetBtnOnClick(button) local scope, namespace, key = strsplit(".", button:GetContext()) local defaultValue = TSM.db:GetDefault(scope, namespace, key) TSM.db:Set(scope, nil, namespace, key, defaultValue) button:GetElement("__parent.input") :SetValue(defaultValue) :Draw() button:SetDisabled(true) :Draw() end -- ============================================================================ -- Private Helper Functions -- ============================================================================ function private.UpdateNavFrame(navFrame, selectedPath) local selectedSetting = strsplit(SETTING_PATH_SEP, selectedPath) for _, location in ipairs(SECTIONS) do for _, settingName in ipairs(private.settingPages[location]) do navFrame:GetElement(location ..".".. settingName) :SetTextColor(settingName == selectedSetting and "TEXT" or "ACTIVE_BG_ALT") if private.childSettingsPages[settingName] then for _, childSettingName in ipairs(private.childSettingsPages[settingName]) do local path = settingName..SETTING_PATH_SEP..childSettingName if settingName == selectedSetting then navFrame:GetElement(location ..".".. path) :SetTextColor(path == selectedPath and "INDICATOR" or "TEXT") :Show() else navFrame:GetElement(location ..".".. path):Hide() end end end end end end