local M, I, COMPAT, _, T = {}, {}, select(4, GetBuildInfo()), ... local MODERN_CONFIG = COMPAT > 11403 local CreateEdge, noop = T.CreateEdge, function() end T.TenSettings = M do -- EscapeCallback local getInfo, setInfo do local info = {} function getInfo(k) return info[k] end function setInfo(k, v) info[k] = v end end local function ESC_OnKeyDown(self, key) local it = getInfo(self) if key and (key == "ESCAPE" or key == it[3]) and GetCurrentKeyBoardFocus() == nil then it[2](it[1], key) else local a, b = it[4], it[5] a:SetScript("OnKeyDown", nil) b:SetScript("OnKeyDown", noop) it[4], it[5] = b, a end end function M:EscapeCallback(parent, key2, callback) if callback == nil then callback, key2 = key2, nil end local f0 = CreateFrame("Frame", nil, parent) local f1 = CreateFrame("Frame", nil, parent) local f2 = CreateFrame("Frame", nil, parent) setInfo(f0, {parent, callback, key2, f1, f2}) f0:SetPropagateKeyboardInput(true) f0:SetScript("OnKeyDown", ESC_OnKeyDown) f1:SetScript("OnKeyDown", noop) end end if MODERN_CONFIG then local WINDOW_PADDING_H, WINDOW_PADDING_TOP, WINDOW_ACTIONS_HEIGHT, WINDOW_PADDING_BOTTOM = 10, 30, 30, 15 local IW_PADDING_TOP, IW_PADDING_RIGHT, IW_PADDING_BOTTOM, IW_PADDING_LEFT = 24, 4, 5, 8 local CONTAINER_TABS_YOFFSET, CONTAINER_CONTENT_TOP_YOFFSET, CONTAINER_TITLE_YOFFSET = 11, -26, -4 local CONTAINER_PADDING_H, CONTAINER_PADDING_V = 10, 6 local PANEL_VIEW_MARGIN_TOP, PANEL_VIEW_MARGIN_TOP_TITLESHIFT = -14, -35 local PANEL_VIEW_MARGIN_LEFT, PANEL_VIEW_MARGIN_RIGHT = -15, -10 local PANEL_WIDTH, PANEL_HEIGHT = 585, 528 local CONTAINER_WIDTH = PANEL_WIDTH + CONTAINER_PADDING_H * 2 local CONTAINER_HEIGHT = PANEL_HEIGHT - CONTAINER_CONTENT_TOP_YOFFSET + CONTAINER_PADDING_V*2 local WINDOW_WIDTH = CONTAINER_WIDTH + WINDOW_PADDING_H * 2 local WINDOW_HEIGHT = CONTAINER_HEIGHT + WINDOW_PADDING_TOP + WINDOW_ACTIONS_HEIGHT + WINDOW_PADDING_BOTTOM local TenSettingsFrame, notifyTenant = CreateFrame("Frame", "TenSettingsFrame", UIParent, "SettingsFrameTemplate") do TenSettingsFrame:SetSize(WINDOW_WIDTH, WINDOW_HEIGHT) TenSettingsFrame:SetPoint("CENTER", 0, 50) TenSettingsFrame.NineSlice.Text:SetText(OPTIONS) TenSettingsFrame:SetFrameStrata("HIGH") TenSettingsFrame:SetToplevel(true) TenSettingsFrame:Hide() TenSettingsFrame:SetMouseClickEnabled(true) TenSettingsFrame:SetMouseMotionEnabled(true) TenSettingsFrame:SetClampedToScreen(true) TenSettingsFrame:SetClampRectInsets(5,0,0,0) local f = CreateFrame("Frame", nil, TenSettingsFrame) f:SetPoint("TOPLEFT", IW_PADDING_LEFT, -IW_PADDING_TOP) f:SetPoint("BOTTOMRIGHT", -IW_PADDING_RIGHT, IW_PADDING_BOTTOM) f.OverlayFaderMargin = 0 TenSettingsFrame.WindowArea = f f = CreateFrame("Frame", nil, f) f:SetPoint("TOPLEFT", 0, IW_PADDING_TOP-WINDOW_PADDING_TOP) f:SetPoint("BOTTOMRIGHT", 0, WINDOW_ACTIONS_HEIGHT+WINDOW_PADDING_BOTTOM-IW_PADDING_BOTTOM) TenSettingsFrame.ContentArea = f local cancel = CreateFrame("Button", nil, TenSettingsFrame.WindowArea, "UIPanelButtonTemplate") cancel:SetSize(110, 24) cancel:SetPoint("BOTTOMRIGHT", IW_PADDING_RIGHT-WINDOW_PADDING_H, WINDOW_PADDING_BOTTOM-IW_PADDING_BOTTOM) cancel:SetText(CANCEL) TenSettingsFrame.Cancel = cancel local save = CreateFrame("Button", nil, TenSettingsFrame.WindowArea, "UIPanelButtonTemplate") save:SetSize(110, 24) save:SetPoint("RIGHT", cancel, "LEFT", -4, 0) save:SetText(OKAY) TenSettingsFrame.Save = save local defaults = CreateFrame("Button", nil, TenSettingsFrame.WindowArea, "UIPanelButtonTemplate") defaults:SetSize(110, 24) defaults:SetPoint("BOTTOMLEFT", WINDOW_PADDING_H - IW_PADDING_LEFT, WINDOW_PADDING_BOTTOM-IW_PADDING_BOTTOM) defaults:SetText(DEFAULTS) TenSettingsFrame.Reset = defaults local revert = CreateFrame("Button", nil, TenSettingsFrame.WindowArea, "UIPanelButtonTemplate") do revert:SetSize(110, 24) revert:SetPoint("LEFT", defaults, "RIGHT", 4, 0) revert:SetText(REVERT) local drop = CreateFrame("Frame", nil, revert, "UIDropDownMenuTemplate") UIDropDownMenu_SetAnchor(drop, 0, 2, "BOTTOM", revert, "TOP") UIDropDownMenu_SetDisplayMode(drop, "MENU") revert:SetScript("OnClick", function() ToggleDropDownMenu(1, nil, drop) PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON) end) function revert:HandlesGlobalMouseEvent(button, _ev) return button == "LeftButton" end local function performRevert(_, idx) if idx == -1 then I.undo:UnwindStack() else I.undo:UnwindArchives(idx) end notifyTenant("OnRefresh") end local function formatTime(td) if GetCVarBool("timeMgrUseMilitaryTime") then return date("%H:%M:%S", time()-td) end return (date("%I:%M:%S %p", time()-td):gsub("^0(%d)", "%1")) end function drop:initialize() local info, text = {func=performRevert, notCheckable=1, justifyH="CENTER"}, revert.optionText or "%2$s" local now, numEntries, numArchives, firstTime = GetServerTime(), I.undo:GetState() for i=1, numArchives + (numEntries > 0 and 1 or 0) do local isCancel = i > numArchives local td = now - (isCancel and firstTime or I.undo:GetArchiveInfo(i)) local cc = isCancel and "|cffffb000" or "" info.text, info.arg1 = cc .. text:format(math.floor(td/60+0.5), formatTime(td)), isCancel and -1 or i UIDropDownMenu_AddButton(info) end end TenSettingsFrame.Revert, revert.drop = revert, drop end M:EscapeCallback(TenSettingsFrame, function(self) self.ClosePanelButton:Click() end) table.insert(UISpecialFrames, TenSettingsFrame:GetName()) local dragHandle = CreateFrame("Frame", nil, TenSettingsFrame) do dragHandle:SetPoint("TOPLEFT", TenSettingsFrame, "TOPLEFT", 4, 0) dragHandle:SetPoint("BOTTOMRIGHT", TenSettingsFrame, "TOPRIGHT", -28, -20) dragHandle:RegisterForDrag("LeftButton") dragHandle:SetScript("OnEnter", function() SetCursor("Interface/CURSOR/UI-Cursor-Move.crosshair") end) dragHandle:SetScript("OnLeave", function() SetCursor(nil) end) dragHandle:SetScript("OnDragStart", function() TenSettingsFrame:SetMovable(true) TenSettingsFrame:StartMoving() end) dragHandle:SetScript("OnDragStop", function() TenSettingsFrame:StopMovingOrSizing() end) end end local ConfusableResetDialog, crd_show, CRD_QUESTION_TEXT = CreateFrame("Frame", nil) do local d, t, tenant = ConfusableResetDialog d:Hide() d:SetSize(460, 105) local function onResetButtonClick(self) local id = self:GetID() if id ~= 0 then notifyTenant("OnDefault", tenant, select(id, "current-panel-only")) end ConfusableResetDialog:Hide() end t = d:CreateFontString(nil, "OVERLAY", "GameFontHighlight") t:SetPoint("TOP", -15, -8) t:SetWidth(410) t, d.Question = d:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall"), t t:SetPoint("BOTTOM", -15, 38) t:SetWidth(410) t, d.Hint = CreateFrame("Button", nil, d, "UIPanelButtonTemplate", 2), t t:SetSize(150, 24) t:SetPoint("BOTTOM", -150, 6) t:SetText(ALL_SETTINGS) t:SetScript("OnClick", onResetButtonClick) t, d.AllSet = CreateFrame("Button", nil, d, "UIPanelButtonTemplate", 0), t t:SetSize(130, 24) t:SetPoint("BOTTOM", 0, 6) t:SetText(CANCEL) t:SetScript("OnClick", onResetButtonClick) t, d.Cancel = CreateFrame("Button", nil, d, "UIPanelButtonTemplate", 1), t t:SetSize(150, 24) t:SetPoint("BOTTOM", 150, 6) t:SetText(CURRENT_SETTINGS) t:SetScript("OnClick", onResetButtonClick) d.OnlyThese = t d:SetScript("OnHide", function(self) self:Hide() tenant = nil end) function crd_show(forTenant, thisName, rootName) local qt, cc = CRD_QUESTION_TEXT or CONFIRM_RESET_INTERFACE_SETTINGS, NORMAL_FONT_COLOR_CODE ConfusableResetDialog.Question:SetFormattedText(qt, cc .. tostring(rootName) .. "|r", cc .. tostring(thisName) .. "|r") tenant = forTenant M:ShowFrameOverlay(TenSettingsFrame.WindowArea, ConfusableResetDialog) end end local minitabs = {} local function minitab_deselect(self) local r = minitabs[self] r.Text:SetPoint("BOTTOM", 0, 6) r.Text:SetFontObject("GameFontNormalSmall") r.Left:SetAtlas("Options_Tab_Left", true) r.Middle:SetAtlas("Options_Tab_Middle", true) r.Right:SetAtlas("Options_Tab_Right", true) r.NormalBG:SetPoint("TOPRIGHT", -2, -15) r.HighlightBG:SetColorTexture(1,1,1,1) r.SelectedBG:SetColorTexture(0,0,0,0) self:SetNormalFontObject(GameFontNormalSmall) end local function minitab_select(self) local r = minitabs[self] r.Text:SetPoint("BOTTOM", 0, 8) r.Text:SetFontObject("GameFontHighlightSmall") r.Left:SetAtlas("Options_Tab_Active_Left", true) r.Middle:SetAtlas("Options_Tab_Active_Middle", true) r.Right:SetAtlas("Options_Tab_Active_Right", true) r.NormalBG:SetPoint("TOPRIGHT", -2, -12) r.HighlightBG:SetColorTexture(0,0,0,0) r.SelectedBG:SetColorTexture(1,1,1,1) self:SetNormalFontObject(GameFontHighlightSmall) end local function minitab_new(parent, text) local b, r, t = CreateFrame("Button", nil, parent), {} minitabs[b], r.f = r, b t = b:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") b:SetFontString(t) t:ClearAllPoints() t:SetPoint("BOTTOM", 0, 6) b:SetNormalFontObject(GameFontNormalSmall) b:SetDisabledFontObject(GameFontDisableSmall) b:SetHighlightFontObject(GameFontHighlightSmall) b:SetPushedTextOffset(0, 0) t:SetText(text) t, r.Text = b:CreateTexture(nil, "BACKGROUND"), t t:SetPoint("BOTTOMLEFT") t, r.Left = b:CreateTexture(nil, "BACKGROUND"), t t:SetPoint("BOTTOMRIGHT") t, r.Right = b:CreateTexture(nil, "BACKGROUND"), t t:SetPoint("TOPLEFT", r.Left, "TOPRIGHT", 0, 0) t:SetPoint("TOPRIGHT", r.Right, "TOPLEFT", 0, 0) t, r.Middle = b:CreateTexture(nil, "BACKGROUND", nil, -2), t t:SetPoint("BOTTOMLEFT", 2, 0) t:SetPoint("TOPRIGHT", -2, -15) t:SetColorTexture(1,1,1,1) t:SetGradient("VERTICAL", {r=0.1, g=0.1, b=0.1, a=0.85}, {r=0.15, g=0.15, b=0.15, a=0.85}) t, r.NormalBG = b:CreateTexture(nil, "HIGHLIGHT"), t t:SetPoint("BOTTOMLEFT", 2, 0) t:SetPoint("TOPRIGHT", b, "BOTTOMRIGHT", -2, 12) t:SetColorTexture(1,1,1, 1) t:SetGradient("VERTICAL", {r=1, g=1, b=1, a=0.15}, {r=0, g=0, b=0, a=0}) t, r.HighlightBG = b:CreateTexture(nil, "BACKGROUND", nil, -1), t t:SetPoint("BOTTOMLEFT", 2, 0) t:SetPoint("TOPRIGHT", b, "BOTTOMRIGHT", -2, 16) t:SetGradient("VERTICAL", {r=1, g=1, b=1, a=0.15}, {r=0, g=0, b=0, a=0}) r.SelectedBG = t b:SetSize(r.Text:GetStringWidth()+40, 37) minitab_deselect(b) return b end local containers = {} local container_notifications, container_notifications_internal = {}, {} do local function container_notify_panels(self, notification, ...) local ci = containers[self] local onlyNotifyCurrentPanel = (...) == "current-panel-only" I.HandlePanelNotification(notification) for i=1, math.max(#ci.tabs, 1) do local panel = ci.tabs[ci.tabs[i]] or ci.root if panel[notification] and (ci.currentPanel == panel or not onlyNotifyCurrentPanel) then securecall(panel[notification], panel) end end if container_notifications_internal[notification] then securecall(container_notifications_internal[notification], self, ...) end end for s in ("okay cancel default refresh"):gmatch("%S+") do container_notifications[s] = function(self, ...) container_notify_panels(self, s, ...) end end end local function container_setTenant(ci, newPanel) if ci.currentPanel == newPanel then return end if ci.currentPanel then ci.currentPanel:Hide() minitab_deselect(ci.tabs[ci.currentPanel]) end ci.currentPanel = newPanel minitab_select(ci.tabs[newPanel]) local oy = -PANEL_VIEW_MARGIN_TOP if newPanel.TenSettings_TitleBlock then newPanel.title:Hide() newPanel.version:Hide() ci.Version:SetText((ci.forceRootVersion and ci.root or newPanel).version:GetText() or "") oy = -PANEL_VIEW_MARGIN_TOP_TITLESHIFT end newPanel:SetParent(ci.View) newPanel:ClearAllPoints() newPanel:SetPoint("TOPLEFT", CONTAINER_PADDING_H + PANEL_VIEW_MARGIN_LEFT, oy - CONTAINER_PADDING_V) newPanel:SetPoint("BOTTOMRIGHT", -PANEL_VIEW_MARGIN_RIGHT - CONTAINER_PADDING_H, CONTAINER_PADDING_V) newPanel:Show() if newPanel.refresh and ci.f:IsShown() then securecall(newPanel.refresh, newPanel) end return true end local function container_selectTab(self, button) local ci = containers[self:GetParent()] local newPanel = ci.tabs[self] if container_setTenant(ci, newPanel) and button then PlaySound(SOUNDKIT.IG_CHARACTER_INFO_TAB) end end local function container_addTab(tabs, parent, panel, text) local prev, idx = tabs[#tabs], #tabs+1 local tab = minitab_new(parent, text or panel.name) tabs[idx], tabs[panel], tabs[tab] = tab, tab, panel tab:SetPoint("TOPRIGHT", -10, CONTAINER_TABS_YOFFSET) tab:SetScript("OnClick", container_selectTab) if prev == nil then container_selectTab(tab, nil) else prev:SetPoint("TOPRIGHT", tab, "TOPLEFT", -4, 0) end return tab end local function container_onCanvasShow(self) local ci = containers[self] local cf = ci.f if cf:GetParent() ~= self then cf:ClearAllPoints() cf:SetParent(self) cf:SetPoint("CENTER", -5, 0) cf:Show() end end local function container_new(name, rootPanel, opts) local cf = CreateFrame("Frame") do cf:Hide() cf:SetClipsChildren(true) cf:SetScript("OnMouseWheel", noop) local cn = container_notifications cf.OnCommit, cf.OnDefault, cf.OnRefresh, cf.OnCancel = cn.okay, cn.default, cn.refresh, cn.cancel cf:SetSize(CONTAINER_WIDTH, CONTAINER_HEIGHT) end local ci = {f=cf, tabs={}, name=name, root=rootPanel} local t = cf:CreateTexture(nil, "BACKGROUND") t:SetAtlas("Options_InnerFrame") t:SetPoint("TOPLEFT", 0, CONTAINER_CONTENT_TOP_YOFFSET) t:SetPoint("BOTTOMRIGHT", cf, "BOTTOM", 0, 0) t:SetTexCoord(1,0.64, 0,1) t = cf:CreateTexture(nil, "BACKGROUND") t:SetAtlas("Options_InnerFrame") t:SetPoint("TOPRIGHT", 0, CONTAINER_CONTENT_TOP_YOFFSET) t:SetPoint("BOTTOMLEFT", cf, "BOTTOM", 0, 0) t:SetTexCoord(0.64,1, 0,1) t = cf:CreateFontString(nil, "OVERLAY", "GameFontHighlightHuge") t:SetPoint("TOPLEFT", 5, CONTAINER_TITLE_YOFFSET) t:SetText(name) t, ci.Title = cf:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall"), t t:SetPoint("TOPLEFT", ci.Title, "TOPRIGHT", 4, 2) t, ci.Version = CreateFrame("Frame", nil, cf), t t:SetPoint("TOPLEFT", 0, CONTAINER_CONTENT_TOP_YOFFSET) t:SetPoint("BOTTOMRIGHT", 0, 0) t:SetClipsChildren(true) t, ci.View = CreateFrame("Frame"), t t:Hide() t:SetScript("OnShow", container_onCanvasShow) t.OnCommit, t.OnDefault, t.OnRefresh, t.OnCancel = cf.OnCommit, cf.OnDefault, cf.OnRefresh, cf.OnCancel containers[t], ci.canvas = ci, t if type(opts) == "table" then ci.forceRootVersion = opts.forceRootVersion end containers[rootPanel], containers[name], containers[cf] = ci, ci, ci return ci end local function container_selectRootPanel(self) local ci = containers[self] if ci and #ci.tabs > 1 then container_setTenant(ci, ci.root) end end local function container_isResetConfusable(f) local ci = containers[f] if ci and ci.f == f and ci.currentPanel.default then local dc = 0 for i=1,#ci.tabs do if ci.tabs[ci.tabs[i]].default ~= nil then dc = dc + 1 if dc == 2 then local ct = ci.tabs[ci.currentPanel] return true, ct and ct:GetText() or ci.currentPanel.name, ci.name end end end end return false end container_notifications_internal.okay = container_selectRootPanel container_notifications_internal.cancel = container_selectRootPanel local currentSettingsTenant function notifyTenant(notification, filter, ...) local nf = currentSettingsTenant and currentSettingsTenant[notification] if nf and (filter == nil or currentSettingsTenant == filter) then securecall(nf, currentSettingsTenant, ...) end end local function settings_show(newTenant) if currentSettingsTenant then currentSettingsTenant:Hide() currentSettingsTenant:ClearAllPoints() currentSettingsTenant = nil end newTenant:ClearAllPoints() newTenant:SetParent(TenSettingsFrame.ContentArea) newTenant:SetPoint("TOP") currentSettingsTenant = newTenant securecall(newTenant.OnRefresh, newTenant) newTenant:Show() if not TenSettingsFrame:IsShown() then TenSettingsFrame:ClearAllPoints() TenSettingsFrame:SetPoint("CENTER", 0, 50) end I.OnUndoStateChange() TenSettingsFrame:Show() PlaySound(SOUNDKIT.IG_MAINMENU_OPEN) end local function settings_hide(dismissCommit, skipSound) if dismissCommit and currentSettingsTenant and currentSettingsTenant.OnCommit then securecall(currentSettingsTenant.OnCommit, currentSettingsTenant, "commit-on-dismiss") end TenSettingsFrame:Hide() if currentSettingsTenant then currentSettingsTenant:Hide() currentSettingsTenant:ClearAllPoints() currentSettingsTenant = nil end I.undo:ArchiveStack() if not skipSound then PlaySound(SOUNDKIT.IG_MAINMENU_CLOSE) end end do -- Detect settings dismissal, archive undo stack local cueWatcher do local waitLeft, watcher = 0, CreateFrame("Frame") watcher:Hide() watcher:SetScript("OnUpdate", function(_, elapsed) if elapsed and elapsed < waitLeft then waitLeft = waitLeft - elapsed return end waitLeft = 0.2 if TenSettingsFrame:IsVisible() or SettingsPanel:IsVisible() or I.undo:GetState() == 0 then watcher:Hide() elseif not (TenSettingsFrame:IsShown() or SettingsPanel:IsShown()) then if currentSettingsTenant then settings_hide(true, true) end I.undo:ArchiveStack() watcher:Hide() end end) function cueWatcher() waitLeft = 0 watcher:Show() end end for i=1,2 do local f = CreateFrame("Frame", nil, i == 1 and SettingsPanel or TenSettingsFrame) f:SetScript("OnHide", cueWatcher) end end TenSettingsFrame.ClosePanelButton:SetScript("OnClick", function() settings_hide(true) end) TenSettingsFrame.Save:SetScript("OnClick", function() if currentSettingsTenant then securecall(currentSettingsTenant.OnCommit, currentSettingsTenant) end settings_hide() end) TenSettingsFrame.Reset:SetScript("OnClick", function() if currentSettingsTenant and currentSettingsTenant.OnDefault then local isConfusable, currentName, rootName = container_isResetConfusable(currentSettingsTenant) if isConfusable then crd_show(currentSettingsTenant, currentName, rootName) else securecall(currentSettingsTenant.OnDefault, currentSettingsTenant) end PlaySound(SOUNDKIT.IG_MAINMENU_OPEN) end end) TenSettingsFrame.Cancel:SetScript("OnClick", function() if currentSettingsTenant and currentSettingsTenant.OnCancel then securecall(currentSettingsTenant.OnCancel, currentSettingsTenant) end settings_hide() end) local function openSettingsPanel(panel) local ci = containers[panel] if SettingsPanel:IsVisible() then container_setTenant(ci, panel) Settings.OpenToCategory(ci.name) else container_setTenant(ci, panel) settings_show(ci.f) end end function I.AddOptionsCategory(panel, opts) local name, parent = panel.name, panel.parent local ci = containers[parent] assert(parent == nil or ci) if parent == nil then ci = container_new(name, panel, opts) panel:SetParent(ci.f) local cat = Settings.RegisterCanvasLayoutCategory(ci.canvas, name) cat.ID = name Settings.RegisterAddOnCategory(cat) else containers[panel] = ci panel:SetParent(ci.f) if #ci.tabs == 0 then container_addTab(ci.tabs, ci.f, ci.root, OPTIONS) end container_addTab(ci.tabs, ci.f, panel) end panel.OpenPanel = openSettingsPanel end function I.GetOverlayDefaults(f) local p2 = f and f:GetParent() p2 = p2 and p2:GetParent() local ci = containers[f] if ci and ci.f == p2 then return p2, f.OverlayFaderMargin or 3.5, 28 end return nil, f.OverlayFaderMargin end function I.OnUndoStateChange() local nEntries, nArchives = I.undo:GetState() TenSettingsFrame.Revert:SetEnabled(nEntries > 0 or nArchives > 0) end function M:Localize(t) CRD_QUESTION_TEXT = t.RESET_QUESTION ConfusableResetDialog.AllSet:SetText(t.DEFAULTS_ALL or ALL_SETTINGS) ConfusableResetDialog.OnlyThese:SetText(t.DEFAULTS_VISIBLE or CURRENT_SETTINGS) TenSettingsFrame.Revert:SetText(t.REVERT or REVERT) TenSettingsFrame.Revert.optionText = t.REVERT_OPTION_LABEL ConfusableResetDialog.Hint:SetText(t.REVERT_CANCEL_HINT or "") end else -- not MODERN_CONFIG local function openInterfaceOptionsFrameCategory(panel) InterfaceOptionsFrame_OpenToCategory(panel) if not panel:IsVisible() then -- Fails on first run as adding a category doesn't trigger a list update, but OTC does. InterfaceOptionsFrame_OpenToCategory(panel) end -- If the panel is offscreen in the AddOns list, both OTC calls above will fail; -- in any case, we want all the children/sibling categories to be visible. local cat, parent = INTERFACEOPTIONS_ADDONCATEGORIES, panel.parent or panel.name local numVisiblePredecessors, parentPanel, lastRelatedPanel = 0 for i=1,#cat do local e = cat[i] if e.name == parent then parentPanel, lastRelatedPanel = e, numVisiblePredecessors+1 elseif parentPanel then if e.parent ~= parent then break end lastRelatedPanel = lastRelatedPanel + 1 elseif not e.hidden then numVisiblePredecessors = numVisiblePredecessors + 1 end end if lastRelatedPanel then local buttons, ofsY = InterfaceOptionsFrameAddOns.buttons if lastRelatedPanel - InterfaceOptionsFrameAddOnsList.offset > #buttons then ofsY = (lastRelatedPanel - #buttons)*buttons[1]:GetHeight() -- If the parent is collapsed, we might only be able to get it to show here local _, maxY = InterfaceOptionsFrameAddOnsListScrollBar:GetMinMaxValues() InterfaceOptionsFrameAddOnsListScrollBar:SetValue(math.min(ofsY, maxY)) end -- If the parent is collapsed, expand it for i=1,parentPanel and parentPanel.collapsed and #buttons or 0 do if buttons[i].element == parentPanel then InterfaceOptionsListButton_ToggleSubCategories(buttons[i]) break end end if ofsY then -- Set the proper scroll value, and force selection highlight to be updated InterfaceOptionsFrameAddOnsListScrollBar:SetValue(ofsY) InterfaceOptionsFrame_OpenToCategory(panel) end end if not panel:IsVisible() then -- I give up. InterfaceOptionsList_DisplayPanel(panel) end end local wrapOkayCancel do local function genHook(notification, ot) return ot, function(...) I.HandlePanelNotification(notification) local oh = ot[...] if oh then return oh(...) end end end local oOkay, okayHook = genHook("okay", {}) local oCancel, cancelHook = genHook("cancel", {}) function wrapOkayCancel(panel) oOkay[panel], oCancel[panel] = panel.okay, panel.cancel return okayHook, cancelHook end end function I.AddOptionsCategory(panel) InterfaceOptions_AddCategory(panel) panel.OpenPanel = openInterfaceOptionsFrameCategory panel.okay, panel.cancel = wrapOkayCancel(panel) end function I.GetOverlayDefaults(f) return nil, f.OverlayFaderMargin end M.Localize = noop end do -- M:CreateUndoHandle() local undoStack, archives, undo, uhandle, pendingNotify = {}, {}, {}, {}, false local MAX_ARCHIVES = 10 I.undo = undo local function storeUndoEntry(idx, ns, key, func, ...) local bot, now = undoStack.bottom, GetServerTime() undoStack[idx] = {ns=ns, key=key, func=func, n=select("#", ...), ...} undoStack.bottom = (bot == nil or bot > idx) and idx or bot undoStack.firstTime, undoStack.lastTime = undoStack.firstTime or now, now end local function unwind(us, msg) undoStack = us == undoStack and {} or undoStack for i=#us, us.bottom or 1, -1 do i = us[i] securecall(i.func, msg, unpack(i, 1, i.n)) end end local function archive(data) for i=1, #archives == MAX_ARCHIVES and MAX_ARCHIVES or 0 do archives[i] = archives[i+1] end data.archiveTime = data.archiveTime or GetServerTime() undoStack = undoStack == data and {} or undoStack archives[#archives+1] = data end local function rearchive(_msg, aa) for i=#aa, 1, -1 do archive(aa[i]) end end local function notifyStateChanged() pendingNotify = false I.OnUndoStateChange() end function undo:UnwindStack() unwind(undoStack, "unwind") undo:NotifyStateChanged() end function undo:UnwindArchives(idx) if undoStack.bottom then unwind(undoStack, "unwind") end local uw, ai = {} for i=#archives, idx, -1 do ai, uw[#uw+1], archives[i] = archives[i], archives[i], nil unwind(ai, "archive-unwind") end if #uw > 0 and undoStack.bottom then storeUndoEntry(#undoStack+1, nil, nil, rearchive, uw) end undo:NotifyStateChanged() end function undo:GetState() local bot = undoStack.bottom return #undoStack + (bot and 1-bot or 0), #archives, undoStack.firstTime end function undo:ArchiveStack() if #undoStack > 0 or undoStack.bottom then archive(undoStack) undo:NotifyStateChanged() end end function undo:GetArchiveInfo(idx) local ai = archives[idx] if ai then return ai.firstTime, ai.lastTime, ai.archiveTime end end function undo:ClearStack() if undoStack.bottom then undoStack = {} undo:NotifyStateChanged() end end function undo:NotifyStateChanged() if not pendingNotify and I.OnUndoStateChange then pendingNotify = true C_Timer.After(0, notifyStateChanged) end end function uhandle:search(key) for i=#undoStack, undoStack.bottom or 1,-1 do local e = undoStack[i] if e.ns == self and e.key == key then return true end end end function uhandle:push(...) storeUndoEntry(#undoStack + 1, self, ...) undo:NotifyStateChanged() end function uhandle:sink(...) storeUndoEntry((undoStack.bottom or 2) - 1, self, ...) undo:NotifyStateChanged() end local uhmeta = {__index=uhandle, __metatable=false} function M:CreateUndoHandle() return setmetatable({}, uhmeta) end end function I.HandlePanelNotification(notification) if notification == "okay" and MODERN_CONFIG then I.undo:ArchiveStack() elseif notification == "okay" then I.undo:ClearStack() elseif notification == "cancel" then I.undo:UnwindStack() end end function M:CreateLineInputBox(parent, common, width) local input = CreateFrame("EditBox", nil, parent) input:SetAutoFocus(nil) input:SetSize(width or 150, 20) input:SetFontObject(ChatFontNormal) input:SetScript("OnEscapePressed", input.ClearFocus) local l, m, r = input:CreateTexture(nil, "BACKGROUND"), input:CreateTexture(nil, "BACKGROUND"), input:CreateTexture(nil, "BACKGROUND") l:SetSize(common and 8 or 32, common and 20 or 32) l:SetPoint("LEFT", common and -5 or -10, 0) l:SetTexture(common and "Interface\\Common\\Common-Input-Border" or "Interface\\ChatFrame\\UI-ChatInputBorder-Left2") r:SetSize(common and 8 or 32, common and 20 or 32) r:SetPoint("RIGHT", common and 0 or 10, 0) r:SetTexture(common and "Interface\\Common\\Common-Input-Border" or "Interface\\ChatFrame\\UI-ChatInputBorder-Right2") m:SetHeight(common and 20 or 32) m:SetPoint("LEFT", l, "RIGHT") m:SetPoint("RIGHT", r, "LEFT") m:SetTexture(common and "Interface\\Common\\Common-Input-Border" or "Interface\\ChatFrame\\UI-ChatInputBorder-Mid2") if common then l:SetTexCoord(0,1/16, 0,5/8) r:SetTexCoord(15/16,1, 0,5/8) m:SetTexCoord(1/16,15/16, 0,5/8) else m:SetHorizTile(true) end return input end function M:CreateOptionsSlider(parent, name, width) local s, t = CreateFrame("Slider", name, parent, MODERN_CONFIG and "MinimalSliderTemplate" or "OptionsSliderTemplate") s:SetWidth(width) if MODERN_CONFIG then t = s:CreateFontString(nil, "OVERLAY", "GameFontHighlight") t, s.text = s:CreateFontString(nil, "OVERLAY"), t t, s.lo = s:CreateFontString(nil, "OVERLAY"), t s.hi = t else s.text, s.hi, s.lo = s.Text, s.High, s.Low s.hi:ClearAllPoints() s.lo:ClearAllPoints() s.text:ClearAllPoints() end s.lo:SetFontObject(GameFontHighlightSmall) s.hi:SetFontObject(GameFontHighlightSmall) s.lo:SetTextColor(0.8, 0.8, 0.8) s.hi:SetTextColor(0.8, 0.8, 0.8) s.lo:SetPoint("RIGHT", s, "LEFT", -2, 1) s.hi:SetPoint("LEFT", s, "RIGHT", 2, 1) s.lo:SetText(LOW) s.hi:SetText(HIGH) s:SetScript("OnValueChanged", nil) return s, MODERN_CONFIG and 4 or 0, s:GetHeight()/2 end do -- M:ShowFrameOverlay(self, overlayFrame) local container, watcher, occupant = CreateFrame("Frame"), CreateFrame("Frame") do container:EnableMouse(true) container:Hide() M:EscapeCallback(container, function(self) self:Hide() end) container:SetScript("OnMouseWheel", function() end) container.fader = container:CreateTexture(nil, "BACKGROUND", nil, -6) container.fader:SetColorTexture(0,0,0, 0.40) local corner = container:CreateTexture(nil, "ARTWORK") corner:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Corner") corner:SetSize(30,30) corner:SetPoint("TOPRIGHT", -5, -6) local close = CreateFrame("Button", nil, container, "UIPanelCloseButton") close:SetPoint("TOPRIGHT", MODERN_CONFIG and -5 or 0, MODERN_CONFIG and -5 or -1) close:SetScript("OnClick", function() container:Hide() end) CreateEdge(container, {edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", edgeSize=32, bgFile="Interface\\FrameGeneral\\UI-Background-Rock", tile=true, tileSize=256, insets={left=10,right=10,top=10,bottom=10}}, 0x4c667f, -5) watcher:SetScript("OnHide", function() if occupant then container:Hide() PlaySound(SOUNDKIT.IG_MAINMENU_CLOSE) occupant:Hide() occupant=nil end end) end function M:ShowFrameOverlay(self, overlayFrame) if occupant and occupant ~= overlayFrame then occupant:Hide() end local cw, ch = overlayFrame:GetSize() local w2, h2 = self:GetSize() local w, h, isRefresh = cw + 24, ch + 24, occupant == overlayFrame local frameLevel = (math.ceil(self:GetFrameLevel()/500)+1)*500 w2, h2, occupant = w2 > w and (w-w2)/2 or 0, h2 > h and (h-h2)/2 or 0 container:SetSize(w, h) container:SetHitRectInsets(w2, w2, h2, h2) container:SetParent(self) container:SetPoint("CENTER") container:SetFrameLevel(frameLevel) container.fader:ClearAllPoints() local oaf, omd, omt, omr, omb, oml = I.GetOverlayDefaults(self) oaf, omd = oaf or self, type(omd) == "number" and omd or 2 container.fader:SetPoint("TOPLEFT", oaf, "TOPLEFT", (oml or omd), -(omt or omd)) container.fader:SetPoint("BOTTOMRIGHT", oaf, "BOTTOMRIGHT", -(omr or omd), omb or omd) container:SetFrameStrata("DIALOG") container:Show() overlayFrame:ClearAllPoints() overlayFrame:SetParent(container) overlayFrame:SetPoint("CENTER") overlayFrame:Show() watcher:SetParent(overlayFrame) watcher:Show() CloseDropDownMenus() if not isRefresh then PlaySound(SOUNDKIT.IG_MAINMENU_OPEN) end occupant = overlayFrame end end do -- M:ShowPromptOverlay(...) local promptFrame, promptInfo = CreateFrame("Frame"), {} do promptFrame:SetSize(400, 130) promptInfo.title = promptFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") promptInfo.prompt = promptFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight") promptInfo.editBox = M:CreateLineInputBox(promptFrame, false, 300) promptInfo.accept = CreateFrame("Button", nil, promptFrame, "UIPanelButtonTemplate") promptInfo.cancel = CreateFrame("Button", nil, promptFrame, "UIPanelButtonTemplate") promptInfo.detail = promptFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall") promptInfo.title:SetPoint("TOP", 0, -3) promptInfo.prompt:SetPoint("TOP", promptInfo.title, "BOTTOM", 0, -8) promptInfo.editBox:SetPoint("TOP", promptInfo.prompt, "BOTTOM", 0, -7) promptInfo.detail:SetPoint("TOP", promptInfo.editBox, "BOTTOM", 0, -8) promptInfo.prompt:SetWidth(380) promptInfo.detail:SetWidth(380) promptInfo.cancel:SetScript("OnClick", function() promptFrame:Hide() end) promptInfo.editBox:SetScript("OnTextChanged", function(self) promptInfo.accept:SetEnabled(promptInfo.callback == nil or promptInfo.callback(self, self:GetText() or "", false, promptInfo.owner)) end) promptInfo.accept:SetScript("OnClick", function() local callback, text = promptInfo.callback, promptInfo.editBox:GetText() or "" if callback == nil or callback(promptInfo.editBox, text, true, promptInfo.owner) then promptFrame:Hide() end end) promptInfo.editBox:SetScript("OnEnterPressed", function() promptInfo.accept:Click() end) promptInfo.editBox:SetScript("OnEscapePressed", function() promptInfo.cancel:Click() end) end function M:ShowPromptOverlay(frame, title, prompt, explainText, acceptText, callback, editBoxWidth, cancelText, editText) local showEditBox = editBoxWidth ~= false editText = showEditBox and type(editText) == "string" and editText or "" promptInfo.owner, promptInfo.callback = frame, callback promptInfo.title:SetText(title or "") promptInfo.prompt:SetText(prompt or "") promptInfo.detail:SetText(explainText or "") promptInfo.editBox:SetText(editText) promptInfo.editBox:HighlightText(0, #editText) promptInfo.editBox:SetShown(editBoxWidth ~= false) promptInfo.editBox:SetWidth(math.max(40, (editBoxWidth or 0.50) * 380)) promptFrame:SetHeight(55 + math.max(20, promptInfo.prompt:GetStringHeight()) + (editBoxWidth ~= false and 30 or 0) + ((explainText or "") ~= "" and 20 or 0)) promptInfo.cancel:ClearAllPoints() promptInfo.accept:ClearAllPoints() if acceptText ~= false then promptInfo.accept:SetText(acceptText or ACCEPT) promptInfo.cancel:SetText(cancelText or CANCEL) promptInfo.cancel:SetPoint("BOTTOMLEFT", promptFrame, "BOTTOM", 5, 2) promptInfo.accept:SetPoint("BOTTOMRIGHT", promptFrame, "BOTTOM", -5, 2) promptInfo.accept:Show() else promptInfo.accept:Hide() promptInfo.cancel:SetText(cancelText or OKAY) promptInfo.cancel:SetPoint("BOTTOM", 5, 2) end promptInfo.cancel:SetWidth(math.max(125, 25+promptInfo.cancel:GetFontString():GetStringWidth())) promptInfo.accept:SetWidth(math.max(125, 25+promptInfo.accept:GetFontString():GetStringWidth())) M:ShowFrameOverlay(frame, promptFrame) if showEditBox then promptInfo.editBox:SetFocus() end end end function M:ShowAlertOverlay(frame, title, message, dissmissText) return M:ShowPromptOverlay(frame, title, message, nil, false, nil, false, dissmissText) end function M:CreateOptionsPanel(name, parent, opts) local f, t, a = CreateFrame("Frame") f:Hide() f.name, f.parent = name, parent a = CreateFrame("Frame", nil, f) a:SetHeight(20) a:SetPoint("TOPLEFT") a:SetPoint("TOPRIGHT") t = a:CreateFontString(nil, "OVERLAY", "GameFontNormalLargeLeftTop") t:SetPoint("TOPLEFT", 16, -16) t:SetText(name) t, f.title = a:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall"), t t:SetPoint("TOPLEFT", f.title, "TOPRIGHT", 4, 3) t, f.version = a:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall2"), t t:SetJustifyH("LEFT") t:SetPoint("TOPLEFT", f.title, "BOTTOMLEFT", 0, -8) t:SetWidth(590) f.desc = t f.TenSettings_TitleBlock = true I.AddOptionsCategory(f, opts) return f end do -- M:CreateOptionsCheckButton(name, parent) local function updateCheckButtonHitRect(self) local b = self:GetParent() b:SetHitRectInsets(0, -self:GetStringWidth()-5, 4, 4) end function M:CreateOptionsCheckButton(name, parent) local b = CreateFrame("CheckButton", name, parent, MODERN_CONFIG and "UICheckButtonTemplate" or "InterfaceOptionsCheckButtonTemplate") if MODERN_CONFIG then b:SetSize(24, 24) b.Text:SetPoint("LEFT", b, "RIGHT", 2, 1) b.Text:SetFontObject(GameFontHighlightLeft) end hooksecurefunc(b.Text, "SetText", updateCheckButtonHitRect) return b end end