--========================================================-- -- Scorpio PopupDialog Widget -- -- -- -- Author : kurapica125@outlook.com -- -- Create Date : 2020/02/04 -- --========================================================-- --========================================================-- Scorpio "Scorpio.Widget.PopupDialog" "1.0.0" --========================================================-- export { running = coroutine.running, yield = coroutine.yield, resume = coroutine.resume, OpenColorPicker = OpenColorPicker, POPUP_TYPE_ALERT = 1, POPUP_TYPE_INPUT = 2, POPUP_TYPE_CONFIRM = 3, POPUP_TYPE_COLORPICKER = 4, POPUP_TYPE_OPACITYPICKER = 5, POPUP_TYPE_RANGEPICKER = 6, POPUP_TYPE_MACROCONDITION = 7, _PopupQueue = Queue(), } ----------------------------------------------------------- -- Popup Widget -- ----------------------------------------------------------- __Sealed__() class "AlertDialog" (function(_ENV) inherit "Dialog" __Bubbling__{ OkayButton = "OnClick", CloseButton = "OnClick" } event "OnOkay" __Template__{ Message = FontString, OkayButton = UIPanelButton, } function __ctor(self) end end) __Sealed__() class "InputDialog" (function(_ENV) inherit "Dialog" __Bubbling__{ ConfirmButton = "OnClick", InputBox = "OnEnterPressed" } event "OnConfirm" __Bubbling__{ CancelButton = "OnClick", InputBox = "OnEscapePressed", CloseButton = "OnClick" } event "OnCancel" __Template__{ Message = FontString, InputBox = InputBox, ConfirmButton = UIPanelButton, CancelButton = UIPanelButton, } function __ctor(self) end end) __Sealed__() class "ConfirmDialog" (function(_ENV) inherit "Dialog" __Bubbling__{ ConfirmButton = "OnClick" } event "OnConfirm" __Bubbling__{ CancelButton = "OnClick", CloseButton = "OnClick" } event "OnCancel" __Template__{ Message = FontString, ConfirmButton = UIPanelButton, CancelButton = UIPanelButton, } function __ctor(self) end end) __Sealed__() class "RangeDialog" (function(_ENV) inherit "Dialog" __Bubbling__{ ConfirmButton = "OnClick" } event "OnConfirm" __Bubbling__{ CancelButton = "OnClick", CloseButton = "OnClick" } event "OnCancel" __Template__{ Message = FontString, RangeBar = TrackBar, ConfirmButton = UIPanelButton, CancelButton = UIPanelButton, } function __ctor(self) end end) __Sealed__() class "MacroConditionDialog" (function(_ENV) inherit "Dialog" import "System.Text" __Bubbling__{ ConfirmButton = "OnClick", InputBox = "OnEnterPressed" } event "OnConfirm" __Bubbling__{ CancelButton = "OnClick", InputBox = "OnEscapePressed", CloseButton = "OnClick" } event "OnCancel" local L = Scorpio("Scorpio")._Locale local targetData = { "target", "pet", "vehicle", "focus", "mouseover", "targettarget", "focustarget", "boss1", "arena1", "party1", "raid1" } local conditionData = { { Condition = "canexitvehicle", Text = L["Player is in a vehicle and can exit it at will."], }, { Condition = "combat", Text = L["Player is in combat."], }, { Condition = "dead", Text = L["Conditional target exists and is dead."], }, { Condition = "exists", Text = L["Conditional target exists."], }, { Condition = "flyable", Text = L["The player can use a flying mount in this zone (though incorrect in Wintergrasp during a battle)."], }, { Condition = "flying", Text = L["Mounted or in flight form AND in the air."], }, { Condition = "form", Text = L["The player is in any form."], }, { Condition = "form:0", Text = L["The player is not in any form."], }, { Condition = "form:1", Text = L["The player is in form 1." .. (GetShapeshiftFormInfo(1) and GetSpellLink(select(4, GetShapeshiftFormInfo(1))) or "")], }, { Condition = "form:2", Text = L["The player is in form 2." .. (GetShapeshiftFormInfo(2) and GetSpellLink(select(4, GetShapeshiftFormInfo(2))) or "")], }, { Condition = "form:3", Text = L["The player is in form 3." .. (GetShapeshiftFormInfo(3) and GetSpellLink(select(4, GetShapeshiftFormInfo(3))) or "")], }, { Condition = "form:4", Text = L["The player is in form 4." .. (GetShapeshiftFormInfo(4) and GetSpellLink(select(4, GetShapeshiftFormInfo(4))) or "")], }, { Condition = "group", Text = L["Player is in a party."], }, { Condition = "group:raid", Text = L["Player is in a raid."], }, { Condition = "harm", Text = L["Conditional target exists and can be targeted by harmful spells (e.g. [Fireball])."], }, { Condition = "help", Text = L["Conditional target exists and can be targeted by helpful spells (e.g. [Heal])."], }, { Condition = "indoors", Text = L["Player is indoors."], }, { Condition = "mounted", Text = L["Player is mounted."], }, { Condition = "outdoors", Text = L["Player is outdoors."], }, { Condition = "party", Text = L["Conditional target exists and is in your party."], }, { Condition = "pet", Text = L["The player has a pet."], }, { Condition = "petbattle", Text = L["Currently participating in a pet battle."], }, { Condition = "raid", Text = L["Conditional target exists and is in your raid/party."], }, { Condition = "resting", Text = L["Player is currently resting."], }, { Condition = "spec:1", Text = L["Player's active the first specialization group (spec, talents and glyphs)."], }, { Condition = "spec:2", Text = L["Player's active the second specialization group (spec, talents and glyphs)."], }, { Condition = "stealth", Text = L["Player is stealthed."], }, { Condition = "swimming", Text = L["Player is swimming."], }, { Condition = "vehicleui", Text = L["Player has vehicle UI."], }, { Condition = "extrabar", Text = L["Player currently has an extra action bar/button."], }, { Condition = "overridebar", Text = L["Player's main action bar is currently replaced by the override action bar."], }, { Condition = "possessbar", Text = L["Player's main action bar is currently replaced by the possess action bar."], }, { Condition = "shapeshift", Text = L["Player's main action bar is currently replaced by a temporary shapeshift action bar."], }, { Condition = "mod:shift", Text = L["Player's holding the shift key"], }, { Condition = "mod:ctrl", Text = L["Player's holding the ctrl key"], }, { Condition = "mod:alt", Text = L["Player's holding the alt key"], }, { Condition = "cursor", Text = L["Player's mouse cursor is currently holding an item/ability/macro/etc"], }, } TEMPLATE_CHOOSER = TemplateString[[ [@condition] ]] TEMPLATE_VIEWER = TemplateString[[

@L["The conditional target :"]

@for _, tar in ipairs(targetData) do [@tar] @end


@L["The macro conditions :"]

@for _, cond in ipairs(conditionData) do

[no] [@cond.Condition] - @cond.Text

@end ]] local function OnShow(self) self.Selection = { "@player" } self:GetChild("InputBox"):SetText("") end local function OnViewerHyperlinkClick(self, data) self = self:GetParent() local selection = self.Selection if data:match("^@") then -- conditional target if data == selection[1] then selection[1] = "@player" else selection[1] = data end else local negData local matched = false if data:match("^no") then negData = data:sub(3, -1) else negData = "no" .. data end if data:match("form") then for i, v in ipairs(selection) do if v:match("form") then matched = true if(v == data) then tremove(selection, i) elseif data == "form" or data == "noform" then selection[i] = data else local forms if v == "noform" then forms = {[0] = true, false, false, false, false} elseif v == "form" then forms = {[0] = false, true, true, true, true} elseif v:match("^no") then forms = {[0] = true, true, true, true, true} v:gsub("%d+", function(num) forms[tonumber(num)] = false end) else forms = {[0] = false, false, false, false, false} v:gsub("%d+", function(num) forms[tonumber(num)] = true end) end if data:match("^no") then data:gsub("%d+", function(num) forms[tonumber(num)] = false end) else data:gsub("%d+", function(num) forms[tonumber(num)] = true end) end local cnt = 0 for j = 1, #forms do if forms[j] then cnt = cnt + 1 end end if cnt == 0 then selection[i] = "noform" elseif cnt == #forms then selection[i] = "form" elseif cnt <= 2 then data = "" for j = 0, #forms do if forms[j] then if data ~= "" then data = data .. "/" .. j else data = data .. j end end end selection[i] = "form:" .. data else data = "" for j = 0, #forms do if not forms[j] then if data ~= "" then data = data .. "/" .. j else data = data .. j end end end selection[i] = "noform:" .. data end end break end end else for i, v in ipairs(selection) do if v == data then matched = true tremove(selection, i) break elseif v == negData then matched = true selection[i] = data end end end if not matched then tinsert(selection, data) end end local text = "" for i, v in ipairs(selection) do if i == 1 then if v ~= "@player" then text = v end else text = text .. (text ~= "" and ", " or "") .. v end end self:GetChild("InputBox"):SetText("[" .. text .. "]") end __Template__{ Message = FontString, InputBox = InputBox, Viewer = HtmlViewer, ConfirmButton = UIPanelButton, CancelButton = UIPanelButton, } function __ctor(self) self:Hide() local viewer = self:GetChild("Viewer") viewer.OnHyperlinkClick = viewer.OnHyperlinkClick + OnViewerHyperlinkClick self.OnShow = self.OnShow + OnShow viewer:SetText(TEMPLATE_VIEWER{ L = L, targetData = targetData, conditionData = conditionData }) end end) ----------------------------------------------------------- -- Dialog Style - Default -- ----------------------------------------------------------- Style.UpdateSkin("Default", { [AlertDialog] = { size = Size(320, 120), resizable = false, frameStrata = "FULLSCREEN_DIALOG", location = { Anchor("TOP", 0, -120) }, -- Childs Message = { location = { Anchor("TOP", 0, -28) }, width = 290, drawLayer = "ARTWORK", fontObject = GameFontHighlight, }, OkayButton = { text = _G.OKAY or "Okay", location = { Anchor("BOTTOM", 0, 16) }, } }, [InputDialog] = { size = Size(360, 130), resizable = false, frameStrata = "FULLSCREEN_DIALOG", location = { Anchor("TOP", 0, -120) }, -- Childs Message = { location = { Anchor("TOP", 0, -16) }, width = 290, drawLayer = "ARTWORK", fontObject = GameFontHighlight, }, InputBox = { location = { Anchor("TOP", 0, -50) }, size = Size(240, 32), autoFocus = true, }, ConfirmButton = { text = _G.OKAY or "Okay", location = { Anchor("BOTTOMRIGHT", -4, 16, nil, "BOTTOM") }, }, CancelButton = { text = _G.CANCEL or "Cancel", location = { Anchor("BOTTOMLEFT", 4, 16, nil, "BOTTOM") }, } }, [ConfirmDialog] = { size = Size(360, 100), resizable = false, frameStrata = "FULLSCREEN_DIALOG", location = { Anchor("TOP", 0, -120) }, -- Childs Message = { location = { Anchor("TOP", 0, -16) }, width = 290, drawLayer = "ARTWORK", fontObject = GameFontHighlight, }, ConfirmButton = { text = _G.OKAY or "Okay", location = { Anchor("BOTTOMRIGHT", -4, 16, nil, "BOTTOM") }, }, CancelButton = { text = _G.CANCEL or "Cancel", location = { Anchor("BOTTOMLEFT", 4, 16, nil, "BOTTOM") }, } }, [RangeDialog] = { size = Size(360, 130), resizable = false, frameStrata = "FULLSCREEN_DIALOG", location = { Anchor("TOP", 0, -120) }, -- Childs Message = { location = { Anchor("TOP", 0, -16) }, width = 290, drawLayer = "ARTWORK", fontObject = GameFontHighlight, }, RangeBar = { location = { Anchor("TOP", 0, -50) }, size = Size(240, 16), }, ConfirmButton = { text = _G.OKAY or "Okay", location = { Anchor("BOTTOMRIGHT", -4, 16, nil, "BOTTOM") }, }, CancelButton = { text = _G.CANCEL or "Cancel", location = { Anchor("BOTTOMLEFT", 4, 16, nil, "BOTTOM") }, } }, [MacroConditionDialog] = { size = Size(560, 430), resizable = false, frameStrata = "FULLSCREEN_DIALOG", location = { Anchor("TOP", 0, -120) }, -- Childs Message = { location = { Anchor("TOP", 0, -16) }, width = 490, drawLayer = "ARTWORK", fontObject = GameFontHighlight, }, InputBox = { location = { Anchor("TOP", 0, -40) }, size = Size(500, 32), autoFocus = true, }, Viewer = { location = { Anchor("TOP", 0, -4, "InputBox", "BOTTOM") }, size = Size(500, 300), }, ConfirmButton = { text = _G.OKAY or "Okay", location = { Anchor("BOTTOMRIGHT", -4, 16, nil, "BOTTOM") }, }, CancelButton = { text = _G.CANCEL or "Cancel", location = { Anchor("BOTTOMLEFT", 4, 16, nil, "BOTTOM") }, } }, }) ----------------------------------------------------------- -- Popup API -- ----------------------------------------------------------- local _CurrentPopup OpacityFrame:HookScript("OnHide", function(self) if _CurrentPopup == self then if OpacityFrame.saveOpacityFunc then OpacityFrame.saveOpacityFunc() end end end) function showPopup() if _CurrentPopup then return end local qtype, message, thread = _PopupQueue:Dequeue(3) if not qtype then return end local isthread = type(thread) == "thread" if qtype == POPUP_TYPE_ALERT then _CurrentPopup = AlertDialog("Scorpio_AlertDialog") _CurrentPopup.OnOkay = function(self) self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread) elseif thread then return thread() end end _CurrentPopup:GetChild("Message"):SetText(message) _CurrentPopup:Show() elseif qtype == POPUP_TYPE_INPUT then _CurrentPopup = InputDialog("Scorpio_InputDialog") _CurrentPopup:GetChild("InputBox"):SetText("") _CurrentPopup.OnConfirm = function(self) local text = self:GetChild("InputBox"):GetText() if text and strtrim(text) == "" then text = nil end self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread, text) else return thread(text) end end _CurrentPopup.OnCancel = function(self) self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread) else return thread() end end _CurrentPopup:GetChild("Message"):SetText(message) _CurrentPopup:Show() elseif qtype == POPUP_TYPE_RANGEPICKER then local min, max, step, v = _PopupQueue:Dequeue(4) _CurrentPopup = RangeDialog("Scorpio_RangeDialog") _CurrentPopup:GetChild("RangeBar"):SetMinMaxValues(min, max) _CurrentPopup:GetChild("RangeBar"):SetValueStep(step) _CurrentPopup:GetChild("RangeBar"):SetValue(v) _CurrentPopup.OnConfirm = function(self) local value = self:GetChild("RangeBar"):GetValue() self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread, value) else return thread(value) end end _CurrentPopup.OnCancel = function(self) self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread) else return thread() end end _CurrentPopup:GetChild("Message"):SetText(message) _CurrentPopup:Show() elseif qtype == POPUP_TYPE_CONFIRM then _CurrentPopup = ConfirmDialog("Scorpio_ConfirmDialog") _CurrentPopup.OnConfirm = function(self) self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread, true) else return thread(true) end end _CurrentPopup.OnCancel = function(self) self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread, false) else return thread(false) end end _CurrentPopup:GetChild("Message"):SetText(message) _CurrentPopup:Show() elseif qtype == POPUP_TYPE_COLORPICKER then _CurrentPopup = ColorPickerFrame local color = ColorType(message) while ColorPickerFrame:IsShown() or InCombatLockdown() do Next() end if isthread then local firstopen = true OpenColorPicker { hasOpacity = true, r = color.r, g = color.g, b = color.b, opacity = 1 - color.a, swatchFunc = function() if firstopen or ColorPickerFrame:IsShown() then firstopen = false return end Next(showPopup) _CurrentPopup = nil local r,g,b = ColorPickerFrame:GetColorRGB() local a = 1 - OpacitySliderFrame:GetValue() return resume(thread, Color(r, g, b, a)) end, cancelFunc = function() Next(showPopup) _CurrentPopup = nil return resume(thread, color) end, } else OpenColorPicker { hasOpacity = true, r = color.r, g = color.g, b = color.b, opacity = 1 - color.a, swatchFunc = function() if not ColorPickerFrame:IsShown() then Next(showPopup) _CurrentPopup = nil end local r,g,b = ColorPickerFrame:GetColorRGB() local a = 1 - OpacitySliderFrame:GetValue() return thread(Color(r, g, b, a)) end, opacityFunc = function() local r,g,b = ColorPickerFrame:GetColorRGB() local a = 1 - OpacitySliderFrame:GetValue() return thread(Color(r, g, b, a)) end, cancelFunc = function() Next(showPopup) _CurrentPopup = nil return thread(color) end, } end elseif qtype == POPUP_TYPE_OPACITYPICKER then _CurrentPopup = OpacityFrame local opacity = message while OpacityFrame:IsShown() or InCombatLockdown() do Next() end OpacityFrame:ClearAllPoints() OpacityFrame:SetPoint("BOTTOMLEFT", GetCursorPosition()) OpacityFrameSlider:SetValue(1 - opacity) if isthread then OpacityFrame.opacityFunc = nil OpacityFrame.saveOpacityFunc = function() if not _CurrentPopup then return end Next(showPopup) _CurrentPopup = nil return resume(thread, 1- OpacityFrameSlider:GetValue()) end else OpacityFrame.opacityFunc = function() return thread(1 - OpacityFrameSlider:GetValue()) end OpacityFrame.saveOpacityFunc = function() if not _CurrentPopup then return end Next(showPopup) _CurrentPopup = nil end end OpacityFrame:Show() elseif qtype == POPUP_TYPE_MACROCONDITION then _CurrentPopup = MacroConditionDialog("Scorpio_MacroConditionDialog") _CurrentPopup:GetChild("InputBox"):SetText("") _CurrentPopup.OnConfirm = function(self) local text = self:GetChild("InputBox"):GetText() if text and strtrim(text) == "" then text = nil end self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread, text) else return thread(text) end end _CurrentPopup.OnCancel = function(self) self:Hide() Next(showPopup) _CurrentPopup = nil if isthread then return resume(thread) else return thread() end end _CurrentPopup:GetChild("Message"):SetText(message) _CurrentPopup:Show() end end function queuePopup(qtype, message, func, min, max, step, value) local current = func or running() if not current then if qtype == POPUP_TYPE_INPUT then error("Usage: Input(message[, callback]) - the api must be used in a coroutine or with a callback", 4) elseif qtype == POPUP_TYPE_CONFIRM then error("Usage: Confirm(message[, callback]) - the api must be used in a coroutine or with a callback", 4) elseif qtype == POPUP_TYPE_COLORPICKER then error("Usage: PickColor([callback]) - the api must be used in a coroutine or with a callback", 4) elseif qtype == POPUP_TYPE_OPACITYPICKER then error("Usage: PickOpacity([callback]) - the api must be used in a coroutine or with a callback", 4) elseif qtype == POPUP_TYPE_RANGEPICKER then error("Usage: PickRange(message, min, max, step[, value[, callback]]) - the api must be used in a coroutine or with a callback", 4) elseif qtype == POPUP_TYPE_MACROCONDITION then error("Usage: PickMacroCondition(message[, callback]) - the api must be used in a coroutine or with a callback") end end _PopupQueue:Enqueue(qtype, message, current) if qtype == POPUP_TYPE_RANGEPICKER then _PopupQueue:Enqueue(min, max, step, value or min) end Next(showPopup) return not func and current and yield() end __Static__() __Arguments__{ String, Function/nil } function Scorpio.Alert(message, func) local value = queuePopup(POPUP_TYPE_ALERT, message, func) return value end __Static__() __Arguments__{ String, Function/nil } function Scorpio.Input(message, func) local value = queuePopup(POPUP_TYPE_INPUT, message, func) return value end __Static__() __Arguments__{ String, Function/nil } function Scorpio.Confirm(message, func) local value = queuePopup(POPUP_TYPE_CONFIRM, message, func) return value end __Static__() __Arguments__{ ColorType/Color(1, 1, 1, 1), Function/nil } function Scorpio.PickColor(color, func) local value = queuePopup(POPUP_TYPE_COLORPICKER, color, func) return value end __Static__() __Arguments__{ ColorFloat/1, Function/nil } function Scorpio.PickOpacity(opacity, func) local value = queuePopup(POPUP_TYPE_OPACITYPICKER, opacity, func) return value end __Static__() __Arguments__{ String, Number, Number, Number, Number/nil, Function/nil } function Scorpio.PickRange(message, min, max, step, value, func) if min >= max then error("Usage: PickRange(message, min, max, step[, value[, callback]]) - the min value must be smaller than the max value", 3) end local value = queuePopup(POPUP_TYPE_RANGEPICKER, message, func, min, max, step, value) return value end __Static__() __Arguments__{ String, Function/nil } function Scorpio.PickMacroCondition(message, func) local value = queuePopup(POPUP_TYPE_MACROCONDITION, message, func) return value end