local MAX_NUM_DIALOGS = 1 local NUM_DIALOG_BUTTONS = 2 local RELEASED_FROM_BUTTON_PRESS = true local BUTTON_SPACING = 20 -- This used to be a table of displayed dialogs, but more than one hasn't been supported since 2013. -- It has now been simplified to a single displayed dialog table (name and dialog object) but all of the existing -- functions have been maintained for backward compatibility. Some functions might seem odd in this context -- (i.e. ZO_Dialogs_FindDialog) but it's only to maintain that compatibility local g_displayedDialog = nil local g_dialogQueue = {} local g_isDialogQueuePaused = false local g_dialogPauseExemptions = {} local g_currencyPool = nil local g_curInstanceId = 0 local QUEUED_DIALOG_INDEX_NAME = 1 local QUEUED_DIALOG_INDEX_DATA = 2 local QUEUED_DIALOG_INDEX_PARAMS = 3 ZO_DIALOG_SYNC_OBJECT = GetOrCreateSynchronizingObject("dialog") local function ContainsName(testName, ...) for i = 1, select("#", ...) do local name = select(i, ...) if name == testName then return true end end return false end local function QueueDialog(name, data, params, isGamepad, dialogInfo, queueInFront) if name and dialogInfo and dialogInfo.onlyQueueOnce then -- If dialog is the same as the currently shown dialog don't queue it. if name == g_displayedDialog.name then return end -- If the dialog is already queued and can only be queued once, don't requeue it for _, dialog in ipairs(g_dialogQueue) do if dialog[QUEUED_DIALOG_INDEX_NAME] == name then return end end end name = name or "" data = data or {} params = params or {} if queueInFront then table.insert(g_dialogQueue, 1, {name, data, params, isGamepad}) else table.insert(g_dialogQueue, {name, data, params, isGamepad}) end end local function RemoveQueuedDialogs(name, filterFunction) local i = 1 while i <= #g_dialogQueue do local dialog = g_dialogQueue[i] if name == dialog[QUEUED_DIALOG_INDEX_NAME] and (not filterFunction or filterFunction(dialog[QUEUED_DIALOG_INDEX_DATA])) then table.remove(g_dialogQueue, i) else i = i + 1 end end end local function RemoveQueuedDialogsExcept(...) local i = 1 while i <= #g_dialogQueue do local dialog = g_dialogQueue[i] if not ContainsName(dialog[QUEUED_DIALOG_INDEX_NAME], ...) then table.remove(g_dialogQueue, i) else i = i + 1 end end end local function GetDisplayedDialog() return g_displayedDialog and g_displayedDialog.dialog or nil end function ZO_Dialogs_IsShowingDialog() return g_displayedDialog ~= nil end function ZO_Dialogs_IsShowing(nameOrDialog) if g_displayedDialog then if type(nameOrDialog) == "string" then return g_displayedDialog.name == nameOrDialog else return g_displayedDialog.dialog == nameOrDialog end end return false end function ZO_Dialogs_FindDialog(nameOrDialog, filterFunction) if ZO_Dialogs_IsShowing(nameOrDialog) and (not filterFunction or filterFunction(g_displayedDialog.dialog.data)) then return g_displayedDialog.dialog end return nil end function ZO_Dialogs_IsDialogHiding(nameOrDialog) local dialog = ZO_Dialogs_FindDialog(nameOrDialog) return dialog and dialog.hiding end function ZO_Dialogs_IsShowingDialogThatShouldShowTooltip() local displayedDialog = GetDisplayedDialog() if not displayedDialog then return false end return displayedDialog.shouldShowTooltip end local function HandleCallback(clickedButton) local dialog = GetDisplayedDialog() if dialog then local instanceId = dialog.instanceId if clickedButton.m_callback then clickedButton.m_callback(dialog) end --Make sure the dialog wasn't released and then reshown if clickedButton.m_noReleaseOnClick == nil and dialog.instanceId == instanceId then ZO_Dialogs_ReleaseDialog(dialog, RELEASED_FROM_BUTTON_PRESS) end end end function ZO_Dialogs_SetupCustomButton(button, text, keybind, clickSound, callback) button:SetKeybind(keybind) button:SetText(text) button:SetClickSound(clickSound) button.m_callback = callback end local function GetDialog(isGamepad) local dialogBaseName = "ZO_Dialog" if isGamepad then dialogBaseName = "ZO_DialogGamepad" end for i = 1, MAX_NUM_DIALOGS do local dialog = GetControl(dialogBaseName..i) if(dialog:IsControlHidden()) then ZO_Dialogs_InitializeDialog(dialog, isGamepad) return dialog end end return nil end local function GetFormattedDialogText(text, params) if text then if params and #params > 0 then text = zo_strformat(text, unpack(params)) elseif type(text) == "number" then text = GetString(text) end else text = "" end return text end function ZO_GetFormattedDialogText(dialog, textTable, params) if not textTable then return end local timer = textTable.timer if params and type(timer) == "number" and type(params[timer]) == "number" then local timerParam = params[timer] if textTable.verboseTimer then timerParam = ZO_FormatTimeMilliseconds(timerParam, TIME_FORMAT_STYLE_DESCRIPTIVE) else timerParam = ZO_FormatTimeMilliseconds(timerParam, TIME_FORMAT_STYLE_DESCRIPTIVE_SHORT_SHOW_ZERO_SECS) end params[timer] = timerParam end local textOrCallback = textTable.text local finalText if type(textOrCallback) == "function" then finalText = textOrCallback(dialog) else finalText = textOrCallback end local formattedText = GetFormattedDialogText(finalText, params) return formattedText end local function SetDialogTextFormatted(dialog, textControl, textTable, params) local formattedText = ZO_GetFormattedDialogText(dialog, textTable, params) if not textControl or not formattedText then return end textControl:SetText(formattedText) textControl:SetHidden(false) if textTable.align then textControl:SetHorizontalAlignment(textTable.align) end return select(2, textControl:GetTextDimensions()) end function ZO_Dialogs_SetDialogLoadingIcon(loadingIcon, textControl, showLoadingIconData) local shouldShowLoadingIcon = false local iconAnchor local showType = type(showLoadingIconData) if showType == "boolean" then shouldShowLoadingIcon = showLoadingIconData elseif showType == "table" then shouldShowLoadingIcon = true iconAnchor = showLoadingIconData end if shouldShowLoadingIcon then loadingIcon:Show() if not iconAnchor then local horizontalAlignment = textControl:GetHorizontalAlignment() loadingIcon:ClearAnchors() if horizontalAlignment == TEXT_ALIGN_LEFT then loadingIcon:SetAnchor(RIGHT, textControl, LEFT, -5, 0) elseif horizontalAlignment == TEXT_ALIGN_RIGHT then loadingIcon:SetAnchor(LEFT, textControl, RIGHT, 5, 0) else local textWidth = textControl:GetTextDimensions() loadingIcon:SetAnchor(RIGHT, textControl, CENTER, -textWidth * 0.5 - 5, 0) end else iconAnchor:Set(loadingIcon) end else loadingIcon:Hide() end end local function ReanchorDialog(dialog, isGamepad) if(not dialog) then return end local dialogNumber = dialog.id dialog:ClearAnchors() if isGamepad then if(dialogNumber == 1) then local dialogBackground = dialog:GetNamedChild("Bg") local anchor1 = ZO_GamepadGrid_GetNavAnchor(GAMEPAD_GRID_NAV1, 1) anchor1:AddToControl(dialogBackground) ZO_GamepadGrid_GetNavAnchor(GAMEPAD_GRID_NAV1, 2):AddToControl(dialogBackground) dialog:SetAnchor(LEFT, GuiRoot, LEFT, anchor1:GetOffsetX()) end else if dialogNumber == 1 then dialog:SetAnchor(CENTER, GuiRoot, CENTER, 0, -55) elseif dialogNumber == 2 then dialog:SetAnchor(TOP, ZO_Dialog1, BOTTOM, 0, 24) elseif dialogNumber == 3 then dialog:SetAnchor(BOTTOM, ZO_Dialog1, TOP, 0, -24) elseif dialogNumber == 4 then dialog:SetAnchor(TOP, ZO_Dialog2, BOTTOM, 0, 24) end end end local function GetButtonControl(dialog, index) local dialogInfo = dialog.info if index <= dialog.numButtons then if dialog.buttonControls then return dialog.buttonControls[index] end if dialogInfo.customControl and dialogInfo.buttons then return dialogInfo.buttons[index].control end end end function ZO_Dialogs_ShowPlatformDialog(...) local dialogFn = IsInGamepadPreferredMode() and ZO_Dialogs_ShowGamepadDialog or ZO_Dialogs_ShowDialog dialogFn(...) end local function RefreshMainText(dialog, dialogInfo, textParams) if not textParams then textParams = {} end local mainText local textControl local isGamepadDialog = dialog.isGamepad and dialogInfo.gamepadInfo and dialogInfo.gamepadInfo.dialogType -- There is a legacy gamepad dialog still in use (for now). if isGamepadDialog then local title = ZO_GetFormattedDialogText(dialog, dialogInfo.title, textParams.titleParams) mainText = ZO_GetFormattedDialogText(dialog, dialogInfo.mainText, textParams.mainTextParams) local warningText = ZO_GetFormattedDialogText(dialog, dialogInfo.warning, textParams.warningParams) local subText = ZO_GetFormattedDialogText(dialog, dialogInfo.subText, textParams.subTextParams) ZO_GenericGamepadDialog_RefreshText(dialog, title, mainText, warningText, subText) else textControl = dialog:GetNamedChild("Text") mainText = dialogInfo.mainText if textControl then if mainText then if type(mainText) == "function" then dialog.mainText = mainText(dialog) else dialog.mainText = mainText end if dialog.mainText.lineSpacing then textControl:SetLineSpacing(dialog.mainText.lineSpacing) else textControl:SetLineSpacing(0) end SetDialogTextFormatted(dialog, textControl, dialog.mainText, textParams.mainTextParams) else textControl:SetText(nil) textControl:SetHidden(true) end end end return mainText, textControl end function ZO_Dialogs_RefreshDialogText(name, dialog, textParams) local dialogInfo = ESO_Dialogs[name] if type(dialogInfo) ~= "table" then return end if ZO_Dialogs_IsShowingDialog() then RefreshMainText(dialog, dialogInfo, textParams) end end -- To show a gamepad style sidebar dialog, call this function. -- See comments about ZO_Dialogs_ShowDialog for more information function ZO_Dialogs_ShowGamepadDialog(name, data, textParams) local IS_GAMEPAD = true local currentScene = SCENE_MANAGER:GetCurrentScene() local dialog = ESO_Dialogs[name] if currentScene and currentScene:IsShowing() then ZO_Dialogs_ShowDialog(name, data, textParams, IS_GAMEPAD) elseif dialog.gamepadInfo and dialog.gamepadInfo.allowShowOnNextScene and SCENE_MANAGER:GetNextScene() then -- if we are waiting for the scene to change and ask to show the same dialog multiple times, only use the latest call's data if dialog.gamepadInfo.nextSceneCallback then SCENE_MANAGER:UnregisterCallback("SceneStateChanged", dialog.gamepadInfo.nextSceneCallback) end dialog.gamepadInfo.nextSceneCallback = function(scene, oldState, newState) if newState == SCENE_SHOWN then SCENE_MANAGER:UnregisterCallback("SceneStateChanged", dialog.gamepadInfo.nextSceneCallback) ZO_Dialogs_ShowGamepadDialog(name, data, textParams) dialog.gamepadInfo.nextSceneCallback = nil end end SCENE_MANAGER:RegisterCallback("SceneStateChanged", dialog.gamepadInfo.nextSceneCallback) else if dialog.noChoiceCallback then dialog.data = data dialog.noChoiceCallback(dialog) end end end -- To show a dialog, call this function. -- The first parameter should be the name of the dialog (as definied in either InGameDialogs or PreGameDialogs). -- The second parameter should be an array table, containing any data that will be needed in the callback functions in the dialog -- you want to display. -- The third parameter is a table, which contains parameters used when filling out the strings in your dialog. -- -- If the main text in the dialog has 2 parameters (e.g "Hello <<1>> <<2>>"), then the 3rd parameter should contain a subtable called -- mainTextParams which itself contains 2 members, the first will go into the <<1>> and the second will go into the <<2>>. The 3rd parameter -- in ZO_Dialogs_ShowDialog can also contain a titleParams subtable which is used to fill in the parameters in the title, if needed. -- -- If the sub text in the dialog has 2 parameters (e.g "Hello <<1>> <<2>>"), then the 3rd parameter should contain a subtable called -- subTextParams which itself contains 2 members, the first will go into the <<1>> and the second will go into the <<2>>. -- -- So as an example, let's say you had defined a dialog in InGameDialogs called "TEST_DIALOG" with -- title = { text = "Dialog <<1>>" } and mainText = { text = "Main <<1>> Text <<2>>" } and subText = { text = "Sub <<1>> Text <<2>>" } -- And you called -- ZO_Dialogs_ShowDialog("TEST_DIALOG", {5}, {titleParams={"Test1"}, mainTextParams={"Test2", "Test3"}, subTextParams={"Test4", "Test5"}}) -- The resulting dialog would have a title that read "Dialog Test1" and a main text field that read "Main Test2 Text Test3" and a sub text field that read "Sub Test4 Text Test5". -- The 5 passed in the second parameter could be used by the callback functions to perform various tasks based on this value. -- Dialogs themselves (see InGameDialogs.lua, etc.) must contain at least a "mainText" table, with at least the "text" member. -- mainText.text is filled in using the mainTextParams subtable of the table passed in the 3rd parameter to ZO_Dialogs_ShowDialog. -- The mainText table can also optionally contain: -- An "align" member to set the alignment of the text (TEXT_ALIGN_LEFT, TEXT_ALIGN_RIGHT, or TEXT_ALIGN_CENTER....left is default). -- A "timer" field, which indicates that a certain parameter should be treated as a sceonds in a timer, and converted to time format -- (so if mainText contains "timer = 1", the 1st parameter in mainText.text is converted to time format before being placed -- in the string). -- -- subText should just not be shown if not set and will not be shown if no mainText exists -- -- Dialogs can also optionally contain: -- -- A "title" table, which works the same way as "mainText" ("text", "align" and "title" fields are allowed), no title is shown if "title" is not set. -- A "noChoiceCallback" field, which is executed when the dialog is closed without making a choice first. -- A "hideSound" field, which is a sound id to be played when the dialog is closed without selecting an option -- An "updateFn" field, which should be a function. If present, this function is called on each update when this dialog is showing. -- An "editBox" field, which adds an edit box to the dialog. It can specify: -- textType = The type of input the edit box accepts. -- To get the value in the editbox, use ZO_Dialogs_GetEditBoxText. -- A "warning" table, which works the same way as "mainText", which shows some red text at the bottom of the dialog to call attention to the specific action that is occuring -- Finally, the is a "buttons" table, in which each member corresponds to a button. Dialogs support a maximum of 2 buttons. -- If the buttons table is present, each of it's members in turn MUST contain a "text" field. Also, each button can optionally contain: -- A "callback" function field (whose first parameter should always be "dialog"....use "dialog.data[i]" to reference the ith data member passed in). -- A "clickSound" field that defines what sound to play when the button is clicked. -- An option to show an animated loading icon near the main text, called "showLoadingIcon" -- -- See the "DESTROY_AUGMENT_PROMPT" and "DEATH_PROMPT" dialogs in InGameDialogs.lua for examples of dialogs that use these various fields. function ZO_Dialogs_ShowDialog(name, data, textParams, isGamepad) -- Get the dialog info from the ESO_Dialogs table local dialogInfo = ESO_Dialogs[name] if type(dialogInfo) ~= "table" then return nil end local forceQueueDialog = false local queueInFront = false if g_isDialogQueuePaused then forceQueueDialog = true if ZO_IsElementInNumericallyIndexedTable(g_dialogPauseExemptions, name) then forceQueueDialog = false queueInFront = true end end if ZO_Dialogs_IsShowingDialog() or forceQueueDialog then if dialogInfo.canQueue then QueueDialog(name, data, textParams, isGamepad, dialogInfo, queueInFront) end return nil end --Acquire Dialog Control ------------------------------ local dialog local isGamepadDialog = isGamepad and dialogInfo.gamepadInfo and dialogInfo.gamepadInfo.dialogType -- There is a legacy gamepad dialog still in use (for now). local isGenericGamepadDialog = isGamepadDialog and dialogInfo.gamepadInfo.dialogType ~= GAMEPAD_DIALOGS.CUSTOM if isGenericGamepadDialog then dialog = ZO_GenericGamepadDialog_GetControl(dialogInfo.gamepadInfo.dialogType) if not dialog then return nil end elseif dialogInfo.customControl then if type(dialogInfo.customControl) == "function" then dialog = dialogInfo.customControl() else dialog = dialogInfo.customControl end if not dialog then return nil end else dialog = GetDialog(isGamepad) if not dialog then -- Dialog can't be created right now, so place it in a queue to be created later QueueDialog(name, data, textParams, isGamepad) return nil end end dialog.isGamepad = isGamepad --Shared Init ------------------ --Clear focus in case the dialog came up when we had an edit control focused. WINDOW_MANAGER:SetFocusByName() dialog.info = dialogInfo dialog.data = data dialog.textParams = textParams --Title local titleControl = dialog:GetNamedChild("Title") local title = dialogInfo.title if not textParams then textParams = {} end if title then SetDialogTextFormatted(dialog, titleControl, title, textParams.titleParams) elseif titleControl and isGamepad then SetDialogTextFormatted(dialog, titleControl, "") end -- Warning Text local warningLabel = dialog:GetNamedChild("WarningText") local warning = dialogInfo.warning if warning then SetDialogTextFormatted(dialog, warningLabel, warning, textParams.warningParams) elseif warningLabel and isGamepad then SetDialogTextFormatted(dialog, warningLabel, "") end --Buttons local buttonInfos = dialogInfo.buttons local numButtonInfos = buttonInfos and #buttonInfos or 0 dialog.numButtons = numButtonInfos if numButtonInfos > 0 and not isGamepadDialog then for i = 1, numButtonInfos do local buttonInfo = buttonInfos[i] local button = GetButtonControl(dialog, i) local buttonVisible = true if buttonInfo.visible ~= nil then if type(buttonInfo.visible) == "function" then buttonVisible = buttonInfo.visible(dialog) else buttonVisible = buttonInfo.visible end end if not buttonVisible then button:SetHidden(true) button:SetKeybindEnabled(false) else local buttonText if textParams and textParams.buttonTextOverrides and textParams.buttonTextOverrides[i] then buttonText = textParams.buttonTextOverrides[i] elseif type(buttonInfo.text) == "number" then buttonText = GetString(buttonInfo.text) elseif type(buttonInfo.text) == "function" then buttonText = buttonInfo.text(dialog) else buttonText = buttonInfo.text end button:SetText(buttonText) button:SetHidden(false) button.m_callback = buttonInfo.callback button.m_noReleaseOnClick = buttonInfo.noReleaseOnClick local keybind local hasKeybind = true if buttonInfo.keybind then if type(buttonInfo.keybind) == "function" then keybind = buttonInfo.keybind(dialog) else keybind = buttonInfo.keybind end elseif buttonInfo.keybind == nil then if i == 1 then keybind = "DIALOG_PRIMARY" else keybind = "DIALOG_NEGATIVE" end else hasKeybind = false end local isButtonEnabled if buttonInfo.enabled ~= nil then if type(buttonInfo.enabled) == "function" then isButtonEnabled = buttonInfo.enabled(dialog) else isButtonEnabled = buttonInfo.enabled end end if isButtonEnabled ~= nil then button:SetEnabled(isButtonEnabled) button:SetKeybindEnabled(hasKeybind and isButtonEnabled) else button:SetKeybindEnabled(hasKeybind) end button:SetKeybind(keybind) if buttonInfo.clickSound then button:SetClickSound(buttonInfo.clickSound) else if keybind == "DIALOG_NEGATIVE" then button:SetClickSound(SOUNDS.DIALOG_DECLINE) else button:SetClickSound(SOUNDS.DIALOG_ACCEPT) end end if buttonInfo.requiresTextInput then dialog.requiredTextFields:AddButton(button) end end end end --Custom Init if dialogInfo.customControl or isGamepadDialog then RefreshMainText(dialog, dialogInfo, textParams) if dialogInfo.setup then dialogInfo.setup(dialog, data, textParams) end else local mainText, textControl = RefreshMainText(dialog, dialogInfo, textParams) if not mainText then return nil end ZO_Dialogs_SetDialogLoadingIcon(dialog.loadingIcon, textControl, dialogInfo.showLoadingIcon) local modalUnderlay = dialog:GetNamedChild("ModalUnderlay") if modalUnderlay then if dialogInfo.modal == nil or dialogInfo.modal then modalUnderlay:SetHidden(false) else modalUnderlay:SetHidden(true) end end local controlAbove = textControl if dialogInfo.editBox then local editControl = dialog:GetNamedChild("EditBox") local editContainer = dialog:GetNamedChild("Edit") local editBoxInfo = dialogInfo.editBox editContainer:SetAnchor(TOPLEFT, controlAbove, BOTTOMLEFT, 0, 10) editContainer:SetAnchor(TOPRIGHT, controlAbove, BOTTOMRIGHT, 0, 10) editContainer:SetHidden(false) if editBoxInfo.textType then editControl:SetTextType(editBoxInfo.textType) if editBoxInfo.specialCharacters then for _, character in pairs(editBoxInfo.specialCharacters) do editControl:AddValidCharacter(character) end else editControl:RemoveAllValidCharacters() end else editControl:SetTextType(TEXT_TYPE_ALL) end if editBoxInfo.maxInputCharacters then editControl:SetMaxInputChars(editBoxInfo.maxInputCharacters) else editControl:SetMaxInputChars(128) end local defaultText = textParams.editBoxDefaultText or editBoxInfo.defaultText if defaultText then if type(defaultText) == "number" then defaultText = GetString(defaultText) end editControl:SetDefaultText(defaultText) else editControl:SetDefaultText("") end if textParams.initialEditText then editControl:SetText(textParams.initialEditText) end if editBoxInfo.autoComplete then if editControl.autoComplete then editControl.autoComplete:SetEnabled(true) else editControl.autoComplete = ZO_AutoComplete:New(editControl) end editControl.autoComplete:SetIncludeFlags(editBoxInfo.autoComplete.includeFlags) editControl.autoComplete:SetExcludeFlags(editBoxInfo.autoComplete.excludeFlags) editControl.autoComplete:SetOnlineOnly(editBoxInfo.autoComplete.onlineOnly) editControl.autoComplete:SetMaxResults(editBoxInfo.autoComplete.maxResults) else if editControl.autoComplete then editControl.autoComplete:SetEnabled(false) end end if editBoxInfo.instructions and #editBoxInfo.instructions > 0 then if editControl.instructions then editControl.instructions:ClearInstructions() editControl.instructions:AddInstructions(editBoxInfo.instructions) else local DEFAULT_TEMPLATE = nil editControl.instructions = ZO_ValidNameInstructions:New(editContainer:GetNamedChild("Instructions"), DEFAULT_TEMPLATE, editBoxInfo.instructions) end editControl.instructions:Show(dialog.nameEdit) elseif editControl.instructions then editControl.instructions:ClearInstructions() editControl.instructions:Hide() end if editBoxInfo.validatesText and editBoxInfo.validator then editControl.validator = editBoxInfo.validator else editControl.validator = nil end if editBoxInfo.matchingString then dialog.requiredTextFields:SetMatchingString(editBoxInfo.matchingString) end if editBoxInfo.selectAll then editControl:SelectAll() end controlAbove = editControl end local radioButtonContainer = dialog:GetNamedChild("RadioButtonContainer") dialog.radioButtonPool:ReleaseAllObjects() dialog.radioButtonGroup:Clear() radioButtonContainer:SetHidden(true) if dialogInfo.radioButtons then radioButtonContainer:SetHidden(false) radioButtonContainer:SetAnchor(TOPLEFT, controlAbove, BOTTOMLEFT, 0, 15) radioButtonContainer:SetAnchor(TOPRIGHT, controlAbove, BOTTOMRIGHT, 0, 15) local prev for i = 1, #dialogInfo.radioButtons do local buttonInfo = dialogInfo.radioButtons[i] local radioButton = dialog.radioButtonPool:AcquireObject() dialog.radioButtonGroup:Add(radioButton) local label = GetControl(radioButton, "Label") label:SetText(buttonInfo.text) radioButton.data = buttonInfo.data if i == 1 then dialog.radioButtonGroup:SetClickedButton(radioButton) radioButton:SetAnchor(TOPLEFT, nil, TOPLEFT, 15, 0) else radioButton:SetAnchor(TOPLEFT, prev, BOTTOMLEFT, 0, 10) end prev = radioButton end controlAbove = radioButtonContainer end if dialogInfo.warning then warningLabel:SetAnchor(TOPLEFT, controlAbove, BOTTOMLEFT, 0, 15) warningLabel:SetAnchor(TOPRIGHT, controlAbove, BOTTOMRIGHT, 0, 15) controlAbove = warningLabel end -- Handle button centering local btn1 = dialog:GetNamedChild("Button1") local btn2 = dialog:GetNamedChild("Button2") if numButtonInfos == 0 then -- Hide both buttons btn1:SetHidden(true) btn2:SetHidden(true) elseif numButtonInfos == 1 then -- Only show one btn2:SetHidden(true) btn1:ClearAnchors() if isGamepad then btn1:SetAnchor(TOPLEFT, controlAbove, BOTTOMLEFT, 0, 23) else btn1:SetAnchor(TOPRIGHT, controlAbove, BOTTOMRIGHT, 0, 23) end elseif numButtonInfos == 2 then -- Show both btn2:ClearAnchors() btn1:ClearAnchors() if isGamepad then btn1:SetAnchor(TOPLEFT, controlAbove, BOTTOMLEFT, 0, 23) btn2:SetAnchor(TOPLEFT, btn1, TOPRIGHT, BUTTON_SPACING, 0) else btn2:SetAnchor(TOPRIGHT, controlAbove, BOTTOMRIGHT, 0, 23) btn1:SetAnchor(TOPRIGHT, btn2, TOPLEFT, -BUTTON_SPACING, 0) end end if dialogInfo.callback then -- Pass in the id of the dialog being shown for this purpose. -- It can (eventually) be used to track this particular dialog instance. dialogInfo.callback(dialog.id) end end dialog:SetHandler("OnUpdate", dialogInfo.updateFn) g_displayedDialog = {name = name, dialog = dialog } ZO_DIALOG_SYNC_OBJECT:Show() ZO_DIALOG_SYNC_OBJECT:SetState(name) if not isGamepad then if(SCENE_MANAGER.RegisterTopLevel) then SCENE_MANAGER:RegisterTopLevel(dialog, TOPLEVEL_LOCKS_UI_MODE) SCENE_MANAGER:ShowTopLevel(dialog) else dialog:SetHidden(false) end else ZO_GenericGamepadDialog_Show(dialog) end -- Append the keybind state index to the dialog so that it knows where its keybinds sit on the keybind stack dialog.keybindStateIndex = KEYBIND_STRIP:GetTopKeybindStateIndex() dialog.name = name dialog:BringWindowToTop() if not isGamepadDialog then PlaySound(SOUNDS.DIALOG_SHOW) end --edit controls cant take focus when hidden if(dialogInfo.editBox) then dialog:GetNamedChild("EditBox"):TakeFocus() end return dialog end function ZO_TwoButtonDialog_OnInitialized(self, id) self.id = id self.requiredTextFields = ZO_RequiredTextFields:New() self.requiredTextFields:AddTextField(GetControl(self, "EditBox")) self.radioButtonGroup = ZO_RadioButtonGroup:New() self.radioButtonPool = ZO_ControlPool:New("ZO_DialogRadioButton", self:GetNamedChild("RadioButtonContainer"), "RadioButton") self.buttonControls = { GetControl(self, "Button1"), GetControl(self, "Button2") } self.loadingIcon = GetControl(self, "Loading") end function ZO_Dialogs_InitializeDialog(dialog, isGamepad) ReanchorDialog(dialog, isGamepad) local textControl = dialog:GetNamedChild("Text") local button1Control = dialog:GetNamedChild("Button1") local button2Control = dialog:GetNamedChild("Button2") local buttonExtraText1Control = dialog:GetNamedChild("ButtonExtraText1") local buttonExtraText2Control = dialog:GetNamedChild("ButtonExtraText2") local editContainer = dialog:GetNamedChild("Edit") local editControl = dialog:GetNamedChild("EditBox") local warningLabel = dialog:GetNamedChild("WarningText") textControl:SetHorizontalAlignment(TEXT_ALIGN_LEFT) warningLabel:SetHorizontalAlignment(TEXT_ALIGN_LEFT) buttonExtraText1Control:SetHidden(true) buttonExtraText2Control:SetHidden(true) button1Control:SetState(BSTATE_NORMAL, false) button2Control:SetState(BSTATE_NORMAL, false) button1Control:SetKeybindEnabled(true) button2Control:SetKeybindEnabled(true) editContainer:SetHidden(true) warningLabel:SetHidden(true) editControl:SetText("") editControl:LoseFocus() if(not g_currencyPool) then g_currencyPool = ZO_ControlPool:New("ZO_CurrencyTemplate", dialog, "Currency") end dialog.buttonCostKeys = {} dialog.currencyKey = nil dialog.requiredTextFields:ClearButtons() dialog.requiredTextFields:SetMatchingString(nil) dialog.instanceId = g_curInstanceId g_curInstanceId = g_curInstanceId + 1 end function ZO_Dialogs_ReleaseAllDialogsOfName(name, filterFunction) RemoveQueuedDialogs(name, filterFunction) local dialog = ZO_Dialogs_FindDialog(name, filterFunction) if dialog then ZO_Dialogs_ReleaseDialog(dialog) end ZO_Dialogs_SetDialogQueuePaused(false) end function ZO_Dialogs_ReleaseAllDialogsExcept(...) RemoveQueuedDialogsExcept(...) if g_displayedDialog and not ContainsName(g_displayedDialog.name, ...) then ZO_Dialogs_ReleaseDialog(g_displayedDialog.dialog) end ZO_Dialogs_SetDialogQueuePaused(false) end function ZO_Dialogs_ReleaseDialogOnButtonPress(nameOrDialog) return ZO_Dialogs_ReleaseDialog(nameOrDialog, RELEASED_FROM_BUTTON_PRESS) end function ZO_Dialogs_ReleaseDialog(nameOrDialog, releasedFromButton, filterFunction) local dialog = ZO_Dialogs_FindDialog(nameOrDialog, filterFunction) if dialog == nil then -- This dialog is not currently visible return false end if dialog.hiding then return false end dialog.hiding = true if dialog.isGamepad then --Gamepad dialog will release when it's finished hiding dialog.hideFunction(dialog, releasedFromButton) else --Keyboard dialogs will hide instantly if SCENE_MANAGER.HideTopLevel then SCENE_MANAGER:HideTopLevel(dialog) else dialog:SetHidden(true) end ZO_CompleteReleaseDialogOnDialogHidden(dialog, releasedFromButton) end return true end function ZO_Dialogs_IsDialogQueuePaused() return g_isDialogQueuePaused end function ZO_Dialogs_SetDialogQueuePaused(isPaused, exemptionList) if isPaused ~= g_isDialogQueuePaused then g_isDialogQueuePaused = isPaused if isPaused then g_dialogPauseExemptions = exemptionList else -- Show next dialog in queue local queuedDialog = table.remove(g_dialogQueue, 1) if queuedDialog then ZO_Dialogs_ShowDialog(unpack(queuedDialog)) end ZO_ClearNumericallyIndexedTable(g_dialogPauseExemptions) end end end function ZO_CompleteReleaseDialogOnDialogHidden(dialog, releasedFromButton) dialog.hiding = false local name = dialog.name local dialogInfo = dialog.info if not dialogInfo.customControl then dialog:SetHandler("OnUpdate", nil) for i = 1, NUM_DIALOG_BUTTONS do local btn = dialog:GetNamedChild("Button"..i) if btn then btn.m_callback = nil btn.m_noReleaseOnClick = nil end if dialog.buttonCostKeys and dialog.buttonCostKeys[i] then g_currencyPool:ReleaseObject(dialog.buttonCostKeys[i]) dialog.buttonCostKeys[i] = nil end end if dialog.currencyKey then g_currencyPool:ReleaseObject(dialog.currencyKey) dialog.currencyKey = nil end end dialog.name = nil if ZO_Dialogs_IsShowing(dialog) then g_displayedDialog = nil ZO_DIALOG_SYNC_OBJECT:SetState(nil) ZO_DIALOG_SYNC_OBJECT:Hide() end if dialogInfo.noChoiceCallback and not releasedFromButton then dialogInfo.noChoiceCallback(dialog) end if next(g_dialogQueue) then local currentScene = SCENE_MANAGER:GetCurrentScene() local state = currentScene and currentScene:GetState() if state == SCENE_HIDING or state == SCENE_HIDDEN or not state then ZO_Dialogs_ReleaseAllDialogs(true) end end if dialogInfo.finishedCallback then dialogInfo.finishedCallback(dialog) end local _, queuedDialog = next(g_dialogQueue) if queuedDialog then if not g_isDialogQueuePaused or ZO_IsElementInNumericallyIndexedTable(g_dialogPauseExemptions, queuedDialog[QUEUED_DIALOG_INDEX_NAME]) then -- Show next dialog in queue table.remove(g_dialogQueue, 1) ZO_Dialogs_ShowDialog(unpack(queuedDialog)) return end end if not ZO_Dialogs_IsShowingDialog() then CALLBACK_MANAGER:FireCallbacks("AllDialogsHidden") end end function ZO_Dialogs_ReleaseAllDialogs(forceAll) for _, dialog in ipairs(g_dialogQueue) do local dialogInfo = ESO_Dialogs[dialog[QUEUED_DIALOG_INDEX_NAME]] if dialogInfo and dialogInfo.removedFromQueueCallback then dialogInfo.removedFromQueueCallback(dialog[QUEUED_DIALOG_INDEX_DATA]) end end ZO_ClearNumericallyIndexedTable(g_dialogQueue) local dialog = GetDisplayedDialog() if dialog and (forceAll or not dialog.info.mustChoose) then ZO_Dialogs_ReleaseDialog(dialog) end ZO_Dialogs_SetDialogQueuePaused(false) end -- If textTable is nil, the default mainText table (defined in the ESO_Dialogs table) is used function ZO_Dialogs_UpdateDialogMainText(dialog, textTable, params) if dialog then if dialog.isGamepad then if dialog.info and dialog.headerData then local mainTextTable = textTable or dialog.info.mainText local mainText = ZO_GetFormattedDialogText(dialog, mainTextTable, params) if mainText and mainText ~= "" then ZO_GenericGamepadDialog_RefreshText(dialog, dialog.headerData.titleText, mainText, dialog.warningTextControl:GetText(), dialog.subTextControl:GetText()) end end else local textControl = dialog:GetNamedChild("Text") if textTable then dialog.mainText = textTable end SetDialogTextFormatted(dialog, textControl, dialog.mainText, params) end end end function ZO_Dialogs_UpdateDialogTitleText(dialog, textTable, params) if dialog then local titleControl = dialog:GetNamedChild("Title") if textTable then dialog.title = textTable end SetDialogTextFormatted(dialog, titleControl, dialog.title, params) end end function ZO_Dialogs_UpdateDialogWarningText(dialog, textTable, params) if dialog then if dialog.isGamepad then if dialog.info and dialog.headerData then textTable = textTable or dialog.info.warning local warningText = ZO_GetFormattedDialogText(dialog, textTable, params) if warningText and warningText ~= "" then ZO_GenericGamepadDialog_RefreshText(dialog, dialog.headerData.titleText, dialog.mainTextControl:GetText(), warningText, dialog.subTextControl:GetText()) end end else local warningLabel = dialog:GetNamedChild("WarningText") if textTable then dialog.warning = textTable end SetDialogTextFormatted(dialog, warningLabel, dialog.warning, params) end end end function ZO_Dialogs_GetEditBoxText(dialog) if(dialog) then local editControl = dialog:GetNamedChild("EditBox") if not editControl:IsHidden() then return editControl:GetText() end end return nil end function ZO_Dialogs_GetSelectedRadioButtonData(dialog) local clickedButton = dialog.radioButtonGroup:GetClickedButton() if(clickedButton) then return clickedButton.data end end function ZO_Dialogs_UpdateButtonVisibilityAndEnabledState(dialog) local buttonInfos = dialog.info.buttons local numButtonInfos = buttonInfos and #buttonInfos or 0 dialog.numButtons = numButtonInfos if numButtonInfos > 0 and not dialog.isGamepad then for i = 1, numButtonInfos do local buttonInfo = buttonInfos[i] local button = GetButtonControl(dialog, i) local buttonVisible = true if buttonInfo.visible ~= nil then if type(buttonInfo.visible) == "function" then buttonVisible = buttonInfo.visible(dialog) else buttonVisible = buttonInfo.visible end end if not buttonVisible then button:SetHidden(true) button:SetKeybindEnabled(false) else local isButtonEnabled = true if buttonInfo.enabled ~= nil then if type(buttonInfo.enabled) == "function" then isButtonEnabled = buttonInfo.enabled(dialog) else isButtonEnabled = buttonInfo.enabled end end local hasKeybind = button:GetKeybind() ~= nil button:SetHidden(false) button:SetEnabled(isButtonEnabled) button:SetKeybindEnabled(hasKeybind and isButtonEnabled) end end end end -- Activate or deactivate a button...use BSTATE_NORMAL to activate and BSTATE_DISABLED to deactivate function ZO_Dialogs_UpdateButtonState(dialog, buttonNumber, buttonState) if(dialog and buttonNumber) then local buttonControl = dialog:GetNamedChild("Button"..buttonNumber) local lockButton = false if(buttonState == BSTATE_DISABLED) then lockButton = true end buttonControl:SetState(buttonState, lockButton) end end -- Update the text on a button itself function ZO_Dialogs_UpdateButtonText(dialog, buttonNumber, text) if(dialog and buttonNumber) then local buttonControl = dialog:GetNamedChild("Button"..buttonNumber) if(text) then if(type(text) == "number") then text = GetString(text) end buttonControl:SetText(text) end end end -- Update the text underneath a button...if textTable is nil, this extra text control is hidden function ZO_Dialogs_UpdateButtonExtraText(dialog, buttonNumber, textTable, params) if dialog and buttonNumber then local textControl = dialog:GetNamedChild("ButtonExtraText"..buttonNumber) if textTable then SetDialogTextFormatted(dialog, textControl, textTable, params) textControl:SetHidden(false) else textControl:SetHidden(true) end end end -- Update the currency control underneath a button function ZO_Dialogs_UpdateButtonCost(dialog, buttonNumber, cost) if dialog then local buttonCostsShown = 0 for i = 1,NUM_DIALOG_BUTTONS do if dialog.buttonCostKeys[i] then buttonCostsShown = buttonCostsShown + 1 end end if cost then local textControl = dialog:GetNamedChild("ButtonExtraText"..buttonNumber) local buttonControl = dialog:GetNamedChild("Button"..buttonNumber) local currencyControl local key = dialog.buttonCostKeys[buttonNumber] if key then currencyControl = g_currencyPool:AcquireObject(key) else local newCurrencyControl, newCurrencyKey = g_currencyPool:AcquireObject() newCurrencyControl:SetParent(dialog) dialog.buttonCostKeys[buttonNumber] = newCurrencyKey currencyControl = newCurrencyControl end ZO_CurrencyControl_SetSimpleCurrency(currencyControl, CURT_MONEY, cost, nil, CURRENCY_DONT_SHOW_ALL) local visibleCurrencyWidth = currencyControl:GetWidth() if textControl:IsHidden() then currencyControl:SetAnchor(TOPLEFT, buttonControl, BOTTOM, -visibleCurrencyWidth / 2, 5) else currencyControl:SetAnchor(TOPLEFT, textControl, BOTTOM, -visibleCurrencyWidth / 2, 5) end currencyControl:SetHidden(false) else local key = dialog.buttonCostKeys[buttonNumber] if key then g_currencyPool:ReleaseObject(key) dialog.buttonCostKeys[buttonNumber] = nil end end end end function ZO_Dialogs_IsDialogRegistered(name) local dialogInfo = ESO_Dialogs[name] return dialogInfo and (type(dialogInfo) == "table") end function ZO_Dialogs_RegisterCustomDialog(name, info) ESO_Dialogs[name] = info end function ZO_Dialogs_CloseKeybindPressed() local dialog = GetDisplayedDialog() if dialog then if not dialog.info.mustChoose then if not ZO_Dialogs_ReleaseDialog(dialog.name, not RELEASED_FROM_BUTTON_PRESS) then dialog:SetHidden(true) end if dialog.info.hideSound then PlaySound(dialog.info.hideSound) else if not dialog.isGamepad then PlaySound(SOUNDS.DIALOG_HIDE) end end if dialog.isGamepad then SCENE_MANAGER:RequestShowLeaderBaseScene() end end end end function ZO_Dialogs_HandleButtonForKeybind(dialog, keybind) local handledButton = false for i = 1, dialog.numButtons do local btn = GetButtonControl(dialog, i) if(btn ~= nil and btn:IsEnabled() and btn:GetKeybind() == keybind) then btn:OnClicked() handledButton = true break end end return handledButton end function ZO_Dialogs_ButtonKeybindPressed(keybind) local dialog = GetDisplayedDialog() if(dialog) then local handledButton = ZO_Dialogs_HandleButtonForKeybind(dialog, keybind) if(not handledButton and IsInGamepadPreferredMode()) then if(ZO_KeybindStrip_HandleKeybindDown(keybind)) then if(not dialog.info.blockDialogReleaseOnPress) then ZO_Dialogs_ReleaseDialogOnButtonPress(dialog.name) end end end end end function ZO_Dialogs_ButtonKeybindReleased(keybind) local dialog = GetDisplayedDialog() if(dialog) then local handledButton = ZO_Dialogs_HandleButtonForKeybind(dialog, keybind) if(not handledButton) then ZO_KeybindStrip_HandleKeybindUp(keybind) end end end function ZO_SharedDialogButton_OnInitialized(self) ZO_KeybindButtonTemplate_OnInitialized(self) self:SetCallback(HandleCallback) end function ZO_CustomDialogButton_OnInitialized(self) ZO_SharedDialogButton_OnInitialized(self) local parent = self:GetParent() local maxButtonIndex local maxButton for i = 1, parent:GetNumChildren() do local child = parent:GetChild(i) local customButtonIndex = child.customButtonIndex if(customButtonIndex ~= nil) then if(maxButtonIndex == nil or customButtonIndex > maxButtonIndex) then maxButtonIndex = customButtonIndex maxButton = child end end end if(maxButton) then self:SetAnchor(TOPRIGHT, maxButton, TOPLEFT, -BUTTON_SPACING, 0) else self:SetAnchor(BOTTOMRIGHT, nil, BOTTOMRIGHT, -25, -15) end if(maxButtonIndex) then self.customButtonIndex = maxButtonIndex + 1 else self.customButtonIndex = 1 end end function ZO_DialogButton_OnInitialized(self) ZO_ChromaKeybindButtonTemplate_OnInitialized(self) self:SetCallback(HandleCallback) end function ZO_TwoButtonDialogEditBox_OnTextChanged(control) if control.instructions then if control.validator then local DEFAULT_ANCHOR_CONTROL = nil local violations = {control.validator(control:GetText())} control.instructions:Show(DEFAULT_ANCHOR_CONTROL, violations) else control.instructions:Hide() end end end function ZO_TwoButtonDialogEditBox_OnFocusGained(control) if control.instructions then if control.validator then local DEFAULT_ANCHOR_CONTROL = nil local violations = {control.validator(control:GetText())} control.instructions:Show(DEFAULT_ANCHOR_CONTROL, violations) end end end function ZO_TwoButtonDialogEditBox_OnFocusLost(control) if control.instructions and control.instructions:HasRules() then if control.validator then local violations = {control.validator(control:GetText())} if #violations == 0 then control.instructions:Hide() end end end end EVENT_MANAGER:RegisterForEvent("ZO_Dialog", EVENT_GUI_UNLOADING, function() if ZO_Dialogs_IsShowingDialog() then ZO_DIALOG_SYNC_OBJECT:Hide() end end) if EVENT_PLAYER_ACTIVATED ~= nil then -- This event only exists in AllIngame GUIs, will not exist in Pregame. EVENT_MANAGER:RegisterForEvent("ZO_Dialog", EVENT_PLAYER_ACTIVATED, function() if not ZO_Dialogs_IsShowingDialog() then -- Catch-all to ensure the dialog action layer was correctly removed -- in case the remove action layer failed due to being in the middle of loading RemoveActionLayerByName(GetString(SI_KEYBINDINGS_LAYER_DIALOG)) end end) end