diff --git a/Librarian.txt b/Librarian.txt index f187721..b5c33ba 100644 --- a/Librarian.txt +++ b/Librarian.txt @@ -1,6 +1,6 @@ ## Title: Librarian ## Author: |c4EFFF6Calia1120|r, Flamage -## APIVersion: 100016 100017 +## APIVersion: 100017 100018 ## SavedVariables: Librarian_SavedVariables ## OptionalDependsOn: LibAddonMenu-2.0 diff --git a/Libs/LibAddonMenu-2.0/LICENSE b/Libs/LibAddonMenu-2.0/LICENSE index 82fcf2f..f69cbd4 100644 --- a/Libs/LibAddonMenu-2.0/LICENSE +++ b/Libs/LibAddonMenu-2.0/LICENSE @@ -1,6 +1,6 @@ The Artistic License 2.0 - Copyright (c) 2015 Ryan Lakanen (Seerah) + Copyright (c) 2016 Ryan Lakanen (Seerah) Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua b/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua index 03fb4c4..cb97a2e 100644 --- a/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua +++ b/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua @@ -1,220 +1,814 @@ --- LibAddonMenu-2.0 & its files © Ryan Lakanen (Seerah) -- --- Distributed under The Artistic License 2.0 (see LICENSE) -- +-- LibAddonMenu-2.0 & its files © Ryan Lakanen (Seerah) -- +-- Distributed under The Artistic License 2.0 (see LICENSE) -- ------------------------------------------------------------------ --Register LAM with LibStub -local MAJOR, MINOR = "LibAddonMenu-2.0", 17 +local MAJOR, MINOR = "LibAddonMenu-2.0", 23 local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR) -if not lam then return end --the same or newer version of this lib is already loaded into memory +if not lam then return end --the same or newer version of this lib is already loaded into memory local messages = {} local MESSAGE_PREFIX = "[LAM2] " local function PrintLater(msg) - if(CHAT_SYSTEM.primaryContainer) then - d(MESSAGE_PREFIX .. msg) - else - messages[#messages + 1] = msg - end + if CHAT_SYSTEM.primaryContainer then + d(MESSAGE_PREFIX .. msg) + else + messages[#messages + 1] = msg + end end local function FlushMessages() - for i = 1, #messages do - d(MESSAGE_PREFIX .. messages[i]) - end - messages = {} + for i = 1, #messages do + d(MESSAGE_PREFIX .. messages[i]) + end + messages = {} end -if(LAMSettingsPanelCreated and not LAMCompatibilityWarning) then - PrintLater("An old version of LibAddonMenu with compatibility issues was detected. For more information on how to proceed search for LibAddonMenu on esoui.com") - LAMCompatibilityWarning = true +if LAMSettingsPanelCreated and not LAMCompatibilityWarning then + PrintLater("An old version of LibAddonMenu with compatibility issues was detected. For more information on how to proceed search for LibAddonMenu on esoui.com") + LAMCompatibilityWarning = true end --UPVALUES-- local wm = WINDOW_MANAGER +local em = EVENT_MANAGER +local sm = SCENE_MANAGER local cm = CALLBACK_MANAGER +local tconcat = table.concat local tinsert = table.insert -local optionsWindow = ZO_OptionsWindowSettingsScrollChild -local _ + +local MIN_HEIGHT = 26 +local HALF_WIDTH_LINE_SPACING = 2 +local OPTIONS_CREATION_RUNNING = 1 +local OPTIONS_CREATED = 2 +local LAM_CONFIRM_DIALOG = "LAM_CONFIRM_DIALOG" +local LAM_DEFAULTS_DIALOG = "LAM_DEFAULTS" +local LAM_RELOAD_DIALOG = "LAM_RELOAD_DIALOG" local addonsForList = {} local addonToOptionsMap = {} -local optionsCreated = {} +local optionsState = {} lam.widgets = lam.widgets or {} local widgets = lam.widgets +lam.util = lam.util or {} +local util = lam.util +lam.controlsForReload = lam.controlsForReload or {} +local controlsForReload = lam.controlsForReload + +local function GetDefaultValue(default) + if type(default) == "function" then + return default() + end + return default +end + +local function GetStringFromValue(value) + if type(value) == "function" then + return value() + elseif type(value) == "number" then + return GetString(value) + end + return value +end + +local function CreateBaseControl(parent, controlData, controlName) + local control = wm:CreateControl(controlName or controlData.reference, parent.scroll or parent, CT_CONTROL) + control.panel = parent.panel or parent -- if this is in a submenu, panel is the submenu's parent + control.data = controlData + + control.isHalfWidth = controlData.width == "half" + local width = 510 -- set default width in case a custom parent object is passed + if control.panel.GetWidth ~= nil then width = control.panel:GetWidth() - 60 end + control:SetWidth(width) + return control +end + +local function CreateLabelAndContainerControl(parent, controlData, controlName) + local control = CreateBaseControl(parent, controlData, controlName) + local width = control:GetWidth() + + local container = wm:CreateControl(nil, control, CT_CONTROL) + container:SetDimensions(width / 3, MIN_HEIGHT) + control.container = container + + local label = wm:CreateControl(nil, control, CT_LABEL) + label:SetFont("ZoFontWinH4") + label:SetHeight(MIN_HEIGHT) + label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) + label:SetText(GetStringFromValue(controlData.name)) + control.label = label + + if control.isHalfWidth then + control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING) + label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0) + label:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0) + container:SetAnchor(TOPRIGHT, control.label, BOTTOMRIGHT, 0, HALF_WIDTH_LINE_SPACING) + else + control:SetDimensions(width, MIN_HEIGHT) + container:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0) + label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0) + label:SetAnchor(TOPRIGHT, container, TOPLEFT, 5, 0) + end + + control.data.tooltipText = GetStringFromValue(control.data.tooltip) + control:SetMouseEnabled(true) + control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + return control +end + +local function GetTopPanel(panel) + while panel.panel and panel.panel ~= panel do + panel = panel.panel + end + return panel +end + +local function IsSame(objA, objB) + if #objA ~= #objB then return false end + for i = 1, #objA do + if objA[i] ~= objB[i] then return false end + end + return true +end + +local function RefreshReloadUIButton() + lam.requiresReload = false + + for i = 1, #controlsForReload do + local reloadControl = controlsForReload[i] + if not IsSame(reloadControl.startValue, {reloadControl.data.getFunc()}) then + lam.requiresReload = true + break + end + end + + lam.applyButton:SetHidden(not lam.requiresReload) +end + +local function RequestRefreshIfNeeded(control) + -- if our parent window wants to refresh controls, then fire the callback + local panel = GetTopPanel(control.panel) + local panelData = panel.data + if panelData.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + RefreshReloadUIButton() +end + +local function RegisterForRefreshIfNeeded(control) + -- if our parent window wants to refresh controls, then add this to the list + local panel = GetTopPanel(control.panel) + local panelData = panel.data + if panelData.registerForRefresh or panelData.registerForDefaults then + tinsert(panel.controlsToRefresh or {}, control) -- prevent errors on custom panels + end +end + +local function RegisterForReloadIfNeeded(control) + if control.data.requiresReload then + tinsert(controlsForReload, control) + control.startValue = {control.data.getFunc()} + end +end + +local function GetConfirmDialog() + if(not ESO_Dialogs[LAM_CONFIRM_DIALOG]) then + ESO_Dialogs[LAM_CONFIRM_DIALOG] = { + canQueue = true, + title = { + text = "", + }, + mainText = { + text = "", + }, + buttons = { + [1] = { + text = SI_DIALOG_CONFIRM, + callback = function(dialog) end, + }, + [2] = { + text = SI_DIALOG_CANCEL, + } + } + } + end + return ESO_Dialogs[LAM_CONFIRM_DIALOG] +end + +local function ShowConfirmationDialog(title, body, callback) + local dialog = GetConfirmDialog() + dialog.title.text = title + dialog.mainText.text = body + dialog.buttons[1].callback = callback + ZO_Dialogs_ShowDialog(LAM_CONFIRM_DIALOG) +end + +local function GetDefaultsDialog() + if(not ESO_Dialogs[LAM_DEFAULTS_DIALOG]) then + ESO_Dialogs[LAM_DEFAULTS_DIALOG] = { + canQueue = true, + title = { + text = SI_INTERFACE_OPTIONS_RESET_TO_DEFAULT_TOOLTIP, + }, + mainText = { + text = SI_OPTIONS_RESET_PROMPT, + }, + buttons = { + [1] = { + text = SI_OPTIONS_RESET, + callback = function(dialog) end, + }, + [2] = { + text = SI_DIALOG_CANCEL, + } + } + } + end + return ESO_Dialogs[LAM_DEFAULTS_DIALOG] +end + +local function ShowDefaultsDialog(panel) + local dialog = GetDefaultsDialog() + dialog.buttons[1].callback = function() + panel:ForceDefaults() + RefreshReloadUIButton() + end + ZO_Dialogs_ShowDialog(LAM_DEFAULTS_DIALOG) +end + +local function DiscardChangesOnReloadControls() + for i = 1, #controlsForReload do + local reloadControl = controlsForReload[i] + if not IsSame(reloadControl.startValue, {reloadControl.data.getFunc()}) then + reloadControl:UpdateValue(false, unpack(reloadControl.startValue)) + end + end + lam.requiresReload = false + lam.applyButton:SetHidden(true) +end + +local function StorePanelForReopening() + local saveData = ZO_Ingame_SavedVariables["LAM"] or {} + saveData.reopenPanel = lam.currentAddonPanel:GetName() + ZO_Ingame_SavedVariables["LAM"] = saveData +end + +local function RetrievePanelForReopening() + local saveData = ZO_Ingame_SavedVariables["LAM"] + if(saveData) then + ZO_Ingame_SavedVariables["LAM"] = nil + return _G[saveData.reopenPanel] + end +end + +local function HandleReloadUIPressed() + StorePanelForReopening() + ReloadUI() +end + +local function HandleLoadDefaultsPressed() + ShowDefaultsDialog(lam.currentAddonPanel) +end + +local function GetReloadDialog() + if(not ESO_Dialogs[LAM_RELOAD_DIALOG]) then + ESO_Dialogs[LAM_RELOAD_DIALOG] = { + canQueue = true, + title = { + text = util.L["RELOAD_DIALOG_TITLE"], + }, + mainText = { + text = util.L["RELOAD_DIALOG_TEXT"], + }, + buttons = { + [1] = { + text = util.L["RELOAD_DIALOG_RELOAD_BUTTON"], + callback = function() ReloadUI() end, + }, + [2] = { + text = util.L["RELOAD_DIALOG_DISCARD_BUTTON"], + callback = DiscardChangesOnReloadControls, + } + }, + noChoiceCallback = DiscardChangesOnReloadControls, + } + end + return ESO_Dialogs[LAM_CONFIRM_DIALOG] +end + +local function ShowReloadDialogIfNeeded() + if lam.requiresReload then + local dialog = GetReloadDialog() + ZO_Dialogs_ShowDialog(LAM_RELOAD_DIALOG) + end +end + +local function UpdateWarning(control) + local warning + if control.data.warning ~= nil then + warning = util.GetStringFromValue(control.data.warning) + end + + if control.data.requiresReload then + if not warning then + warning = string.format("|cff0000%s", util.L["RELOAD_UI_WARNING"]) + else + warning = string.format("%s\n\n|cff0000%s", warning, util.L["RELOAD_UI_WARNING"]) + end + end + + if not warning then + control.warning:SetHidden(true) + else + control.warning.data = {tooltipText = warning} + control.warning:SetHidden(false) + end +end + +local localization = { + en = { + PANEL_NAME = "Addons", + AUTHOR = string.format("%s: <<X:1>>", GetString(SI_ADDON_MANAGER_AUTHOR)), -- "Author: <<X:1>>" + VERSION = "Version: <<X:1>>", + WEBSITE = "Visit Website", + PANEL_INFO_FONT = "$(CHAT_FONT)|14|soft-shadow-thin", + RELOAD_UI_WARNING = "Changes to this setting require an UI reload in order to take effect.", + RELOAD_DIALOG_TITLE = "UI Reload required", + RELOAD_DIALOG_TEXT = "Some changes require an UI reload in order to take effect. Do you want to reload now or discard the changes?", + RELOAD_DIALOG_RELOAD_BUTTON = "Reload", + RELOAD_DIALOG_DISCARD_BUTTON = "Discard", + }, + fr = { -- provided by Ayantir + PANEL_NAME = "Extensions", + WEBSITE = "Visiter le site Web", + RELOAD_UI_WARNING = "La modification de ce paramètre requiert un rechargement de l'UI pour qu'il soit pris en compte.", + RELOAD_DIALOG_TITLE = "Reload UI requis", + RELOAD_DIALOG_TEXT = "Certaines modifications requièrent un rechargement de l'UI pour qu'ils soient pris en compte. Souhaitez-vous recharger l'interface maintenant ou annuler les modifications ?", + RELOAD_DIALOG_RELOAD_BUTTON = "Recharger", + RELOAD_DIALOG_DISCARD_BUTTON = "Annuler", + }, + de = { -- provided by sirinsidiator + PANEL_NAME = "Erweiterungen", + WEBSITE = "Webseite besuchen", + RELOAD_UI_WARNING = "Änderungen an dieser Option werden erst übernommen nachdem die Benutzeroberfläche neu geladen wird.", + RELOAD_DIALOG_TITLE = "Neuladen benötigt", + RELOAD_DIALOG_TEXT = "Einige Änderungen werden erst übernommen nachdem die Benutzeroberfläche neu geladen wird. Wollt Ihr sie jetzt neu laden oder die Änderungen verwerfen?", + RELOAD_DIALOG_RELOAD_BUTTON = "Neu laden", + RELOAD_DIALOG_DISCARD_BUTTON = "Verwerfen", + }, + ru = { -- provided by TERAB1T + PANEL_NAME = "Дополнения", + VERSION = "Версия: <<X:1>>", + WEBSITE = "Посетить сайт", + PANEL_INFO_FONT = "RuESO/fonts/Univers57.otf|14|soft-shadow-thin", + }, + es = { -- provided by silvereyes333 + WEBSITE = "Vaya al sitio web", + }, + jp = { -- provided by k0ta0uchi + PANEL_NAME = "アドオン設定", + WEBSITE = "ウェブサイトを見る", + }, + zh = { -- provided by bssthu + PANEL_NAME = "插件", + VERSION = "版本: <<X:1>>", + WEBSITE = "访问网站", + PANEL_INFO_FONT = "EsoZh/fonts/univers57.otf|14|soft-shadow-thin", + }, +} + +util.L = ZO_ShallowTableCopy(localization[GetCVar("Language.2")], localization["en"]) +util.GetTooltipText = GetStringFromValue -- deprecated, use util.GetStringFromValue instead +util.GetStringFromValue = GetStringFromValue +util.GetDefaultValue = GetDefaultValue +util.CreateBaseControl = CreateBaseControl +util.CreateLabelAndContainerControl = CreateLabelAndContainerControl +util.RequestRefreshIfNeeded = RequestRefreshIfNeeded +util.RegisterForRefreshIfNeeded = RegisterForRefreshIfNeeded +util.RegisterForReloadIfNeeded = RegisterForReloadIfNeeded +util.GetTopPanel = GetTopPanel +util.ShowConfirmationDialog = ShowConfirmationDialog +util.UpdateWarning = UpdateWarning + +local ADDON_DATA_TYPE = 1 +local RESELECTING_DURING_REBUILD = true +local USER_REQUESTED_OPEN = true + + +--INTERNAL FUNCTION +--scrolls ZO_ScrollList `list` to move the row corresponding to `data` +-- into view (does nothing if there is no such row in the list) +--unlike ZO_ScrollList_ScrollDataIntoView, this function accounts for +-- fading near the list's edges - it avoids the fading area by scrolling +-- a little further than the ZO function +local function ScrollDataIntoView(list, data) + local targetIndex = data.sortIndex + if not targetIndex then return end + + local scrollMin, scrollMax = list.scrollbar:GetMinMax() + local scrollTop = list.scrollbar:GetValue() + local controlHeight = list.controlHeight + local targetMin = controlHeight * (targetIndex - 1) - 64 + -- subtracting 64 ain't arbitrary, it's the maximum fading height + -- (libraries/zo_templates/scrolltemplates.lua/UpdateScrollFade) + + if targetMin < scrollTop then + ZO_ScrollList_ScrollAbsolute(list, zo_max(targetMin, scrollMin)) + else + local listHeight = ZO_ScrollList_GetHeight(list) + local targetMax = controlHeight * targetIndex + 64 - listHeight + + if targetMax > scrollTop then + ZO_ScrollList_ScrollAbsolute(list, zo_min(targetMax, scrollMax)) + end + end +end + + +--INTERNAL FUNCTION +--constructs a string pattern from the text in `searchEdit` control +-- * metacharacters are escaped, losing their special meaning +-- * whitespace matches anything (including empty substring) +--if there is nothing but whitespace, returns nil +--otherwise returns a filter function, which takes a `data` table argument +-- and returns true iff `data.filterText` matches the pattern +local function GetSearchFilterFunc(searchEdit) + local text = searchEdit:GetText():lower() + local pattern = text:match("(%S+.-)%s*$") + + if not pattern then -- nothing but whitespace + return nil + end + + -- escape metacharacters, e.g. "ESO-Datenbank.de" => "ESO%-Datenbank%.de" + pattern = pattern:gsub("[-*+?^$().[%]%%]", "%%%0") + + -- replace whitespace with "match shortest anything" + pattern = pattern:gsub("%s+", ".-") + + return function(data) + return data.filterText:lower():find(pattern) ~= nil + end +end + + +--INTERNAL FUNCTION +--populates `addonList` with entries from `addonsForList` +-- addonList = ZO_ScrollList control +-- filter = [optional] function(data) +local function PopulateAddonList(addonList, filter) + local entryList = ZO_ScrollList_GetDataList(addonList) + local numEntries = 0 + local selectedData = nil + + ZO_ScrollList_Clear(addonList) + + for i, data in ipairs(addonsForList) do + if not filter or filter(data) then + local dataEntry = ZO_ScrollList_CreateDataEntry(ADDON_DATA_TYPE, data) + numEntries = numEntries + 1 + data.sortIndex = numEntries + entryList[numEntries] = dataEntry + -- select the first panel passing the filter, or the currently + -- shown panel, but only if it passes the filter as well + if selectedData == nil or data.panel == lam.currentAddonPanel then + selectedData = data + end + else + data.sortIndex = nil + end + end + + ZO_ScrollList_Commit(addonList) + + if selectedData then + if selectedData.panel == lam.currentAddonPanel then + ZO_ScrollList_SelectData(addonList, selectedData, nil, RESELECTING_DURING_REBUILD) + else + ZO_ScrollList_SelectData(addonList, selectedData, nil) + end + ScrollDataIntoView(addonList, selectedData) + end +end --METHOD: REGISTER WIDGET-- --each widget has its version checked before loading, --so we only have the most recent one in memory --Usage: --- widgetType = "string"; the type of widget being registered --- widgetVersion = integer; the widget's version number +-- widgetType = "string"; the type of widget being registered +-- widgetVersion = integer; the widget's version number LAMCreateControl = LAMCreateControl or {} local lamcc = LAMCreateControl function lam:RegisterWidget(widgetType, widgetVersion) - if widgets[widgetType] and widgets[widgetType] >= widgetVersion then - return false - else - widgets[widgetType] = widgetVersion - return true - end + if widgets[widgetType] and widgets[widgetType] >= widgetVersion then + return false + else + widgets[widgetType] = widgetVersion + return true + end end +-- INTERNAL METHOD: hijacks the handlers for the actions in the OptionsWindow layer if not already done +local function InitKeybindActions() + if not lam.keybindsInitialized then + lam.keybindsInitialized = true + ZO_PreHook(KEYBOARD_OPTIONS, "ApplySettings", function() + if lam.currentPanelOpened then + if not lam.applyButton:IsHidden() then + HandleReloadUIPressed() + end + return true + end + end) + ZO_PreHook("ZO_Dialogs_ShowDialog", function(dialogName) + if lam.currentPanelOpened and dialogName == "OPTIONS_RESET_TO_DEFAULTS" then + if not lam.defaultButton:IsHidden() then + HandleLoadDefaultsPressed() + end + return true + end + end) + end +end + +-- INTERNAL METHOD: fires the LAM-PanelOpened callback if not already done +local function OpenCurrentPanel() + if lam.currentAddonPanel and not lam.currentPanelOpened then + lam.currentPanelOpened = true + lam.defaultButton:SetHidden(not lam.currentAddonPanel.data.registerForDefaults) + cm:FireCallbacks("LAM-PanelOpened", lam.currentAddonPanel) + end +end + +-- INTERNAL METHOD: fires the LAM-PanelClosed callback if not already done +local function CloseCurrentPanel() + if lam.currentAddonPanel and lam.currentPanelOpened then + lam.currentPanelOpened = false + cm:FireCallbacks("LAM-PanelClosed", lam.currentAddonPanel) + end +end --METHOD: OPEN TO ADDON PANEL-- --opens to a specific addon's option panel --Usage: --- panel = userdata; the panel returned by the :RegisterOptionsPanel method +-- panel = userdata; the panel returned by the :RegisterOptionsPanel method local locSettings = GetString(SI_GAME_MENU_SETTINGS) function lam:OpenToPanel(panel) - SCENE_MANAGER:Show("gameMenuInGame") - zo_callLater(function() - local settingsMenu = ZO_GameMenu_InGame.gameMenu.headerControls[locSettings] - settingsMenu:SetOpen(true) - SCENE_MANAGER:AddFragment(OPTIONS_WINDOW_FRAGMENT) - KEYBOARD_OPTIONS:ChangePanels(lam.panelID) - for i, child in pairs(settingsMenu.children) do - if type(child) == "table" and child.data.name == KEYBOARD_OPTIONS.panelNames[lam.panelID] then - ZO_TreeEntry_OnMouseUp(child.control, true) - break - end - end - local scroll = LAMAddonPanelsMenuScrollChild - for i = 1, scroll:GetNumChildren() do - local button = scroll:GetChild(i) - if button.panel == panel then - zo_callHandler(button, "OnClicked") - ZO_Scroll_ScrollControlToTop(LAMAddonPanelsMenu, button) - break - end - end - end, 200) + + -- find and select the panel's row in addon list + + local addonList = lam.addonList + local selectedData = nil + + for _, addonData in ipairs(addonsForList) do + if addonData.panel == panel then + selectedData = addonData + ScrollDataIntoView(addonList, selectedData) + break + end + end + + ZO_ScrollList_SelectData(addonList, selectedData) + ZO_ScrollList_RefreshVisible(addonList, selectedData) + + local srchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit") + srchEdit:Clear() + + -- note that ZO_ScrollList doesn't require `selectedData` to be actually + -- present in the list, and that the list will only be populated once LAM + -- "Addon Settings" menu entry is selected for the first time + + local function openAddonSettingsMenu() + local gameMenu = ZO_GameMenu_InGame.gameMenu + local settingsMenu = gameMenu.headerControls[locSettings] + + if settingsMenu then -- an instance of ZO_TreeNode + local children = settingsMenu:GetChildren() + for i = 1, (children and #children or 0) do + local childNode = children[i] + local data = childNode:GetData() + if data and data.id == lam.panelId then + -- found LAM "Addon Settings" node, yay! + childNode:GetTree():SelectNode(childNode) + break + end + end + end + end + + if sm:GetScene("gameMenuInGame"):GetState() == SCENE_SHOWN then + openAddonSettingsMenu() + else + sm:CallWhen("gameMenuInGame", SCENE_SHOWN, openAddonSettingsMenu) + sm:Show("gameMenuInGame") + end end +local TwinOptionsContainer_Index = 0 +local function TwinOptionsContainer(parent, leftWidget, rightWidget) + TwinOptionsContainer_Index = TwinOptionsContainer_Index + 1 + local cParent = parent.scroll or parent + local panel = parent.panel or cParent + local container = wm:CreateControl("$(parent)TwinContainer" .. tostring(TwinOptionsContainer_Index), + cParent, CT_CONTROL) + container:SetResizeToFitDescendents(true) + container:SetAnchor(select(2, leftWidget:GetAnchor(0) )) + + leftWidget:ClearAnchors() + leftWidget:SetAnchor(TOPLEFT, container, TOPLEFT) + rightWidget:SetAnchor(TOPLEFT, leftWidget, TOPRIGHT, 5, 0) + + leftWidget:SetWidth( leftWidget:GetWidth() - 2.5 ) -- fixes bad alignment with 'full' controls + rightWidget:SetWidth( rightWidget:GetWidth() - 2.5 ) + + leftWidget:SetParent(container) + rightWidget:SetParent(container) + + container.data = {type = "container"} + container.panel = panel + return container +end --INTERNAL FUNCTION --creates controls when options panel is first shown --controls anchoring of these controls in the panel local function CreateOptionsControls(panel) - local addonID = panel:GetName() - local optionsTable = addonToOptionsMap[addonID] - - if optionsTable then - local isHalf, widget - local lastAddedControl, lacAtHalfRow, oIndex, widgetData, widgetType - local submenu, subWidgetData, sIndex, subWidgetType, subWidget - local anchorOffset = 0 - local anchorOffsetSub - local lastAddedControlSub, lacAtHalfRowSub - for oIndex=1,#optionsTable do - widgetData = optionsTable[oIndex] - widgetType = widgetData.type - if widgetType == "submenu" then - submenu = LAMCreateControl[widgetType](panel, widgetData) - if lastAddedControl then - submenu:SetAnchor(TOPLEFT, lastAddedControl, BOTTOMLEFT, 0, 15 + anchorOffset) - else - submenu:SetAnchor(TOPLEFT) - end - lastAddedControl = submenu - lacAtHalfRow = false - - anchorOffsetSub = 0 - lacAtHalfRowSub = nil - lastAddedControlSub = nil - for sIndex=1,#widgetData.controls do - subWidgetData = widgetData.controls[sIndex] - subWidgetType = subWidgetData.type - subWidget = LAMCreateControl[subWidgetType](submenu, subWidgetData) - isHalf = subWidgetData.width == "half" - if lastAddedControlSub then - if lacAtHalfRowSub and isHalf then - subWidget:SetAnchor(TOPLEFT, lastAddedControlSub, TOPRIGHT, 5, 0) - lacAtHalfRowSub = false - anchorOffsetSub = zo_max(0, subWidget:GetHeight() - lastAddedControlSub:GetHeight()) - else - subWidget:SetAnchor(TOPLEFT, lastAddedControlSub, BOTTOMLEFT, 0, 15 + anchorOffsetSub) - lacAtHalfRowSub = isHalf - anchorOffsetSub = 0 - lastAddedControlSub = subWidget - end - else - subWidget:SetAnchor(TOPLEFT) - lacAtHalfRowSub = isHalf - lastAddedControlSub = subWidget - end - end - else - widget = LAMCreateControl[widgetType](panel, widgetData) - isHalf = widgetData.width == "half" - if lastAddedControl then - if lacAtHalfRow and isHalf then - widget:SetAnchor(TOPLEFT, lastAddedControl, TOPRIGHT, 10, 0) - anchorOffset = zo_max(0, widget:GetHeight() - lastAddedControl:GetHeight()) - lacAtHalfRow = false - else - widget:SetAnchor(TOPLEFT, lastAddedControl, BOTTOMLEFT, 0, 15 + anchorOffset) - lacAtHalfRow = isHalf - anchorOffset = 0 - lastAddedControl = widget - end - else - widget:SetAnchor(TOPLEFT) - lacAtHalfRow = isHalf - lastAddedControl = widget - end - end - end - end - - optionsCreated[addonID] = true - cm:FireCallbacks("LAM-PanelControlsCreated", panel) -end + local addonID = panel:GetName() + if(optionsState[addonID] == OPTIONS_CREATED) then + return false + elseif(optionsState[addonID] == OPTIONS_CREATION_RUNNING) then + return true + end + optionsState[addonID] = OPTIONS_CREATION_RUNNING + + local function CreationFinished() + optionsState[addonID] = OPTIONS_CREATED + cm:FireCallbacks("LAM-PanelControlsCreated", panel) + OpenCurrentPanel() + end + + local optionsTable = addonToOptionsMap[addonID] + if optionsTable then + local function CreateAndAnchorWidget(parent, widgetData, offsetX, offsetY, anchorTarget, wasHalf) + local widget + local status, err = pcall(function() widget = LAMCreateControl[widgetData.type](parent, widgetData) end) + if not status then + return err or true, offsetY, anchorTarget, wasHalf + else + local isHalf = (widgetData.width == "half") + if not anchorTarget then -- the first widget in a panel is just placed in the top left corner + widget:SetAnchor(TOPLEFT) + anchorTarget = widget + elseif wasHalf and isHalf then -- when the previous widget was only half width and this one is too, we place it on the right side + widget.lineControl = anchorTarget + isHalf = false + offsetY = 0 + anchorTarget = TwinOptionsContainer(parent, anchorTarget, widget) + else -- otherwise we just put it below the previous one normally + widget:SetAnchor(TOPLEFT, anchorTarget, BOTTOMLEFT, 0, 15) + offsetY = 0 + anchorTarget = widget + end + return false, offsetY, anchorTarget, isHalf + end + end + + local THROTTLE_TIMEOUT, THROTTLE_COUNT = 10, 20 + local fifo = {} + local anchorOffset, lastAddedControl, wasHalf + local CreateWidgetsInPanel, err + + local function PrepareForNextPanel() + anchorOffset, lastAddedControl, wasHalf = 0, nil, false + end + + local function SetupCreationCalls(parent, widgetDataTable) + fifo[#fifo + 1] = PrepareForNextPanel + local count = #widgetDataTable + for i = 1, count, THROTTLE_COUNT do + fifo[#fifo + 1] = function() + CreateWidgetsInPanel(parent, widgetDataTable, i, zo_min(i + THROTTLE_COUNT - 1, count)) + end + end + return count ~= NonContiguousCount(widgetDataTable) + end + + CreateWidgetsInPanel = function(parent, widgetDataTable, startIndex, endIndex) + for i=startIndex,endIndex do + local widgetData = widgetDataTable[i] + if not widgetData then + PrintLater("Skipped creation of missing entry in the settings menu of " .. addonID .. ".") + else + local widgetType = widgetData.type + local offsetX = 0 + local isSubmenu = (widgetType == "submenu") + if isSubmenu then + wasHalf = false + offsetX = 5 + end + err, anchorOffset, lastAddedControl, wasHalf = CreateAndAnchorWidget(parent, widgetData, offsetX, anchorOffset, lastAddedControl, wasHalf) + if err then + PrintLater(("Could not create %s '%s' of %s."):format(widgetData.type, widgetData.name or "unnamed", addonID)) + end + + if isSubmenu then + if SetupCreationCalls(lastAddedControl, widgetData.controls) then + PrintLater(("The sub menu '%s' of %s is missing some entries."):format(widgetData.name or "unnamed", addonID)) + end + end + end + end + end + + local function DoCreateSettings() + if #fifo > 0 then + local nextCall = table.remove(fifo, 1) + nextCall() + if(nextCall == PrepareForNextPanel) then + DoCreateSettings() + else + zo_callLater(DoCreateSettings, THROTTLE_TIMEOUT) + end + else + CreationFinished() + end + end + + if SetupCreationCalls(panel, optionsTable) then + PrintLater(("The settings menu of %s is missing some entries."):format(addonID)) + end + DoCreateSettings() + else + CreationFinished() + end + + return true +end --INTERNAL FUNCTION --handles switching between panels -local function ToggleAddonPanels(panel) --called in OnShow of newly shown panel - local currentlySelected = LAMAddonPanelsMenu.currentlySelected - if currentlySelected and currentlySelected ~= panel then - currentlySelected:SetHidden(true) - end - LAMAddonPanelsMenu.currentlySelected = panel +local function ToggleAddonPanels(panel) --called in OnShow of newly shown panel + local currentlySelected = lam.currentAddonPanel + if currentlySelected and currentlySelected ~= panel then + currentlySelected:SetHidden(true) + CloseCurrentPanel() + end + lam.currentAddonPanel = panel + + -- refresh visible rows to reflect panel IsHidden status + ZO_ScrollList_RefreshVisible(lam.addonList) - if not optionsCreated[panel:GetName()] then --if this is the first time opening this panel, create these options - CreateOptionsControls(panel) - end + if not CreateOptionsControls(panel) then + OpenCurrentPanel() + end - cm:FireCallbacks("LAM-RefreshPanel", panel) + cm:FireCallbacks("LAM-RefreshPanel", panel) end -local Initialize -local hasInitialized = false +local CheckSafetyAndInitialize --METHOD: REGISTER ADDON PANEL --registers your addon with LibAddonMenu and creates a panel --Usage: --- addonID = "string"; unique ID which will be the global name of your panel --- panelData = table; data object for your panel - see controls\panel.lua +-- addonID = "string"; unique ID which will be the global name of your panel +-- panelData = table; data object for your panel - see controls\panel.lua function lam:RegisterAddonPanel(addonID, panelData) - if(not hasInitialized) then Initialize(addonID) end - local panel = lamcc.panel(nil, panelData, addonID) --addonID==global name of panel - panel:SetHidden(true) - panel:SetAnchor(TOPLEFT, LAMAddonPanelsMenu, TOPRIGHT, 10, 0) - panel:SetAnchor(BOTTOMLEFT, LAMAddonPanelsMenu, BOTTOMRIGHT, 10, 0) - panel:SetWidth(549) - panel:SetDrawLayer(DL_OVERLAY) - tinsert(addonsForList, {panel = addonID, name = panelData.name}) - panel:SetHandler("OnShow", ToggleAddonPanels) - if panelData.slashCommand then - SLASH_COMMANDS[panelData.slashCommand] = function() - lam:OpenToPanel(panel) - end - end - - return panel --return for authors creating options manually + CheckSafetyAndInitialize(addonID) + local container = lam:GetAddonPanelContainer() + local panel = lamcc.panel(container, panelData, addonID) --addonID==global name of panel + panel:SetHidden(true) + panel:SetAnchorFill(container) + panel:SetHandler("OnShow", ToggleAddonPanels) + + local function stripMarkup(str) + return str:gsub("|[Cc]%x%x%x%x%x%x", ""):gsub("|[Rr]", "") + end + + local filterParts = {panelData.name, nil, nil} + -- append keywords and author separately, the may be nil + filterParts[#filterParts + 1] = panelData.keywords + filterParts[#filterParts + 1] = panelData.author + + local addonData = { + panel = panel, + name = stripMarkup(panelData.name), + filterText = stripMarkup(tconcat(filterParts, "\t")):lower(), + } + + tinsert(addonsForList, addonData) + + if panelData.slashCommand then + SLASH_COMMANDS[panelData.slashCommand] = function() + lam:OpenToPanel(panel) + end + end + + return panel --return for authors creating options manually end @@ -225,160 +819,368 @@ end --see exampleoptions.lua for an example --see controls\<widget>.lua for each widget type --Usage: --- addonID = "string"; the same string passed to :RegisterAddonPanel --- optionsTable = table; the table containing all of the options controls and their data -function lam:RegisterOptionControls(addonID, optionsTable) --optionsTable = {sliderData, buttonData, etc} - addonToOptionsMap[addonID] = optionsTable +-- addonID = "string"; the same string passed to :RegisterAddonPanel +-- optionsTable = table; the table containing all of the options controls and their data +function lam:RegisterOptionControls(addonID, optionsTable) --optionsTable = {sliderData, buttonData, etc} + addonToOptionsMap[addonID] = optionsTable end - --INTERNAL FUNCTION ---handles switching between LAM's Addon Settings panel and other panels in the Settings menu -local oldDefaultButton = ZO_OptionsWindowResetToDefaultButton -local oldCallback = oldDefaultButton.callback -local dummyFunc = function() end -local panelWindow = ZO_OptionsWindow -local bgL = ZO_OptionsWindowBGLeft -local bgR = ZO_OptionsWindowBGLeftBGRight -local function HandlePanelSwitching(self, panel) - if panel == lam.panelID then --our addon settings panel - oldDefaultButton:SetCallback(dummyFunc) - oldDefaultButton:SetHidden(true) - oldDefaultButton:SetAlpha(0) --just because it still bugs out - panelWindow:SetDimensions(999, 960) - bgL:SetWidth(666) - bgR:SetWidth(333) - else - local shown = LAMAddonPanelsMenu.currentlySelected - if shown then shown:SetHidden(true) end - oldDefaultButton:SetCallback(oldCallback) - oldDefaultButton:SetHidden(false) - oldDefaultButton:SetAlpha(1) - panelWindow:SetDimensions(768, 914) - bgL:SetWidth(512) - bgR:SetWidth(256) - end +--creates LAM's Addon Settings entry in ZO_GameMenu +local function CreateAddonSettingsMenuEntry() + local panelData = { + id = KEYBOARD_OPTIONS.currentPanelId, + name = util.L["PANEL_NAME"], + } + + KEYBOARD_OPTIONS.currentPanelId = panelData.id + 1 + KEYBOARD_OPTIONS.panelNames[panelData.id] = panelData.name + + lam.panelId = panelData.id + + local addonListSorted = false + + function panelData.callback() + sm:AddFragment(lam:GetAddonSettingsFragment()) + KEYBOARD_OPTIONS:ChangePanels(lam.panelId) + + local title = LAMAddonSettingsWindow:GetNamedChild("Title") + title:SetText(panelData.name) + + if not addonListSorted and #addonsForList > 0 then + local searchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit") + --we're about to show our list for the first time - let's sort it + table.sort(addonsForList, function(a, b) return a.name < b.name end) + PopulateAddonList(lam.addonList, GetSearchFilterFunc(searchEdit)) + addonListSorted = true + end + end + + function panelData.unselectedCallback() + sm:RemoveFragment(lam:GetAddonSettingsFragment()) + if SetCameraOptionsPreviewModeEnabled then -- available since API version 100011 + SetCameraOptionsPreviewModeEnabled(false) + end + end + + ZO_GameMenu_AddSettingPanel(panelData) end --INTERNAL FUNCTION ---creates LAM's Addon Settings panel -local function CreateAddonSettingsPanel() - if not LAMSettingsPanelCreated then - local controlPanelID = "LAM_ADDON_SETTINGS_PANEL" - --Russian for TERAB1T's RuESO addon, which creates an "ru" locale - --game font does not support Cyrillic, so they are using custom fonts + extended latin charset - --Spanish provided by Luisen75 for their translation project - local controlPanelNames = { - en = "Addon Settings", - fr = "Extensions", - de = "Erweiterungen", - ru = "Îacòpoéêè äoïoìîeîèé", - es = "Configura Addons", - } - - ZO_OptionsWindow_AddUserPanel(controlPanelID, controlPanelNames[GetCVar("Language.2")] or controlPanelNames["en"], PANEL_TYPE_SETTINGS) - - lam.panelID = _G[controlPanelID] - - ZO_PreHook(ZO_KeyboardOptions, "ChangePanels", HandlePanelSwitching) - - LAMSettingsPanelCreated = true - end +--creates the left-hand menu in LAM's window +local function CreateAddonList(name, parent) + local addonList = wm:CreateControlFromVirtual(name, parent, "ZO_ScrollList") + + local function addonListRow_OnMouseDown(control, button) + if button == 1 then + local data = ZO_ScrollList_GetData(control) + ZO_ScrollList_SelectData(addonList, data, control) + end + end + + local function addonListRow_OnMouseEnter(control) + ZO_ScrollList_MouseEnter(addonList, control) + end + + local function addonListRow_OnMouseExit(control) + ZO_ScrollList_MouseExit(addonList, control) + end + + local function addonListRow_Select(previouslySelectedData, selectedData, reselectingDuringRebuild) + if not reselectingDuringRebuild then + if previouslySelectedData then + previouslySelectedData.panel:SetHidden(true) + end + if selectedData then + selectedData.panel:SetHidden(false) + PlaySound(SOUNDS.MENU_SUBCATEGORY_SELECTION) + end + end + end + + local function addonListRow_Setup(control, data) + control:SetText(data.name) + control:SetSelected(not data.panel:IsHidden()) + end + + ZO_ScrollList_AddDataType(addonList, ADDON_DATA_TYPE, "ZO_SelectableLabel", 28, addonListRow_Setup) + -- I don't know how to make highlights clear properly; they often + -- get stuck and after a while the list is full of highlighted rows + --ZO_ScrollList_EnableHighlight(addonList, "ZO_ThinListHighlight") + ZO_ScrollList_EnableSelection(addonList, "ZO_ThinListHighlight", addonListRow_Select) + + local addonDataType = ZO_ScrollList_GetDataTypeTable(addonList, ADDON_DATA_TYPE) + local addonListRow_CreateRaw = addonDataType.pool.m_Factory + + local function addonListRow_Create(pool) + local control = addonListRow_CreateRaw(pool) + control:SetHandler("OnMouseDown", addonListRow_OnMouseDown) + --control:SetHandler("OnMouseEnter", addonListRow_OnMouseEnter) + --control:SetHandler("OnMouseExit", addonListRow_OnMouseExit) + control:SetHeight(28) + control:SetFont("ZoFontHeader") + control:SetHorizontalAlignment(TEXT_ALIGN_LEFT) + control:SetVerticalAlignment(TEXT_ALIGN_CENTER) + control:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) + return control + end + + addonDataType.pool.m_Factory = addonListRow_Create + + return addonList end --INTERNAL FUNCTION ---adds each registered addon to the menu in LAM's panel -local function CreateAddonButtons(list, addons) - for i = 1, #addons do - local button = wm:CreateControlFromVirtual("LAMAddonMenuButton"..i, list.scrollChild, "ZO_DefaultTextButton") - button.name = addons[i].name - button.panel = _G[addons[i].panel] - button:SetText(button.name) - button:SetHorizontalAlignment(TEXT_ALIGN_LEFT) - button:SetWidth(190) - if i == 1 then - button:SetAnchor(TOPLEFT, list.scrollChild, TOPLEFT, 5, 5) - else - button:SetAnchor(TOPLEFT, _G["LAMAddonMenuButton"..i-1], BOTTOMLEFT) - end - button:SetHandler("OnClicked", function(self) self.panel:SetHidden(false) end) - end +local function CreateSearchFilterBox(name, parent) + local boxControl = wm:CreateControl(name, parent, CT_CONTROL) + + local srchButton = wm:CreateControl("$(parent)Button", boxControl, CT_BUTTON) + srchButton:SetDimensions(32, 32) + srchButton:SetAnchor(LEFT, nil, LEFT, 2, 0) + srchButton:SetNormalTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_up.dds") + srchButton:SetPressedTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_down.dds") + srchButton:SetMouseOverTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_over.dds") + + local srchEdit = wm:CreateControlFromVirtual("$(parent)Edit", boxControl, "ZO_DefaultEdit") + srchEdit:SetAnchor(LEFT, srchButton, RIGHT, 4, 1) + srchEdit:SetAnchor(RIGHT, nil, RIGHT, -4, 1) + srchEdit:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) + + local srchBg = wm:CreateControl("$(parent)Bg", boxControl, CT_BACKDROP) + srchBg:SetAnchorFill() + srchBg:SetAlpha(0) + srchBg:SetCenterColor(0, 0, 0, 0.5) + srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA()) + srchBg:SetEdgeTexture("", 1, 1, 0, 0) + + -- search backdrop should appear whenever you hover over either + -- the magnifying glass button or the edit field (which is only + -- visible when it contains some text), and also while the edit + -- field has keyboard focus + + local srchActive = false + local srchHover = false + + local function srchBgUpdateAlpha() + if srchActive or srchEdit:HasFocus() then + srchBg:SetAlpha(srchHover and 0.8 or 0.6) + else + srchBg:SetAlpha(srchHover and 0.6 or 0.0) + end + end + + local function srchMouseEnter(control) + srchHover = true + srchBgUpdateAlpha() + end + + local function srchMouseExit(control) + srchHover = false + srchBgUpdateAlpha() + end + + boxControl:SetMouseEnabled(true) + boxControl:SetHitInsets(1, 1, -1, -1) + boxControl:SetHandler("OnMouseEnter", srchMouseEnter) + boxControl:SetHandler("OnMouseExit", srchMouseExit) + + srchButton:SetHandler("OnMouseEnter", srchMouseEnter) + srchButton:SetHandler("OnMouseExit", srchMouseExit) + + local focusLostTime = 0 + + srchButton:SetHandler("OnClicked", function(self) + srchEdit:Clear() + if GetFrameTimeMilliseconds() - focusLostTime < 100 then + -- re-focus the edit box if it lost focus due to this + -- button click (note that this handler may run a few + -- frames later) + srchEdit:TakeFocus() + end + end) + + srchEdit:SetHandler("OnMouseEnter", srchMouseEnter) + srchEdit:SetHandler("OnMouseExit", srchMouseExit) + srchEdit:SetHandler("OnFocusGained", srchBgUpdateAlpha) + + srchEdit:SetHandler("OnFocusLost", function() + focusLostTime = GetFrameTimeMilliseconds() + srchBgUpdateAlpha() + end) + + srchEdit:SetHandler("OnEscape", function(self) + self:Clear() + self:LoseFocus() + end) + + srchEdit:SetHandler("OnTextChanged", function(self) + local filterFunc = GetSearchFilterFunc(self) + if filterFunc then + srchActive = true + srchBg:SetEdgeColor(ZO_SECOND_CONTRAST_TEXT:UnpackRGBA()) + srchButton:SetState(BSTATE_PRESSED) + else + srchActive = false + srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA()) + srchButton:SetState(BSTATE_NORMAL) + end + srchBgUpdateAlpha() + PopulateAddonList(lam.addonList, filterFunc) + PlaySound(SOUNDS.SPINNER_DOWN) + end) + + return boxControl end --INTERNAL FUNCTION ---creates the left-hand menu in LAM's panel -local function CreateAddonList() - local list - --check if an earlier loaded copy of LAM created it already - list = LAMAddonPanelsMenu or wm:CreateControlFromVirtual("LAMAddonPanelsMenu", optionsWindow, "ZO_ScrollContainer") - list:ClearAnchors() - list:SetAnchor(TOPLEFT) - list:SetHeight(675) - list:SetWidth(200) - - list.bg = list.bg or wm:CreateControl(nil, list, CT_BACKDROP) - local bg = list.bg - bg:SetAnchorFill() --offsets of 8? - bg:SetEdgeTexture("EsoUI\\Art\\miscellaneous\\borderedinsettransparent_edgefile.dds", 128, 16) - bg:SetCenterColor(0, 0, 0, 0) - - list.scrollChild = LAMAddonPanelsMenuScrollChild - list.scrollChild:SetResizeToFitPadding(0, 15) - - local generatedButtons - list:SetHandler("OnShow", function(self) - if not generatedButtons and #addonsForList > 0 then - --we're about to show our list for the first time - let's sort the buttons before creating them - table.sort(addonsForList, function(a, b) - return a.name < b.name - end) - CreateAddonButtons(list, addonsForList) - self.currentlySelected = LAMAddonMenuButton1 and LAMAddonMenuButton1.panel - --since our addon panels don't have a parent, let's make sure they hide when we're done with them - ZO_PreHookHandler(ZO_OptionsWindow, "OnHide", function() self.currentlySelected:SetHidden(true) end) - generatedButtons = true - end - if self.currentlySelected then self.currentlySelected:SetHidden(false) end - end) - - --list.controlType = OPTIONS_CUSTOM - --list.panel = lam.panelID - list.data = { - controlType = OPTIONS_CUSTOM, - panel = lam.panelID, - } - - ZO_OptionsWindow_InitializeControl(list) - - return list +--creates LAM's Addon Settings top-level window +local function CreateAddonSettingsWindow() + local tlw = wm:CreateTopLevelWindow("LAMAddonSettingsWindow") + tlw:SetHidden(true) + tlw:SetDimensions(1010, 914) -- same height as ZO_OptionsWindow + + ZO_ReanchorControlForLeftSidePanel(tlw) + + -- create black background for the window (mimic ZO_RightFootPrintBackground) + + local bgLeft = wm:CreateControl("$(parent)BackgroundLeft", tlw, CT_TEXTURE) + bgLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_left.dds") + bgLeft:SetDimensions(1024, 1024) + bgLeft:SetAnchor(TOPLEFT, nil, TOPLEFT) + bgLeft:SetDrawLayer(DL_BACKGROUND) + bgLeft:SetExcludeFromResizeToFitExtents(true) + + local bgRight = wm:CreateControl("$(parent)BackgroundRight", tlw, CT_TEXTURE) + bgRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_right.dds") + bgRight:SetDimensions(64, 1024) + bgRight:SetAnchor(TOPLEFT, bgLeft, TOPRIGHT) + bgRight:SetDrawLayer(DL_BACKGROUND) + bgRight:SetExcludeFromResizeToFitExtents(true) + + -- create gray background for addon list (mimic ZO_TreeUnderlay) + + local underlayLeft = wm:CreateControl("$(parent)UnderlayLeft", tlw, CT_TEXTURE) + underlayLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_left.dds") + underlayLeft:SetDimensions(256, 1024) + underlayLeft:SetAnchor(TOPLEFT, bgLeft, TOPLEFT) + underlayLeft:SetDrawLayer(DL_BACKGROUND) + underlayLeft:SetExcludeFromResizeToFitExtents(true) + + local underlayRight = wm:CreateControl("$(parent)UnderlayRight", tlw, CT_TEXTURE) + underlayRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_right.dds") + underlayRight:SetDimensions(128, 1024) + underlayRight:SetAnchor(TOPLEFT, underlayLeft, TOPRIGHT) + underlayRight:SetDrawLayer(DL_BACKGROUND) + underlayRight:SetExcludeFromResizeToFitExtents(true) + + -- create title bar (mimic ZO_OptionsWindow) + + local title = wm:CreateControl("$(parent)Title", tlw, CT_LABEL) + title:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 70) + title:SetFont("ZoFontWinH1") + title:SetModifyTextType(MODIFY_TEXT_TYPE_UPPERCASE) + + local divider = wm:CreateControlFromVirtual("$(parent)Divider", tlw, "ZO_Options_Divider") + divider:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 108) + + -- create search filter box + + local srchBox = CreateSearchFilterBox("$(parent)SearchFilter", tlw) + srchBox:SetAnchor(TOPLEFT, nil, TOPLEFT, 63, 120) + srchBox:SetDimensions(260, 30) + + -- create scrollable addon list + + local addonList = CreateAddonList("$(parent)AddonList", tlw) + addonList:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 160) + addonList:SetDimensions(285, 665) + + lam.addonList = addonList -- for easy access from elsewhere + + -- create container for option panels + + local panelContainer = wm:CreateControl("$(parent)PanelContainer", tlw, CT_CONTROL) + panelContainer:SetAnchor(TOPLEFT, nil, TOPLEFT, 365, 120) + panelContainer:SetDimensions(645, 675) + + local defaultButton = wm:CreateControlFromVirtual("$(parent)ResetToDefaultButton", tlw, "ZO_DialogButton") + ZO_KeybindButtonTemplate_Setup(defaultButton, "OPTIONS_LOAD_DEFAULTS", HandleLoadDefaultsPressed, GetString(SI_OPTIONS_DEFAULTS)) + defaultButton:SetAnchor(TOPLEFT, panelContainer, BOTTOMLEFT, 0, 2) + lam.defaultButton = defaultButton + + local applyButton = wm:CreateControlFromVirtual("$(parent)ApplyButton", tlw, "ZO_DialogButton") + ZO_KeybindButtonTemplate_Setup(applyButton, "OPTIONS_APPLY_CHANGES", HandleReloadUIPressed, GetString(SI_ADDON_MANAGER_RELOAD)) + applyButton:SetAnchor(TOPRIGHT, panelContainer, BOTTOMRIGHT, 0, 2) + applyButton:SetHidden(true) + lam.applyButton = applyButton + + return tlw end + --INITIALIZING local safeToInitialize = false +local hasInitialized = false local eventHandle = table.concat({MAJOR, MINOR}, "r") local function OnLoad(_, addonName) - -- wait for the first loaded event - EVENT_MANAGER:UnregisterForEvent(eventHandle, EVENT_ADD_ON_LOADED) - safeToInitialize = true -end -EVENT_MANAGER:RegisterForEvent(eventHandle, EVENT_ADD_ON_LOADED, OnLoad) - -local function OnActivated(_, addonName) - EVENT_MANAGER:UnregisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED) - FlushMessages() -end -EVENT_MANAGER:RegisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED, OnActivated) - -function Initialize(addonID) - if(not safeToInitialize) then - local msg = string.format("The panel with id '%s' was registered before addon loading has completed. This might break the AddOn Settings menu.", addonID) - PrintLater(msg) - end - CreateAddonSettingsPanel() - CreateAddonList() - hasInitialized = true + -- wait for the first loaded event + em:UnregisterForEvent(eventHandle, EVENT_ADD_ON_LOADED) + safeToInitialize = true +end +em:RegisterForEvent(eventHandle, EVENT_ADD_ON_LOADED, OnLoad) + +local function OnActivated(_, initial) + em:UnregisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED) + FlushMessages() + + local reopenPanel = RetrievePanelForReopening() + if not initial and reopenPanel then + lam:OpenToPanel(reopenPanel) + end +end +em:RegisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED, OnActivated) + +function CheckSafetyAndInitialize(addonID) + if not safeToInitialize then + local msg = string.format("The panel with id '%s' was registered before addon loading has completed. This might break the AddOn Settings menu.", addonID) + PrintLater(msg) + end + if not hasInitialized then + hasInitialized = true + end +end + + +--TODO documentation +function lam:GetAddonPanelContainer() + local fragment = lam:GetAddonSettingsFragment() + local window = fragment:GetControl() + return window:GetNamedChild("PanelContainer") +end + + +--TODO documentation +function lam:GetAddonSettingsFragment() + assert(hasInitialized or safeToInitialize) + if not LAMAddonSettingsFragment then + local window = CreateAddonSettingsWindow() + LAMAddonSettingsFragment = ZO_FadeSceneFragment:New(window, true, 100) + LAMAddonSettingsFragment:RegisterCallback("StateChange", function(oldState, newState) + if(newState == SCENE_FRAGMENT_SHOWN) then + InitKeybindActions() + PushActionLayerByName("OptionsWindow") + OpenCurrentPanel() + elseif(newState == SCENE_FRAGMENT_HIDDEN) then + CloseCurrentPanel() + RemoveActionLayerByName("OptionsWindow") + ShowReloadDialogIfNeeded() + end + end) + CreateAddonSettingsMenuEntry() + end + return LAMAddonSettingsFragment end diff --git a/Libs/LibAddonMenu-2.0/controls/button.lua b/Libs/LibAddonMenu-2.0/controls/button.lua index 8a6ecda..82b5032 100644 --- a/Libs/LibAddonMenu-2.0/controls/button.lua +++ b/Libs/LibAddonMenu-2.0/controls/button.lua @@ -1,88 +1,91 @@ --[[buttonData = { - type = "button", - name = "My Button", - tooltip = "Button's tooltip text.", - func = function() end, - width = "full", --or "half" (optional) - disabled = function() return db.someBooleanSetting end, --or boolean (optional) - icon = "icon\\path.dds", --(optional) - warning = "Will need to reload the UI.", --(optional) - reference = "MyAddonButton" --(optional) unique global reference to control -} ]] + type = "button", + name = "My Button", -- string id or function returning a string + func = function() end, + tooltip = "Button's tooltip text.", -- string id or function returning a string (optional) + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + icon = "icon\\path.dds", --(optional) + isDangerous = false, -- boolean, if set to true, the button text will be red and a confirmation dialog with the button label and warning text will show on click before the callback is executed (optional) + warning = "Will need to reload the UI.", --(optional) + reference = "MyAddonButton", -- unique global reference to control (optional) +} ]] - -local widgetVersion = 6 +local widgetVersion = 11 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("button", widgetVersion) then return end local wm = WINDOW_MANAGER -local cm = CALLBACK_MANAGER -local tinsert = table.insert local function UpdateDisabled(control) - local disable - if type(control.data.disabled) == "function" then - disable = control.data.disabled() - else - disable = control.data.disabled - end - - control.button:SetEnabled(not disable) + local disable = control.data.disabled + if type(disable) == "function" then + disable = disable() + end + control.button:SetEnabled(not disable) end - --controlName is optional +local MIN_HEIGHT = 28 -- default_button height +local HALF_WIDTH_LINE_SPACING = 2 function LAMCreateControl.button(parent, buttonData, controlName) - local control = wm:CreateControl(controlName or buttonData.reference, parent.scroll or parent, CT_CONTROL) + local control = LAM.util.CreateBaseControl(parent, buttonData, controlName) + control:SetMouseEnabled(true) - local isHalfWidth = buttonData.width == "half" - control:SetDimensions(isHalfWidth and 250 or 510, isHalfWidth and 55 or 28) - control:SetMouseEnabled(true) + local width = control:GetWidth() + if control.isHalfWidth then + control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING) + else + control:SetDimensions(width, MIN_HEIGHT) + end - if buttonData.icon then - control.button = wm:CreateControl(nil, control, CT_BUTTON) - control.button:SetDimensions(26, 26) - control.button:SetNormalTexture(buttonData.icon) - control.button:SetPressedOffset(2, 2) - else - --control.button = wm:CreateControlFromVirtual(controlName.."Button", control, "ZO_DefaultButton") - control.button = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultButton") - control.button:SetWidth(isHalfWidth and 180 or 200) - control.button:SetText(buttonData.name) - end - local button = control.button - button:SetAnchor(isHalfWidth and CENTER or RIGHT) - button:SetClickSound("Click") - --button.tooltipText = buttonData.tooltip - button.data = {tooltipText = buttonData.tooltip} - button:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - button:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) - button:SetHandler("OnClicked", function(self, ...) - buttonData.func(self, ...) - if control.panel.data.registerForRefresh then - cm:FireCallbacks("LAM-RefreshPanel", control) - end - end) + if buttonData.icon then + control.button = wm:CreateControl(nil, control, CT_BUTTON) + control.button:SetDimensions(26, 26) + control.button:SetNormalTexture(buttonData.icon) + control.button:SetPressedOffset(2, 2) + else + --control.button = wm:CreateControlFromVirtual(controlName.."Button", control, "ZO_DefaultButton") + control.button = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultButton") + control.button:SetWidth(width / 3) + control.button:SetText(LAM.util.GetStringFromValue(buttonData.name)) + if buttonData.isDangerous then control.button:SetNormalFontColor(ZO_ERROR_COLOR:UnpackRGBA()) end + end + local button = control.button + button:SetAnchor(control.isHalfWidth and CENTER or RIGHT) + button:SetClickSound("Click") + button.data = {tooltipText = LAM.util.GetStringFromValue(buttonData.tooltip)} + button:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + button:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + button:SetHandler("OnClicked", function(...) + local args = {...} + local function callback() + buttonData.func(unpack(args)) + LAM.util.RequestRefreshIfNeeded(control) + end - if buttonData.warning then - control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") - control.warning:SetAnchor(RIGHT, button, LEFT, -5, 0) - --control.warning.tooltipText = buttonData.warning - control.warning.data = {tooltipText = buttonData.warning} - end + if(buttonData.isDangerous) then + local title = LAM.util.GetStringFromValue(buttonData.name) + local body = LAM.util.GetStringFromValue(buttonData.warning) + LAM.util.ShowConfirmationDialog(title, body, callback) + else + callback() + end + end) - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = buttonData + if buttonData.warning ~= nil then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, button, LEFT, -5, 0) + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end - if buttonData.disabled then - control.UpdateDisabled = UpdateDisabled - control:UpdateDisabled() + if buttonData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end - --this is here because buttons don't have an UpdateValue method - if control.panel.data.registerForRefresh then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - end + LAM.util.RegisterForRefreshIfNeeded(control) - return control -end \ No newline at end of file + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/checkbox.lua b/Libs/LibAddonMenu-2.0/controls/checkbox.lua index ef8bf09..6696dd7 100644 --- a/Libs/LibAddonMenu-2.0/controls/checkbox.lua +++ b/Libs/LibAddonMenu-2.0/controls/checkbox.lua @@ -1,24 +1,24 @@ --[[checkboxData = { - type = "checkbox", - name = "My Checkbox", - tooltip = "Checkbox's tooltip text.", - getFunc = function() return db.var end, - setFunc = function(value) db.var = value doStuff() end, - width = "full", --or "half" (optional) - disabled = function() return db.someBooleanSetting end, --or boolean (optional) - warning = "Will need to reload the UI.", --(optional) - default = defaults.var, --(optional) - reference = "MyAddonCheckbox" --(optional) unique global reference to control -} ]] - - -local widgetVersion = 8 + type = "checkbox", + name = "My Checkbox", -- or string id or function returning a string + getFunc = function() return db.var end, + setFunc = function(value) db.var = value doStuff() end, + tooltip = "Checkbox's tooltip text.", -- or string id or function returning a string (optional) + width = "full", -- or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional) + requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional) + default = defaults.var, -- a boolean or function that returns a boolean (optional) + reference = "MyAddonCheckbox", -- unique global reference to control (optional) +} ]] + + +local widgetVersion = 14 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("checkbox", widgetVersion) then return end local wm = WINDOW_MANAGER -local cm = CALLBACK_MANAGER -local tinsert = table.insert + --label local enabledColor = ZO_DEFAULT_ENABLED_COLOR local enabledHLcolor = ZO_HIGHLIGHT_TEXT @@ -30,142 +30,113 @@ local checkboxHLcolor = ZO_HIGHLIGHT_TEXT local function UpdateDisabled(control) - local disable - if type(control.data.disabled) == "function" then - disable = control.data.disabled() - else - disable = control.data.disabled - end - - control.label:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or control.value and ZO_DEFAULT_ENABLED_COLOR or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA()) - control.checkbox:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or ZO_NORMAL_TEXT):UnpackRGBA()) - --control:SetMouseEnabled(not disable) - --control:SetMouseEnabled(true) - - control.isDisabled = disable + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.label:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or control.value and ZO_DEFAULT_ENABLED_COLOR or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA()) + control.checkbox:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or ZO_NORMAL_TEXT):UnpackRGBA()) + --control:SetMouseEnabled(not disable) + --control:SetMouseEnabled(true) + + control.isDisabled = disable end local function ToggleCheckbox(control) - if control.value then - control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - control.checkbox:SetText(control.checkedText) - else - control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - control.checkbox:SetText(control.uncheckedText) - end + if control.value then + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.checkbox:SetText(control.checkedText) + else + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.checkbox:SetText(control.uncheckedText) + end end local function UpdateValue(control, forceDefault, value) - if forceDefault then --if we are forcing defaults - value = control.data.default - control.data.setFunc(value) - elseif value ~= nil then --our value could be false - control.data.setFunc(value) - --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed - if control.panel.data.registerForRefresh then - cm:FireCallbacks("LAM-RefreshPanel", control) - end - else - value = control.data.getFunc() - end - control.value = value - - ToggleCheckbox(control) + if forceDefault then --if we are forcing defaults + value = LAM.util.GetDefaultValue(control.data.default) + control.data.setFunc(value) + elseif value ~= nil then --our value could be false + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + LAM.util.RequestRefreshIfNeeded(control) + else + value = control.data.getFunc() + end + control.value = value + + ToggleCheckbox(control) end local function OnMouseEnter(control) - ZO_Options_OnMouseEnter(control) + ZO_Options_OnMouseEnter(control) - if control.isDisabled then return end + if control.isDisabled then return end - local label = control.label - if control.value then - label:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) - else - label:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) - end - control.checkbox:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) + local label = control.label + if control.value then + label:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) + else + label:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) + end + control.checkbox:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) end local function OnMouseExit(control) - ZO_Options_OnMouseExit(control) + ZO_Options_OnMouseExit(control) - if control.isDisabled then return end + if control.isDisabled then return end - local label = control.label - if control.value then - label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - else - label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - end - control.checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) + local label = control.label + if control.value then + label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + else + label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + end + control.checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) end - --controlName is optional function LAMCreateControl.checkbox(parent, checkboxData, controlName) - local control = wm:CreateControl(controlName or checkboxData.reference, parent.scroll or parent, CT_CONTROL) - control:SetMouseEnabled(true) - --control.tooltipText = checkboxData.tooltip - control:SetHandler("OnMouseEnter", OnMouseEnter) - control:SetHandler("OnMouseExit", OnMouseExit) - control:SetHandler("OnMouseUp", function(control) - if control.isDisabled then return end - PlaySound(SOUNDS.DEFAULT_CLICK) - control.value = not control.value - control:UpdateValue(false, control.value) - end) - - control.label = wm:CreateControl(nil, control, CT_LABEL) - local label = control.label - label:SetFont("ZoFontWinH4") - label:SetText(checkboxData.name) - label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) - label:SetHeight(26) - - control.checkbox = wm:CreateControl(nil, control, CT_LABEL) - local checkbox = control.checkbox - checkbox:SetFont("ZoFontGameBold") - checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) - control.checkedText = GetString(SI_CHECK_BUTTON_ON):upper() - control.uncheckedText = GetString(SI_CHECK_BUTTON_OFF):upper() - - local isHalfWidth = checkboxData.width == "half" - if isHalfWidth then - control:SetDimensions(250, 55) - checkbox:SetDimensions(100, 26) - checkbox:SetAnchor(BOTTOMRIGHT) - label:SetAnchor(TOPLEFT) - label:SetAnchor(TOPRIGHT) - else - control:SetDimensions(510, 30) - checkbox:SetDimensions(200, 26) - checkbox:SetAnchor(RIGHT) - label:SetAnchor(LEFT) - label:SetAnchor(RIGHT, checkbox, LEFT, -5, 0) - end - - if checkboxData.warning then - control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") - control.warning:SetAnchor(RIGHT, checkbox, LEFT, -5, 0) - --control.warning.tooltipText = checkboxData.warning - control.warning.data = {tooltipText = checkboxData.warning} - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = checkboxData - control.data.tooltipText = checkboxData.tooltip - - if checkboxData.disabled then - control.UpdateDisabled = UpdateDisabled - control:UpdateDisabled() - end - control.UpdateValue = UpdateValue - control:UpdateValue() - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control -end \ No newline at end of file + local control = LAM.util.CreateLabelAndContainerControl(parent, checkboxData, controlName) + control:SetHandler("OnMouseEnter", OnMouseEnter) + control:SetHandler("OnMouseExit", OnMouseExit) + control:SetHandler("OnMouseUp", function(control) + if control.isDisabled then return end + PlaySound(SOUNDS.DEFAULT_CLICK) + control.value = not control.value + control:UpdateValue(false, control.value) + end) + + control.checkbox = wm:CreateControl(nil, control.container, CT_LABEL) + local checkbox = control.checkbox + checkbox:SetAnchor(LEFT, control.container, LEFT, 0, 0) + checkbox:SetFont("ZoFontGameBold") + checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) + control.checkedText = GetString(SI_CHECK_BUTTON_ON):upper() + control.uncheckedText = GetString(SI_CHECK_BUTTON_OFF):upper() + + if checkboxData.warning ~= nil or checkboxData.requiresReload then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, checkbox, LEFT, -5, 0) + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end + + control.data.tooltipText = LAM.util.GetStringFromValue(checkboxData.tooltip) + + control.UpdateValue = UpdateValue + control:UpdateValue() + if checkboxData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + LAM.util.RegisterForRefreshIfNeeded(control) + LAM.util.RegisterForReloadIfNeeded(control) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/colorpicker.lua b/Libs/LibAddonMenu-2.0/controls/colorpicker.lua index c5724b0..a57aab0 100644 --- a/Libs/LibAddonMenu-2.0/controls/colorpicker.lua +++ b/Libs/LibAddonMenu-2.0/controls/colorpicker.lua @@ -1,138 +1,106 @@ --[[colorpickerData = { - type = "colorpicker", - name = "My Color Picker", - tooltip = "Color Picker's tooltip text.", - getFunc = function() return db.r, db.g, db.b, db.a end, --(alpha is optional) - setFunc = function(r,g,b,a) db.r=r, db.g=g, db.b=b, db.a=a end, --(alpha is optional) - width = "full", --or "half" (optional) - disabled = function() return db.someBooleanSetting end, --or boolean (optional) - warning = "Will need to reload the UI.", --(optional) - default = {r = defaults.r, g = defaults.g, b = defaults.b, a = defaults.a}, --(optional) table of default color values (or default = defaultColor, where defaultColor is a table with keys of r, g, b[, a]) - reference = "MyAddonColorpicker" --(optional) unique global reference to control -} ]] - - -local widgetVersion = 6 + type = "colorpicker", + name = "My Color Picker", -- or string id or function returning a string + getFunc = function() return db.r, db.g, db.b, db.a end, --(alpha is optional) + setFunc = function(r,g,b,a) db.r=r, db.g=g, db.b=b, db.a=a end, --(alpha is optional) + tooltip = "Color Picker's tooltip text.", -- or string id or function returning a string (optional) + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional) + requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional) + default = {r = defaults.r, g = defaults.g, b = defaults.b, a = defaults.a}, --(optional) table of default color values (or default = defaultColor, where defaultColor is a table with keys of r, g, b[, a]) or a function that returns the color + reference = "MyAddonColorpicker" -- unique global reference to control (optional) +} ]] + + +local widgetVersion = 13 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("colorpicker", widgetVersion) then return end local wm = WINDOW_MANAGER -local cm = CALLBACK_MANAGER -local tinsert = table.insert - local function UpdateDisabled(control) - local disable - if type(control.data.disabled) == "function" then - disable = control.data.disabled() - else - disable = control.data.disabled - end - - if disable then - control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - else - control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - end - - control.isDisabled = disable + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end + + control.isDisabled = disable end -local function UpdateValue(control, forceDefault, valueR, valueG, valueB, valueA) - if forceDefault then --if we are forcing defaults - local color = control.data.default - valueR, valueG, valueB, valueA = color.r, color.g, color.b, color.a - control.data.setFunc(valueR, valueG, valueB, valueA) - elseif valueR and valueG and valueB then - control.data.setFunc(valueR, valueG, valueB, valueA or 1) - --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed - if control.panel.data.registerForRefresh then - cm:FireCallbacks("LAM-RefreshPanel", control) - end - else - valueR, valueG, valueB, valueA = control.data.getFunc() - end - - control.thumb:SetColor(valueR, valueG, valueB, valueA or 1) +local function UpdateValue(control, forceDefault, valueR, valueG, valueB, valueA) + if forceDefault then --if we are forcing defaults + local color = LAM.util.GetDefaultValue(control.data.default) + valueR, valueG, valueB, valueA = color.r, color.g, color.b, color.a + control.data.setFunc(valueR, valueG, valueB, valueA) + elseif valueR and valueG and valueB then + control.data.setFunc(valueR, valueG, valueB, valueA or 1) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + LAM.util.RequestRefreshIfNeeded(control) + else + valueR, valueG, valueB, valueA = control.data.getFunc() + end + + control.thumb:SetColor(valueR, valueG, valueB, valueA or 1) end - function LAMCreateControl.colorpicker(parent, colorpickerData, controlName) - local control = wm:CreateControl(controlName or colorpickerData.reference, parent.scroll or parent, CT_CONTROL) - control:SetMouseEnabled(true) - control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) - - control.label = wm:CreateControl(nil, control, CT_LABEL) - local label = control.label - label:SetDimensions(300, 26) - label:SetAnchor(TOPLEFT) - label:SetFont("ZoFontWinH4") - label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) - label:SetText(colorpickerData.name) - - control.color = wm:CreateControl(nil, control, CT_CONTROL) - local color = control.color - - local isHalfWidth = colorpickerData.width == "half" - if isHalfWidth then - control:SetDimensions(250, 55) - label:SetDimensions(250, 26) - color:SetDimensions(100, 24) - color:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT) - else - control:SetDimensions(510, 30) - label:SetDimensions(300, 26) - color:SetDimensions(200, 24) - color:SetAnchor(TOPRIGHT) - end - - control.thumb = wm:CreateControl(nil, color, CT_TEXTURE) - local thumb = control.thumb - thumb:SetDimensions(36, 18) - thumb:SetAnchor(LEFT, color, LEFT, 4, 0) - - color.border = wm:CreateControl(nil, color, CT_TEXTURE) - local border = color.border - border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds") - border:SetTextureCoords(0, .625, 0, .8125) - border:SetDimensions(40, 22) - border:SetAnchor(CENTER, thumb, CENTER, 0, 0) - - local function ColorPickerCallback(r, g, b, a) - control:UpdateValue(false, r, g, b, a) - end - - control:SetHandler("OnMouseUp", function(self, btn, upInside) - if self.isDisabled then return end - - if upInside then - local r, g, b, a = colorpickerData.getFunc() - COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, colorpickerData.name) - end - end) - - if colorpickerData.warning then - control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") - control.warning:SetAnchor(RIGHT, control.color, LEFT, -5, 0) - --control.warning.tooltipText = colorpickerData.warning - control.warning.data = {tooltipText = colorpickerData.warning} - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = colorpickerData - control.data.tooltipText = colorpickerData.tooltip - - if colorpickerData.disabled then - control.UpdateDisabled = UpdateDisabled - control:UpdateDisabled() - end - control.UpdateValue = UpdateValue - control:UpdateValue() - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control -end \ No newline at end of file + local control = LAM.util.CreateLabelAndContainerControl(parent, colorpickerData, controlName) + + control.color = control.container + local color = control.color + + control.thumb = wm:CreateControl(nil, color, CT_TEXTURE) + local thumb = control.thumb + thumb:SetDimensions(36, 18) + thumb:SetAnchor(LEFT, color, LEFT, 4, 0) + + color.border = wm:CreateControl(nil, color, CT_TEXTURE) + local border = color.border + border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds") + border:SetTextureCoords(0, .625, 0, .8125) + border:SetDimensions(40, 22) + border:SetAnchor(CENTER, thumb, CENTER, 0, 0) + + local function ColorPickerCallback(r, g, b, a) + control:UpdateValue(false, r, g, b, a) + end + + control:SetHandler("OnMouseUp", function(self, btn, upInside) + if self.isDisabled then return end + + if upInside then + local r, g, b, a = colorpickerData.getFunc() + COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, LAM.util.GetStringFromValue(colorpickerData.name)) + end + end) + + if colorpickerData.warning ~= nil or colorpickerData.requiresReload then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, control.color, LEFT, -5, 0) + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end + + control.data.tooltipText = LAM.util.GetStringFromValue(colorpickerData.tooltip) + + control.UpdateValue = UpdateValue + control:UpdateValue() + if colorpickerData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + LAM.util.RegisterForRefreshIfNeeded(control) + LAM.util.RegisterForReloadIfNeeded(control) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/custom.lua b/Libs/LibAddonMenu-2.0/controls/custom.lua index 6a31c34..40a7c42 100644 --- a/Libs/LibAddonMenu-2.0/controls/custom.lua +++ b/Libs/LibAddonMenu-2.0/controls/custom.lua @@ -1,44 +1,35 @@ --[[customData = { - type = "custom", - reference = "MyAddonCustomControl", --(optional) unique name for your control to use as reference - refreshFunc = function(customControl) end, --(optional) function to call when panel/controls refresh - width = "full", --or "half" (optional) -} ]] + type = "custom", + reference = "MyAddonCustomControl", --(optional) unique name for your control to use as reference + refreshFunc = function(customControl) end, --(optional) function to call when panel/controls refresh + width = "full", --or "half" (optional) +} ]] -local widgetVersion = 5 +local widgetVersion = 7 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("custom", widgetVersion) then return end -local wm = WINDOW_MANAGER -local tinsert = table.insert - local function UpdateValue(control) - if control.data.refreshFunc then - control.data.refreshFunc(control) - end + if control.data.refreshFunc then + control.data.refreshFunc(control) + end end +local MIN_HEIGHT = 26 function LAMCreateControl.custom(parent, customData, controlName) - local control = wm:CreateControl(controlName or customData.reference, parent.scroll or parent, CT_CONTROL) - control:SetResizeToFitDescendents(true) + local control = LAM.util.CreateBaseControl(parent, customData, controlName) + local width = control:GetWidth() + control:SetResizeToFitDescendents(true) - local isHalfWidth = customData.width == "half" - if isHalfWidth then --note these restrictions - control:SetDimensionConstraints(250, 55, 250, 100) - control:SetDimensions(250, 55) - else - control:SetDimensionConstraints(510, 30, 510, 100) - control:SetDimensions(510, 30) - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = customData + if control.isHalfWidth then --note these restrictions + control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4) + else + control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4) + end - control.UpdateValue = UpdateValue + control.UpdateValue = UpdateValue - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end + LAM.util.RegisterForRefreshIfNeeded(control) - return control -end \ No newline at end of file + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/description.lua b/Libs/LibAddonMenu-2.0/controls/description.lua index b886b83..da207a0 100644 --- a/Libs/LibAddonMenu-2.0/controls/description.lua +++ b/Libs/LibAddonMenu-2.0/controls/description.lua @@ -1,66 +1,60 @@ --[[descriptionData = { - type = "description", - title = "My Title", --(optional) - text = "My description text to display.", - width = "full", --or "half" (optional) - reference = "MyAddonDescription" --(optional) unique global reference to control -} ]] + type = "description", + text = "My description text to display.", -- or string id or function returning a string + title = "My Title", -- or string id or function returning a string (optional) + width = "full", --or "half" (optional) + reference = "MyAddonDescription" -- unique global reference to control (optional) +} ]] -local widgetVersion = 5 +local widgetVersion = 8 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("description", widgetVersion) then return end local wm = WINDOW_MANAGER -local tinsert = table.insert local function UpdateValue(control) - if control.title then - control.title:SetText(control.data.title) - end - control.desc:SetText(control.data.text) + if control.title then + control.title:SetText(LAM.util.GetStringFromValue(control.data.title)) + end + control.desc:SetText(LAM.util.GetStringFromValue(control.data.text)) end function LAMCreateControl.description(parent, descriptionData, controlName) - local control = wm:CreateControl(controlName or descriptionData.reference, parent.scroll or parent, CT_CONTROL) - control:SetResizeToFitDescendents(true) - local isHalfWidth = descriptionData.width == "half" - if isHalfWidth then - control:SetDimensionConstraints(250, 55, 250, 100) - control:SetDimensions(250, 55) - else - control:SetDimensionConstraints(510, 40, 510, 100) - control:SetDimensions(510, 30) - end + local control = LAM.util.CreateBaseControl(parent, descriptionData, controlName) + local isHalfWidth = control.isHalfWidth + local width = control:GetWidth() + control:SetResizeToFitDescendents(true) + + if isHalfWidth then + control:SetDimensionConstraints(width / 2, 0, width / 2, 0) + else + control:SetDimensionConstraints(width, 0, width, 0) + end + + control.desc = wm:CreateControl(nil, control, CT_LABEL) + local desc = control.desc + desc:SetVerticalAlignment(TEXT_ALIGN_TOP) + desc:SetFont("ZoFontGame") + desc:SetText(LAM.util.GetStringFromValue(descriptionData.text)) + desc:SetWidth(isHalfWidth and width / 2 or width) + + if descriptionData.title then + control.title = wm:CreateControl(nil, control, CT_LABEL) + local title = control.title + title:SetWidth(isHalfWidth and width / 2 or width) + title:SetAnchor(TOPLEFT, control, TOPLEFT) + title:SetFont("ZoFontWinH4") + title:SetText(LAM.util.GetStringFromValue(descriptionData.title)) + desc:SetAnchor(TOPLEFT, title, BOTTOMLEFT) + else + desc:SetAnchor(TOPLEFT) + end + + control.UpdateValue = UpdateValue + + LAM.util.RegisterForRefreshIfNeeded(control) + + return control - control.desc = wm:CreateControl(nil, control, CT_LABEL) - local desc = control.desc - desc:SetVerticalAlignment(TEXT_ALIGN_TOP) - desc:SetFont("ZoFontGame") - desc:SetText(descriptionData.text) - desc:SetWidth(isHalfWidth and 250 or 510) - - if descriptionData.title then - control.title = wm:CreateControl(nil, control, CT_LABEL) - local title = control.title - title:SetWidth(isHalfWidth and 250 or 510) - title:SetAnchor(TOPLEFT, control, TOPLEFT) - title:SetFont("ZoFontWinH4") - title:SetText(descriptionData.title) - desc:SetAnchor(TOPLEFT, title, BOTTOMLEFT) - else - desc:SetAnchor(TOPLEFT) - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = descriptionData - - control.UpdateValue = UpdateValue - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control - -end \ No newline at end of file +end diff --git a/Libs/LibAddonMenu-2.0/controls/divider.lua b/Libs/LibAddonMenu-2.0/controls/divider.lua new file mode 100644 index 0000000..8089539 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/divider.lua @@ -0,0 +1,45 @@ +--[[dividerData = { + type = "divider", + width = "full", --or "half" (optional) + height = 10, (optional) + alpha = 0.25, (optional) + reference = "MyAddonDivider" -- unique global reference to control (optional) +} ]] + + +local widgetVersion = 2 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("divider", widgetVersion) then return end + +local wm = WINDOW_MANAGER + +local MIN_HEIGHT = 10 +local MAX_HEIGHT = 50 +local MIN_ALPHA = 0 +local MAX_ALPHA = 1 +local DEFAULT_ALPHA = 0.25 + +local function GetValueInRange(value, min, max, default) + if not value or type(value) ~= "number" then + return default + end + return math.min(math.max(min, value), max) +end + +function LAMCreateControl.divider(parent, dividerData, controlName) + local control = LAM.util.CreateBaseControl(parent, dividerData, controlName) + local isHalfWidth = control.isHalfWidth + local width = control:GetWidth() + local height = GetValueInRange(dividerData.height, MIN_HEIGHT, MAX_HEIGHT, MIN_HEIGHT) + local alpha = GetValueInRange(dividerData.alpha, MIN_ALPHA, MAX_ALPHA, DEFAULT_ALPHA) + + control:SetDimensions(isHalfWidth and width / 2 or width, height) + + control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider") + local divider = control.divider + divider:SetWidth(isHalfWidth and width / 2 or width) + divider:SetAnchor(TOPLEFT) + divider:SetAlpha(alpha) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/dropdown.lua b/Libs/LibAddonMenu-2.0/controls/dropdown.lua index 795404c..2349ba6 100644 --- a/Libs/LibAddonMenu-2.0/controls/dropdown.lua +++ b/Libs/LibAddonMenu-2.0/controls/dropdown.lua @@ -1,157 +1,211 @@ --[[dropdownData = { - type = "dropdown", - name = "My Dropdown", - tooltip = "Dropdown's tooltip text.", - choices = {"table", "of", "choices"}, - sort = "name-up", --or "name-down", "numeric-up", "numeric-down" (optional) - if not provided, list will not be sorted - getFunc = function() return db.var end, - setFunc = function(var) db.var = var doStuff() end, - width = "full", --or "half" (optional) - disabled = function() return db.someBooleanSetting end, --or boolean (optional) - warning = "Will need to reload the UI.", --(optional) - default = defaults.var, --(optional) - reference = "MyAddonDropdown" --(optional) unique global reference to control -} ]] - - -local widgetVersion = 8 + type = "dropdown", + name = "My Dropdown", -- or string id or function returning a string + choices = {"table", "of", "choices"}, + choicesValues = {"foo", 2, "three"}, -- if specified, these values will get passed to setFunc instead (optional) + getFunc = function() return db.var end, + setFunc = function(var) db.var = var doStuff() end, + tooltip = "Dropdown's tooltip text.", -- or string id or function returning a string (optional) + choicesTooltips = {"tooltip 1", "tooltip 2", "tooltip 3"}, -- or array of string ids or array of functions returning a string (optional) + sort = "name-up", --or "name-down", "numeric-up", "numeric-down", "value-up", "value-down", "numericvalue-up", "numericvalue-down" (optional) - if not provided, list will not be sorted + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional) + requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional) + default = defaults.var, -- default value or function that returns the default value (optional) + reference = "MyAddonDropdown" -- unique global reference to control (optional) +} ]] + + +local widgetVersion = 16 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("dropdown", widgetVersion) then return end local wm = WINDOW_MANAGER -local cm = CALLBACK_MANAGER -local tinsert = table.insert - +local SORT_BY_VALUE = { ["value"] = {} } +local SORT_BY_VALUE_NUMERIC = { ["value"] = { isNumeric = true } } +local SORT_TYPES = { + name = ZO_SORT_BY_NAME, + numeric = ZO_SORT_BY_NAME_NUMERIC, + value = SORT_BY_VALUE, + numericvalue = SORT_BY_VALUE_NUMERIC, +} +local SORT_ORDERS = { + up = ZO_SORT_ORDER_UP, + down = ZO_SORT_ORDER_DOWN, +} local function UpdateDisabled(control) - local disable - if type(control.data.disabled) == "function" then - disable = control.data.disabled() - else - disable = control.data.disabled - end - - control.dropdown:SetEnabled(not disable) - if disable then - control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - else - control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - end + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.dropdown:SetEnabled(not disable) + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end end local function UpdateValue(control, forceDefault, value) - if forceDefault then --if we are forcing defaults - value = control.data.default - control.data.setFunc(value) - control.dropdown:SetSelectedItem(value) - elseif value then - control.data.setFunc(value) - --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed - if control.panel.data.registerForRefresh then - cm:FireCallbacks("LAM-RefreshPanel", control) - end - else - value = control.data.getFunc() - control.dropdown:SetSelectedItem(value) - end + if forceDefault then --if we are forcing defaults + value = LAM.util.GetDefaultValue(control.data.default) + control.data.setFunc(value) + control.dropdown:SetSelectedItem(control.choices[value]) + elseif value then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + LAM.util.RequestRefreshIfNeeded(control) + else + value = control.data.getFunc() + control.dropdown:SetSelectedItem(control.choices[value]) + end end local function DropdownCallback(control, choiceText, choice) - choice.control:UpdateValue(false, choiceText) + choice.control:UpdateValue(false, choice.value or choiceText) end -local function UpdateChoices(control, choices) - control.dropdown:ClearItems() --remove previous choices --(need to call :SetSelectedItem()?) +local function SetupTooltips(comboBox, choicesTooltips) + local function ShowTooltip(control) + InitializeTooltip(InformationTooltip, control, TOPLEFT, 0, 0, BOTTOMRIGHT) + SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(control.tooltip)) + InformationTooltipTopLevel:BringWindowToTop() + end + local function HideTooltip(control) + ClearTooltip(InformationTooltip) + end + + -- allow for tooltips on the drop down entries + local originalShow = comboBox.ShowDropdownInternal + comboBox.ShowDropdownInternal = function(comboBox) + originalShow(comboBox) + local entries = ZO_Menu.items + for i = 1, #entries do + local entry = entries[i] + local control = entries[i].item + control.tooltip = choicesTooltips[i] + entry.onMouseEnter = control:GetHandler("OnMouseEnter") + entry.onMouseExit = control:GetHandler("OnMouseExit") + ZO_PreHookHandler(control, "OnMouseEnter", ShowTooltip) + ZO_PreHookHandler(control, "OnMouseExit", HideTooltip) + end + end + + local originalHide = comboBox.HideDropdownInternal + comboBox.HideDropdownInternal = function(self) + local entries = ZO_Menu.items + for i = 1, #entries do + local entry = entries[i] + local control = entries[i].item + control:SetHandler("OnMouseEnter", entry.onMouseEnter) + control:SetHandler("OnMouseExit", entry.onMouseExit) + control.tooltip = nil + end + originalHide(self) + end +end - --build new list of choices - local choices = choices or control.data.choices - for i = 1, #choices do - local entry = control.dropdown:CreateItemEntry(choices[i], DropdownCallback) - entry.control = control - control.dropdown:AddItem(entry, not control.data.sort and ZO_COMBOBOX_SUPRESS_UPDATE) --if sort type/order isn't specified, then don't sort - end +local function UpdateChoices(control, choices, choicesValues, choicesTooltips) + control.dropdown:ClearItems() --remove previous choices --(need to call :SetSelectedItem()?) + ZO_ClearTable(control.choices) + + --build new list of choices + local choices = choices or control.data.choices + local choicesValues = choicesValues or control.data.choicesValues + local choicesTooltips = choicesTooltips or control.data.choicesTooltips + + if choicesValues then + assert(#choices == #choicesValues, "choices and choicesValues need to have the same size") + end + + if choicesTooltips then + assert(#choices == #choicesTooltips, "choices and choicesTooltips need to have the same size") + SetupTooltips(control.dropdown, choicesTooltips) + end + + for i = 1, #choices do + local entry = control.dropdown:CreateItemEntry(choices[i], DropdownCallback) + entry.control = control + if choicesValues then + entry.value = choicesValues[i] + end + control.choices[entry.value or entry.name] = entry.name + control.dropdown:AddItem(entry, not control.data.sort and ZO_COMBOBOX_SUPRESS_UPDATE) --if sort type/order isn't specified, then don't sort + end end local function GrabSortingInfo(sortInfo) - local t, i = {}, 1 - for info in string.gmatch(sortInfo, "([^%-]+)") do - t[i] = info - i = i + 1 - end + local t, i = {}, 1 + for info in string.gmatch(sortInfo, "([^%-]+)") do + t[i] = info + i = i + 1 + end - return t + return t end - function LAMCreateControl.dropdown(parent, dropdownData, controlName) - local control = wm:CreateControl(controlName or dropdownData.reference, parent.scroll or parent, CT_CONTROL) - control:SetMouseEnabled(true) - control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) - - control.label = wm:CreateControl(nil, control, CT_LABEL) - local label = control.label - label:SetAnchor(TOPLEFT) - label:SetFont("ZoFontWinH4") - label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) - label:SetText(dropdownData.name) - - local countControl = parent - local name = parent:GetName() - if not name or #name == 0 then - countControl = LAMCreateControl - name = "LAM" - end - local comboboxCount = (countControl.comboboxCount or 0) + 1 - countControl.comboboxCount = comboboxCount - control.combobox = wm:CreateControlFromVirtual(zo_strjoin(nil, name, "Combobox", comboboxCount), control, "ZO_ComboBox") - - local combobox = control.combobox - combobox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) - combobox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) - control.dropdown = ZO_ComboBox_ObjectFromContainer(combobox) - local dropdown = control.dropdown - if dropdownData.sort then - local sortInfo = GrabSortingInfo(dropdownData.sort) - local sortType, sortOrder = sortInfo[1], sortInfo[2] - dropdown:SetSortOrder(sortOrder == "up" and ZO_SORT_ORDER_UP or ZO_SORT_ORDER_DOWN, sortType == "name" and ZO_SORT_BY_NAME or ZO_SORT_BY_NAME_NUMERIC) - end - - local isHalfWidth = dropdownData.width == "half" - if isHalfWidth then - control:SetDimensions(250, 55) - label:SetDimensions(250, 26) - combobox:SetDimensions(225, 26) - combobox:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT) - else - control:SetDimensions(510, 30) - label:SetDimensions(300, 26) - combobox:SetDimensions(200, 26) - combobox:SetAnchor(TOPRIGHT) - end - - if dropdownData.warning then - control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") - control.warning:SetAnchor(RIGHT, combobox, LEFT, -5, 0) - control.warning.data = {tooltipText = dropdownData.warning} - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = dropdownData - control.data.tooltipText = dropdownData.tooltip - - if dropdownData.disabled then - control.UpdateDisabled = UpdateDisabled - control:UpdateDisabled() - end - control.UpdateChoices = UpdateChoices - control:UpdateChoices(dropdownData.choices) - control.UpdateValue = UpdateValue - control:UpdateValue() - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control -end \ No newline at end of file + local control = LAM.util.CreateLabelAndContainerControl(parent, dropdownData, controlName) + control.choices = {} + + local countControl = parent + local name = parent:GetName() + if not name or #name == 0 then + countControl = LAMCreateControl + name = "LAM" + end + local comboboxCount = (countControl.comboboxCount or 0) + 1 + countControl.comboboxCount = comboboxCount + control.combobox = wm:CreateControlFromVirtual(zo_strjoin(nil, name, "Combobox", comboboxCount), control.container, "ZO_ComboBox") + + local combobox = control.combobox + combobox:SetAnchor(TOPLEFT) + combobox:SetDimensions(control.container:GetDimensions()) + combobox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + combobox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + control.dropdown = ZO_ComboBox_ObjectFromContainer(combobox) + local dropdown = control.dropdown + dropdown:SetSortsItems(false) -- need to sort ourselves in order to be able to sort by value + + ZO_PreHook(dropdown, "UpdateItems", function(self) + assert(not self.m_sortsItems, "built-in dropdown sorting was reactivated, sorting is handled by LAM") + if control.m_sortOrder ~= nil and control.m_sortType then + local sortKey = next(control.m_sortType) + local sortFunc = function(item1, item2) return ZO_TableOrderingFunction(item1, item2, sortKey, control.m_sortType, control.m_sortOrder) end + table.sort(self.m_sortedItems, sortFunc) + end + end) + + if dropdownData.sort then + local sortInfo = GrabSortingInfo(dropdownData.sort) + control.m_sortType, control.m_sortOrder = SORT_TYPES[sortInfo[1]], SORT_ORDERS[sortInfo[2]] + elseif dropdownData.choicesValues then + control.m_sortType, control.m_sortOrder = ZO_SORT_ORDER_UP, SORT_BY_VALUE + end + + if dropdownData.warning ~= nil or dropdownData.requiresReload then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, combobox, LEFT, -5, 0) + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end + + control.UpdateChoices = UpdateChoices + control:UpdateChoices(dropdownData.choices, dropdownData.choicesValues) + control.UpdateValue = UpdateValue + control:UpdateValue() + if dropdownData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + LAM.util.RegisterForRefreshIfNeeded(control) + LAM.util.RegisterForReloadIfNeeded(control) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/editbox.lua b/Libs/LibAddonMenu-2.0/controls/editbox.lua index 3ab069f..d6baf11 100644 --- a/Libs/LibAddonMenu-2.0/controls/editbox.lua +++ b/Libs/LibAddonMenu-2.0/controls/editbox.lua @@ -1,153 +1,156 @@ --[[editboxData = { - type = "editbox", - name = "My Editbox", - tooltip = "Editbox's tooltip text.", - getFunc = function() return db.text end, - setFunc = function(text) db.text = text doStuff() end, - isMultiline = true, --boolean - width = "full", --or "half" (optional) - disabled = function() return db.someBooleanSetting end, --or boolean (optional) - warning = "Will need to reload the UI.", --(optional) - default = defaults.text, --(optional) - reference = "MyAddonEditbox" --(optional) unique global reference to control -} ]] - - -local widgetVersion = 7 + type = "editbox", + name = "My Editbox", -- or string id or function returning a string + getFunc = function() return db.text end, + setFunc = function(text) db.text = text doStuff() end, + tooltip = "Editbox's tooltip text.", -- or string id or function returning a string (optional) + isMultiline = true, --boolean (optional) + isExtraWide = true, --boolean (optional) + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional) + requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional) + default = defaults.text, -- default value or function that returns the default value (optional) + reference = "MyAddonEditbox" -- unique global reference to control (optional) +} ]] + + +local widgetVersion = 14 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("editbox", widgetVersion) then return end local wm = WINDOW_MANAGER -local cm = CALLBACK_MANAGER -local tinsert = table.insert - local function UpdateDisabled(control) - local disable - if type(control.data.disabled) == "function" then - disable = control.data.disabled() - else - disable = control.data.disabled - end - - if disable then - control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - control.editbox:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) - else - control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - control.editbox:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - end - --control.editbox:SetEditEnabled(not disable) - control.editbox:SetMouseEnabled(not disable) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.editbox:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.editbox:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end + --control.editbox:SetEditEnabled(not disable) + control.editbox:SetMouseEnabled(not disable) end -local function UpdateValue(control, forceDefault, value) - if forceDefault then --if we are forcing defaults - value = control.data.default - control.data.setFunc(value) - control.editbox:SetText(value) - elseif value then - control.data.setFunc(value) - --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed - if control.panel.data.registerForRefresh then - cm:FireCallbacks("LAM-RefreshPanel", control) - end - else - value = control.data.getFunc() - control.editbox:SetText(value) - end +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = LAM.util.GetDefaultValue(control.data.default) + control.data.setFunc(value) + control.editbox:SetText(value) + elseif value then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + LAM.util.RequestRefreshIfNeeded(control) + else + value = control.data.getFunc() + control.editbox:SetText(value) + end end - +local MIN_HEIGHT = 24 +local HALF_WIDTH_LINE_SPACING = 2 function LAMCreateControl.editbox(parent, editboxData, controlName) - local control = wm:CreateControl(controlName or editboxData.reference, parent.scroll or parent, CT_CONTROL) - control:SetMouseEnabled(true) - control:SetResizeToFitDescendents(true) - control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) - - control.label = wm:CreateControl(nil, control, CT_LABEL) - local label = control.label - label:SetAnchor(TOPLEFT) - label:SetFont("ZoFontWinH4") - label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) - label:SetText(editboxData.name) - - control.bg = wm:CreateControlFromVirtual(nil, control, "ZO_EditBackdrop") - local bg = control.bg - - if editboxData.isMultiline then - control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditMultiLineForBackdrop") - control.editbox:SetHandler("OnMouseWheel", function(self, delta) - if self:HasFocus() then --only set focus to new spots if the editbox is currently in use - local cursorPos = self:GetCursorPosition() - local text = self:GetText() - local textLen = text:len() - local newPos - if delta > 0 then --scrolling up - local reverseText = text:reverse() - local revCursorPos = textLen - cursorPos - local revPos = reverseText:find("\n", revCursorPos+1) - newPos = revPos and textLen - revPos - else --scrolling down - newPos = text:find("\n", cursorPos+1) - end - if newPos then --if we found a new line, then scroll, otherwise don't - self:SetCursorPosition(newPos) - end - end - end) - else - control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditForBackdrop") - end - local editbox = control.editbox - editbox:SetText(editboxData.getFunc()) - editbox:SetMaxInputChars(3000) - editbox:SetHandler("OnFocusLost", function(self) control:UpdateValue(false, self:GetText()) end) - editbox:SetHandler("OnEscape", function(self) self:LoseFocus() control:UpdateValue(false, self:GetText()) end) - editbox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) - editbox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) - - local isHalfWidth = editboxData.width == "half" - if isHalfWidth then - control:SetDimensions(250, 55) - label:SetDimensions(250, 26) - bg:SetDimensions(225, editboxData.isMultiline and 74 or 24) - bg:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT) - if editboxData.isMultiline then - editbox:SetDimensionConstraints(210, 74, 210, 500) - end - else - control:SetDimensions(510, 30) - label:SetDimensions(300, 26) - bg:SetDimensions(200, editboxData.isMultiline and 100 or 24) - bg:SetAnchor(TOPRIGHT) - if editboxData.isMultiline then - editbox:SetDimensionConstraints(185, 100, 185, 500) - end - end - - if editboxData.warning then - control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") - control.warning:SetAnchor(TOPRIGHT, control.bg, TOPLEFT, -5, 0) - --control.warning.tooltipText = editboxData.warning - control.warning.data = {tooltipText = editboxData.warning} - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = editboxData - control.data.tooltipText = editboxData.tooltip - - if editboxData.disabled then - control.UpdateDisabled = UpdateDisabled - control:UpdateDisabled() - end - control.UpdateValue = UpdateValue - control:UpdateValue() - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control -end \ No newline at end of file + local control = LAM.util.CreateLabelAndContainerControl(parent, editboxData, controlName) + + local container = control.container + control.bg = wm:CreateControlFromVirtual(nil, container, "ZO_EditBackdrop") + local bg = control.bg + bg:SetAnchorFill() + + if editboxData.isMultiline then + control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditMultiLineForBackdrop") + control.editbox:SetHandler("OnMouseWheel", function(self, delta) + if self:HasFocus() then --only set focus to new spots if the editbox is currently in use + local cursorPos = self:GetCursorPosition() + local text = self:GetText() + local textLen = text:len() + local newPos + if delta > 0 then --scrolling up + local reverseText = text:reverse() + local revCursorPos = textLen - cursorPos + local revPos = reverseText:find("\n", revCursorPos+1) + newPos = revPos and textLen - revPos + else --scrolling down + newPos = text:find("\n", cursorPos+1) + end + if newPos then --if we found a new line, then scroll, otherwise don't + self:SetCursorPosition(newPos) + end + end + end) + else + control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditForBackdrop") + end + local editbox = control.editbox + editbox:SetText(editboxData.getFunc()) + editbox:SetMaxInputChars(3000) + editbox:SetHandler("OnFocusLost", function(self) control:UpdateValue(false, self:GetText()) end) + editbox:SetHandler("OnEscape", function(self) self:LoseFocus() control:UpdateValue(false, self:GetText()) end) + editbox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + editbox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + + local MIN_WIDTH = (parent.GetWidth and (parent:GetWidth() / 10)) or (parent.panel.GetWidth and (parent.panel:GetWidth() / 10)) or 0 + + control.label:ClearAnchors() + container:ClearAnchors() + + control.label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0) + container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, 0, 0) + + if control.isHalfWidth then + container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, 0, 0) + end + + if editboxData.isExtraWide then + container:SetAnchor(BOTTOMLEFT, control, BOTTOMLEFT, 0, 0) + else + container:SetWidth(MIN_WIDTH * 3.2) + end + + if editboxData.isMultiline then + container:SetHeight(MIN_HEIGHT * 3) + else + container:SetHeight(MIN_HEIGHT) + end + + if control.isHalfWidth ~= true and editboxData.isExtraWide ~= true then + control:SetHeight(container:GetHeight()) + else + control:SetHeight(container:GetHeight() + control.label:GetHeight()) + end + + editbox:ClearAnchors() + editbox:SetAnchor(TOPLEFT, container, TOPLEFT, 2, 2) + editbox:SetAnchor(BOTTOMRIGHT, container, BOTTOMRIGHT, -2, -2) + + if editboxData.warning ~= nil or editboxData.requiresReload then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + if editboxData.isExtraWide then + control.warning:SetAnchor(BOTTOMRIGHT, control.bg, TOPRIGHT, 2, 0) + else + control.warning:SetAnchor(TOPRIGHT, control.bg, TOPLEFT, -5, 0) + end + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end + + control.UpdateValue = UpdateValue + control:UpdateValue() + if editboxData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + LAM.util.RegisterForRefreshIfNeeded(control) + LAM.util.RegisterForReloadIfNeeded(control) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/header.lua b/Libs/LibAddonMenu-2.0/controls/header.lua index 83c862b..eadff38 100644 --- a/Libs/LibAddonMenu-2.0/controls/header.lua +++ b/Libs/LibAddonMenu-2.0/controls/header.lua @@ -1,46 +1,42 @@ --[[headerData = { - type = "header", - name = "My Header", - width = "full", --or "half" (optional) - reference = "MyAddonHeader" --(optional) unique global reference to control -} ]] + type = "header", + name = "My Header", -- or string id or function returning a string + width = "full", --or "half" (optional) + reference = "MyAddonHeader" -- unique global reference to control (optional) +} ]] -local widgetVersion = 5 +local widgetVersion = 8 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("header", widgetVersion) then return end local wm = WINDOW_MANAGER -local tinsert = table.insert local function UpdateValue(control) - control.header:SetText(control.data.name) + control.header:SetText(LAM.util.GetStringFromValue(control.data.name)) end +local MIN_HEIGHT = 30 function LAMCreateControl.header(parent, headerData, controlName) - local control = wm:CreateControl(controlName or headerData.reference, parent.scroll or parent, CT_CONTROL) - local isHalfWidth = headerData.width == "half" - control:SetDimensions(isHalfWidth and 250 or 510, 30) + local control = LAM.util.CreateBaseControl(parent, headerData, controlName) + local isHalfWidth = control.isHalfWidth + local width = control:GetWidth() + control:SetDimensions(isHalfWidth and width / 2 or width, MIN_HEIGHT) - control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider") - local divider = control.divider - divider:SetWidth(isHalfWidth and 250 or 510) - divider:SetAnchor(TOPLEFT) + control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider") + local divider = control.divider + divider:SetWidth(isHalfWidth and width / 2 or width) + divider:SetAnchor(TOPLEFT) - control.header = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") - local header = control.header - header:SetAnchor(TOPLEFT, divider, BOTTOMLEFT) - header:SetAnchor(BOTTOMRIGHT) - header:SetText(headerData.name) + control.header = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") + local header = control.header + header:SetAnchor(TOPLEFT, divider, BOTTOMLEFT) + header:SetAnchor(BOTTOMRIGHT) + header:SetText(LAM.util.GetStringFromValue(headerData.name)) - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = headerData + control.UpdateValue = UpdateValue - control.UpdateValue = UpdateValue + LAM.util.RegisterForRefreshIfNeeded(control) - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control -end \ No newline at end of file + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/iconpicker.lua b/Libs/LibAddonMenu-2.0/controls/iconpicker.lua new file mode 100644 index 0000000..65c7782 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/iconpicker.lua @@ -0,0 +1,436 @@ +--[[iconpickerData = { + type = "iconpicker", + name = "My Icon Picker", -- or string id or function returning a string + choices = {"texture path 1", "texture path 2", "texture path 3"}, + getFunc = function() return db.var end, + setFunc = function(var) db.var = var doStuff() end, + tooltip = "Color Picker's tooltip text.", -- or string id or function returning a string (optional) + choicesTooltips = {"icon tooltip 1", "icon tooltip 2", "icon tooltip 3"}, -- or array of string ids or array of functions returning a string (optional) + maxColumns = 5, -- number of icons in one row (optional) + visibleRows = 4.5, -- number of visible rows (optional) + iconSize = 28, -- size of the icons (optional) + defaultColor = ZO_ColorDef:New("FFFFFF"), -- default color of the icons (optional) + width = "full", --or "half" (optional) + beforeShow = function(control, iconPicker) return preventShow end, --(optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional) + requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional) + default = defaults.var, -- default value or function that returns the default value (optional) + reference = "MyAddonIconPicker" -- unique global reference to control (optional) +} ]] + +local widgetVersion = 8 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("iconpicker", widgetVersion) then return end + +local wm = WINDOW_MANAGER + +local IconPickerMenu = ZO_Object:Subclass() +local iconPicker +LAM.util.GetIconPickerMenu = function() + if not iconPicker then + iconPicker = IconPickerMenu:New("LAMIconPicker") + local sceneFragment = LAM:GetAddonSettingsFragment() + ZO_PreHook(sceneFragment, "OnHidden", function() + if not iconPicker.control:IsHidden() then + iconPicker:Clear() + end + end) + end + return iconPicker +end + +function IconPickerMenu:New(...) + local object = ZO_Object.New(self) + object:Initialize(...) + return object +end + +function IconPickerMenu:Initialize(name) + local control = wm:CreateTopLevelWindow(name) + control:SetDrawTier(DT_HIGH) + control:SetHidden(true) + self.control = control + + local scrollContainer = wm:CreateControlFromVirtual(name .. "ScrollContainer", control, "ZO_ScrollContainer") + -- control:SetDimensions(control.container:GetWidth(), height) -- adjust to icon size / col count + scrollContainer:SetAnchorFill() + ZO_Scroll_SetUseFadeGradient(scrollContainer, false) + ZO_Scroll_SetHideScrollbarOnDisable(scrollContainer, false) + ZO_VerticalScrollbarBase_OnMouseExit(scrollContainer:GetNamedChild("ScrollBar")) -- scrollbar initialization seems to be broken so we force it to update the correct alpha value + local scroll = GetControl(scrollContainer, "ScrollChild") + self.scroll = scroll + self.scrollContainer = scrollContainer + + local bg = wm:CreateControl(nil, scrollContainer, CT_BACKDROP) + bg:SetAnchor(TOPLEFT, scrollContainer, TOPLEFT, 0, -3) + bg:SetAnchor(BOTTOMRIGHT, scrollContainer, BOTTOMRIGHT, 2, 5) + bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16) + bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds") + bg:SetInsets(16, 16, -16, -16) + + local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE) + mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds") + mungeOverlay:SetDrawLevel(1) + mungeOverlay:SetAddressMode(TEX_MODE_WRAP) + mungeOverlay:SetAnchorFill() + + local mouseOver = wm:CreateControl(nil, scrollContainer, CT_TEXTURE) + mouseOver:SetDrawLevel(2) + mouseOver:SetTexture("EsoUI/Art/Buttons/minmax_mouseover.dds") + mouseOver:SetHidden(true) + + local function IconFactory(pool) + local icon = wm:CreateControl(name .. "Entry" .. pool:GetNextControlId(), scroll, CT_TEXTURE) + icon:SetMouseEnabled(true) + icon:SetDrawLevel(3) + icon:SetHandler("OnMouseEnter", function() + mouseOver:SetAnchor(TOPLEFT, icon, TOPLEFT, 0, 0) + mouseOver:SetAnchor(BOTTOMRIGHT, icon, BOTTOMRIGHT, 0, 0) + mouseOver:SetHidden(false) + if self.customOnMouseEnter then + self.customOnMouseEnter(icon) + else + self:OnMouseEnter(icon) + end + end) + icon:SetHandler("OnMouseExit", function() + mouseOver:ClearAnchors() + mouseOver:SetHidden(true) + if self.customOnMouseExit then + self.customOnMouseExit(icon) + else + self:OnMouseExit(icon) + end + end) + icon:SetHandler("OnMouseUp", function(control, ...) + PlaySound("Click") + icon.OnSelect(icon, icon.texture) + self:Clear() + end) + return icon + end + + local function ResetFunction(icon) + icon:ClearAnchors() + end + + self.iconPool = ZO_ObjectPool:New(IconFactory, ResetFunction) + self:SetMaxColumns(1) + self.icons = {} + self.color = ZO_DEFAULT_ENABLED_COLOR + + EVENT_MANAGER:RegisterForEvent(name .. "_OnGlobalMouseUp", EVENT_GLOBAL_MOUSE_UP, function() + if self.refCount ~= nil then + local moc = wm:GetMouseOverControl() + if(moc:GetOwningWindow() ~= control) then + self.refCount = self.refCount - 1 + if self.refCount <= 0 then + self:Clear() + end + end + end + end) +end + +function IconPickerMenu:OnMouseEnter(icon) + InitializeTooltip(InformationTooltip, icon, TOPLEFT, 0, 0, BOTTOMRIGHT) + SetTooltipText(InformationTooltip, LAM.util.GetStringFromValue(icon.tooltip)) + InformationTooltipTopLevel:BringWindowToTop() +end + +function IconPickerMenu:OnMouseExit(icon) + ClearTooltip(InformationTooltip) +end + +function IconPickerMenu:SetMaxColumns(value) + self.maxCols = value ~= nil and value or 5 +end + +local DEFAULT_SIZE = 28 +function IconPickerMenu:SetIconSize(value) + local iconSize = DEFAULT_SIZE + if value ~= nil then iconSize = math.max(iconSize, value) end + self.iconSize = iconSize +end + +function IconPickerMenu:SetVisibleRows(value) + self.visibleRows = value ~= nil and value or 4.5 +end + +function IconPickerMenu:SetMouseHandlers(onEnter, onExit) + self.customOnMouseEnter = onEnter + self.customOnMouseExit = onExit +end + +function IconPickerMenu:UpdateDimensions() + local iconSize = self.iconSize + local width = iconSize * self.maxCols + 20 + local height = iconSize * self.visibleRows + self.control:SetDimensions(width, height) + + local icons = self.icons + for i = 1, #icons do + local icon = icons[i] + icon:SetDimensions(iconSize, iconSize) + end +end + +function IconPickerMenu:UpdateAnchors() + local iconSize = self.iconSize + local col, maxCols = 1, self.maxCols + local previousCol, previousRow + local scroll = self.scroll + local icons = self.icons + + for i = 1, #icons do + local icon = icons[i] + icon:ClearAnchors() + if i == 1 then + icon:SetAnchor(TOPLEFT, scroll, TOPLEFT, 0, 0) + previousRow = icon + elseif col == 1 then + icon:SetAnchor(TOPLEFT, previousRow, BOTTOMLEFT, 0, 0) + previousRow = icon + else + icon:SetAnchor(TOPLEFT, previousCol, TOPRIGHT, 0, 0) + end + previousCol = icon + col = col >= maxCols and 1 or col + 1 + end +end + +function IconPickerMenu:Clear() + self.icons = {} + self.iconPool:ReleaseAllObjects() + self.control:SetHidden(true) + self.color = ZO_DEFAULT_ENABLED_COLOR + self.refCount = nil + self.parent = nil + self.customOnMouseEnter = nil + self.customOnMouseExit = nil +end + +function IconPickerMenu:AddIcon(texturePath, callback, tooltip) + local icon, key = self.iconPool:AcquireObject() + icon:SetTexture(texturePath) + icon:SetColor(self.color:UnpackRGBA()) + icon.texture = texturePath + icon.tooltip = tooltip + icon.OnSelect = callback + self.icons[#self.icons + 1] = icon +end + +function IconPickerMenu:Show(parent) + if #self.icons == 0 then return false end + if not self.control:IsHidden() then self:Clear() return false end + self:UpdateDimensions() + self:UpdateAnchors() + + local control = self.control + control:ClearAnchors() + control:SetAnchor(TOPLEFT, parent, BOTTOMLEFT, 0, 8) + control:SetHidden(false) + control:BringWindowToTop() + self.parent = parent + self.refCount = 2 + + return true +end + +function IconPickerMenu:SetColor(color) + local icons = self.icons + self.color = color + for i = 1, #icons do + local icon = icons[i] + icon:SetColor(color:UnpackRGBA()) + end +end + +------------------------------------------------------------- + +local function UpdateChoices(control, choices, choicesTooltips) + local data = control.data + if not choices then + choices, choicesTooltips = data.choices, data.choicesTooltips or {} + end + local addedChoices = {} + + local iconPicker = LAM.util.GetIconPickerMenu() + iconPicker:Clear() + for i = 1, #choices do + local texture = choices[i] + if not addedChoices[texture] then -- remove duplicates + iconPicker:AddIcon(choices[i], function(self, texture) + control.icon:SetTexture(texture) + data.setFunc(texture) + LAM.util.RequestRefreshIfNeeded(control) + end, LAM.util.GetStringFromValue(choicesTooltips[i])) + addedChoices[texture] = true + end + end +end + +local function IsDisabled(control) + if type(control.data.disabled) == "function" then + return control.data.disabled() + else + return control.data.disabled + end +end + +local function SetColor(control, color) + local icon = control.icon + if IsDisabled(control) then + icon:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + icon.color = color or control.data.defaultColor or ZO_DEFAULT_ENABLED_COLOR + icon:SetColor(icon.color:UnpackRGBA()) + end + + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then + iconPicker:SetColor(icon.color) + end +end + +local function UpdateDisabled(control) + local disable = IsDisabled(control) + + control.dropdown:SetMouseEnabled(not disable) + control.dropdownButton:SetEnabled(not disable) + + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then + iconPicker:Clear() + end + + SetColor(control, control.icon.color) + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end +end + +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = LAM.util.GetDefaultValue(control.data.default) + control.data.setFunc(value) + control.icon:SetTexture(value) + elseif value then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + LAM.util.RequestRefreshIfNeeded(control) + else + value = control.data.getFunc() + control.icon:SetTexture(value) + end +end + +local MIN_HEIGHT = 26 +local HALF_WIDTH_LINE_SPACING = 2 +local function SetIconSize(control, size) + local icon = control.icon + icon.size = size + icon:SetDimensions(size, size) + + local height = size + 4 + control.dropdown:SetDimensions(size + 20, height) + height = math.max(height, MIN_HEIGHT) + control.container:SetHeight(height) + if control.lineControl then + control.lineControl:SetHeight(MIN_HEIGHT + size + HALF_WIDTH_LINE_SPACING) + else + control:SetHeight(height) + end + + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then + iconPicker:SetIconSize(size) + iconPicker:UpdateDimensions() + iconPicker:UpdateAnchors() + end +end + +function LAMCreateControl.iconpicker(parent, iconpickerData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, iconpickerData, controlName) + + local function ShowIconPicker() + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container then + iconPicker:Clear() + else + iconPicker:SetMaxColumns(iconpickerData.maxColumns) + iconPicker:SetVisibleRows(iconpickerData.visibleRows) + iconPicker:SetIconSize(control.icon.size) + UpdateChoices(control) + iconPicker:SetColor(control.icon.color) + if iconpickerData.beforeShow then + if iconpickerData.beforeShow(control, iconPicker) then + iconPicker:Clear() + return + end + end + iconPicker:Show(control.container) + end + end + + local iconSize = iconpickerData.iconSize ~= nil and iconpickerData.iconSize or DEFAULT_SIZE + control.dropdown = wm:CreateControl(nil, control.container, CT_CONTROL) + local dropdown = control.dropdown + dropdown:SetAnchor(LEFT, control.container, LEFT, 0, 0) + dropdown:SetMouseEnabled(true) + dropdown:SetHandler("OnMouseUp", ShowIconPicker) + dropdown:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + dropdown:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + + control.icon = wm:CreateControl(nil, dropdown, CT_TEXTURE) + local icon = control.icon + icon:SetAnchor(LEFT, dropdown, LEFT, 3, 0) + icon:SetDrawLevel(2) + + local dropdownButton = wm:CreateControlFromVirtual(nil, dropdown, "ZO_DropdownButton") + dropdownButton:SetDimensions(16, 16) + dropdownButton:SetHandler("OnClicked", ShowIconPicker) + dropdownButton:SetAnchor(RIGHT, dropdown, RIGHT, -3, 0) + control.dropdownButton = dropdownButton + + control.bg = wm:CreateControl(nil, dropdown, CT_BACKDROP) + local bg = control.bg + bg:SetAnchor(TOPLEFT, dropdown, TOPLEFT, 0, -3) + bg:SetAnchor(BOTTOMRIGHT, dropdown, BOTTOMRIGHT, 2, 5) + bg:SetEdgeTexture("EsoUI/Art/Tooltips/UI-Border.dds", 128, 16) + bg:SetCenterTexture("EsoUI/Art/Tooltips/UI-TooltipCenter.dds") + bg:SetInsets(16, 16, -16, -16) + local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE) + mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds") + mungeOverlay:SetDrawLevel(1) + mungeOverlay:SetAddressMode(TEX_MODE_WRAP) + mungeOverlay:SetAnchorFill() + + if iconpickerData.warning ~= nil or iconpickerData.requiresReload then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, control.container, LEFT, -5, 0) + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end + + control.UpdateChoices = UpdateChoices + control.UpdateValue = UpdateValue + control:UpdateValue() + control.SetColor = SetColor + control:SetColor() + control.SetIconSize = SetIconSize + control:SetIconSize(iconSize) + + if iconpickerData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + LAM.util.RegisterForRefreshIfNeeded(control) + LAM.util.RegisterForReloadIfNeeded(control) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/panel.lua b/Libs/LibAddonMenu-2.0/controls/panel.lua index 8db73ee..1404686 100644 --- a/Libs/LibAddonMenu-2.0/controls/panel.lua +++ b/Libs/LibAddonMenu-2.0/controls/panel.lua @@ -1,17 +1,19 @@ --[[panelData = { - type = "panel", - name = "Window Title", - displayName = "My Longer Window Title", --(optional) (can be useful for long addon names or if you want to colorize it) - author = "Seerah", --(optional) - version = "2.0", --(optional) - slashCommand = "/myaddon", --(optional) will register a keybind to open to this panel (don't forget to include the slash!) - registerForRefresh = true, --boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown) - registerForDefaults = true, --boolean (optional) (will set all options controls back to default values) - resetFunc = function() print("defaults reset") end, --(optional) custom function to run after settings are reset to defaults -} ]] - - -local widgetVersion = 8 + type = "panel", + name = "Window Title", -- or string id or function returning a string + displayName = "My Longer Window Title", -- or string id or function returning a string (optional) (can be useful for long addon names or if you want to colorize it) + author = "Seerah", -- or string id or function returning a string (optional) + version = "2.0", -- or string id or function returning a string (optional) + website = "http://www.esoui.com/downloads/info7-LibAddonMenu.html", -- URL of website where the addon can be updated (optional) + keywords = "settings", -- additional keywords for search filter (it looks for matches in name..keywords..author) (optional) + slashCommand = "/myaddon", -- will register a keybind to open to this panel (don't forget to include the slash!) (optional) + registerForRefresh = true, --boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown) + registerForDefaults = true, --boolean (optional) (will set all options controls back to default values) + resetFunc = function() print("defaults reset") end, --(optional) custom function to run after settings are reset to defaults +} ]] + + +local widgetVersion = 13 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("panel", widgetVersion) then return end @@ -19,120 +21,106 @@ local wm = WINDOW_MANAGER local cm = CALLBACK_MANAGER local function RefreshPanel(control) - local panel = control.panel or control --callback can be fired by a single control or by the panel showing - local panelControls = panel.controlsToRefresh - - for i = 1, #panelControls do - local updateControl = panelControls[i] - if updateControl ~= control then - if updateControl.UpdateValue then - updateControl:UpdateValue() - end - if updateControl.UpdateDisabled then - updateControl:UpdateDisabled() - end - end - end + local panel = LAM.util.GetTopPanel(control) --callback can be fired by a single control, by the panel showing or by a nested submenu + local panelControls = panel.controlsToRefresh + + for i = 1, #panelControls do + local updateControl = panelControls[i] + if updateControl ~= control and updateControl.UpdateValue then + updateControl:UpdateValue() + end + if updateControl.UpdateDisabled then + updateControl:UpdateDisabled() + end + if updateControl.UpdateWarning then + updateControl:UpdateWarning() + end + end end local function ForceDefaults(panel) - local panelControls = panel.controlsToRefresh + local panelControls = panel.controlsToRefresh - for i = 1, #panelControls do - local updateControl = panelControls[i] - if updateControl.UpdateValue and updateControl.data.default ~= nil then - updateControl:UpdateValue(true) - end - end + for i = 1, #panelControls do + local updateControl = panelControls[i] + if updateControl.UpdateValue and updateControl.data.default ~= nil then + updateControl:UpdateValue(true) + end + end - if panel.data.resetFunc then - panel.data.resetFunc() - end + if panel.data.resetFunc then + panel.data.resetFunc() + end - cm:FireCallbacks("LAM-RefreshPanel", panel) + cm:FireCallbacks("LAM-RefreshPanel", panel) end -ESO_Dialogs["LAM_DEFAULTS"] = { - title = { - text = SI_OPTIONS_RESET_TITLE, - }, - mainText = { - text = SI_OPTIONS_RESET_PROMPT, - align = TEXT_ALIGN_CENTER, - }, - buttons = { - [1] = { - text = SI_OPTIONS_RESET, - callback = function(dialog) ForceDefaults(dialog.data[1]) end, - }, - [2] = { - text = SI_DIALOG_CANCEL, - }, - }, -} local callbackRegistered = false LAMCreateControl.scrollCount = LAMCreateControl.scrollCount or 1 +local SEPARATOR = " - " +local LINK_COLOR = ZO_ColorDef:New("5959D5") +local LINK_MOUSE_OVER_COLOR = ZO_ColorDef:New("B8B8D3") + function LAMCreateControl.panel(parent, panelData, controlName) - local control = wm:CreateTopLevelWindow(controlName) - control:SetParent(parent) - - control.bg = wm:CreateControl(nil, control, CT_BACKDROP) - local bg = control.bg - bg:SetAnchorFill() - bg:SetEdgeTexture("EsoUI\\Art\\miscellaneous\\borderedinsettransparent_edgefile.dds", 128, 16) - bg:SetCenterColor(0, 0, 0, 0) - - control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") - local label = control.label - label:SetAnchor(TOPLEFT, control, TOPLEFT, 10, 10) - label:SetText(panelData.displayName and panelData.displayName or panelData.name) - - if panelData.author or panelData.version then - control.info = wm:CreateControl(nil, control, CT_LABEL) - local info = control.info - info:SetFont("$(CHAT_FONT)|14|soft-shadow-thin") - info:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) - info:SetHeight(13) - info:SetAnchor(TOPRIGHT, control, BOTTOMRIGHT, -5, 2) - if panelData.author and panelData.version then - --info:SetText("Version: "..panelData.version.." - "..GetString(SI_ADDON_MANAGER_AUTHOR)..": "..panelData.author) - info:SetText(string.format("Version: %s - %s: %s", panelData.version, GetString(SI_ADDON_MANAGER_AUTHOR), panelData.author)) - elseif panelData.author then - info:SetText(string.format("%s: %s", GetString(SI_ADDON_MANAGER_AUTHOR), panelData.author)) - else - info:SetText("Version: "..panelData.version) - end - end - - control.container = wm:CreateControlFromVirtual("LAMAddonPanelContainer"..LAMCreateControl.scrollCount, control, "ZO_ScrollContainer") - LAMCreateControl.scrollCount = LAMCreateControl.scrollCount + 1 - local container = control.container - container:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 20) - container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, -3, -3) - control.scroll = GetControl(control.container, "ScrollChild") - control.scroll:SetResizeToFitPadding(0, 20) - - if panelData.registerForDefaults then - control.defaultButton = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultTextButton") - local defaultButton = control.defaultButton - defaultButton:SetFont("ZoFontDialogKeybindDescription") - defaultButton:SetHorizontalAlignment(TEXT_ALIGN_LEFT) - --defaultButton:SetText("Reset To Defaults") - defaultButton:SetText(GetString(SI_OPTIONS_RESET_TITLE)) - defaultButton:SetDimensions(200, 30) - defaultButton:SetAnchor(TOPLEFT, control, BOTTOMLEFT, 0, 2) - defaultButton:SetHandler("OnClicked", function() - ZO_Dialogs_ShowDialog("LAM_DEFAULTS", {control}) - end) - end - - if panelData.registerForRefresh and not callbackRegistered then --don't want to register our callback more than once - cm:RegisterCallback("LAM-RefreshPanel", RefreshPanel) - callbackRegistered = true - end - - control.data = panelData - control.controlsToRefresh = {} - - return control -end \ No newline at end of file + local control = wm:CreateControl(controlName, parent, CT_CONTROL) + + control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") + local label = control.label + label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 4) + label:SetText(LAM.util.GetStringFromValue(panelData.displayName or panelData.name)) + + if panelData.author or panelData.version then + control.info = wm:CreateControl(nil, control, CT_LABEL) + local info = control.info + info:SetFont(LAM.util.L["PANEL_INFO_FONT"]) + info:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2) + + local output = {} + if panelData.author then + output[#output + 1] = zo_strformat(LAM.util.L["AUTHOR"], LAM.util.GetStringFromValue(panelData.author)) + end + if panelData.version then + output[#output + 1] = zo_strformat(LAM.util.L["VERSION"], LAM.util.GetStringFromValue(panelData.version)) + end + info:SetText(table.concat(output, SEPARATOR)) + end + + if panelData.website then + control.website = wm:CreateControl(nil, control, CT_BUTTON) + local website = control.website + website:SetClickSound("Click") + website:SetFont(LAM.util.L["PANEL_INFO_FONT"]) + website:SetNormalFontColor(LINK_COLOR:UnpackRGBA()) + website:SetMouseOverFontColor(LINK_MOUSE_OVER_COLOR:UnpackRGBA()) + if(control.info) then + website:SetAnchor(TOPLEFT, control.info, TOPRIGHT, 0, 0) + website:SetText(string.format("|cffffff%s|r%s", SEPARATOR, LAM.util.L["WEBSITE"])) + else + website:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2) + website:SetText(LAM.util.L["WEBSITE"]) + end + website:SetDimensions(website:GetLabelControl():GetTextDimensions()) + website:SetHandler("OnClicked", function() + RequestOpenUnsafeURL(panelData.website) + end) + end + + control.container = wm:CreateControlFromVirtual("LAMAddonPanelContainer"..LAMCreateControl.scrollCount, control, "ZO_ScrollContainer") + LAMCreateControl.scrollCount = LAMCreateControl.scrollCount + 1 + local container = control.container + container:SetAnchor(TOPLEFT, control.info or label, BOTTOMLEFT, 0, 20) + container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, -3, -3) + control.scroll = GetControl(control.container, "ScrollChild") + control.scroll:SetResizeToFitPadding(0, 20) + + if panelData.registerForRefresh and not callbackRegistered then --don't want to register our callback more than once + cm:RegisterCallback("LAM-RefreshPanel", RefreshPanel) + callbackRegistered = true + end + + control.ForceDefaults = ForceDefaults + control.data = panelData + control.controlsToRefresh = {} + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/slider.lua b/Libs/LibAddonMenu-2.0/controls/slider.lua index 845fa7d..bd721c5 100644 --- a/Libs/LibAddonMenu-2.0/controls/slider.lua +++ b/Libs/LibAddonMenu-2.0/controls/slider.lua @@ -1,183 +1,212 @@ --[[sliderData = { - type = "slider", - name = "My Slider", - tooltip = "Slider's tooltip text.", - min = 0, - max = 20, - step = 1, --(optional) - getFunc = function() return db.var end, - setFunc = function(value) db.var = value doStuff() end, - width = "full", --or "half" (optional) - disabled = function() return db.someBooleanSetting end, --or boolean (optional) - warning = "Will need to reload the UI.", --(optional) - default = defaults.var, --(optional) - reference = "MyAddonSlider" --(optional) unique global reference to control -} ]] - - -local widgetVersion = 6 + type = "slider", + name = "My Slider", -- or string id or function returning a string + getFunc = function() return db.var end, + setFunc = function(value) db.var = value doStuff() end, + min = 0, + max = 20, + step = 1, --(optional) + clampInput = true, -- boolean, if set to false the input won't clamp to min and max and allow any number instead (optional) + decimals = 0, -- when specified the input value is rounded to the specified number of decimals (optional) + autoSelect = false, -- boolean, automatically select everything in the text input field when it gains focus (optional) + inputLocation = "below", -- or "right", determines where the input field is shown. This should not be used within the addon menu and is for custom sliders (optional) + tooltip = "Slider's tooltip text.", -- or string id or function returning a string (optional) + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "May cause permanent awesomeness.", -- or string id or function returning a string (optional) + requiresReload = false, -- boolean, if set to true, the warning text will contain a notice that changes are only applied after an UI reload and any change to the value will make the "Apply Settings" button appear on the panel which will reload the UI when pressed (optional) + default = defaults.var, -- default value or function that returns the default value (optional) + reference = "MyAddonSlider" -- unique global reference to control (optional) +} ]] + +local widgetVersion = 12 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("slider", widgetVersion) then return end local wm = WINDOW_MANAGER -local cm = CALLBACK_MANAGER -local round = zo_round local strformat = string.format -local tinsert = table.insert + +local function RoundDecimalToPlace(d, place) + return tonumber(strformat("%." .. tostring(place) .. "f", d)) +end local function UpdateDisabled(control) - local disable - if type(control.data.disabled) == "function" then - disable = control.data.disabled() - else - disable = control.data.disabled - end - - control.slider:SetEnabled(not disable) - control.slidervalue:SetEditEnabled(not disable) - if disable then - control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - control.minText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - control.maxText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) - control.slidervalue:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) - else - control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - control.minText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - control.maxText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - control.slidervalue:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) - end + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.slider:SetEnabled(not disable) + control.slidervalue:SetEditEnabled(not disable) + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.minText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.maxText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.slidervalue:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.minText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.maxText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.slidervalue:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end end local function UpdateValue(control, forceDefault, value) - if forceDefault then --if we are forcing defaults - value = control.data.default - control.data.setFunc(value) - elseif value and value >= control.data.min and value <= control.data.max then - control.data.setFunc(value) - --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed - if control.panel.data.registerForRefresh then - cm:FireCallbacks("LAM-RefreshPanel", control) - end - else - value = control.data.getFunc() - end - - control.slider:SetValue(value) - control.slidervalue:SetText(value) + if forceDefault then --if we are forcing defaults + value = LAM.util.GetDefaultValue(control.data.default) + control.data.setFunc(value) + elseif value then + if control.data.decimals then + value = RoundDecimalToPlace(value, control.data.decimals) + end + if control.data.clampInput ~= false then + value = math.max(math.min(value, control.data.max), control.data.min) + end + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + LAM.util.RequestRefreshIfNeeded(control) + else + value = control.data.getFunc() + end + + control.slider:SetValue(value) + control.slidervalue:SetText(value) end - function LAMCreateControl.slider(parent, sliderData, controlName) - local control = wm:CreateControl(controlName or sliderData.reference, parent.scroll or parent, CT_CONTROL) - local isHalfWidth = sliderData.width == "half" - if isHalfWidth then - control:SetDimensions(250, 55) - else - control:SetDimensions(510, 40) - end - control:SetMouseEnabled(true) - --control.tooltipText = sliderData.tooltip - control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) - - control.label = wm:CreateControl(nil, control, CT_LABEL) - local label = control.label - label:SetFont("ZoFontWinH4") - label:SetDimensions(isHalfWidth and 250 or 300, 26) - label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) - label:SetAnchor(isHalfWidth and TOPLEFT or LEFT) - label:SetText(sliderData.name) - - --skipping creating the backdrop... Is this the actual slider texture? - control.slider = wm:CreateControl(nil, control, CT_SLIDER) - local slider = control.slider - slider:SetDimensions(190, 14) - if isHalfWidth then - slider:SetAnchor(TOPRIGHT, label, BOTTOMRIGHT, -5, 2) - else - slider:SetAnchor(RIGHT, control, RIGHT, -5, -5) - end - slider:SetMouseEnabled(true) - slider:SetOrientation(ORIENTATION_HORIZONTAL) - --put nil for highlighted texture file path, and what look to be texture coords - slider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16) - local minValue = sliderData.min - local maxValue = sliderData.max - slider:SetMinMax(minValue, maxValue) - slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) - slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseExit(control) end) - - slider.bg = wm:CreateControl(nil, slider, CT_BACKDROP) - local bg = slider.bg - bg:SetCenterColor(0, 0, 0) - bg:SetAnchor(TOPLEFT, slider, TOPLEFT, 0, 4) - bg:SetAnchor(BOTTOMRIGHT, slider, BOTTOMRIGHT, 0, -4) - bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4) - - control.minText = wm:CreateControl(nil, slider, CT_LABEL) - local minText = control.minText - minText:SetFont("ZoFontGameSmall") - minText:SetAnchor(TOPLEFT, slider, BOTTOMLEFT) - minText:SetText(sliderData.min) - - control.maxText = wm:CreateControl(nil, slider, CT_LABEL) - local maxText = control.maxText - maxText:SetFont("ZoFontGameSmall") - maxText:SetAnchor(TOPRIGHT, slider, BOTTOMRIGHT) - maxText:SetText(sliderData.max) - - control.slidervalueBG = wm:CreateControlFromVirtual(nil, slider, "ZO_EditBackdrop") - control.slidervalueBG:SetDimensions(50, 16) - control.slidervalueBG:SetAnchor(TOP, slider, BOTTOM, 0, 0) - control.slidervalue = wm:CreateControlFromVirtual(nil, control.slidervalueBG, "ZO_DefaultEditForBackdrop") - local slidervalue = control.slidervalue - slidervalue:ClearAnchors() - slidervalue:SetAnchor(TOPLEFT, slidervaluebg, TOPLEFT, 3, 1) - slidervalue:SetAnchor(BOTTOMRIGHT, slidervaluebg, BOTTOMRIGHT, -3, -1) - slidervalue:SetTextType(TEXT_TYPE_NUMERIC) - slidervalue:SetFont("ZoFontGameSmall") - slidervalue:SetHandler("OnEscape", function(self) - self:LoseFocus() - control:UpdateValue() - end) - slidervalue:SetHandler("OnEnter", function(self) - self:LoseFocus() - control:UpdateValue(false, tonumber(self:GetText())) - end) - - local range = maxValue - minValue - slider:SetValueStep(sliderData.step or 1) - slider:SetHandler("OnValueChanged", function(self, value, eventReason) - if eventReason == EVENT_REASON_SOFTWARE then return end - self:SetValue(value) --do we actually need this line? - slidervalue:SetText(value) - end) - slider:SetHandler("OnSliderReleased", function(self, value) - --sliderData.setFunc(value) - control:UpdateValue(false, value) --does this work here instead? - end) - - if sliderData.warning then - control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") - control.warning:SetAnchor(RIGHT, slider, LEFT, -5, 0) - --control.warning.tooltipText = sliderData.warning - control.warning.data = {tooltipText = sliderData.warning} - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is the submenu's parent - control.data = sliderData - control.data.tooltipText = sliderData.tooltip - - if sliderData.disabled then - control.UpdateDisabled = UpdateDisabled - control:UpdateDisabled() - end - control.UpdateValue = UpdateValue - control:UpdateValue() - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control -end \ No newline at end of file + local control = LAM.util.CreateLabelAndContainerControl(parent, sliderData, controlName) + local isInputOnRight = sliderData.inputLocation == "right" + + --skipping creating the backdrop... Is this the actual slider texture? + control.slider = wm:CreateControl(nil, control.container, CT_SLIDER) + local slider = control.slider + slider:SetAnchor(TOPLEFT) + slider:SetHeight(14) + if(isInputOnRight) then + slider:SetAnchor(TOPRIGHT, nil, nil, -60) + else + slider:SetAnchor(TOPRIGHT) + end + slider:SetMouseEnabled(true) + slider:SetOrientation(ORIENTATION_HORIZONTAL) + --put nil for highlighted texture file path, and what look to be texture coords + slider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16) + local minValue = sliderData.min + local maxValue = sliderData.max + slider:SetMinMax(minValue, maxValue) + slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + slider:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + + slider.bg = wm:CreateControl(nil, slider, CT_BACKDROP) + local bg = slider.bg + bg:SetCenterColor(0, 0, 0) + bg:SetAnchor(TOPLEFT, slider, TOPLEFT, 0, 4) + bg:SetAnchor(BOTTOMRIGHT, slider, BOTTOMRIGHT, 0, -4) + bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4) + + control.minText = wm:CreateControl(nil, slider, CT_LABEL) + local minText = control.minText + minText:SetFont("ZoFontGameSmall") + minText:SetAnchor(TOPLEFT, slider, BOTTOMLEFT) + minText:SetText(sliderData.min) + + control.maxText = wm:CreateControl(nil, slider, CT_LABEL) + local maxText = control.maxText + maxText:SetFont("ZoFontGameSmall") + maxText:SetAnchor(TOPRIGHT, slider, BOTTOMRIGHT) + maxText:SetText(sliderData.max) + + control.slidervalueBG = wm:CreateControlFromVirtual(nil, slider, "ZO_EditBackdrop") + if(isInputOnRight) then + control.slidervalueBG:SetDimensions(60, 26) + control.slidervalueBG:SetAnchor(LEFT, slider, RIGHT, 5, 0) + else + control.slidervalueBG:SetDimensions(50, 16) + control.slidervalueBG:SetAnchor(TOP, slider, BOTTOM, 0, 0) + end + control.slidervalue = wm:CreateControlFromVirtual(nil, control.slidervalueBG, "ZO_DefaultEditForBackdrop") + local slidervalue = control.slidervalue + slidervalue:ClearAnchors() + slidervalue:SetAnchor(TOPLEFT, control.slidervalueBG, TOPLEFT, 3, 1) + slidervalue:SetAnchor(BOTTOMRIGHT, control.slidervalueBG, BOTTOMRIGHT, -3, -1) + slidervalue:SetTextType(TEXT_TYPE_NUMERIC) + if(isInputOnRight) then + slidervalue:SetFont("ZoFontGameLarge") + else + slidervalue:SetFont("ZoFontGameSmall") + end + + local isHandlingChange = false + local function HandleValueChanged(value) + if isHandlingChange then return end + if sliderData.decimals then + value = RoundDecimalToPlace(value, sliderData.decimals) + end + isHandlingChange = true + slider:SetValue(value) + slidervalue:SetText(value) + isHandlingChange = false + end + + slidervalue:SetHandler("OnEscape", function(self) + HandleValueChanged(sliderData.getFunc()) + self:LoseFocus() + end) + slidervalue:SetHandler("OnEnter", function(self) + self:LoseFocus() + end) + slidervalue:SetHandler("OnFocusLost", function(self) + local value = tonumber(self:GetText()) + control:UpdateValue(false, value) + end) + slidervalue:SetHandler("OnTextChanged", function(self) + local input = self:GetText() + if(#input > 1 and not input:sub(-1):match("[0-9]")) then return end + local value = tonumber(input) + if(value) then + HandleValueChanged(value) + end + end) + if(sliderData.autoSelect) then + ZO_PreHookHandler(slidervalue, "OnFocusGained", function(self) + self:SelectAll() + end) + end + + local range = maxValue - minValue + slider:SetValueStep(sliderData.step or 1) + slider:SetHandler("OnValueChanged", function(self, value, eventReason) + if eventReason == EVENT_REASON_SOFTWARE then return end + HandleValueChanged(value) + end) + slider:SetHandler("OnSliderReleased", function(self, value) + control:UpdateValue(false, value) + end) + slider:SetHandler("OnMouseWheel", function(self, value) + if(not self:GetEnabled()) then return end + local new_value = (tonumber(slidervalue:GetText()) or sliderData.min or 0) + ((sliderData.step or 1) * value) + control:UpdateValue(false, new_value) + end) + + if sliderData.warning ~= nil or sliderData.requiresReload then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, slider, LEFT, -5, 0) + control.UpdateWarning = LAM.util.UpdateWarning + control:UpdateWarning() + end + + control.UpdateValue = UpdateValue + control:UpdateValue() + + if sliderData.disabled ~= nil then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + LAM.util.RegisterForRefreshIfNeeded(control) + LAM.util.RegisterForReloadIfNeeded(control) + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/submenu.lua b/Libs/LibAddonMenu-2.0/controls/submenu.lua index d3da3eb..1766a1f 100644 --- a/Libs/LibAddonMenu-2.0/controls/submenu.lua +++ b/Libs/LibAddonMenu-2.0/controls/submenu.lua @@ -1,114 +1,108 @@ --[[submenuData = { - type = "submenu", - name = "Submenu Title", - tooltip = "My submenu tooltip", --(optional) - controls = {sliderData, buttonData} --(optional) used by LAM - reference = "MyAddonSubmenu" --(optional) unique global reference to control -} ]] - -local widgetVersion = 8 + type = "submenu", + name = "Submenu Title", -- or string id or function returning a string + tooltip = "My submenu tooltip", -- -- or string id or function returning a string (optional) + controls = {sliderData, buttonData} --(optional) used by LAM + reference = "MyAddonSubmenu" --(optional) unique global reference to control +} ]] + +local widgetVersion = 11 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("submenu", widgetVersion) then return end local wm = WINDOW_MANAGER local am = ANIMATION_MANAGER -local tinsert = table.insert - local function UpdateValue(control) - control.label:SetText(control.data.name) - if control.data.tooltip then - control.label.data = {tooltipText = control.data.tooltip} - end + control.label:SetText(LAM.util.GetStringFromValue(control.data.name)) + if control.data.tooltip then + control.label.data.tooltipText = LAM.util.GetStringFromValue(control.data.tooltip) + end end local function AnimateSubmenu(clicked) - local control = clicked:GetParent() - control.open = not control.open - - if control.open then - control.animation:PlayFromStart() - else - control.animation:PlayFromEnd() - end + local control = clicked:GetParent() + control.open = not control.open + + if control.open then + control.animation:PlayFromStart() + else + control.animation:PlayFromEnd() + end end - function LAMCreateControl.submenu(parent, submenuData, controlName) - local control = wm:CreateControl(controlName or submenuData.reference, parent.scroll or parent, CT_CONTROL) - control.panel = parent - control:SetDimensions(523, 40) - - control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") - local label = control.label - label:SetAnchor(TOPLEFT, control, TOPLEFT, 5, 5) - label:SetDimensions(520, 30) - label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) - label:SetText(submenuData.name) - label:SetMouseEnabled(true) - if submenuData.tooltip then - label.data = {tooltipText = submenuData.tooltip} - label:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - label:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) - end - - control.scroll = wm:CreateControl(nil, control, CT_SCROLL) - local scroll = control.scroll - scroll:SetParent(control) - scroll:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 10) - scroll:SetDimensionConstraints(525, 0, 525, 2500) - - control.bg = wm:CreateControl(nil, label, CT_BACKDROP) - local bg = control.bg - bg:SetAnchor(TOPLEFT, label, TOPLEFT, -5, -5) - bg:SetAnchor(BOTTOMRIGHT, scroll, BOTTOMRIGHT, -7, 0) - bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16) - bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds") - bg:SetInsets(16, 16, -16, -16) - - control.arrow = wm:CreateControl(nil, bg, CT_TEXTURE) - local arrow = control.arrow - arrow:SetDimensions(28, 28) - arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") --list_sortup for the other way - arrow:SetAnchor(TOPRIGHT, bg, TOPRIGHT, -5, 5) - - --figure out the cool animation later... - control.animation = am:CreateTimeline() - local animation = control.animation - animation:SetPlaybackType(ANIMATION_SIZE, 0) --2nd arg = loop count - - control:SetResizeToFitDescendents(true) - control.open = false - label:SetHandler("OnMouseUp", AnimateSubmenu) - animation:SetHandler("OnStop", function(self, completedPlaying) - scroll:SetResizeToFitDescendents(control.open) - if control.open then - control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortup.dds") - scroll:SetResizeToFitPadding(5, 20) - else - control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") - scroll:SetResizeToFitPadding(5, 0) - scroll:SetHeight(0) - end - end) - - --small strip at the bottom of the submenu that you can click to close it - control.btmToggle = wm:CreateControl(nil, control, CT_TEXTURE) - local btmToggle = control.btmToggle - btmToggle:SetMouseEnabled(true) - btmToggle:SetAnchor(BOTTOMLEFT, control.scroll, BOTTOMLEFT) - btmToggle:SetAnchor(BOTTOMRIGHT, control.scroll, BOTTOMRIGHT) - btmToggle:SetHeight(15) - btmToggle:SetAlpha(0) - btmToggle:SetHandler("OnMouseUp", AnimateSubmenu) - - control.data = submenuData - - control.UpdateValue = UpdateValue - - if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list - tinsert(control.panel.controlsToRefresh, control) - end - - return control + local width = parent:GetWidth() - 45 + local control = wm:CreateControl(controlName or submenuData.reference, parent.scroll or parent, CT_CONTROL) + control.panel = parent + control.data = submenuData + + control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") + local label = control.label + label:SetAnchor(TOPLEFT, control, TOPLEFT, 5, 5) + label:SetDimensions(width, 30) + label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) + label:SetText(LAM.util.GetStringFromValue(submenuData.name)) + label:SetMouseEnabled(true) + if submenuData.tooltip then + label.data = {tooltipText = LAM.util.GetStringFromValue(submenuData.tooltip)} + label:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + label:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + end + + control.scroll = wm:CreateControl(nil, control, CT_SCROLL) + local scroll = control.scroll + scroll:SetParent(control) + scroll:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 10) + scroll:SetDimensionConstraints(width + 5, 0, width + 5, 0) + + control.bg = wm:CreateControl(nil, label, CT_BACKDROP) + local bg = control.bg + bg:SetAnchor(TOPLEFT, label, TOPLEFT, -5, -5) + bg:SetAnchor(BOTTOMRIGHT, scroll, BOTTOMRIGHT, -7, 0) + bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16) + bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds") + bg:SetInsets(16, 16, -16, -16) + + control.arrow = wm:CreateControl(nil, bg, CT_TEXTURE) + local arrow = control.arrow + arrow:SetDimensions(28, 28) + arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") --list_sortup for the other way + arrow:SetAnchor(TOPRIGHT, bg, TOPRIGHT, -5, 5) + + --figure out the cool animation later... + control.animation = am:CreateTimeline() + local animation = control.animation + animation:SetPlaybackType(ANIMATION_SIZE, 0) --2nd arg = loop count + + control:SetResizeToFitDescendents(true) + control.open = false + label:SetHandler("OnMouseUp", AnimateSubmenu) + animation:SetHandler("OnStop", function(self, completedPlaying) + scroll:SetResizeToFitDescendents(control.open) + if control.open then + control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortup.dds") + scroll:SetResizeToFitPadding(5, 20) + else + control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") + scroll:SetResizeToFitPadding(5, 0) + scroll:SetHeight(0) + end + end) + + --small strip at the bottom of the submenu that you can click to close it + control.btmToggle = wm:CreateControl(nil, control, CT_TEXTURE) + local btmToggle = control.btmToggle + btmToggle:SetMouseEnabled(true) + btmToggle:SetAnchor(BOTTOMLEFT, control.scroll, BOTTOMLEFT) + btmToggle:SetAnchor(BOTTOMRIGHT, control.scroll, BOTTOMRIGHT) + btmToggle:SetHeight(15) + btmToggle:SetAlpha(0) + btmToggle:SetHandler("OnMouseUp", AnimateSubmenu) + + control.UpdateValue = UpdateValue + + LAM.util.RegisterForRefreshIfNeeded(control) + + return control end diff --git a/Libs/LibAddonMenu-2.0/controls/texture.lua b/Libs/LibAddonMenu-2.0/controls/texture.lua index 26b0353..29dda7c 100644 --- a/Libs/LibAddonMenu-2.0/controls/texture.lua +++ b/Libs/LibAddonMenu-2.0/controls/texture.lua @@ -1,50 +1,45 @@ --[[textureData = { - type = "texture", - image = "file/path.dds", - imageWidth = 64, --max of 250 for half width, 510 for full - imageHeight = 32, --max of 100 - tooltip = "Image's tooltip text.", --(optional) - width = "full", --or "half" (optional) - reference = "MyAddonTexture" --(optional) unique global reference to control -} ]] + type = "texture", + image = "file/path.dds", + imageWidth = 64, --max of 250 for half width, 510 for full + imageHeight = 32, --max of 100 + tooltip = "Image's tooltip text.", -- or string id or function returning a string (optional) + width = "full", --or "half" (optional) + reference = "MyAddonTexture" --(optional) unique global reference to control +} ]] --add texture coords support? -local widgetVersion = 6 +local widgetVersion = 9 local LAM = LibStub("LibAddonMenu-2.0") if not LAM:RegisterWidget("texture", widgetVersion) then return end local wm = WINDOW_MANAGER +local MIN_HEIGHT = 26 function LAMCreateControl.texture(parent, textureData, controlName) - local control = wm:CreateControl(controlName or textureData.reference, parent.scroll or parent, CT_CONTROL) - control:SetResizeToFitDescendents(true) - - local isHalfWidth = textureData.width == "half" - if isHalfWidth then - control:SetDimensionConstraints(250, 55, 250, 100) - control:SetDimensions(250, 55) - else - control:SetDimensionConstraints(510, 30, 510, 100) - control:SetDimensions(510, 30) - end - - control.texture = wm:CreateControl(nil, control, CT_TEXTURE) - local texture = control.texture - texture:SetAnchor(CENTER) - texture:SetDimensions(textureData.imageWidth, textureData.imageHeight) - texture:SetTexture(textureData.image) - - if textureData.tooltip then - texture:SetMouseEnabled(true) - --texture.tooltipText = textureData.tooltip - texture.data = {tooltipText = textureData.tooltip} - texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) - texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseExit) - end - - control.panel = parent.panel or parent --if this is in a submenu, panel is its parent - control.data = textureData - - return control -end \ No newline at end of file + local control = LAM.util.CreateBaseControl(parent, textureData, controlName) + local width = control:GetWidth() + control:SetResizeToFitDescendents(true) + + if control.isHalfWidth then --note these restrictions + control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4) + else + control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4) + end + + control.texture = wm:CreateControl(nil, control, CT_TEXTURE) + local texture = control.texture + texture:SetAnchor(CENTER) + texture:SetDimensions(textureData.imageWidth, textureData.imageHeight) + texture:SetTexture(textureData.image) + + if textureData.tooltip then + texture:SetMouseEnabled(true) + texture.data = {tooltipText = LAM.util.GetStringFromValue(textureData.tooltip)} + texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + texture:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + end + + return control +end diff --git a/Libs/LibStub/LibStub.lua b/Libs/LibStub/LibStub.lua index 879d132..0e6bf67 100644 --- a/Libs/LibStub/LibStub.lua +++ b/Libs/LibStub/LibStub.lua @@ -3,7 +3,7 @@ -- LibStub developed for World of Warcraft by above members of the WowAce community. -- Ported to Elder Scrolls Online by Seerah -local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! +local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 4 local LibStub = _G[LIBSTUB_MAJOR] local strformat = string.format @@ -14,7 +14,9 @@ if not LibStub or LibStub.minor < LIBSTUB_MINOR then function LibStub:NewLibrary(major, minor) assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") - minor = assert(tonumber(zo_strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + if type(minor) ~= "number" then + minor = assert(tonumber(zo_strmatch(minor, "%d+%.?%d*")), "Minor version must either be a number or contain a number.") + end local oldminor = self.minors[major] if oldminor and oldminor >= minor then return nil end @@ -32,3 +34,5 @@ if not LibStub or LibStub.minor < LIBSTUB_MINOR then function LibStub:IterateLibraries() return pairs(self.libs) end setmetatable(LibStub, { __call = LibStub.GetLibrary }) end + +LibStub.SILENT = true \ No newline at end of file