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.

1005 lines
33 KiB

--- **LibDialog-1.0** provides methods for creating dialogs similar to Blizzard's default StaticPopup dialogs,
-- with additions (such as multiple CheckButtons) and improvements (such as multiple EditBoxes, frame and widget
-- recycling, and not tainting default UI elements).
-- @class file
-- @name LibDialog-1.0.lua
-- @release 1
-----------------------------------------------------------------------
-- Upvalued Lua API.
-----------------------------------------------------------------------
-- Functions
local error = _G.error
local pairs = _G.pairs
local tonumber = _G.tonumber
-- Libraries
local table = _G.table
-----------------------------------------------------------------------
-- Library namespace.
-----------------------------------------------------------------------
local LibStub = _G.LibStub
local MAJOR = "LibDialog-1.0"
_G.assert(LibStub, MAJOR .. " requires LibStub")
local MINOR = 8 -- Should be manually increased
local lib, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then
return
end -- No upgrade needed
local dialog_prototype = _G.CreateFrame("Frame", nil, _G.UIParent, _G.BackdropTemplateMixin and "BackdropTemplate")
local dialog_meta = {
__index = dialog_prototype
}
-----------------------------------------------------------------------
-- Migrations.
-----------------------------------------------------------------------
lib.delegates = lib.delegates or {}
lib.queued_delegates = lib.queues_delegates or {}
lib.delegate_queue = lib.delegate_queue or {}
lib.active_dialogs = lib.active_dialogs or {}
lib.active_buttons = lib.active_buttons or {}
lib.active_checkboxes = lib.active_checkboxes or {}
lib.active_editboxes = lib.active_editboxes or {}
lib.active_icons = lib.active_icons or {}
lib.dialog_heap = lib.dialog_heap or {}
lib.button_heap = lib.button_heap or {}
lib.checkbox_heap = lib.checkbox_heap or {}
lib.editbox_heap = lib.editbox_heap or {}
lib.icon_heap = lib.icon_heap or {}
-----------------------------------------------------------------------
-- Constants.
-----------------------------------------------------------------------
local METHOD_USAGE_FORMAT = MAJOR .. ":%s() - %s."
local DEFAULT_DIALOG_WIDTH = 320
local DEFAULT_DIALOG_HEIGHT = 72
local DEFAULT_BUTTON_WIDTH = 128
local DEFAULT_BUTTON_HEIGHT = 21
local DEFAULT_EDITBOX_WIDTH = 130
local DEFAULT_EDITBOX_HEIGHT = 32
local DEFAULT_CHECKBOX_SIZE = 32
local DEFAULT_ICON_SIZE = 36
local DEFAULT_DIALOG_TEXT_WIDTH = 290
local MAX_DIALOGS = 4
local MAX_BUTTONS = 3
local DEFAULT_DIALOG_BACKDROP = {
bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]],
edgeFile = [[Interface\DialogFrame\UI-DialogBox-Border]],
tile = true,
tileSize = 32,
edgeSize = 32,
insets = {
left = 11,
right = 12,
top = 12,
bottom = 11,
},
}
local TEXT_HORIZONTAL_JUSTIFICATIONS = {
CENTER = "CENTER",
LEFT = "LEFT",
RIGHT = "RIGHT",
}
local TEXT_VERTICAL_JUSTIFICATIONS = {
BOTTOM = "BOTTOM",
MIDDLE = "MIDDLE",
TOP = "TOP",
}
-----------------------------------------------------------------------
-- Upvalues.
-----------------------------------------------------------------------
local delegates = lib.delegates
local queued_delegates = lib.queued_delegates
local delegate_queue = lib.delegate_queue
local active_dialogs = lib.active_dialogs
local active_buttons = lib.active_buttons
local active_checkboxes = lib.active_checkboxes
local active_editboxes = lib.active_editboxes
local dialog_heap = lib.dialog_heap
local button_heap = lib.button_heap
local checkbox_heap = lib.checkbox_heap
local editbox_heap = lib.editbox_heap
-----------------------------------------------------------------------
-- Helper functions.
-----------------------------------------------------------------------
local function _ProcessQueue()
if #active_dialogs == MAX_DIALOGS then
return
end
local delegate = table.remove(delegate_queue)
if not delegate then
return
end
local data = queued_delegates[delegate]
queued_delegates[delegate] = nil
if data == "" then
data = nil
end
return lib:Spawn(delegate, data)
end
local function _RefreshDialogAnchors()
for index = 1, #active_dialogs do
local current_dialog = active_dialogs[index]
current_dialog:ClearAllPoints()
if index == 1 then
_SetupAnchor(current_dialog)
else
current_dialog:SetPoint("TOP", active_dialogs[index - 1], "BOTTOM", 0, 0)
end
end
end
local function _RecycleWidget(widget, actives, heap)
local remove_index
for index = 1, #actives do
if actives[index] == widget then
remove_index = index
end
end
if not remove_index then
return
end
table.remove(actives, remove_index):ClearAllPoints()
table.insert(heap, widget)
end
local function _ReleaseCheckBox(checkbox)
checkbox.container:Hide()
_RecycleWidget(checkbox, active_checkboxes, checkbox_heap)
checkbox.container:ClearAllPoints()
checkbox.container:SetParent(nil)
end
local function _ReleaseEditBox(editbox)
editbox:SetMaxLetters(0)
editbox:SetMaxBytes(0)
editbox:Hide()
_RecycleWidget(editbox, active_editboxes, editbox_heap)
editbox:SetParent(nil)
end
local function _ReleaseButton(button)
button:Hide()
_RecycleWidget(button, active_buttons, button_heap)
button:SetParent(nil)
end
local function _ReleaseDialog(dialog)
dialog.delegate = nil
dialog.data = nil
if dialog.editboxes then
for index = 1, #dialog.editboxes do
_ReleaseEditBox(dialog.editboxes[index])
end
dialog.editboxes = nil
end
if dialog.checkboxes then
for index = 1, #dialog.checkboxes do
_ReleaseCheckBox(dialog.checkboxes[index])
end
dialog.checkboxes = nil
end
if dialog.buttons then
for index = 1, #dialog.buttons do
_ReleaseButton(dialog.buttons[index])
end
dialog.buttons = nil
end
_RecycleWidget(dialog, active_dialogs, dialog_heap)
_RefreshDialogAnchors()
end
local function _Dialog_OnShow(dialog)
local delegate = dialog.delegate
if not delegate then
return
end
_G.PlaySound(SOUNDKIT.IG_MAINMENU_OPEN, "Master")
if delegate.on_show then
delegate.on_show(dialog, dialog.data)
end
end
local function _Dialog_OnHide(dialog)
_G.PlaySound(SOUNDKIT.IG_MAINMENU_CLOSE, "Master")
-- Required so lib:ActiveDialog() will return false if called from code which is called from the delegate's on_hide
_RecycleWidget(dialog, active_dialogs, dialog_heap)
local delegate = dialog.delegate
if delegate.on_hide then
delegate.on_hide(dialog, dialog.data)
end
_ReleaseDialog(dialog)
if #delegate_queue > 0 then
local delegate
repeat
delegate = _ProcessQueue()
until not delegate
end
end
local function _Dialog_OnUpdate(dialog, elapsed)
local delegate = dialog.delegate
if dialog.time_remaining and dialog.time_remaining > 0 then
local remaining = dialog.time_remaining - elapsed
if remaining <= 0 then
dialog.time_remaining = nil
if delegate and delegate.on_cancel then
delegate.on_cancel(dialog, dialog.data, "timeout")
end
dialog:Hide()
return
end
dialog.time_remaining = remaining
end
if delegate and delegate.on_update then
delegate.on_update(dialog, elapsed)
end
end
local function _Dialog_OnEvent(self, event, ...)
if self[event] then
return self[event](self, event, ...)
end
end
-----------------------------------------------------------------------
--helper function for cata classic
--thx Road-block
--https://github.com/Torhal/LibDialog-1.0/issues/5
-----------------------------------------------------------------------
local function _SetupAnchor(dialog)
local default_dialog
if _G.StaticPopup_DisplayedFrames then
default_dialog = _G.StaticPopup_DisplayedFrames[#_G.StaticPopup_DisplayedFrames]
elseif (_G.StaticPopup_HasDisplayedFrames and _G.StaticPopup_IsLastDisplayedFrame) then
if StaticPopup_HasDisplayedFrames() then
for idx = STATICPOPUP_NUMDIALOGS,1,-1 do
local test_dialog = _G["StaticPopup"..idx]
if StaticPopup_IsLastDisplayedFrame(test_dialog) then
default_dialog = test_dialog
end
end
end
end
if default_dialog then
dialog:SetPoint("TOP", default_dialog, "BOTTOM", 0, 0)
else
dialog:SetPoint("TOP", _G.UIParent, "TOP", 0, -135)
end
end
if not lib.hooked_onhide then
_G.hooksecurefunc("StaticPopup_OnHide", function()
_RefreshDialogAnchors()
if #delegate_queue > 0 then
local delegate
repeat
delegate = _ProcessQueue()
until not delegate
end
end)
lib.hooked_onhide = true
end
if not lib.hooked_set_up_position then
_G.hooksecurefunc("StaticPopup_SetUpPosition", function()
_RefreshDialogAnchors()
end)
lib.hooked_set_up_position = true
end
if not lib.hooked_escape_pressed then
local dialogs_to_release = {}
_G.hooksecurefunc("StaticPopup_EscapePressed", function()
table.wipe(dialogs_to_release)
for index = 1, #active_dialogs do
local dialog = active_dialogs[index]
if dialog.delegate.hide_on_escape then
dialogs_to_release[dialog] = true
end
end
for dialog in pairs(dialogs_to_release) do
local delegate = dialog.delegate
if delegate.on_cancel and not delegate.no_cancel_on_escape then
delegate.on_cancel(dialog)
end
dialog:Hide()
end
if #delegate_queue > 0 then
local delegate
repeat
delegate = _ProcessQueue()
until not delegate
end
end)
lib.hooked_escape_pressed = true
end
local function CheckBox_GetValue(checkbox)
local dialog = checkbox:GetParent():GetParent()
local get_value = dialog.delegate.checkboxes[checkbox:GetID()].get_value
if get_value then
return get_value(checkbox, dialog.data)
end
end
local function CheckBox_OnClick(checkbox, mouse_button, down)
local dialog = checkbox:GetParent():GetParent()
local set_value = dialog.delegate.checkboxes[checkbox:GetID()].set_value
if set_value then
set_value(checkbox, CheckBox_GetValue(checkbox), dialog.data, mouse_button, down)
end
checkbox:SetChecked(CheckBox_GetValue(checkbox))
end
local function _AcquireCheckBox(parent, index)
local checkbox = table.remove(checkbox_heap)
if not checkbox then
local container = _G.CreateFrame("Frame", ("%s_CheckBoxContainer%d"):format(MAJOR, #active_checkboxes + 1), _G.UIParent)
container:SetHeight(DEFAULT_CHECKBOX_SIZE)
checkbox = _G.CreateFrame("CheckButton", ("%s_CheckBox%d"):format(MAJOR, #active_checkboxes + 1), container, "UICheckButtonTemplate")
checkbox:SetScript("OnClick", CheckBox_OnClick)
checkbox.container = container
end
active_checkboxes[#active_checkboxes + 1] = checkbox
checkbox:SetPoint("LEFT", 0, 0)
checkbox.text:SetText(parent.delegate.checkboxes[index].label or "")
checkbox.container:SetParent(parent)
checkbox:SetID(index)
checkbox:SetChecked(CheckBox_GetValue(checkbox))
checkbox.container:Show()
return checkbox
end
local function EditBox_OnEnterPressed(editbox)
if not editbox.autoCompleteParams or not _G.AutoCompleteEditBox_OnEnterPressed(editbox) then
local dialog = editbox:GetParent()
local on_enter_pressed = dialog.delegate.editboxes[editbox:GetID()].on_enter_pressed
if on_enter_pressed then
on_enter_pressed(editbox, dialog.data)
end
end
end
local function EditBox_OnEscapePressed(editbox)
local dialog = editbox:GetParent()
local on_escape_pressed = dialog.delegate.editboxes[editbox:GetID()].on_escape_pressed
if on_escape_pressed then
on_escape_pressed(editbox, dialog.data)
end
end
local function EditBox_OnShow(editbox)
local dialog = editbox:GetParent()
local on_show = dialog.delegate.editboxes[editbox:GetID()].on_show
if on_show then
on_show(editbox, dialog.data)
end
end
local function EditBox_OnTextChanged(editbox, user_input)
if not editbox.autoCompleteParams or not _G.AutoCompleteEditBox_OnTextChanged(editbox, user_input) then
local dialog = editbox:GetParent()
local on_text_changed = dialog.delegate.editboxes[editbox:GetID()].on_text_changed
if on_text_changed then
on_text_changed(editbox, dialog.data)
end
end
end
local function _AcquireEditBox(dialog, index)
local editbox = table.remove(editbox_heap)
if not editbox then
local editbox_name = ("%s_EditBox%d"):format(MAJOR, #active_editboxes + 1)
editbox = _G.CreateFrame("EditBox", editbox_name, _G.UIParent, "AutoCompleteEditBoxTemplate")
editbox:SetWidth(130)
editbox:SetHeight(32)
editbox:SetFontObject("ChatFontNormal")
local left = editbox:CreateTexture(("%sLeft"):format(editbox_name), "BACKGROUND")
left:SetTexture([[Interface\ChatFrame\UI-ChatInputBorder-Left2]])
left:SetWidth(32)
left:SetHeight(32)
left:SetPoint("LEFT", -10, 0)
local right = editbox:CreateTexture(("%sRight"):format(editbox_name), "BACKGROUND")
right:SetTexture([[Interface\ChatFrame\UI-ChatInputBorder-Right2]])
right:SetWidth(32)
right:SetHeight(32)
right:SetPoint("RIGHT", 10, 0)
local mid = editbox:CreateTexture(("%sMid"):format(editbox_name), "BACKGROUND")
mid:SetTexture([[Interface\ChatFrame\UI-ChatInputBorder-Mid2]])
mid:SetHeight(32)
mid:SetPoint("TOPLEFT", left, "TOPRIGHT", 0, 0)
mid:SetPoint("TOPRIGHT", right, "TOPLEFT", 0, 0)
local label = editbox:CreateFontString(("%sLabel"):format(editbox_name), "ARTWORK", "GameFontNormalSmall")
label:SetPoint("RIGHT", editbox, "LEFT", -15, 0)
editbox.left = left
editbox.right = right
editbox.mid = mid
editbox.label = label
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
editbox:SetScript("OnShow", EditBox_OnShow)
editbox:SetScript("OnTextChanged", EditBox_OnTextChanged)
end
local template = dialog.delegate.editboxes[index]
active_editboxes[#active_editboxes + 1] = editbox
editbox.addHighlightedText = true
editbox:SetParent(dialog)
editbox:SetID(index)
editbox:SetWidth(template.width or DEFAULT_EDITBOX_WIDTH)
editbox:SetAutoFocus(template.auto_focus)
if not template.auto_focus then
editbox:ClearFocus()
end
editbox:SetMaxLetters(template.max_letters or 0)
editbox:SetMaxBytes(template.max_bytes or 0)
editbox:SetText(template.text or "")
if template.label and template.label ~= "" then
editbox.label:SetText(template.label)
editbox.label:SetWidth(editbox.label:GetStringWidth())
else
editbox.label:Hide()
end
editbox:Show()
return editbox
end
local function Button_OnClick(button, mouse_button, down)
local dialog = button:GetParent()
local hide = true
local on_click = dialog.delegate.buttons[button:GetID()].on_click
if on_click then
hide = not on_click(dialog, dialog.data, "clicked")
end
if hide then
dialog:Hide()
end
end
local function _AcquireButton(parent, index)
local button = table.remove(button_heap)
if not button then
local button_name = ("%s_Button%d"):format(MAJOR, #active_buttons + 1)
button = _G.CreateFrame("Button", button_name, _G.UIParent)
button:SetWidth(DEFAULT_BUTTON_WIDTH)
button:SetHeight(DEFAULT_BUTTON_HEIGHT)
button:SetNormalTexture([[Interface\Buttons\UI-DialogBox-Button-Up]])
button:GetNormalTexture():SetTexCoord(0, 1, 0, 0.71875)
button:SetPushedTexture([[Interface\Buttons\UI-DialogBox-Button-Down]])
button:GetPushedTexture():SetTexCoord(0, 1, 0, 0.71875)
button:SetDisabledTexture([[Interface\Buttons\UI-DialogBox-Button-Disabled]])
button:GetDisabledTexture():SetTexCoord(0, 1, 0, 0.71875)
button:SetHighlightTexture([[Interface\Buttons\UI-DialogBox-Button-Highlight]], "ADD")
button:GetHighlightTexture():SetTexCoord(0, 1, 0, 0.71875)
button:SetNormalFontObject("GameFontNormal")
button:SetDisabledFontObject("GameFontDisable")
button:SetHighlightFontObject("GameFontHighlight")
button:SetScript("OnClick", Button_OnClick)
end
active_buttons[#active_buttons + 1] = button
button:SetText(parent.delegate.buttons[index].text or "FOOF")
button:SetParent(parent)
button:SetID(index)
button:Show()
return button
end
local function _BuildDialog(delegate, data)
if #active_dialogs == MAX_DIALOGS then
if not queued_delegates[delegate] then
delegate_queue[#delegate_queue + 1] = delegate
queued_delegates[delegate] = data or ""
end
return
end
local dialog = table.remove(dialog_heap)
if not dialog then
dialog = _G.setmetatable(_G.CreateFrame("Frame", ("%s_Dialog%d"):format(MAJOR, #active_dialogs + 1), _G.UIParent), dialog_meta)
dialog.is_new = true
local close_button = _G.CreateFrame("Button", nil, dialog, "UIPanelCloseButton")
close_button:SetPoint("TOPRIGHT", -3, -3)
close_button:Hide()
dialog.close_button = close_button
local text = dialog:CreateFontString(nil, nil, "GameFontHighlight")
text:SetWidth(DEFAULT_DIALOG_TEXT_WIDTH)
text:SetPoint("TOP", 0, -16)
dialog.text = text
end
dialog:Reset()
dialog.delegate = delegate
dialog.data = data
dialog.text:SetText(delegate.text or "")
dialog.text:SetJustifyH(delegate.text_justify_h and TEXT_HORIZONTAL_JUSTIFICATIONS[delegate.text_justify_h:upper()] or "CENTER")
dialog.text:SetJustifyV(delegate.text_justify_v and TEXT_VERTICAL_JUSTIFICATIONS[delegate.text_justify_v:upper()] or "MIDDLE")
if delegate.no_close_button then
dialog.close_button:Hide()
else
dialog.close_button:Show()
end
if _G.type(delegate.icon) == "string" then
if not dialog.icon then
dialog.icon = dialog:CreateTexture(("%sIcon"):format(dialog:GetName()), "ARTWORK")
dialog.icon:SetPoint("LEFT", dialog, "LEFT", 16, 0)
end
local icon_size = tonumber(delegate.icon_size) and delegate.icon_size or DEFAULT_ICON_SIZE
dialog.icon:SetSize(icon_size, icon_size)
dialog.icon:SetTexture(delegate.icon)
dialog.icon:Show()
elseif dialog.icon then
dialog.icon:SetTexture()
dialog.icon:Hide()
end
if delegate.buttons and #delegate.buttons > 0 then
dialog.buttons = {}
for index = 1, MAX_BUTTONS do
local button = delegate.buttons[index]
if not button then
break
end
if button.text then
table.insert(dialog.buttons, _AcquireButton(dialog, index))
end
end
local NUM_BUTTONS = #dialog.buttons
for index = 1, NUM_BUTTONS do
local button = dialog.buttons[index]
if index == 1 then
if NUM_BUTTONS == 3 then
button:SetPoint("BOTTOMRIGHT", dialog, "BOTTOM", -72, 16)
elseif NUM_BUTTONS == 2 then
button:SetPoint("BOTTOMRIGHT", dialog, "BOTTOM", -6, 16)
elseif NUM_BUTTONS == 1 then
button:SetPoint("BOTTOM", dialog, "BOTTOM", 0, 16)
end
else
button:SetPoint("LEFT", dialog.buttons[index - 1], "RIGHT", 13, 0)
end
local width = button:GetTextWidth()
if width > 110 then
button:SetWidth(width + 20)
else
button:SetWidth(120)
end
button:Enable()
end
end
local NUM_EDITBOXES = 0
if delegate.editboxes and #delegate.editboxes > 0 then
dialog.editboxes = {}
for index = 1, #delegate.editboxes do
table.insert(dialog.editboxes, _AcquireEditBox(dialog, index))
end
NUM_EDITBOXES = #dialog.editboxes
for index = 1, NUM_EDITBOXES do
local editbox = dialog.editboxes[index]
if index == 1 then
editbox:SetPoint("TOP", dialog.text, "BOTTOM", 0, -8)
else
editbox:SetPoint("TOP", dialog.editboxes[index - 1], "BOTTOM", 0, 0)
end
end
end
if delegate.checkboxes and #delegate.checkboxes > 0 then
dialog.checkboxes = {}
for index = 1, #delegate.checkboxes do
table.insert(dialog.checkboxes, _AcquireCheckBox(dialog, index))
end
local max_string_width = 0
for index = 1, #dialog.checkboxes do
local string_width = dialog.checkboxes[index].text:GetStringWidth()
if string_width > max_string_width then
max_string_width = string_width
end
end
for index = 1, #dialog.checkboxes do
local checkbox = dialog.checkboxes[index]
checkbox.container:SetWidth(DEFAULT_CHECKBOX_SIZE + max_string_width)
if index == 1 then
if NUM_EDITBOXES > 0 then
checkbox.container:SetPoint("TOPLEFT", dialog.editboxes[NUM_EDITBOXES], "BOTTOMLEFT", -12, 0)
else
checkbox.container:SetPoint("TOP", dialog.text, "BOTTOM", 0, -8)
end
else
checkbox.container:SetPoint("TOPLEFT", dialog.checkboxes[index - 1].container, "BOTTOMLEFT", 0, 0)
end
end
end
dialog.time_remaining = delegate.duration
return dialog
end
-----------------------------------------------------------------------
-- Library methods.
-----------------------------------------------------------------------
--- Register a new dialog delegate.
-- @name LibDialog-1.0:Register
-- @class function
-- @paramsig delegate_name, delegate
-- @param delegate_name The name the delegate table will be registered under.
-- @param delegate The delegate table definition.
function lib:Register(delegate_name, delegate)
if _G.type(delegate_name) ~= "string" or delegate_name == "" then
error(METHOD_USAGE_FORMAT:format("Register", "delegate_name must be a non-empty string"), 2)
end
if _G.type(delegate) ~= "table" then
error(METHOD_USAGE_FORMAT:format("Register", "delegate must be a table"), 2)
end
delegates[delegate_name] = delegate
end
local function _FindDelegate(method_name, reference)
local reference_type = _G.type(reference)
if reference == "" or (reference_type ~= "string" and reference_type ~= "table") then
error(METHOD_USAGE_FORMAT:format(method_name, "reference must be a delegate table or a non-empty string"), 3)
end
local delegate
if reference_type == "string" then
if not delegates[reference] then
error(METHOD_USAGE_FORMAT:format(method_name, ("\"%s\" does not match a registered delegate"):format(reference)), 3)
end
delegate = delegates[reference]
else
delegate = reference
end
return delegate
end
--- Spawns a dialog from a delegate reference.
-- @name LibDialog-1.0:Spawn
-- @class function
-- @paramsig reference[, data]
-- @param reference The delegate to be used for the spawned dialog. Can be either a string, in which case the delegate must be registered, or a delegate definition table.
-- @param data Additional data to be passed on to the resultant dialog.
function lib:Spawn(reference, data)
local delegate = _FindDelegate("Spawn", reference)
-----------------------------------------------------------------------
-- Check delegate conditionals before building.
-----------------------------------------------------------------------
if _G.UnitIsDeadOrGhost("player") and not delegate.show_while_dead then
if delegate.on_cancel then
delegate.on_cancel()
end
return
end
if _G.InCinematic() and not delegate.show_during_cinematic then
if delegate.on_cancel then
delegate.on_cancel()
end
return
end
if delegate.is_exclusive then
for index = 1, #active_dialogs do
local dialog = active_dialogs[index]
if dialog.delegate.is_exclusive then
if dialog.delegate.on_cancel then
dialog.delegate.on_cancel(dialog, dialog.data, "override")
end
dialog:Hide()
end
end
end
local cancel_list = delegate.cancels_on_spawn
if cancel_list then
for index = 1, #cancel_list do
local delegate_name = cancel_list[index]
local delegate_to_cancel = delegates[delegate_name]
if delegate_to_cancel then
for index = 1, #active_dialogs do
local dialog = active_dialogs[index]
if dialog.delegate == delegate_to_cancel then
dialog:Hide()
if dialog.delegate.on_cancel then
dialog.delegate.on_cancel(dialog, dialog.data, "override")
end
end
end
else
error(("\"%s\" does not match a registered delegate - unable to cancel"):format(delegate_name), 2)
end
end
end
local dialog = self:ActiveDialog(reference, data)
if dialog then
local delegate = dialog.delegate
if not delegate.no_cancel_on_reuse and delegate.on_cancel then
delegate.on_cancel(dialog, dialog.data, "override")
end
dialog:Hide()
end
-----------------------------------------------------------------------
-- Build new dialog and anchor it.
-----------------------------------------------------------------------
dialog = _BuildDialog(delegate, data)
if not dialog then
return
end
if delegate.sound then
_G.PlaySound(delegate.sound)
end
-- Anchor to the bottom of existing dialogs. If none exist, check to see if there are visible default StaticPopupDialogs and anchor to that instead; else, anchor to UIParent.
if #active_dialogs > 0 then
dialog:SetPoint("TOP", active_dialogs[#active_dialogs], "BOTTOM", 0, 0)
else
_SetupAnchor(dialog)
end
active_dialogs[#active_dialogs + 1] = dialog
dialog:Show()
if dialog.is_new then
_Dialog_OnShow(dialog)
dialog.is_new = nil
end
dialog:Resize()
return dialog
end
--- Determines whether or not a specific dialog is currently active.
-- @name LibDialog-1.0:ActiveDialog
-- @class function
-- @paramsig reference[, data]
-- @param reference The delegate criteria for the dialog being targeted. Can be either a string, in which case the delegate must be registered, or a delegate definition table.
-- @param data Additional data to be used as further criteria to determine if the target dialog is active - this would be the same data used to spawn the dialog.
function lib:ActiveDialog(reference, data)
local delegate = _FindDelegate("ActiveDialog", reference)
for index = 1, #active_dialogs do
local dialog = active_dialogs[index]
if dialog.delegate == delegate and (not data or dialog.data == data) then
return dialog
end
end
end
--- Dismisses a specific dialog.
-- @name LibDialog-1.0:Dismiss
-- @class function
-- @paramsig reference[, data]
-- @param reference The delegate criteria for the dialog being targeted. Can be either a string, in which case the delegate must be registered, or a delegate definition table.
-- @param data Additional data to be used as further criteria to identify the target dialog - this would be the same data used to spawn the dialog.
function lib:Dismiss(reference, data)
local delegate = _FindDelegate("Dismiss", reference)
for index = 1, #active_dialogs do
local dialog = active_dialogs[index]
if dialog.delegate == delegate and (not data or dialog.data == data) then
dialog:Hide()
end
end
end
-----------------------------------------------------------------------
-- Dialog methods.
-----------------------------------------------------------------------
function dialog_prototype:Reset()
self:SetWidth(DEFAULT_DIALOG_WIDTH)
self:SetHeight(DEFAULT_DIALOG_HEIGHT)
self:SetBackdrop(DEFAULT_DIALOG_BACKDROP)
self:SetToplevel(true)
self:EnableKeyboard(true)
self:EnableMouse(true)
self:SetFrameStrata("DIALOG")
self:SetScript("OnShow", _Dialog_OnShow)
self:SetScript("OnHide", _Dialog_OnHide)
self:SetScript("OnUpdate", _Dialog_OnUpdate)
self:SetScript("OnEvent", _Dialog_OnEvent)
self:RegisterEvent("DISPLAY_SIZE_CHANGED")
end
function dialog_prototype:Resize()
local delegate = self.delegate
if not delegate then
return
end
local width = delegate.width or DEFAULT_DIALOG_WIDTH
local height = delegate.height or 0
-- Static size ignores widgets for resizing purposes.
if delegate.static_size then
if width > 0 then
self:SetWidth(width)
self.text:SetWidth(self:GetWidth() - 30)
end
if height > 0 then
self:SetHeight(height)
end
return
end
if self.buttons and #self.buttons > 0 then
height = height + 8 + DEFAULT_BUTTON_HEIGHT
if #self.buttons == MAX_BUTTONS then
width = 440
end
end
if self.editboxes and #self.editboxes > 0 then
local dialog_left = self:GetLeft()
local dialog_right = self:GetRight()
local label_offset = 0
local editbox_offset = 0
for index = 1, #self.editboxes do
local editbox = self.editboxes[index]
local editbox_right = editbox:GetRight()
height = height + DEFAULT_EDITBOX_HEIGHT
if editbox_right then
if editbox_offset < 25 and dialog_right > editbox_right and (dialog_right - editbox_right) < 25 then
editbox_offset = 25
elseif editbox_right > dialog_right and editbox_offset < editbox_right - dialog_right then
editbox_offset = 25 + (editbox_right - dialog_right)
end
end
if editbox.label:IsShown() then
local label_left = editbox.label:GetLeft()
if label_left and label_left < dialog_left and label_offset < dialog_left - label_left then
label_offset = dialog_left - label_left
end
end
end
width = width + label_offset + editbox_offset
end
if self.checkboxes then
height = height + (DEFAULT_CHECKBOX_SIZE * #self.checkboxes)
end
if self.icon and self.icon:IsShown() then
local icon_width = self.icon:GetWidth() + 32
width = width + icon_width
self.text:SetWidth(width - icon_width - (self.close_button:GetWidth() + 16))
else
self.text:SetWidth(width - 60)
end
height = height + 32 + self.text:GetHeight()
self:SetWidth(width)
self:SetHeight(height)
end
-----------------------------------------------------------------------
-- Default dialog events.
-----------------------------------------------------------------------
function dialog_prototype:DISPLAY_SIZE_CHANGED()
self:Resize()
end