From d86ded8d53d02d0691c192b8491a805bffbb4493 Mon Sep 17 00:00:00 2001 From: Eyal Solnik Date: Sat, 14 May 2016 10:58:40 +0300 Subject: [PATCH] Update Libra to v37 --- DefaultCastingBar.toc | 5 +- Features/Icon.lua | 2 +- Libs/Libra/AceDBControls.lua | 352 +++++++++++++++++++++++++ Libs/Libra/Addon.lua | 214 +++++++++++++++ Libs/Libra/Button.lua | 27 ++ Libs/Libra/Core.lua | 65 +++++ Libs/Libra/Dropdown.lua | 553 +++++++++++++++++++++++++++++++++++++++ Libs/Libra/Editbox.lua | 12 + Libs/Libra/Libra.xml | 16 ++ Libs/Libra/OptionsFrame.lua | 475 +++++++++++++++++++++++++++++++++ Libs/Libra/ScrollFrame.lua | 137 ++++++++++ Libs/Libra/Slider.lua | 54 ++++ Libs/Libra/UIPanel.lua | 102 ++++++++ Libs/Libra/Utils.lua | 45 ++++ libs/Libra-36/AceDBControls.lua | 352 ------------------------- libs/Libra-36/Addon.lua | 193 -------------- libs/Libra-36/Button.lua | 27 -- libs/Libra-36/Core.lua | 65 ----- libs/Libra-36/Dropdown.lua | 553 --------------------------------------- libs/Libra-36/Editbox.lua | 12 - libs/Libra-36/Libra.xml | 16 -- libs/Libra-36/OptionsFrame.lua | 475 --------------------------------- libs/Libra-36/ScrollFrame.lua | 137 ---------- libs/Libra-36/Slider.lua | 54 ---- libs/Libra-36/UIPanel.lua | 102 -------- libs/Libra-36/Utils.lua | 45 ---- 26 files changed, 2054 insertions(+), 2036 deletions(-) create mode 100644 Libs/Libra/AceDBControls.lua create mode 100644 Libs/Libra/Addon.lua create mode 100644 Libs/Libra/Button.lua create mode 100644 Libs/Libra/Core.lua create mode 100644 Libs/Libra/Dropdown.lua create mode 100644 Libs/Libra/Editbox.lua create mode 100644 Libs/Libra/Libra.xml create mode 100644 Libs/Libra/OptionsFrame.lua create mode 100644 Libs/Libra/ScrollFrame.lua create mode 100644 Libs/Libra/Slider.lua create mode 100644 Libs/Libra/UIPanel.lua create mode 100644 Libs/Libra/Utils.lua delete mode 100644 libs/Libra-36/AceDBControls.lua delete mode 100644 libs/Libra-36/Addon.lua delete mode 100644 libs/Libra-36/Button.lua delete mode 100644 libs/Libra-36/Core.lua delete mode 100644 libs/Libra-36/Dropdown.lua delete mode 100644 libs/Libra-36/Editbox.lua delete mode 100644 libs/Libra-36/Libra.xml delete mode 100644 libs/Libra-36/OptionsFrame.lua delete mode 100644 libs/Libra-36/ScrollFrame.lua delete mode 100644 libs/Libra-36/Slider.lua delete mode 100644 libs/Libra-36/UIPanel.lua delete mode 100644 libs/Libra-36/Utils.lua diff --git a/DefaultCastingBar.toc b/DefaultCastingBar.toc index 731afe0..10a327a 100644 --- a/DefaultCastingBar.toc +++ b/DefaultCastingBar.toc @@ -16,16 +16,13 @@ Libs\LibStub.lua Libs\CallbackHandler-1.0.lua Libs\AceLocale-3.0\AceLocale-3.0.xml Libs\AceDB-3.0\AceDB-3.0.xml -Libs\Libra-36\Libra.xml +Libs\Libra\Libra.xml Locale\enUS.lua Core.lua - Features\Timer.lua Features\Icon.lua Features\DragFrame.xml - CastingBar.lua - Options.lua \ No newline at end of file diff --git a/Features/Icon.lua b/Features/Icon.lua index d10058c..ba458b4 100644 --- a/Features/Icon.lua +++ b/Features/Icon.lua @@ -29,7 +29,7 @@ function icon:SetPosition(parent) -- NYI end -function timer:SetVisibility(parent) +function icon:SetVisibility(parent) if not self:IsHidden() then self:SetPosition(parent) self:Show() diff --git a/Libs/Libra/AceDBControls.lua b/Libs/Libra/AceDBControls.lua new file mode 100644 index 0000000..d0cfc8c --- /dev/null +++ b/Libs/Libra/AceDBControls.lua @@ -0,0 +1,352 @@ +local Libra = LibStub("Libra") +local Type, Version = "AceDBControls", 4 +if Libra:GetModuleVersion(Type) >= Version then return end + +Libra.modules[Type] = Libra.modules[Type] or {} + +local AceDBControls = Libra.modules[Type] +AceDBControls.Prototype = AceDBControls.Prototype or CreateFrame("Frame") + +local Prototype = AceDBControls.Prototype +local mt = {__index = Prototype} + +local L = { + default = "Default", + reset = "Reset profile", + new = "Create new profile", + choose = "Active profile", + copy = "Copy from", + delete = "Delete a profile", + delete_confirm = "Are you sure you want to delete the selected profile?", + profiles = "Profiles", + + dual_profile = "Dual profile", + enabled = "Enable dual profile", +} + +local LOCALE = GetLocale() +if LOCALE == "deDE" then + L["default"] = "Standard" + L["reset"] = "Profil zur\195\188cksetzen" + L["new"] = "Neu" + L["choose"] = "Vorhandene Profile" + L["copy"] = "Kopieren von..." + L["delete"] = "Profil l\195\182schen" + L["delete_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?" + L["profiles"] = "Profile" + + L["dual_profile"] = "Duales Profil" + L["enabled"] = "Aktiviere Duale Profile" +elseif LOCALE == "frFR" then + L["default"] = "D\195\169faut" + L["reset"] = "R\195\169initialiser le profil" + L["new"] = "Nouveau" + L["choose"] = "Profils existants" + L["copy"] = "Copier \195\160 partir de" + L["delete"] = "Supprimer un profil" + L["delete_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?" + L["profiles"] = "Profils" + + L["dual_profile"] = 'Second profil' + L["enabled"] = 'Activez le second profil' +elseif LOCALE == "koKR" then + L["default"] = "기본값" + L["reset"] = "프로필 초기화" + L["new"] = "새로운 프로필" + L["choose"] = "프로필 선택" + L["copy"] = "복사" + L["delete"] = "프로필 삭제" + L["delete_confirm"] = "정말로 선택한 프로필의 삭제를 원하십니까?" + L["profiles"] = "프로필" + + L["dual_profile"] = "이중 프로필" + L["enabled"] = "이중 프로필 사용" +elseif LOCALE == "esES" or LOCALE == "esMX" then + L["default"] = "Por defecto" + L["reset"] = "Reiniciar Perfil" + L["new"] = "Nuevo" + L["choose"] = "Perfiles existentes" + L["copy"] = "Copiar de" + L["delete"] = "Borrar un Perfil" + L["delete_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?" + L["profiles"] = "Perfiles" +elseif LOCALE == "zhTW" then + L["default"] = "預設" + L["reset"] = "重置設定檔" + L["new"] = "新建" + L["choose"] = "現有的設定檔" + L["copy"] = "複製自" + L["delete"] = "刪除一個設定檔" + L["delete_confirm"] = "你確定要刪除所選擇的設定檔嗎?" + L["profiles"] = "設定檔" +elseif LOCALE == "zhCN" then + L["default"] = "默认" + L["reset"] = "重置配置文件" + L["choose_desc"] = "你可以通过在文本框内输入一个名字创立一个新的配置文件,也可以选择一个已经存在的配置文件。" + L["new"] = "新建" + L["choose"] = "现有的配置文件" + L["copy"] = "复制自" + L["delete"] = "删除一个配置文件" + L["delete_confirm"] = "你确定要删除所选择的配置文件么?" + L["profiles"] = "配置文件" + + L["dual_profile"] = "双重配置文件" + L["enabled"] = "开启双重配置文件" +elseif LOCALE == "ruRU" then + L["default"] = "По умолчанию" + L["reset"] = "Сброс профиля" + L["new"] = "Новый" + L["choose"] = "Существующие профили" + L["copy"] = "Скопировать из" + L["delete"] = "Удалить профиль" + L["delete_confirm"] = "Вы уверены, что вы хотите удалить выбранный профиль?" + L["profiles"] = "Профили" + + L["dual_profile"] = "Второй профиль" + L["enabled"] = "Включить двойной профиль" +end + +local defaultProfiles = {} + +local function profileSort(a, b) + return a.value < b.value +end + +local tempProfiles = {} + +local function getProfiles(db, common, nocurrent) + local profiles = {} + + -- copy existing profiles into the table + local currentProfile = db:GetCurrentProfile() + for _, v in ipairs(db:GetProfiles(tempProfiles)) do + if not (nocurrent and v == currentProfile) then + profiles[v] = v + end + end + + -- add our default profiles to choose from (or rename existing profiles) + for k, v in pairs(defaultProfiles) do + if (common or profiles[k]) and not (nocurrent and k == currentProfile) then + profiles[k] = v + end + end + + local sortProfiles = {} + for k, v in pairs(profiles) do + tinsert(sortProfiles, {text = v, value = k}) + end + sort(sortProfiles, profileSort) + + return sortProfiles +end + +local function dropdownOnClick(self, profile, func) + func(self.owner.db, profile, self.owner) +end + +local function initializeDropdown(self, level, menuList) + for i, v in ipairs(getProfiles(self.db, self.common, self.nocurrent)) do + local info = UIDropDownMenu_CreateInfo() + info.text = v.text + info.func = dropdownOnClick + info.arg1 = v.value + info.arg2 = self.func + info.checked = not self.nocurrent and (v.value == self.getCurrent(self.db)) + info.notCheckable = self.nocurrent + self:AddButton(info) + end +end + +local function createDropdown(parent) + local dropdown = Libra:CreateDropdown("Frame", parent) + dropdown:SetWidth(160) + dropdown:JustifyText("LEFT") + dropdown.initialize = initializeDropdown + return dropdown +end + +local function menuButton_OnClick(self) + self.menu:Toggle() +end + +local function createMenuButton(parent) + local button = Libra:CreateButton(parent) + button:SetScript("OnClick", menuButton_OnClick) + button.arrow:Show() + button:SetWidth(88) + + local menu = Libra:CreateDropdown("Menu") + menu.relativeTo = button + menu.initialize = initializeDropdown + menu.nocurrent = true + menu.db = parent.db + button.menu = menu + + return button +end + +local createProfileScripts = { + OnEnterPressed = function(self) + self.db:SetProfile(self:GetText()) + self:ClearFocus() + end, + OnEditFocusGained = function(self) + self:SetTextColor(1, 1, 1) + end, + OnEditFocusLost = function(self) + self:SetTextColor(0.5, 0.5, 0.5) + self:SetText("") + end, +} + +local function enableDualProfileOnClick(self) + local checked = self:GetChecked() + self.db:SetDualSpecEnabled(checked) + self.dualProfile:SetEnabled(checked) +end + +local function dualProfileOnClick(db, profile, frame) + db:SetDualSpecProfile(profile) + frame:SetText(profile) +end + +local function deleteProfile(db, profile) + StaticPopup_Show("DELETE_PROFILE", nil, nil, {db = db, profile = profile}) +end + +StaticPopupDialogs["DELETE_PROFILE"] = { + text = L.delete_confirm, + button1 = YES, + button2 = NO, + OnAccept = function(self, data) + data.db:DeleteProfile(data.profile) + end, +} + +local function constructor(self, db, parent) + local frame = setmetatable(CreateFrame("Frame", nil, parent), mt) + frame:SetSize(192, 192) + frame.db = db + + db.RegisterCallback(frame, "OnNewProfile") + db.RegisterCallback(frame, "OnProfileChanged") + db.RegisterCallback(frame, "OnProfileDeleted") + + local keys = db.keys + defaultProfiles["Default"] = L.default + defaultProfiles[keys.char] = keys.char + defaultProfiles[keys.realm] = keys.realm + defaultProfiles[keys.class] = UnitClass("player") + + local objects = {} + + do -- create the controls + local choose = createDropdown(frame) + choose:SetPoint("TOP") + choose.label:SetText(L.choose) + choose.func = db.SetProfile + choose.getCurrent = db.GetCurrentProfile + choose.common = true + objects.choose = choose + + local newProfile = Libra:CreateEditbox(frame) + newProfile:SetPoint("TOPLEFT", choose, "BOTTOMLEFT", 24, -8) + newProfile:SetPoint("TOPRIGHT", choose, "BOTTOMRIGHT", -17, -8) + newProfile:SetTextColor(0.5, 0.5, 0.5) + newProfile:SetScript("OnEscapePressed", newProfile.ClearFocus) + for script, handler in pairs(createProfileScripts) do + newProfile:SetScript(script, handler) + end + objects.newProfile = newProfile + + local label = newProfile:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + label:SetHeight(18) + label:SetPoint("BOTTOMLEFT", newProfile, "TOPLEFT", -5, -2) + label:SetPoint("BOTTOMRIGHT", newProfile, "TOPRIGHT", 0, -2) + label:SetJustifyH("LEFT") + label:SetText(L.new) + + local copy = createMenuButton(frame) + copy:SetPoint("TOPLEFT", newProfile, "BOTTOMLEFT", -9, -4) + copy:SetText("Copy from") + copy.menu.func = db.CopyProfile + objects.copy = copy + + local delete = createMenuButton(frame) + delete:SetPoint("TOPRIGHT", newProfile, "BOTTOMRIGHT", 4, -4) + delete:SetText("Delete") + delete.menu.func = deleteProfile + objects.delete = delete + + local reset = Libra:CreateButton(frame) + reset:SetPoint("TOPLEFT", copy, "BOTTOM", 0, -4) + reset:SetPoint("TOPRIGHT", delete, "BOTTOM", 0, -4) + reset:SetScript("OnClick", function(self) self.db:ResetProfile() end) + reset:SetText(L.reset) + objects.reset = reset + + local hasDualProfile = db:GetNamespace("LibDualSpec-1.0", true) + if hasDualProfile then + local isDualSpecEnabled = db:IsDualSpecEnabled() + + local dualProfile = createDropdown(frame) + dualProfile:SetPoint("TOP", reset, "BOTTOM", 0, -28) + dualProfile:SetEnabled(isDualSpecEnabled) + dualProfile:SetText(db:GetDualSpecProfile()) + dualProfile.func = dualProfileOnClick + dualProfile.getCurrent = db.GetDualSpecProfile + dualProfile.common = true + objects.dualProfile = dualProfile + + local enabled = CreateFrame("CheckButton", nil, frame, "OptionsBaseCheckButtonTemplate") + enabled:SetPoint("BOTTOMLEFT", dualProfile, "TOPLEFT", 16, 0) + enabled:SetPushedTextOffset(0, 0) + enabled:SetScript("OnClick", enableDualProfileOnClick) + enabled:SetChecked(isDualSpecEnabled) + enabled.tooltipText = L.enable_desc + enabled.dualProfile = dualProfile + objects.dualEnabled = enabled + + local text = enabled:CreateFontString(nil, nil, "GameFontHighlight") + text:SetPoint("LEFT", enabled, "RIGHT", 0, 1) + text:SetText(L.enabled) + + frame.hasDualProfile = true + end + end + + for k, object in pairs(objects) do + object.db = db + frame[k] = object + end + + frame.choose:SetText(db:GetCurrentProfile()) + + frame:CheckProfiles() + + return frame +end + +function Prototype:CheckProfiles() + local hasProfiles = not self:HasNoProfiles() + self.copy:SetEnabled(hasProfiles) + self.delete:SetEnabled(hasProfiles) +end + +function Prototype:HasNoProfiles() + return next(getProfiles(self.db, nil, true)) == nil +end + +function Prototype:OnProfileChanged(event, db, profile) + self.choose:SetText(profile) + self:CheckProfiles() + if self.hasDualProfile then + self.dualProfile:SetText(db:GetDualSpecProfile()) + end +end + +Prototype.OnNewProfile = Prototype.CheckProfiles +Prototype.OnProfileDeleted = Prototype.CheckProfiles + +Libra:RegisterModule(Type, Version, constructor) \ No newline at end of file diff --git a/Libs/Libra/Addon.lua b/Libs/Libra/Addon.lua new file mode 100644 index 0000000..3746933 --- /dev/null +++ b/Libs/Libra/Addon.lua @@ -0,0 +1,214 @@ +local Libra = LibStub("Libra") +local Type, Version = "Addon", 4 +if Libra:GetModuleVersion(Type) >= Version then return end + +Libra.modules[Type] = Libra.modules[Type] or {} + +local object = Libra.modules[Type] +object.frame = object.frame or CreateFrame("Frame") +object.addons = object.addons or {} +object.events = object.events or {} +object.onUpdates = object.onUpdates or {} +object.defaults = object.defaults or {} + +local function safecall(object, method, ...) + if object[method] then + object[method](object, ...) + end +end + +local function removeDefaults(tbl, defaults) + for k, v in pairs(defaults) do + if type(v) == "table" then + removeDefaults(tbl[k], v) + if not next(tbl[k]) then + tbl[k] = nil + end + elseif v == tbl[k] then + tbl[k] = nil + end + end +end + +object.frame:RegisterEvent("ADDON_LOADED") +object.frame:RegisterEvent("PLAYER_LOGOUT") +object.frame:SetScript("OnEvent", function(self, event, ...) + if event == "ADDON_LOADED" then + local addon = object.addons[...] + if addon then + safecall(addon, "OnInitialize") + addon.OnInitialize = nil + for i, module in addon:IterateModules() do + safecall(module, "OnInitialize") + module.OnInitialize = nil + end + end + end + if event == "PLAYER_LOGOUT" then + for tbl, defaults in pairs(object.defaults) do + removeDefaults(tbl, defaults) + end + end + for module, eventHandler in pairs(object.events[event]) do + eventHandler(module, ...) + end +end) + +local function onUpdate(self, elapsed) + for module, update in pairs(object.onUpdates) do + update(module, elapsed) + end +end + +local mt = { + __index = function(table, key) + local newTable = {} + table[key] = newTable + return newTable + end +} + +setmetatable(object.events, mt) + +local AddonPrototype = {} +local ObjectPrototype = {} + +local function AddonEmbed(target) + for k, v in pairs(AddonPrototype) do + target[k] = v + end +end + +local function ObjectEmbed(target) + for k, v in pairs(ObjectPrototype) do + target[k] = v + end +end + +function Libra:NewAddon(name, addonObject) + if object.addons[name] then + error(format("Addon '%s' already exists.", name), 2) + end + + local addon = addonObject or {} + addon.name = name + addon.modules = {} + addon.messages = setmetatable({}, mt) + AddonEmbed(addon) + ObjectEmbed(addon) + object.addons[name] = addon + return addon, name +end + +function Libra:GetAddon(name) + return object.addons[name] +end + +function AddonPrototype:NewModule(name, table) + if self:GetModule(name) then + error(format("Module '%s' already exists in %s.", name, self.name), 2) + end + + local module = table or {} + ObjectEmbed(module) + module.name = name + module.messages = setmetatable({}, mt) + tinsert(self.modules, module) + safecall(self, "OnModuleCreated", name, module) + return module, name +end + +function AddonPrototype:GetModule(name) + for i, module in self:IterateModules() do + if module.name == name then + return module + end + end +end + +function AddonPrototype:IterateModules() + return next, self.modules +end + +local function copyDefaults(src, dst) + if not src then return {} end + if not dst then dst = {} end + for k, v in pairs(src) do + if type(v) == "table" then + dst[k] = copyDefaults(v, dst[k]) + elseif type(v) ~= type(dst[k]) then + dst[k] = v + end + end + return dst +end + +function AddonPrototype:CreateDB(global, defaults) + local db = _G[global] + db = copyDefaults(defaults, db) + _G[global] = db + object.defaults[db] = defaults + return db +end + +function ObjectPrototype:RegisterEvent(event, handler) + if not next(object.events[event]) then + object.frame:RegisterEvent(event) + end + if type(handler) ~= "function" then + handler = self[handler] or self[event] + end + object.events[event][self] = handler +end + +function ObjectPrototype:UnregisterEvent(event) + object.events[event][self] = nil + if not next(object.events[event]) then + object.frame:UnregisterEvent(event) + end +end + +function ObjectPrototype:SendMessage(message, ...) + for module, messageHandler in pairs(self.messages[message]) do + messageHandler(module, ...) + end +end + +function ObjectPrototype:RegisterMessage(module, message, handler) + if type(handler) ~= "function" then + handler = self[handler] or self[message] + end + self.messages[message][module] = handler +end + +function ObjectPrototype:UnregisterMessage(module, message) + self.messages[message][module] = nil +end + +function ObjectPrototype:SetOnUpdate(handler) + if not next(object.onUpdates) then + object.frame:SetScript("OnUpdate", onUpdate) + end + if type(handler) ~= "function" then + handler = self[handler] + end + object.onUpdates[self] = handler +end + +function ObjectPrototype:RemoveOnUpdate() + object.onUpdates[self] = nil + if not next(object.onUpdates) then + object.frame:SetScript("OnUpdate", nil) + end +end + +-- upgrade embeds +for k, v in pairs(object.addons) do + AddonEmbed(v) + ObjectEmbed(v) + for i, v in v:IterateModules() do + ObjectEmbed(v) + end +end + +Libra:RegisterModule(Type, Version) \ No newline at end of file diff --git a/Libs/Libra/Button.lua b/Libs/Libra/Button.lua new file mode 100644 index 0000000..9ee43a7 --- /dev/null +++ b/Libs/Libra/Button.lua @@ -0,0 +1,27 @@ +local Libra = LibStub("Libra") +local Type, Version = "Button", 2 +if Libra:GetModuleVersion(Type) >= Version then return end + +local function onEnable(self) + self.arrow:SetDesaturated(false) +end + +local function onDisable(self) + self.arrow:SetDesaturated(true) +end + +local function constructor(self, parent) + local button = CreateFrame("Button", nil, parent, "UIMenuButtonStretchTemplate") + button:SetHeight(23) + button:SetScript("OnEnable", onEnable) + button:SetScript("OnDisable", onDisable) + button.arrow = button:CreateTexture() + button.arrow:SetSize(10, 12) + button.arrow:SetPoint("RIGHT", -5, 0) + button.arrow:SetTexture([[Interface\ChatFrame\ChatFrameExpandArrow]]) + button.arrow:Hide() + button.Icon = button.arrow + return button +end + +Libra:RegisterModule(Type, Version, constructor) \ No newline at end of file diff --git a/Libs/Libra/Core.lua b/Libs/Libra/Core.lua new file mode 100644 index 0000000..70f2943 --- /dev/null +++ b/Libs/Libra/Core.lua @@ -0,0 +1,65 @@ +local MAJOR, MINOR = "Libra", 2 +local lib = LibStub:NewLibrary(MAJOR, MINOR) + +if not lib then return end + +lib.modules = lib.modules or {} +lib.moduleVersions = lib.moduleVersions or {} +lib.embeds = lib.widgetEmbeds or lib.embeds or {} +lib.methods = lib.methods or {} +lib.controls = lib.widgets or lib.controls or {} +lib.namespaces = lib.namespaces or {} + +function lib:RegisterModule(object, version, constructor) + self.moduleVersions[object] = version + if constructor then + self.controls[object] = constructor + self["Create"..object] = constructor + for k in pairs(self.embeds) do + k["Create"..object] = constructor + end + end +end + +function lib:GetModuleVersion(module) + return self.moduleVersions[module] or 0 +end + +function lib:RegisterMethods(tbl) + for k, v in pairs(tbl) do + for target in pairs(self.embeds) do + target[k] = v + end + self.methods[k] = v + end +end + +function lib:GetWidgetName(name) + name = name or "Generic" + local namespace = self.namespaces[name] + if not namespace then + local n = 0 + namespace = function() + n = n + 1 + return format("%sLibraWidget%d", name, n) + end + self.namespaces[name] = namespace + end + return namespace() +end + +function lib:Embed(target) + for k, v in pairs(self.methods) do + target[k] = v + end + for k, v in pairs(self.controls) do + target["Create"..k] = v + end + self.embeds[target] = true +end + +lib.EmbedWidgets = lib.Embed + +for k in pairs(lib.embeds) do + lib:Embed(k) +end \ No newline at end of file diff --git a/Libs/Libra/Dropdown.lua b/Libs/Libra/Dropdown.lua new file mode 100644 index 0000000..33d12b4 --- /dev/null +++ b/Libs/Libra/Dropdown.lua @@ -0,0 +1,553 @@ +local Libra = LibStub("Libra") +local Type, Version = "Dropdown", 13 +if Libra:GetModuleVersion(Type) >= Version then return end + +Libra.modules[Type] = Libra.modules[Type] or {} + +local Dropdown = Libra.modules[Type] +Dropdown.Prototype = Dropdown.Prototype or CreateFrame("Frame") +Dropdown.MenuPrototype = Dropdown.MenuPrototype or setmetatable({}, {__index = Dropdown.Prototype}) +Dropdown.FramePrototype = Dropdown.FramePrototype or setmetatable({}, {__index = Dropdown.Prototype}) +Dropdown.objects = Dropdown.objects or {} +Dropdown.listData = Dropdown.listData or {} +Dropdown.hookedLists = Dropdown.hookedLists or {} +Dropdown.hookedButtons = Dropdown.hookedButtons or {} +Dropdown.secureButtons = Dropdown.secureButtons or {} +Dropdown.secureBin = Dropdown.secureBin or {} + +local menuMT = {__index = Dropdown.MenuPrototype} +local frameMT = {__index = Dropdown.FramePrototype} + +local Prototype = Dropdown.Prototype +local MenuPrototype = Dropdown.MenuPrototype +local FramePrototype = Dropdown.FramePrototype +local objects = Dropdown.objects +local listData = Dropdown.listData + +local function setHeight() end + +local function constructor(self, type, parent, name) + local dropdown + if type == "Menu" then + -- adding a SetHeight dummy lets us use a simple table instead of a frame, no side effects noticed so far + dropdown = setmetatable({}, menuMT) + dropdown:SetDisplayMode("MENU") + dropdown.SetHeight = setHeight + dropdown.xOffset = 0 + dropdown.yOffset = 0 + end + if type == "Frame" then + name = name or Libra:GetWidgetName(self.name) + dropdown = setmetatable(CreateFrame("Frame", name, parent, "UIDropDownMenuTemplate"), frameMT) + dropdown:SetWidth(115) + dropdown.label = dropdown:CreateFontString(name.."Label", "BACKGROUND", "GameFontNormalSmall") + dropdown.label:SetPoint("BOTTOMLEFT", dropdown, "TOPLEFT", 16, 3) + end + + objects[dropdown] = true + + return dropdown +end + + +local methods = { + Refresh = UIDropDownMenu_Refresh, +} + +for k, v in pairs(methods) do + Prototype[k] = v +end + +function Prototype:AddButton(info, level) + info.owner = self + if info.icon and not info.iconOnly then + -- hack to properly increase button width for icon when .iconOnly is not set + info.padding = (info.padding or 0) + 10 + end + self.displayMode = self._displayMode + self.selectedName = self._selectedName + self.selectedValue = self._selectedValue + self.selectedID = self._selectedID + UIDropDownMenu_AddButton(info, level) + self.displayMode = nil + self.selectedName = nil + self.selectedValue = nil + self.selectedID = nil +end + +function Prototype:ToggleMenu(value, anchorName, xOffset, yOffset, menuList, level, ...) + ToggleDropDownMenu(level, value, self, anchorName, xOffset, yOffset, menuList, ...) +end + +function Prototype:RebuildMenu(level) + level = level or 1 + if self:IsMenuShown(level) then + -- hiding a menu will also hide all deeper level menus, so we'll check which ones are open and restore them afterwards + local maxLevel + for i = level, UIDROPDOWNMENU_MENU_LEVEL do + if _G["DropDownList"..i]:IsShown() then + maxLevel = i + else + break + end + end + self:HideMenu(level) + for i = level, maxLevel do + local listData = listData[i] + -- set .rebuild to indicate that we don't want to reset the scroll offset on the next ToggleDropDownMenu + self.rebuild = true + self:ToggleMenu(listData.value, listData.anchorName, listData.xOffset, listData.yOffset, listData.menuList, i, listData.button, listData.autoHideDelay) + end + end +end + +function Prototype:HideMenu(level) + if UIDropDownMenu_GetCurrentDropDown() == self then + HideDropDownMenu(level) + end +end + +function Prototype:CloseMenus(level) + if UIDropDownMenu_GetCurrentDropDown() == self then + CloseDropDownMenus(level) + end +end + +function Prototype:IsMenuShown(level) + level = level or 1 + local listFrame = _G["DropDownList"..level] + return UIDropDownMenu_GetCurrentDropDown() == self and listFrame and listFrame:IsShown() +end + +function Prototype:SetSelectedName(name, useValue) + self._selectedName = name + self._selectedValue = nil + self._selectedID = nil + self.selectedName = name + self:Refresh(useValue) + self.selectedName = nil +end + +function Prototype:SetSelectedValue(value, useValue) + self._selectedValue = value + self._selectedName = nil + self._selectedID = nil + self.selectedValue = value + self:Refresh(useValue) + self.selectedValue = nil +end + +function Prototype:SetSelectedID(id, useValue) + self._selectedID = id + self._selectedName = nil + self._selectedValue = nil + self.selectedID = id + self:Refresh(useValue) + self.selectedID = nil +end + +function Prototype:GetSelectedName() + return self._selectedName +end + +function Prototype:GetSelectedValue() + return self._selectedValue +end + +function Prototype:GetSelectedID() + if self._selectedID then + return self._selectedID + else + -- If no explicit selectedID then try to send the id of a selected value or name + for i=1, UIDROPDOWNMENU_MAXBUTTONS do + local button = _G["DropDownList"..UIDROPDOWNMENU_MENU_LEVEL.."Button"..i] + -- See if checked or not + if self:GetSelectedName() then + if button:GetText() == self:GetSelectedName() then + return i + end + elseif self:GetSelectedValue() then + if button.value == self:GetSelectedValue() then + return i + end + end + end + end +end + +function Prototype:SetDisplayMode(mode) + self._displayMode = mode +end + + +local menuMethods = { + Toggle = Prototype.ToggleMenu, + Rebuild = Prototype.RebuildMenu, + Hide = Prototype.HideMenu, + Close = Prototype.CloseMenus, + IsShown = Prototype.IsMenuShown, +} + +for k, v in pairs(menuMethods) do + MenuPrototype[k] = v +end + + +local frameMethods = { + Enable = UIDropDownMenu_EnableDropDown, + Disable = UIDropDownMenu_DisableDropDown, + IsEnabled = UIDropDownMenu_IsEnabled, + JustifyText = UIDropDownMenu_JustifyText, + SetButtonWidth = UIDropDownMenu_SetButtonWidth, + SetText = UIDropDownMenu_SetText, + GetText = UIDropDownMenu_GetText, +} + +for k, v in pairs(frameMethods) do + FramePrototype[k] = v +end + +local setWidth = Prototype.SetWidth + +function FramePrototype:SetWidth(width, padding) + _G[self:GetName().."Middle"]:SetWidth(width) + local defaultPadding = 25 + if padding then + setWidth(self, width + padding) + _G[self:GetName().."Text"]:SetWidth(width) + else + setWidth(self, width + defaultPadding + defaultPadding) + _G[self:GetName().."Text"]:SetWidth(width - defaultPadding) + end + self.noResize = 1 +end + +function FramePrototype:SetLabel(text) + self.label:SetText(text) +end + +function FramePrototype:SetEnabled(enable) + if enable then + self:Enable() + else + self:Disable() + end +end + + +local numShownButtons + +local function update(level) + local scroll = listData[level].scroll + for i = 1, UIDROPDOWNMENU_MAXBUTTONS do + local button = _G["DropDownList"..level.."Button"..i] + local _, _, _, x, y = button:GetPoint() + local y = -((button:GetID() - 1 - scroll) * UIDROPDOWNMENU_BUTTON_HEIGHT) - UIDROPDOWNMENU_BORDER_HEIGHT + button:SetPoint("TOPLEFT", x, y) + button:SetShown(i > scroll and i <= (numShownButtons + scroll)) + end + Dropdown.scrollButtons[level].up:SetShown(scroll > 0) + Dropdown.scrollButtons[level].down:SetShown(scroll < _G["DropDownList"..level].numButtons - numShownButtons) +end + +local function scroll(self, delta) + local level = self:GetID() + local listData = listData[level] + delta = (type(delta) == "number" and delta or self.delta) + if IsShiftKeyDown() then delta = delta * (numShownButtons - 1) end + listData.scroll = listData.scroll - (type(delta) == "number" and delta or self.delta) + listData.scroll = min(listData.scroll, (self.numButtons or self:GetParent().numButtons) - numShownButtons) + listData.scroll = max(listData.scroll, 0) + update(level) +end + +local scrollScripts = { + OnEnter = function(self) + UIDropDownMenu_StopCounting(self:GetParent()) + end, + OnLeave = function(self) + UIDropDownMenu_StartCounting(self:GetParent()) + end, + OnMouseDown = function(self) + self.texture:SetPoint("CENTER", 1, -1) + end, + OnMouseUp = function(self) + self.texture:SetPoint("CENTER") + end, + OnHide = function(self) + self.texture:SetPoint("CENTER") + -- explicitly hide so that they are hidden for unmanaged dropdowns + self:Hide() + end, +} + +local function createScrollButton(listFrame) + local level = listFrame:GetID() + local button = CreateFrame("Button", nil, listFrame) + button:SetSize(16, 16) + button:SetScript("OnClick", scroll) + for script, handler in pairs(scrollScripts) do + button:SetScript(script, handler) + end + button:SetID(level) + button.texture = button:CreateTexture() + button.texture:SetSize(16, 16) + button.texture:SetPoint("CENTER") + button.texture:SetTexture([[Interface\Calendar\MoreArrow]]) + return button +end + +local function createScrollButtons(listFrame) + local scrollUp = listFrame.scrollUp or createScrollButton(listFrame) + scrollUp:SetPoint("TOP") + scrollUp.delta = 1 + scrollUp.texture:SetTexCoord(0, 1, 1, 0) + listFrame.scrollUp = scrollUp + + local scrollDown = listFrame.scrollDown or createScrollButton(listFrame) + scrollDown:SetPoint("BOTTOM") + scrollDown.delta = -1 + listFrame.scrollDown = scrollDown +end + +Dropdown.scrollButtons = Dropdown.scrollButtons or setmetatable({}, { + __index = function(self, level) + local listFrame = _G["DropDownList"..level] + createScrollButtons(listFrame) + self[level] = { + up = listFrame.scrollUp, + down = listFrame.scrollDown, + } + return self[level] + end, +}) + +function Dropdown:ToggleDropDownMenuHook(level, value, dropdownFrame, anchorName, xOffset, yOffset, menuList, button, autoHideDelay) + level = level or 1 + if level ~= 1 then + dropdownFrame = dropdownFrame or UIDROPDOWNMENU_OPEN_MENU + end + local listFrameName = "DropDownList"..level + local listFrame = _G[listFrameName] + if not objects[dropdownFrame] then return end + if dropdownFrame and dropdownFrame._displayMode == "MENU" then + _G[listFrameName.."Backdrop"]:Hide() + _G[listFrameName.."MenuBackdrop"]:Show() + end + + -- store all parameters per level so we can use them to rebuild the menu + listData[level] = listData[level] or {} + local listData = listData[level] + listData.value = value + listData.anchorName = anchorName + listData.xOffset = xOffset + listData.yOffset = yOffset + listData.menuList = menuList + listData.button = button + listData.autoHideDelay = autoHideDelay + + numShownButtons = dropdownFrame.numShownButtons or floor((UIParent:GetHeight() - UIDROPDOWNMENU_BORDER_HEIGHT * 2) / UIDROPDOWNMENU_BUTTON_HEIGHT) + local scrollable = numShownButtons < listFrame.numButtons + if scrollable then + -- make scrollable + listData.scroll = listData.scroll or 0 + if not dropdownFrame.rebuild then + listData.scroll = 0 + end + listFrame:SetScript("OnMouseWheel", scroll) + listFrame:SetHeight((numShownButtons * UIDROPDOWNMENU_BUTTON_HEIGHT) + (UIDROPDOWNMENU_BORDER_HEIGHT * 2)) + if listFrame:GetTop() > GetScreenHeight() then + local point, anchorFrame, relativePoint, x, y = listFrame:GetPoint() + local offTop = (GetScreenHeight() - listFrame:GetTop())-- / listFrame:GetScale() + listFrame:SetPoint(point, anchorFrame, relativePoint, x, y + offTop) + end + update(level) + else + if listFrame:GetTop() > GetScreenHeight() then + local point, anchorFrame, relativePoint, x, y = listFrame:GetPoint() + local offTop = (GetScreenHeight() - listFrame:GetTop())-- / listFrame:GetScale() + listFrame:SetPoint(point, anchorFrame, relativePoint, x, y + offTop) + end + listFrame:SetScript("OnMouseWheel", nil) + self.scrollButtons[level].up:Hide() + self.scrollButtons[level].down:Hide() + end + dropdownFrame.rebuild = nil +end + +if not Dropdown.hookToggleDropDownMenu then + hooksecurefunc("ToggleDropDownMenu", function(...) + Dropdown:ToggleDropDownMenuHook(...) + end) + Dropdown.hookToggleDropDownMenu = true +end + +function Dropdown:AddButtonHook(info, level) + if not objects[UIDropDownMenu_GetCurrentDropDown()] then return end + local listFrameName = "DropDownList"..(level or 1) + local listFrame = _G[listFrameName] + local button = _G[listFrameName.."Button"..(listFrame.numButtons)] + button.onEnter = info.onEnter + button.onLeave = info.onLeave + button.tooltipLines = info.tooltipLines + if info.attributes and not InCombatLockdown() then + local secureButton = self.secureBin[1] + tremove(Dropdown.secureBin, 1) + -- since this is a separate button, we need to set the disabled state on it too + secureButton:SetEnabled(not info.disabled) + secureButton:SetParent(button) + secureButton:SetAllPoints() + secureButton:Show() + -- clear existing attributes + for attribute in pairs(secureButton.attributes) do + secureButton:SetAttribute(attribute, nil) + secureButton.attributes[attribute] = nil + end + for attribute, value in pairs(info.attributes) do + secureButton:SetAttribute(attribute, value) + secureButton.attributes[attribute] = true + end + tinsert(Dropdown.secureButtons, secureButton) + end +end + +if not Dropdown.hookAddButton then + hooksecurefunc("UIDropDownMenu_AddButton", function(...) + Dropdown:AddButtonHook(...) + end) + Dropdown.hookAddButton = true +end + +local function onEnter(self) + if self.onEnter then + self:onEnter() + elseif self.tooltipLines and self.tooltipTitle then + GameTooltip:SetOwner(self, "ANCHOR_RIGHT") + GameTooltip:AddLine(self.tooltipTitle, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) + if self.tooltipText then + for line in self.tooltipText:gmatch("[^\n]+") do + GameTooltip:AddLine(line) + end + end + GameTooltip:Show() + end +end + +local function onLeave(self) + if self.onLeave then + self:onLeave() + end +end + +local function invisibleButtonOnEnter(self) + local parent = self:GetParent() + if parent.onEnter or parent.tooltipWhileDisabled then + onEnter(parent) + end +end + +local function invisibleButtonOnLeave(self) + local parent = self:GetParent() + if parent.onLeave or parent.tooltipWhileDisabled then + onLeave(parent) + end +end + +function Dropdown:HideListHook(self) + if not InCombatLockdown() then + for i = #Dropdown.secureButtons, 1, -1 do + -- hide secure buttons attached to this list frame + local button = Dropdown.secureButtons[i] + if button:GetParent():GetParent() == self then + Dropdown:DismissSecureButton(button) + tremove(Dropdown.secureButtons, i) + end + end + end + if objects[UIDropDownMenu_GetCurrentDropDown()] then + self:SetScript("OnMouseWheel", nil) + end +end + +local function listOnHide(self) + Dropdown:HideListHook(self) +end + +function Dropdown:CreateFramesHook(numLevels, numButtons) + for level = 1, numLevels do + if not self.hookedLists[level] then + _G["DropDownList"..level]:HookScript("OnHide", listOnHide) + self.hookedLists[level] = true + end + self.hookedButtons[level] = self.hookedButtons[level] or {} + for i = 1, numButtons do + if not self.hookedButtons[level][i] then + local button = _G["DropDownList"..level.."Button"..i] + button:HookScript("OnEnter", onEnter) + button:HookScript("OnLeave", onLeave) + button.invisibleButton:HookScript("OnEnter", invisibleButtonOnEnter) + button.invisibleButton:HookScript("OnLeave", invisibleButtonOnLeave) + self.hookedButtons[level][i] = true + end + end + end +end + +if not Dropdown.hookCreateFrames then + Dropdown:CreateFramesHook(UIDROPDOWNMENU_MAXLEVELS, UIDROPDOWNMENU_MAXBUTTONS) + hooksecurefunc("UIDropDownMenu_CreateFrames", function(...) + Dropdown:CreateFramesHook(...) + end) + Dropdown.hookCreateFrames = true +end + +-- script handlers to mimic regular dropdown button behaviour +local scripts = { + PreClick = function(self) + local parent = self:GetParent() + parent:GetScript("OnClick")(parent) + end, + OnMouseDown = function(self) + self:GetParent():SetButtonState("PUSHED") + end, + OnMouseUp = function(self) + self:GetParent():SetButtonState("NORMAL") + end, + OnEnter = function(self) + local parent = self:GetParent() + parent:GetScript("OnEnter")(parent) + end, + OnLeave = function(self) + local parent = self:GetParent() + parent:GetScript("OnLeave")(parent) + end, +} + +setmetatable(Dropdown.secureBin, { + __index = function(self, index) + local button = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate") + for script, handler in pairs(scripts) do + button:SetScript(script, handler) + end + button.attributes = {} + return button + end, +}) + +function Dropdown:DismissSecureButton(button) + button:Hide() + button:ClearAllPoints() + button:SetParent(nil) + tinsert(Dropdown.secureBin, button) +end + +Dropdown.frame = Dropdown.frame or CreateFrame("Frame") +Dropdown.frame:RegisterEvent("PLAYER_REGEN_DISABLED") +Dropdown.frame:SetScript("OnEvent", function(self) + for i, button in ipairs(Dropdown.secureButtons) do + Dropdown:DismissSecureButton(button) + end + wipe(Dropdown.secureButtons) +end) + +Libra:RegisterModule(Type, Version, constructor) \ No newline at end of file diff --git a/Libs/Libra/Editbox.lua b/Libs/Libra/Editbox.lua new file mode 100644 index 0000000..28358a2 --- /dev/null +++ b/Libs/Libra/Editbox.lua @@ -0,0 +1,12 @@ +local Libra = LibStub("Libra") +local Type, Version = "Editbox", 3 +if Libra:GetModuleVersion(Type) >= Version then return end + +local function constructor(self, parent, isSearchBox) + local editbox = CreateFrame("EditBox", nil, parent, isSearchBox and "SearchBoxTemplate" or "InputBoxTemplate") + editbox:SetHeight(20) + editbox:SetAutoFocus(false) + return editbox +end + +Libra:RegisterModule(Type, Version, constructor) \ No newline at end of file diff --git a/Libs/Libra/Libra.xml b/Libs/Libra/Libra.xml new file mode 100644 index 0000000..02a5fb6 --- /dev/null +++ b/Libs/Libra/Libra.xml @@ -0,0 +1,16 @@ + +