--- **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) 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 local default_dialog = _G.StaticPopup_DisplayedFrames[#_G.StaticPopup_DisplayedFrames] if default_dialog then current_dialog:SetPoint("TOP", default_dialog, "BOTTOM", 0, 0) else current_dialog:SetPoint("TOP", _G.UIParent, "TOP", 0, -135) end 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 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 local default_dialog = _G.StaticPopup_DisplayedFrames[#_G.StaticPopup_DisplayedFrames] if default_dialog then dialog:SetPoint("TOP", default_dialog, "BOTTOM", 0, 0) else dialog:SetPoint("TOP", _G.UIParent, "TOP", 0, -135) end 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