local config, COMPAT, _, T = {}, select(4,GetBuildInfo()), ... local MODERN, CF_WRATH = COMPAT >= 10e4, COMPAT < 10e4 and COMPAT >= 3e4 local L, EV, TS, XU, frame = T.L, T.Evie, T.TenSettings, T.exUI, nil local GameTooltip = T.NotGameTooltip or GameTooltip T.config = config do -- /opie local slashExtensions = {} local function addSuffix(func, word, ...) if word then slashExtensions[word:lower()] = func addSuffix(func, ...) end end addSuffix(function() print("|cff0080ffOPie|r |cffffffff" .. GetAddOnMetadata("OPie", "Version") .. "|r") end, "version", "v") T.AddSlashSuffix = addSuffix SLASH_OPIE1, SLASH_OPIE2 = "/opie", "/op" SlashCmdList["OPIE"] = function(args, ...) local ext = slashExtensions[(args:match("%S+") or ""):lower()] if ext then ext(args, ...) else frame:OpenPanel() end end end if TS and TS.Localize then TS:Localize({ REVERT=L"Revert...", REVERT_OPTION_LABEL=L"%d |4minute:minutes; ago (%s)", RESET_QUESTION=L"Do you want to reset all %s settings to their defaults, or only the settings in the %s category?", REVERT_CANCEL_HINT=L"You can cancel or revert to previous settings later.", DEFAULTS_ALL=L("All Settings", ALL_SETTINGS), DEFAULTS_VISIBLE=L("These Settings", CURRENT_SETTINGS), }) end local KR, PC = T.ActionBook:compatible("Kindred",1,0), T.OPieCore local CreateEdge = T.CreateEdge do -- config.ui config.ui = {} do -- multilineInput local function onNavigate(self, _x,y, _w,h) local scroller, insT, insB = self.scroll, 2, 2 local occH, occP, y = scroller:GetHeight(), scroller:GetVerticalScroll(), -y if occP > y-insT then occP = y > insT and y-insT or 0 -- too far elseif occP < y+h-occH+insB+insT then occP = y+h-occH+insB+insT -- not far enough else return end local _, mx = scroller.ScrollBar:GetMinMaxValues() occP = (mx-occP)^2 < 1 and mx or math.floor(occP) scroller.ScrollBar:SetMinMaxValues(0, occP < mx and mx or occP) scroller.ScrollBar:SetValue(occP) end local function onClick(self) self.input:SetFocus() end function config.ui.multilineInput(name, parent, width) local scroller = CreateFrame("ScrollFrame", name .. "Scroll", parent, "UIPanelScrollFrameTemplate") local input = CreateFrame("Editbox", name, scroller) input:SetWidth(width) input:SetMultiLine(true) input:SetAutoFocus(false) input:SetTextInsets(2,4,2,2) input:SetFontObject(GameFontHighlight) input:SetScript("OnCursorChanged", onNavigate) scroller:EnableMouse(1) scroller:SetScript("OnMouseDown", onClick) scroller:SetScrollChild(input) input.scroll, scroller.input = scroller, input return input, scroller end end function config.ui.HideTooltip(self) if GameTooltip:IsOwned(self) then GameTooltip:Hide() end end function config.ui.ShowControlTooltip(self) local title, text = self.tooltipTitle, self.tooltipText if not (title or text) then return end GameTooltip:SetOwner(self, self.tooltipOwnerPoint or "ANCHOR_BOTTOMRIGHT") GameTooltip:AddLine(title or "", nil, nil, nil) GameTooltip:AddLine(text or "", nil, nil, nil, true) GameTooltip:Show() end end do -- config.bind local activeCaptureButton local alternateFrame = CreateFrame("Frame", nil, UIParent) do alternateFrame:Hide() CreateEdge(alternateFrame, { bgFile="Interface/ChatFrame/ChatFrameBackground", edgeFile="Interface/DialogFrame/UI-DialogBox-Border", tile=true, tileSize=32, edgeSize=32, insets={left=11, right=11, top=11, bottom=10}}, 0xd8000000) alternateFrame:SetSize(380, 115) alternateFrame:EnableMouse(1) alternateFrame:SetScript("OnHide", alternateFrame.Hide) local extReminder = CreateFrame("Button", nil, alternateFrame) extReminder:SetHeight(16) extReminder:SetPoint("TOPLEFT", 12, -10) extReminder:SetPoint("TOPRIGHT", -12, -10) extReminder:SetNormalTexture("Interface/Buttons/UI-OptionsButton") extReminder:SetPushedTextOffset(0,0) extReminder:SetText(" ") extReminder:SetNormalFontObject(GameFontHighlightSmall) do local fs, tex = extReminder:GetFontString(), extReminder:GetNormalTexture() fs:ClearAllPoints() tex:ClearAllPoints() fs:SetPoint("LEFT", 18, -1) tex:SetSize(14,14) tex:SetPoint("LEFT") end alternateFrame.caption = extReminder extReminder:SetScript("OnEnter", function(self) GameTooltip:SetOwner(self, "ANCHOR_NONE") GameTooltip:SetPoint("TOP", self, "BOTTOM") GameTooltip:AddLine(L"Conditional Bindings", NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b) GameTooltip:AddLine(L"The binding will update to reflect the value of this macro conditional.", HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b, 1) GameTooltip:AddLine((L"You may use extended macro conditionals; see %s for details."):format("|cff33DDFFhttps://townlong-yak.com/addons/opie/extended-conditionals|r"), HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b, 1) GameTooltip:AddLine((L"Example: %s."):format(GREEN_FONT_COLOR_CODE .. "[combat] ALT-C; [nomounted] CTRL-F|r"), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b) GameTooltip:Show() end) extReminder:SetScript("OnLeave", config.ui.HideTooltip) extReminder:SetScript("OnHide", extReminder:GetScript("OnLeave")) local input, scroll = config.ui.multilineInput("OPC_AlternateBindInput", alternateFrame, 335) alternateFrame.input, alternateFrame.scroll = input, scroll scroll:SetPoint("TOPLEFT", 10, -28) scroll:SetPoint("BOTTOMRIGHT", -33, 10) input:SetMaxBytes(1023) input:SetScript("OnEscapePressed", function() alternateFrame:Hide() end) input:SetScript("OnChar", function(self, c) if c == "\n" then local bind = strtrim((self:GetText():gsub("[\r\n]", ""))) if bind ~= "" then alternateFrame.apiFrame.SetBinding(alternateFrame.owner, bind) end alternateFrame:Hide() end end) end local captureFrame = CreateFrame("Button") do captureFrame:Hide() captureFrame:RegisterForClicks("AnyUp") captureFrame:SetScript("OnClick", function(_, ...) if activeCaptureButton then activeCaptureButton:Click(...) end end) end local function MapMouseButton(button) if button == "MiddleButton" then return "BUTTON3" elseif type(button) == "string" and (tonumber(button:match("^Button(%d+)")) or 1) > 3 then return button:upper() end end local function Deactivate(self) self:UnlockHighlight() self:EnableKeyboard(false) self:EnableMouseWheel(false) self:EnableGamePadButton(false) self:SetScript("OnKeyDown", nil) self:SetScript("OnGamePadButtonDown", nil) self:SetScript("OnHide", nil) captureFrame:Hide() activeCaptureButton = activeCaptureButton ~= self and activeCaptureButton or nil return self end local unbindableKeys = { UNKNOWN=1, ESCAPE=1, ALT=1, SHIFT=1, META=1, LALT=1, LCTRL=1, LSHIFT=1, LMETA=1, RALT=1, RCTRL=1, RSHIFT=1, RMETA=1, PADRSTICKUP=1, PADRSTICKDOWN=1, PADRSTICKLEFT=1, PADRSTICKRIGHT=1, PADLSTICKUP=1, PADLSTICKDOWN=1, PADLSTICKLEFT=1, PADLSTICKRIGHT=1, } local function SetBind(self, bind) if bind == "ESCAPE" then return Deactivate(self) elseif unbindableKeys[bind] then return end Deactivate(self) local bind, p = bind and ((IsAltKeyDown() and "ALT-" or "") .. (IsControlKeyDown() and "CTRL-" or "") .. (IsShiftKeyDown() and "SHIFT-" or "") .. (IsMetaKeyDown() and "META-" or "") .. bind), self:GetParent() if p and type(p.SetBinding) == "function" then p.SetBinding(self, bind) end end local function OnClick(self, button) local parent = self:GetParent() PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON) if activeCaptureButton then local deactivated, mappedButton = Deactivate(activeCaptureButton), MapMouseButton(button) if deactivated == self and (mappedButton or button == "RightButton") then SetBind(self, mappedButton) end if deactivated == self then return end elseif parent and parent.OnBindingAltClick and IsAltKeyDown() then config.ui.HideTooltip(self) return parent.OnBindingAltClick(self, button) elseif parent and parent.OnBindingShiftClick and IsShiftKeyDown() then config.ui.HideTooltip(self) return parent.OnBindingShiftClick(self, button) elseif button == "RightButton" then PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON) if parent and type(parent.SetBinding) == "function" then SetBind(self, false) end return end activeCaptureButton = self self:LockHighlight() self:EnableKeyboard(true) self:EnableGamePadButton(true) self:EnableMouseWheel(true) self:SetScript("OnKeyDown", SetBind) self:SetScript("OnGamePadButtonDown", SetBind) self:SetScript("OnHide", Deactivate) config.ui.HideTooltip(self) if parent then captureFrame:SetParent(parent.bindingContainerFrame or parent) captureFrame:SetAllPoints() captureFrame:Show() captureFrame:SetFrameLevel(self:GetFrameLevel()-1) end end local function OnWheel(self, delta) local aw = self:GetParent().AllowWheelBinding if activeCaptureButton == self and aw and (type(aw) ~= "function" or aw(self)) then SetBind(self, delta > 0 and "MOUSEWHEELUP" or "MOUSEWHEELDOWN") end end local function IsCapturingBinding(self) return activeCaptureButton == self end local function Binding_OnEnter(self) local hc, header = HIGHLIGHT_FONT_COLOR, self.bindingName if not header or IsCapturingBinding(self) or alternateFrame:IsVisible() then return end local parent = self:GetParent() GameTooltip:SetOwner(self, self.tooltipOwnerPoint or "ANCHOR_BOTTOMRIGHT") GameTooltip:AddLine(header) if parent.OnBindingAltClick then GameTooltip:AddLine(L"Alt click to set conditional binding", hc.r, hc.g, hc.b) end if parent.OnBindingShiftClick then GameTooltip:AddLine(L"Shift click to view ring macro command", hc.r, hc.g, hc.b) end if self.hasSetBinding then GameTooltip:AddLine(L"Right click to unbind", hc.r, hc.g, hc.b) end local title, text = self.tooltipTitle, self.tooltipText if title and text then GameTooltip:AddLine(" ") GameTooltip:AddLine(title) GameTooltip:AddLine(text, hc.r, hc.g, hc.b, true) end GameTooltip:Show() end local specialSymbolMap = {OPEN="[", CLOSE="]", SEMICOLON=";"} local function bindNameLookup(key) return GetBindingText(specialSymbolMap[key] or key) end local function SetBindingText(self, bind, pre, post, hasBinding) if type(bind) == "string" and bind:match("%[.*%]") then return SetBindingText(self, KR:EvaluateCmdOptions(bind), pre, post or " |cff20ff20[+]|r", true) end local bindText = bind and bind ~= "" and GetBindingText((bind:gsub("[^%-]+$", bindNameLookup))) self.hasSetBinding, self.bindCoreText = not not (hasBinding or bindText), bindText return self:SetText((pre or "") .. (bindText or L"Not bound") .. (post or "")) end local function ToggleAlternateEditor(self, bind) if alternateFrame:IsShown() and alternateFrame.owner == self then alternateFrame:Hide() else alternateFrame.apiFrame, alternateFrame.owner = self:GetParent(), self alternateFrame.caption:SetFormattedText(L"Press %s to save.", NORMAL_FONT_COLOR_CODE .. GetBindingText("ENTER") .. "|r") alternateFrame.input:SetText(bind or "") alternateFrame:SetParent(self) alternateFrame:SetFrameLevel(self:GetFrameLevel()+10) alternateFrame:ClearAllPoints() local yOfs, clipParent = 4, self:GetParent() clipParent = clipParent.clipContainer or clipParent.bindingContainerFrame or clipParent alternateFrame:SetPoint("TOP", self, "BOTTOM", 0, yOfs) if alternateFrame:GetBottom() < clipParent:GetBottom() then yOfs = -yOfs alternateFrame:ClearAllPoints() alternateFrame:SetPoint("BOTTOM", self, "TOP", 0, yOfs) end local point, relpoint, xOfs if alternateFrame:GetLeft() < clipParent:GetLeft() then point, relpoint, xOfs = "TOPLEFT", "BOTTOMLEFT", -8 elseif alternateFrame:GetRight() > clipParent:GetRight() then point, relpoint, xOfs = "TOPRIGHT", "BOTTOMRIGHT", 8 end if point then if yOfs < 0 then point, relpoint = relpoint, point end alternateFrame:ClearAllPoints() alternateFrame:SetPoint(point, self, relpoint, xOfs, yOfs) end alternateFrame:Show() alternateFrame.input:SetFocus() end end function config.createBindingButton(parent, w) local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate") btn:SetSize(w or 120, 22) btn:RegisterForClicks("AnyUp") btn:SetScript("OnClick", OnClick) btn:SetScript("OnMouseWheel", OnWheel) btn:EnableMouseWheel(false) btn:SetText(" ") btn:SetNormalFontObject(GameFontNormalSmall) btn:SetHighlightFontObject(GameFontHighlightSmall) btn:SetScript("OnEnter", Binding_OnEnter) btn:SetScript("OnLeave", config.ui.HideTooltip) local fs = btn:GetFontString() fs:SetMaxLines(1) fs:ClearAllPoints() fs:SetPoint("LEFT", 5, 0) fs:SetPoint("RIGHT", 5, 0) fs:SetJustifyH("CENTER") btn.IsCapturingBinding, btn.SetBindingText, btn.ToggleAlternateEditor = IsCapturingBinding, SetBindingText, ToggleAlternateEditor return btn end end do -- config.pulseDropdown local function cloneTex(tex) local l, sl = tex:GetDrawLayer() local r = tex:GetParent():CreateTexture(nil, l, nil, sl+1) r:SetAllPoints(tex) r:SetTexture(tex:GetTexture()) r:SetTexCoord(tex:GetTexCoord()) r:SetVertexColor(0, 0.5, 0.75) r:SetBlendMode("ADD") return r end function config.pulseDropdown(drop) if not drop.LeftA then drop.LeftA, drop.MiddleA, drop.RightA = cloneTex(drop.Left), cloneTex(drop.Middle), cloneTex(drop.Right) end local endTime = GetTime()+2 local function pulse() if drop.pulseFunc ~= pulse then return end local t = GetTime() if t >= endTime or not drop:IsVisible() then drop.MiddleA:SetAlpha(0) drop.LeftA:SetAlpha(0) drop.RightA:SetAlpha(0) drop.pulseFunc = nil return end local p = 1-(endTime-t)/2 local s = 0.5+sin(p*360*3-90)/2 drop.LeftA:SetAlpha(s) drop.MiddleA:SetAlpha(s) drop.RightA:SetAlpha(s) C_Timer.After(0, pulse) end drop.pulseFunc = pulse pulse() end end config.undo = TS:CreateUndoHandle() local function CallSwitchProfile(msg, ...) if msg == "archive-unwind" then config.undo:saveActiveProfile() end return PC:SwitchProfile(...) end local function CallSetSpecProfiles(msg, ...) if msg == "archive-unwind" then config.undo:saveSpecProfiles() end return PC:SetSpecProfiles(...) end local function CallDeleteProfile(msg, ...) if msg == "archive-unwind" then config.undo:saveSpecProfiles() end return PC:DeleteProfile(...) end function config.undo:saveSpecProfiles() if not self:search("profile-specs-init") then self:sink("profile-specs-init", CallSetSpecProfiles, PC:GetSpecProfiles()) end end function config.undo:saveActiveProfile() self:saveSpecProfiles() local name = PC:GetCurrentProfile() if not self:search("OPieProfile#" .. name) then self:push("OPieProfile#" .. name, CallSwitchProfile, name, (PC:ExportProfile(name))) end end function config.checkSVState(frame) if not PC:GetSVState() then TS:ShowAlertOverlay(frame, L"Changes will not be saved", L"World of Warcraft could not load OPie's saved variables due to a lack of memory. Try disabling other addons.\n\nAny changes you make now will not be saved.", L"Understood; edit anyway") end end local OPC_OptionSets = { { L"Behavior", {"bool", "RingAtMouse", caption=L"Center rings at mouse"}, {"bool", "ClickPriority", caption=L"Make rings top-most"}, {"bool", "CenterAction", caption=L"Quick action at ring center"}, {"bool", "MotionAction", caption=L"Quick action if mouse remains still"}, {"bool", "SliceBinding", caption=L"Per-slice bindings"}, {"bool", "ClickActivation", caption=L"Activate on left click"}, {"bool", "NoClose", caption=L"Leave open after use", depOn="ClickActivation", depValue=true, otherwise=false}, {"bool", "UseDefaultBindings", caption=L"Use default ring bindings"}, {"drop", "PadSupportMode", {"freelook", "cursor", "none", freelook=L"Camera analog stick", cursor=L"Virtual mouse cursor", none=L"None"}, caption=L"Controller interaction mode", hideFeature="GamePad"}, {"range", "IndicationOffsetX", -500, 500, 50, caption=L"Move rings right", valueFormat="%d"}, {"range", "IndicationOffsetY", -300, 300, 50, caption=L"Move rings down", valueFormat="%d"}, {"range", "MouseBucket", 5, 1, 1, caption=L"Scroll wheel sensitivity", stdLabels=true}, {"range", "RingScale", 0.1, 2, caption=L"Ring scale", valueFormat="%0.1f"}, }, { L"Appearance", {"bool", "GhostMIRings", caption=L"Nested rings"}, {"bool", "ShowKeys", caption=L"Per-slice bindings", depOn="SliceBinding", depValue=true, otherwise=false}, {"bool", "ShowCooldowns", caption=L"Show cooldown numbers", depIndicatorFeature="CooldownNumbers"}, {"bool", "ShowRecharge", caption=L"Show recharge numbers", depIndicatorFeature="CooldownNumbers"}, {"bool", "ShowShortLabels", caption=L"Show slice labels", depIndicatorFeature="ShortLabels"}, {"bool", "UseGameTooltip", caption=L"Show tooltips"}, {"bool", "HideStanceBar", caption=L"Hide stance bar", global=true}, }, { L"Animation", {"bool", "XTAnimation", caption=L"Animate transitions"}, {"bool", "MISpinOnHide", caption=L"Outward spiral on hide", depOn="XTAnimation", depValue=true, otherwise=false}, {"bool", "XTPointerSnap", caption=L"Snap pointer to mouse cursor"}, {"bool", "MIScale", caption=L"Enlarge selected slice"}, } } frame = TS:CreateOptionsPanel("OPie", nil, {forceRootVersion=true}) frame.version:SetFormattedText("%s", PC:GetVersion() or "") frame.desc:SetText(L"Customize OPie's appearance and behavior. Right clicking a checkbox restores it to its default state." .. (MODERN and "\n" .. L"Profiles activate automatically when you switch character specializations." or "")) local OPC_Profile = CreateFrame("Frame", "OPC_Profile", frame, "UIDropDownMenuTemplate") OPC_Profile:SetPoint("TOPLEFT", frame, 0, -80) UIDropDownMenu_SetWidth(OPC_Profile, 200) local OPC_OptionDomain = CreateFrame("Frame", "OPC_OptionDomain", frame, "UIDropDownMenuTemplate") OPC_OptionDomain:SetPoint("LEFT", OPC_Profile, "RIGHT") UIDropDownMenu_SetWidth(OPC_OptionDomain, 250) local OPC_WidgetControl, OPC_AlterOption, OPC_BlockInput = {} do -- Widget construction local build = {} local function notifyChange(self, ...) if not OPC_BlockInput then OPC_AlterOption(self, self.id, self:IsObjectType("Slider") and self:GetValue() or (not not self:GetChecked()), ...) end end local function OnStateChange(self) local a = self:IsEnabled() and 1 or 0.6 self.text:SetVertexColor(a,a,a) end local function dropSelect(_, nv, drop) local dd = OPC_WidgetControl[drop] OPC_AlterOption(drop, dd[2], nv) end local function dropInitialize(self) local dda = OPC_WidgetControl[self][3] local info = {func=dropSelect, arg2=self, minWidth=self:GetWidth()-40} for i=1,#dda do local k = dda[i] info.text, info.arg1, info.checked = dda[k], k, OPC_WidgetControl[self].cv == k UIDropDownMenu_AddButton(info) end end local function dropSetValue(self, v) local dd = OPC_WidgetControl[self] dd.cv = v UIDropDownMenu_SetText(self, dd[3][v]) end local function anchor_OnVisibilityChange(self) local v = OPC_WidgetControl[self] local r, y = v.anchorOffsetRelFrame, v[self:IsVisible() and "anchorOffsetVisible" or "anchorOffsetHidden"] self:SetPoint("TOPLEFT", r, "TOPLEFT", 0, y) self:SetPoint("TOPRIGHT", r, "TOPRIGHT", 0, y) end function build.bool(v, ofsY, halfpoint, rowHeight, rframe) local b = TS:CreateOptionsCheckButton(nil, frame) b:RegisterForClicks("LeftButtonUp", "RightButtonUp") b:SetMotionScriptsWhileDisabled(true) b.id, b.text, b.desc = v[2], b.Text, v b:SetPoint("TOPLEFT", rframe, "TOPLEFT", halfpoint and 315 or 15, ofsY) b:SetScript("OnClick", notifyChange) hooksecurefunc(b, "SetEnabled", OnStateChange) return b, ofsY - (halfpoint and rowHeight or 0), not halfpoint, halfpoint and 0 or 20 end function build.range(v, ofsY, halfpoint, rowHeight, rframe) if halfpoint then ofsY = ofsY - rowHeight end local s, leftMargin, centerLine = TS:CreateOptionsSlider(frame, nil, 212) s:SetPoint("TOPLEFT", rframe, "TOPLEFT", 319-leftMargin, ofsY-5) s.text:SetPoint("LEFT", rframe, "TOPLEFT", 44, ofsY-5-centerLine) s.text:Show() s:SetValueStep(v[5] or 0.1) s:SetMinMaxValues(v[3] < v[4] and v[3] or -v[3], v[4] > v[3] and v[4] or -v[4]) s:SetObeyStepOnDrag(true) s:SetScript("OnValueChanged", notifyChange) s.id, s.desc = v[2], v if not v.stdLabels then s.lo:SetText(v[3]) s.hi:SetText(v[4]) end return s, ofsY - 20, false, 0 end function build.drop(v, ofsY, halfpoint, rowHeight, rframe) local f = CreateFrame("Frame", "OPC_Drop" .. v[2], frame, "UIDropDownMenuTemplate") if halfpoint then ofsY = ofsY - rowHeight end f:SetPoint("TOPLEFT", rframe, "TOPLEFT", 300, ofsY-4) UIDropDownMenu_SetWidth(f, 210) UIDropDownMenu_SetText(f, "Chicken-doom") f.initialize, f.refresh = dropInitialize, dropSetValue f.text = f:CreateFontString(nil, "OVERLAY", "GameFontHighlight") f.text:SetPoint("TOPLEFT", rframe, "TOPLEFT", 44, ofsY-12.5) f.text:SetText(v.caption) return f, ofsY - 32, false, 0 end function build.anchor(v, oY, cY, rframe) local f = CreateFrame("Frame", nil, v.widget) f:SetHeight(1) OPC_WidgetControl[f], v.anchorOffsetVisible, v.anchorOffsetHidden, v.anchorOffsetRelFrame = v, cY, oY, rframe f:SetScript("OnHide", anchor_OnVisibilityChange) f:SetScript("OnShow", anchor_OnVisibilityChange) anchor_OnVisibilityChange(f) return f end local cY, halfpoint, rframe, rowHeight = -100, false, frame for _, v in ipairs(OPC_OptionSets) do v.label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") v.label:SetPoint("TOP", rframe, "TOP", -50, cY-15) v.label:SetJustifyH("LEFT") v.label:SetPoint("LEFT", rframe, "LEFT", 16, 0) v.label:SetText(v[1]) cY, halfpoint, rowHeight = cY - 36, false, 0 for j=2,#v do local vj, oY = v[j], cY - (halfpoint and rowHeight or 0) vj.widget, cY, halfpoint, rowHeight = build[vj[1]](vj, cY, halfpoint, rowHeight, rframe) OPC_WidgetControl[vj.widget] = vj if vj.hideFeature then cY, rframe = 0, build.anchor(vj, oY, cY, rframe) end end if halfpoint then cY = cY - rowHeight end end end local OPC_AppearanceFactory = CreateFrame("Frame", "OPC_AppearanceDropdown", frame, "UIDropDownMenuTemplate") OPC_AppearanceFactory:SetPoint("LEFT", OPC_OptionSets[2].label, "LEFT", 280, -2) UIDropDownMenu_SetWidth(OPC_AppearanceFactory, 200) T.OPC_RingScopePrefixes = { [30] = "|cff25bdff", [20] = "|c" .. RAID_CLASS_COLORS[select(2,UnitClass("player"))].colorStr, [10] = "|cffabffd5", } local OR_CurrentOptionsDomain local function OPC_UpdateControlReqs(v) local enabled, disabledHint = true, nil if v.depOn then enabled = PC:GetOption(v.depOn, OR_CurrentOptionsDomain) == v.depValue elseif v.depIndicatorFeature then enabled = T.OPieUI:DoesIndicatorConstructorSupport(PC:GetOption("IndicatorFactory", OR_CurrentOptionsDomain), v.depIndicatorFeature) disabledHint = L"Not supported by selected appearance." end v.widget:SetEnabled(enabled) -- It just so happens they're all checkboxes. This will explode when they are not. if enabled then v.widget:SetChecked(PC:GetOption(v[2], OR_CurrentOptionsDomain) or nil) v.widget.tooltipText = nil else v.widget:SetChecked(v.otherwise or nil) v.widget.tooltipText = disabledHint end end function OPC_AlterOption(widget, option, newval, ...) local control = OPC_WidgetControl[widget] if (...) == "RightButton" then newval = nil end if control[1] == "range" and control[3] > control[4] and type(newval) == "number" then newval = -newval end config.undo:saveActiveProfile() PC:SetOption(option, newval, OR_CurrentOptionsDomain) local setval = PC:GetOption(option, OR_CurrentOptionsDomain) if widget:IsObjectType("Slider") then local text, vf = widget.desc.caption, widget.desc.valueFormat if vf then text = text .. " |cffffd500(" .. vf:format(setval) .. ")|r" end widget.text:SetText(text) OPC_BlockInput = true widget:SetValue(setval * (control[3] > control[4] and -1 or 1)) OPC_BlockInput = false elseif control[1] == "drop" then widget:refresh(newval) elseif setval ~= newval then widget:SetChecked(setval and 1 or nil) end for _,set in ipairs(OPC_OptionSets) do for j=2,#set do local v = set[j] if v.depOn == option then OPC_UpdateControlReqs(v) end end end end local function OPC_OptionDomain_click(_, ringName) OR_CurrentOptionsDomain = ringName or nil frame.resetOnHide = nil frame.refresh() end local function OPC_OptionDomain_Format(key, list) return list[key], OR_CurrentOptionsDomain == (key or nil) end function OPC_OptionDomain:initialize() local list = {false, [false]=L"Defaults for all rings"} local ct = T.OPC_RingScopePrefixes for key, name, scope in PC:IterateRings(IsAltKeyDown()) do local color = ct and ct[scope] or "|cffacd7e6" list[#list+1], list[key] = key, (L"Ring: %s"):format(color .. (name or key) .. "|r") end XU:Create("ScrollableDropDownList", 1, list, OPC_OptionDomain_Format, OPC_OptionDomain_click) end function OPC_OptionDomain:text() local label = L"Defaults for all rings" if OR_CurrentOptionsDomain then local name, key = PC:GetRingInfo(OR_CurrentOptionsDomain) label = (L"Ring: %s"):format("|cffaaffff" .. (name or key) .."|r") end UIDropDownMenu_SetText(self, label) end local function OPC_Profile_FormatName(ident) return ident == "default" and L"default" or ident end do -- OPC_Profile:initialize local curProfile local function dup(n, v) if n > 0 then return v, dup(n-1, v) end end local function prependCount(...) return select("#", ...), ... end local function OPC_Profile_format(ident, list) return list[ident], curProfile == ident, ident end local function OPC_Profile_switch(_, ident) config.undo:saveSpecProfiles() PC:SwitchProfile(ident) end local function OPC_Profile_new_callback(self, text, apply, _frame) local name = text:match("^%s*(.-)%s*$") if name == "" or PC:ProfileExists(name) then if apply then self:SetText("") end return false elseif apply then config.undo:saveSpecProfiles() PC:SwitchProfile(name, true) if not config.undo:search("OPieProfile#" .. name) then config.undo:push("OPieProfile#" .. name, CallDeleteProfile, name) end end return true end local function OPC_Profile_new(_, _, frame) TS:ShowPromptOverlay(frame, L"Create a New Profile", L"New profile name:", L"Profiles save options and ring bindings.", L"Create Profile", OPC_Profile_new_callback) end local function OPC_Profile_delete() config.undo:saveActiveProfile() PC:DeleteProfile(PC:GetCurrentProfile()) end local function OPC_Profile_assignAllSpecs(_, curProfile) config.undo:saveSpecProfiles() PC:SetSpecProfiles(dup(select("#", PC:GetSpecProfiles()), curProfile)) end function OPC_Profile:initialize() local hasPartialSpecProfiles, ns, p1, p2, p3, p4, plist = false, prependCount(PC:GetSpecProfiles()) curProfile, plist, ns = PC:GetCurrentProfile(), PC:GetAllProfiles(), ns > 1 and ns or 0 for k=1, #plist do local ident = plist[k] local name, suf, ni = OPC_Profile_FormatName(ident), "", 0 for i=1, ns do if ident == select(i, p1, p2, p3, p4) then if MODERN then local _, _, _, ico = GetSpecializationInfo(i) ni, suf = ni + 1, (ni > 0 and suf .. "|T" or " |T") .. (ico or "Interface/Icons/INV_Misc_QuestionMark") .. ":16:16:4:0:64:64:4:60:4:60|t" elseif CF_WRATH then ni, suf = ni + 1, suf .. " |cffff99ff[" .. i .."]|r" end end end if ni > 0 and ni < ns then name, hasPartialSpecProfiles = name .. suf, true end plist[ident] = name end XU:Create("ScrollableDropDownList", 1, plist, OPC_Profile_format, OPC_Profile_switch, true) local info = {arg2=self:GetParent(), text="", disabled=true, notCheckable=true, justifyH="CENTER"} UIDropDownMenu_AddButton(info) info.text, info.disabled, info.func, info.arg1 = L"Assign to all specializations", not hasPartialSpecProfiles, OPC_Profile_assignAllSpecs, curProfile if ns > 1 then UIDropDownMenu_AddButton(info) end info.text, info.minWidth, info.func, info.arg1, info.disabled = L"Create a new profile", self:GetWidth()-40, OPC_Profile_new, nil, nil UIDropDownMenu_AddButton(info) info.text, info.func = curProfile ~= "default" and L"Delete current profile" or L"Restore default settings", OPC_Profile_delete UIDropDownMenu_AddButton(info) end end function OPC_Profile:text() UIDropDownMenu_SetText(self, L"Profile" .. ": " .. OPC_Profile_FormatName(PC:GetCurrentProfile())) end function OPC_AppearanceFactory:formatText(key, outOfDate, name) name = name or T.OPieUI:GetIndicatorConstructorName(key) if not name then name = "|cffa0a0a0*[" .. T.OPieUI:GetIndicatorConstructorName() .. "]|r" end if outOfDate then name = "|cffff6060" .. name .. "|r" end if key == "mirage" then name = "|cff00e800" .. name .. "|r" elseif key == "_" then name = L"Not customized" .. " (|cffb0b0b0" .. name .. "|r)" end return name end function OPC_AppearanceFactory:text() local key, own = PC:GetOption("IndicatorFactory", OR_CurrentOptionsDomain) UIDropDownMenu_SetText(self, OR_CurrentOptionsDomain and own == nil and L"Use global setting" or self:formatText(key, false)) end local function OPC_AppearanceFactory_set(_, key) PC:SetOption("IndicatorFactory", key, OR_CurrentOptionsDomain) OPC_AppearanceFactory:text() for _,set in ipairs(OPC_OptionSets) do for j=2,#set do local v = set[j] if v.depIndicatorFeature then OPC_UpdateControlReqs(v) end end end end function OPC_AppearanceFactory:initialize() local info = {func=OPC_AppearanceFactory_set, minWidth=UIDROPDOWNMENU_OPEN_MENU:GetWidth()-40, tooltipOnButton=true} local current, own = PC:GetOption("IndicatorFactory", OR_CurrentOptionsDomain) for k, name, outOfDate in T.OPieUI:EnumerateRegisteredIndicatorConstructors() do name = self:formatText(k, outOfDate, name) if k == "_" then UIDropDownMenu_AddSeparator() end if outOfDate then info.tooltipTitle, info.tooltipText = "|cffff2020" .. L"Update required", L"This appearance may not support all OPie features." else info.tooltipTitle, info.tooltipText = nil end info.arg1, info.text, info.checked = k, name, k == own or (own == nil and not OR_CurrentOptionsDomain and current == k) UIDropDownMenu_AddButton(info) end if OR_CurrentOptionsDomain then info.text, info.arg1, info.checked = L"Use global setting", nil, own == nil info.tooltipTitle, info.tooltipText = nil UIDropDownMenu_AddButton(info) end end function frame.refresh() OPC_BlockInput = true if OR_CurrentOptionsDomain and not PC:GetRingInfo(OR_CurrentOptionsDomain) then OR_CurrentOptionsDomain = nil end for _, v in pairs(OPC_OptionSets) do v.label:SetText(v[1]) end OPC_OptionDomain:text() OPC_Profile:text() OPC_AppearanceFactory:text() OPC_AppearanceFactory:SetShown(T.OPieUI:HasMultipleIndicatorConstructors()) for _, set in pairs(OPC_OptionSets) do for j=2,#set do local v, opttype, option = set[j], set[j][1], set[j][2] if opttype == "range" then v.widget:SetValue(PC:GetOption(option) * (v[3] < v[4] and 1 or -1)) local text = v.caption if v.valueFormat then local vf = v.valueFormat:format(v.widget:GetValue()) text = text .. " |cffffd500(" .. vf .. ")|r" end v.widget.text:SetText(text) elseif opttype == "bool" then v.widget:SetChecked(PC:GetOption(option, OR_CurrentOptionsDomain) or nil) v.widget.text:SetText(v.caption) elseif opttype == "drop" then v.widget:refresh(PC:GetOption(option, OR_CurrentOptionsDomain) or nil) end if v.depOn or v.depIndicatorFeature then OPC_UpdateControlReqs(v) end if v.hideFeature == "GamePad" and not C_GamePad.IsEnabled() then v.widget:Hide() else v.widget:SetShown(not v.global or OR_CurrentOptionsDomain == nil) end end end OPC_BlockInput = false config.checkSVState(frame) end local function resetView() OR_CurrentOptionsDomain = nil end frame.cancel, frame.okay = resetView, resetView function frame.default() config.undo:saveActiveProfile() PC:ResetOptions(true) frame.refresh() end frame:SetScript("OnShow", frame.refresh) frame:SetScript("OnHide", function() if frame.resetOnHide then OR_CurrentOptionsDomain, frame.resetOnHide = nil end end) function EV:OPIE_PROFILE_SWITCHED(_new, _old) if frame:IsVisible() then frame.refresh() end end function T.ShowOPieOptionsPanel(ringKey) frame:OpenPanel() OPC_OptionDomain_click(nil, ringKey) frame.resetOnHide = true config.pulseDropdown(OPC_OptionDomain) end function OPie_OpenSettings() frame:OpenPanel() end