You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

956 lines
35 KiB

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