Compare commits
10 Commits
db6c7d79b0
...
948ac29aaf
| Author | SHA1 | Date | |
|---|---|---|---|
| 948ac29aaf | |||
| 015891449e | |||
| 284f26b8b4 | |||
| fd2ee1903c | |||
| bf9972e624 | |||
| 50824cc0e1 | |||
| 97c4defa51 | |||
| 3a5344e31f | |||
| d769cebbbe | |||
| d5b67920a6 |
@@ -0,0 +1,3 @@
|
||||
<Bindings>
|
||||
<Binding name="CLICK ExtraActionBarPoolFrameExtraActionButtonTemplate1:LeftButton" header="BINDING_HEADER_COA"/>
|
||||
</Bindings>
|
||||
@@ -5,7 +5,12 @@
|
||||
## Notes: Hides and skins custom CoA-server frames for ElvUI.
|
||||
## RequiredDeps: ElvUI
|
||||
## X-IconTexture: Interface\AddOns\ElvUI\Media\ElvUILogo
|
||||
## SavedVariables: ElvUI_CoADB
|
||||
|
||||
Bindings.xml
|
||||
Libraries\AceDBOptions-3.0.lua
|
||||
core.lua
|
||||
Modules\ExtraActionBar.lua
|
||||
Modules\LayerPicker.lua
|
||||
Modules\DispelHighlight.lua
|
||||
Modules\ClassResources.lua
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
--- AceDBOptions-3.0 provides a universal AceConfig options screen for managing AceDB-3.0 profiles.
|
||||
-- @class file
|
||||
-- @name AceDBOptions-3.0
|
||||
-- @release $Id$
|
||||
local ACEDBO_MAJOR, ACEDBO_MINOR = "AceDBOptions-3.0", 15
|
||||
local AceDBOptions = LibStub:NewLibrary(ACEDBO_MAJOR, ACEDBO_MINOR)
|
||||
|
||||
if not AceDBOptions then return end -- No upgrade needed
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, next = pairs, next
|
||||
|
||||
-- WoW APIs
|
||||
local UnitClass = UnitClass
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: NORMAL_FONT_COLOR_CODE, FONT_COLOR_CODE_CLOSE
|
||||
|
||||
AceDBOptions.optionTables = AceDBOptions.optionTables or {}
|
||||
AceDBOptions.handlers = AceDBOptions.handlers or {}
|
||||
|
||||
--[[
|
||||
Localization of AceDBOptions-3.0
|
||||
]]
|
||||
|
||||
local L = {
|
||||
choose = "Existing Profiles",
|
||||
choose_desc = "You can either create a new profile by entering a name in the editbox, or choose one of the already existing profiles.",
|
||||
choose_sub = "Select one of your currently available profiles.",
|
||||
copy = "Copy From",
|
||||
copy_desc = "Copy the settings from one existing profile into the currently active profile.",
|
||||
current = "Current Profile:",
|
||||
default = "Default",
|
||||
delete = "Delete a Profile",
|
||||
delete_confirm = "Are you sure you want to delete the selected profile?",
|
||||
delete_desc = "Delete existing and unused profiles from the database to save space, and cleanup the SavedVariables file.",
|
||||
delete_sub = "Deletes a profile from the database.",
|
||||
intro = "You can change the active database profile, so you can have different settings for every character.",
|
||||
new = "New",
|
||||
new_sub = "Create a new empty profile.",
|
||||
profiles = "Profiles",
|
||||
profiles_sub = "Manage Profiles",
|
||||
reset = "Reset Profile",
|
||||
reset_desc = "Reset the current profile back to its default values, in case your configuration is broken, or you simply want to start over.",
|
||||
reset_sub = "Reset the current profile to the default",
|
||||
}
|
||||
|
||||
local LOCALE = GetLocale()
|
||||
if LOCALE == "deDE" then
|
||||
L["choose"] = "Vorhandene Profile"
|
||||
L["choose_desc"] = "Du kannst ein neues Profil erstellen, indem du einen neuen Namen in der Eingabebox 'Neu' eingibst, oder wähle eines der vorhandenen Profile aus."
|
||||
L["choose_sub"] = "Wählt ein bereits vorhandenes Profil aus."
|
||||
L["copy"] = "Kopieren von..."
|
||||
L["copy_desc"] = "Kopiere die Einstellungen von einem vorhandenen Profil in das aktive Profil."
|
||||
L["current"] = "Aktuelles Profil:"
|
||||
L["default"] = "Standard"
|
||||
L["delete"] = "Profil löschen"
|
||||
L["delete_confirm"] = "Willst du das ausgewählte Profil wirklich löschen?"
|
||||
L["delete_desc"] = "Lösche vorhandene oder unbenutzte Profile aus der Datenbank, um Platz zu sparen und die SavedVariables-Datei 'sauber' zu halten."
|
||||
L["delete_sub"] = "Löscht ein Profil aus der Datenbank."
|
||||
L["intro"] = "Hier kannst du das aktive Datenbankprofil ändern, damit du verschiedene Einstellungen für jeden Charakter erstellen kannst, wodurch eine sehr flexible Konfiguration möglich wird."
|
||||
L["new"] = "Neu"
|
||||
L["new_sub"] = "Ein neues Profil erstellen."
|
||||
L["profiles"] = "Profile"
|
||||
L["profiles_sub"] = "Profile verwalten"
|
||||
L["reset"] = "Profil zurücksetzen"
|
||||
L["reset_desc"] = "Setzt das momentane Profil auf Standardwerte zurück, für den Fall, dass mit der Konfiguration etwas schief lief oder weil du einfach neu starten willst."
|
||||
L["reset_sub"] = "Das aktuelle Profil auf Standard zurücksetzen."
|
||||
elseif LOCALE == "frFR" then
|
||||
L["choose"] = "Profils existants"
|
||||
L["choose_desc"] = "Vous pouvez créer un nouveau profil en entrant un nouveau nom dans la boîte de saisie, ou en choississant un des profils déjà existants."
|
||||
L["choose_sub"] = "Permet de choisir un des profils déjà disponibles."
|
||||
L["copy"] = "Copier à partir de"
|
||||
L["copy_desc"] = "Copie les paramètres d'un profil déjà existant dans le profil actuellement actif."
|
||||
L["current"] = "Profil actuel :"
|
||||
L["default"] = "Défaut"
|
||||
L["delete"] = "Supprimer un profil"
|
||||
L["delete_confirm"] = "Etes-vous sûr de vouloir supprimer le profil sélectionné ?"
|
||||
L["delete_desc"] = "Supprime les profils existants inutilisés de la base de données afin de gagner de la place et de nettoyer le fichier SavedVariables."
|
||||
L["delete_sub"] = "Supprime un profil de la base de données."
|
||||
L["intro"] = "Vous pouvez changer le profil actuel afin d'avoir des paramètres différents pour chaque personnage, permettant ainsi d'avoir une configuration très flexible."
|
||||
L["new"] = "Nouveau"
|
||||
L["new_sub"] = "Créée un nouveau profil vierge."
|
||||
L["profiles"] = "Profils"
|
||||
L["profiles_sub"] = "Gestion des profils"
|
||||
L["reset"] = "Réinitialiser le profil"
|
||||
L["reset_desc"] = "Réinitialise le profil actuel au cas où votre configuration est corrompue ou si vous voulez tout simplement faire table rase."
|
||||
L["reset_sub"] = "Réinitialise le profil actuel avec les paramètres par défaut."
|
||||
elseif LOCALE == "koKR" then
|
||||
L["choose"] = "저장 중인 프로필"
|
||||
L["choose_desc"] = "입력창에 새로운 이름을 입력하거나 저장 중인 프로필 중 하나를 선택하여 새로운 프로필을 만들 수 있습니다."
|
||||
L["choose_sub"] = "현재 이용할 수 있는 프로필 중 하나를 선택합니다."
|
||||
L["copy"] = "복사해오기"
|
||||
L["copy_desc"] = "현재 사용 중인 프로필에 선택한 프로필의 설정을 복사합니다."
|
||||
L["current"] = "현재 프로필:"
|
||||
L["default"] = "기본값"
|
||||
L["delete"] = "프로필 삭제"
|
||||
L["delete_confirm"] = "정말로 선택한 프로필을 삭제할까요?"
|
||||
L["delete_desc"] = "저장 공간 절약과 SavedVariables 파일의 정리를 위해 데이터베이스에서 사용하지 않는 프로필을 삭제하세요."
|
||||
L["delete_sub"] = "데이터베이스의 프로필을 삭제합니다."
|
||||
L["intro"] = "활성 데이터베이스 프로필을 변경할 수 있고, 각 캐릭터 별로 다른 설정을 할 수 있습니다."
|
||||
L["new"] = "새로운 프로필"
|
||||
L["new_sub"] = "새로운 프로필을 만듭니다."
|
||||
L["profiles"] = "프로필"
|
||||
L["profiles_sub"] = "프로필 관리"
|
||||
L["reset"] = "프로필 초기화"
|
||||
L["reset_desc"] = "설정이 깨졌거나 처음부터 다시 설정을 원하는 경우, 현재 프로필을 기본값으로 초기화하세요."
|
||||
L["reset_sub"] = "현재 프로필을 기본값으로 초기화합니다"
|
||||
elseif LOCALE == "esES" or LOCALE == "esMX" then
|
||||
L["choose"] = "Perfiles existentes"
|
||||
L["choose_desc"] = "Puedes crear un nuevo perfil introduciendo un nombre en el recuadro o puedes seleccionar un perfil de los ya existentes."
|
||||
L["choose_sub"] = "Selecciona uno de los perfiles disponibles."
|
||||
L["copy"] = "Copiar de"
|
||||
L["copy_desc"] = "Copia los ajustes de un perfil existente al perfil actual."
|
||||
L["current"] = "Perfil actual:"
|
||||
L["default"] = "Por defecto"
|
||||
L["delete"] = "Borrar un Perfil"
|
||||
L["delete_confirm"] = "¿Estas seguro que quieres borrar el perfil seleccionado?"
|
||||
L["delete_desc"] = "Borra los perfiles existentes y sin uso de la base de datos para ganar espacio y limpiar el archivo SavedVariables."
|
||||
L["delete_sub"] = "Borra un perfil de la base de datos."
|
||||
L["intro"] = "Puedes cambiar el perfil activo de tal manera que cada personaje tenga diferentes configuraciones."
|
||||
L["new"] = "Nuevo"
|
||||
L["new_sub"] = "Crear un nuevo perfil vacio."
|
||||
L["profiles"] = "Perfiles"
|
||||
L["profiles_sub"] = "Manejar Perfiles"
|
||||
L["reset"] = "Reiniciar Perfil"
|
||||
L["reset_desc"] = "Reinicia el perfil actual a los valores por defectos, en caso de que se haya estropeado la configuración o quieras volver a empezar de nuevo."
|
||||
L["reset_sub"] = "Reinicar el perfil actual al de por defecto"
|
||||
elseif LOCALE == "zhTW" then
|
||||
L["choose"] = "現有的設定檔"
|
||||
L["choose_desc"] = "您可以在文字方塊內輸入名字以建立新的設定檔,或是選擇一個現有的設定檔使用。"
|
||||
L["choose_sub"] = "從當前可用的設定檔裡面選擇一個。"
|
||||
L["copy"] = "複製自"
|
||||
L["copy_desc"] = "從一個現有的設定檔,將設定複製到現在使用中的設定檔。"
|
||||
L["current"] = "目前設定檔:"
|
||||
L["default"] = "預設"
|
||||
L["delete"] = "刪除一個設定檔"
|
||||
L["delete_confirm"] = "確定要刪除所選擇的設定檔嗎?"
|
||||
L["delete_desc"] = "從資料庫裡刪除不再使用的設定檔,以節省空間,並且清理 SavedVariables 檔案。"
|
||||
L["delete_sub"] = "從資料庫裡刪除一個設定檔。"
|
||||
L["intro"] = "您可以從資料庫中選擇一個設定檔來使用,如此就可以讓每個角色使用不同的設定。"
|
||||
L["new"] = "新建"
|
||||
L["new_sub"] = "新建一個空的設定檔。"
|
||||
L["profiles"] = "設定檔"
|
||||
L["profiles_sub"] = "管理設定檔"
|
||||
L["reset"] = "重置設定檔"
|
||||
L["reset_desc"] = "將現用的設定檔重置為預設值;用於設定檔損壞,或者單純想要重來的情況。"
|
||||
L["reset_sub"] = "將目前的設定檔重置為預設值"
|
||||
elseif LOCALE == "zhCN" then
|
||||
L["choose"] = "现有的配置文件"
|
||||
L["choose_desc"] = "你可以通过在文本框内输入一个名字创立一个新的配置文件,也可以选择一个已经存在的配置文件。"
|
||||
L["choose_sub"] = "从当前可用的配置文件里面选择一个。"
|
||||
L["copy"] = "复制自"
|
||||
L["copy_desc"] = "从当前某个已保存的配置文件复制到当前正使用的配置文件。"
|
||||
L["current"] = "当前配置文件:"
|
||||
L["default"] = "默认"
|
||||
L["delete"] = "删除一个配置文件"
|
||||
L["delete_confirm"] = "你确定要删除所选择的配置文件么?"
|
||||
L["delete_desc"] = "从数据库里删除不再使用的配置文件,以节省空间,并且清理SavedVariables文件。"
|
||||
L["delete_sub"] = "从数据库里删除一个配置文件。"
|
||||
L["intro"] = "你可以选择一个活动的数据配置文件,这样你的每个角色就可以拥有不同的设置值,可以给你的插件配置带来极大的灵活性。"
|
||||
L["new"] = "新建"
|
||||
L["new_sub"] = "新建一个空的配置文件。"
|
||||
L["profiles"] = "配置文件"
|
||||
L["profiles_sub"] = "管理配置文件"
|
||||
L["reset"] = "重置配置文件"
|
||||
L["reset_desc"] = "将当前的配置文件恢复到它的默认值,用于你的配置文件损坏,或者你只是想重来的情况。"
|
||||
L["reset_sub"] = "将当前的配置文件恢复为默认值"
|
||||
elseif LOCALE == "ruRU" then
|
||||
L["choose"] = "Существующие профили"
|
||||
L["choose_desc"] = "Вы можете создать новый профиль, введя название в поле ввода, или выбрать один из уже существующих профилей."
|
||||
L["choose_sub"] = "Выбор одиного из уже доступных профилей"
|
||||
L["copy"] = "Скопировать из"
|
||||
L["copy_desc"] = "Скопировать настройки из выбранного профиля в активный."
|
||||
L["current"] = "Текущий профиль:"
|
||||
L["default"] = "По умолчанию"
|
||||
L["delete"] = "Удалить профиль"
|
||||
L["delete_confirm"] = "Вы уверены, что вы хотите удалить выбранный профиль?"
|
||||
L["delete_desc"] = "Удалить существующий и неиспользуемый профиль из БД для сохранения места, и очистить SavedVariables файл."
|
||||
L["delete_sub"] = "Удаление профиля из БД"
|
||||
L["intro"] = "Изменяя активный профиль, вы можете задать различные настройки модификаций для каждого персонажа."
|
||||
L["new"] = "Новый"
|
||||
L["new_sub"] = "Создать новый чистый профиль"
|
||||
L["profiles"] = "Профили"
|
||||
L["profiles_sub"] = "Управление профилями"
|
||||
L["reset"] = "Сброс профиля"
|
||||
L["reset_desc"] = "Сбросить текущий профиль к стандартным настройкам, если ваша конфигурация испорчена или вы хотите настроить всё заново."
|
||||
L["reset_sub"] = "Сброс текущего профиля на стандартный"
|
||||
elseif LOCALE == "itIT" then
|
||||
L["choose"] = "Profili Esistenti"
|
||||
L["choose_desc"] = "Puoi creare un nuovo profilo digitando il nome della casella di testo, oppure scegliendone uno tra i profili già esistenti."
|
||||
L["choose_sub"] = "Seleziona uno dei profili attualmente disponibili."
|
||||
L["copy"] = "Copia Da"
|
||||
L["copy_desc"] = "Copia le impostazioni da un profilo esistente, nel profilo attivo in questo momento."
|
||||
L["current"] = "Profilo Attivo:"
|
||||
L["default"] = "Standard"
|
||||
L["delete"] = "Cancella un Profilo"
|
||||
L["delete_confirm"] = "Sei sicuro di voler cancellare il profilo selezionato?"
|
||||
L["delete_desc"] = "Cancella i profili non utilizzati dal database per risparmiare spazio e mantenere puliti i file di configurazione SavedVariables."
|
||||
L["delete_sub"] = "Cancella un profilo dal Database."
|
||||
L["intro"] = "Puoi cambiare il profilo attivo, in modo da usare impostazioni diverse per ogni personaggio."
|
||||
L["new"] = "Nuovo"
|
||||
L["new_sub"] = "Crea un nuovo profilo vuoto."
|
||||
L["profiles"] = "Profili"
|
||||
L["profiles_sub"] = "Gestisci Profili"
|
||||
L["reset"] = "Reimposta Profilo"
|
||||
L["reset_desc"] = "Riporta il tuo profilo attivo alle sue impostazioni predefinite, nel caso in cui la tua configurazione si sia corrotta, o semplicemente tu voglia re-inizializzarla."
|
||||
L["reset_sub"] = "Reimposta il profilo ai suoi valori predefiniti."
|
||||
elseif LOCALE == "ptBR" then
|
||||
L["choose"] = "Perfis Existentes"
|
||||
L["choose_desc"] = "Você pode tanto criar um perfil novo tanto digitando um nome na caixa de texto, quanto escolher um dos perfis já existentes."
|
||||
L["choose_sub"] = "Selecione um de seus perfis atualmente disponíveis."
|
||||
L["copy"] = "Copiar De"
|
||||
L["copy_desc"] = "Copia as definições de um perfil existente no perfil atualmente ativo."
|
||||
L["current"] = "Perfil Autal:"
|
||||
L["default"] = "Padrão"
|
||||
L["delete"] = "Remover um Perfil"
|
||||
L["delete_confirm"] = "Tem certeza que deseja remover o perfil selecionado?"
|
||||
L["delete_desc"] = "Remove perfis existentes e inutilizados do banco de dados para economizar espaço, e limpar o arquivo SavedVariables."
|
||||
L["delete_sub"] = "Remove um perfil do banco de dados."
|
||||
L["intro"] = "Você pode alterar o perfil do banco de dados ativo, para que possa ter definições diferentes para cada personagem."
|
||||
L["new"] = "Novo"
|
||||
L["new_sub"] = "Cria um novo perfil vazio."
|
||||
L["profiles"] = "Perfis"
|
||||
L["profiles_sub"] = "Gerenciar Perfis"
|
||||
L["reset"] = "Resetar Perfil"
|
||||
L["reset_desc"] = "Reseta o perfil atual para os valores padrões, no caso de sua configuração estar quebrada, ou simplesmente se deseja começar novamente."
|
||||
L["reset_sub"] = "Resetar o perfil atual ao padrão"
|
||||
end
|
||||
|
||||
local defaultProfiles
|
||||
local tmpprofiles = {}
|
||||
|
||||
-- Get a list of available profiles for the specified database.
|
||||
-- You can specify which profiles to include/exclude in the list using the two boolean parameters listed below.
|
||||
-- @param db The db object to retrieve the profiles from
|
||||
-- @param common If true, getProfileList will add the default profiles to the return list, even if they have not been created yet
|
||||
-- @param nocurrent If true, then getProfileList will not display the current profile in the list
|
||||
-- @return Hashtable of all profiles with the internal name as keys and the display name as value.
|
||||
local function getProfileList(db, common, nocurrent)
|
||||
local profiles = {}
|
||||
|
||||
-- copy existing profiles into the table
|
||||
local currentProfile = db:GetCurrentProfile()
|
||||
for i,v in pairs(db:GetProfiles(tmpprofiles)) 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
|
||||
|
||||
return profiles
|
||||
end
|
||||
|
||||
--[[
|
||||
OptionsHandlerPrototype
|
||||
prototype class for handling the options in a sane way
|
||||
]]
|
||||
local OptionsHandlerPrototype = {}
|
||||
|
||||
--[[ Reset the profile ]]
|
||||
function OptionsHandlerPrototype:Reset()
|
||||
self.db:ResetProfile()
|
||||
end
|
||||
|
||||
--[[ Set the profile to value ]]
|
||||
function OptionsHandlerPrototype:SetProfile(info, value)
|
||||
self.db:SetProfile(value)
|
||||
end
|
||||
|
||||
--[[ returns the currently active profile ]]
|
||||
function OptionsHandlerPrototype:GetCurrentProfile()
|
||||
return self.db:GetCurrentProfile()
|
||||
end
|
||||
|
||||
--[[
|
||||
List all active profiles
|
||||
you can control the output with the .arg variable
|
||||
currently four modes are supported
|
||||
|
||||
(empty) - return all available profiles
|
||||
"nocurrent" - returns all available profiles except the currently active profile
|
||||
"common" - returns all avaialble profiles + some commonly used profiles ("char - realm", "realm", "class", "Default")
|
||||
"both" - common except the active profile
|
||||
]]
|
||||
function OptionsHandlerPrototype:ListProfiles(info)
|
||||
local arg = info.arg
|
||||
local profiles
|
||||
if arg == "common" and not self.noDefaultProfiles then
|
||||
profiles = getProfileList(self.db, true, nil)
|
||||
elseif arg == "nocurrent" then
|
||||
profiles = getProfileList(self.db, nil, true)
|
||||
elseif arg == "both" then -- currently not used
|
||||
profiles = getProfileList(self.db, (not self.noDefaultProfiles) and true, true)
|
||||
else
|
||||
profiles = getProfileList(self.db)
|
||||
end
|
||||
|
||||
return profiles
|
||||
end
|
||||
|
||||
function OptionsHandlerPrototype:HasNoProfiles(info)
|
||||
local profiles = self:ListProfiles(info)
|
||||
return ((not next(profiles)) and true or false)
|
||||
end
|
||||
|
||||
--[[ Copy a profile ]]
|
||||
function OptionsHandlerPrototype:CopyProfile(info, value)
|
||||
self.db:CopyProfile(value)
|
||||
end
|
||||
|
||||
--[[ Delete a profile from the db ]]
|
||||
function OptionsHandlerPrototype:DeleteProfile(info, value)
|
||||
self.db:DeleteProfile(value)
|
||||
end
|
||||
|
||||
--[[ fill defaultProfiles with some generic values ]]
|
||||
local function generateDefaultProfiles(db)
|
||||
defaultProfiles = {
|
||||
["Default"] = L["default"],
|
||||
[db.keys.char] = db.keys.char,
|
||||
[db.keys.realm] = db.keys.realm,
|
||||
[db.keys.class] = UnitClass("player")
|
||||
}
|
||||
end
|
||||
|
||||
--[[ create and return a handler object for the db, or upgrade it if it already existed ]]
|
||||
local function getOptionsHandler(db, noDefaultProfiles)
|
||||
if not defaultProfiles then
|
||||
generateDefaultProfiles(db)
|
||||
end
|
||||
|
||||
local handler = AceDBOptions.handlers[db] or { db = db, noDefaultProfiles = noDefaultProfiles }
|
||||
|
||||
for k,v in pairs(OptionsHandlerPrototype) do
|
||||
handler[k] = v
|
||||
end
|
||||
|
||||
AceDBOptions.handlers[db] = handler
|
||||
return handler
|
||||
end
|
||||
|
||||
--[[
|
||||
the real options table
|
||||
]]
|
||||
local optionsTable = {
|
||||
desc = {
|
||||
order = 1,
|
||||
type = "description",
|
||||
name = L["intro"] .. "\n",
|
||||
},
|
||||
descreset = {
|
||||
order = 9,
|
||||
type = "description",
|
||||
name = L["reset_desc"],
|
||||
},
|
||||
reset = {
|
||||
order = 10,
|
||||
type = "execute",
|
||||
name = L["reset"],
|
||||
desc = L["reset_sub"],
|
||||
func = "Reset",
|
||||
},
|
||||
current = {
|
||||
order = 11,
|
||||
type = "description",
|
||||
name = function(info) return L["current"] .. " " .. NORMAL_FONT_COLOR_CODE .. info.handler:GetCurrentProfile() .. FONT_COLOR_CODE_CLOSE end,
|
||||
width = "default",
|
||||
},
|
||||
choosedesc = {
|
||||
order = 20,
|
||||
type = "description",
|
||||
name = "\n" .. L["choose_desc"],
|
||||
},
|
||||
new = {
|
||||
name = L["new"],
|
||||
desc = L["new_sub"],
|
||||
type = "input",
|
||||
order = 30,
|
||||
get = false,
|
||||
set = "SetProfile",
|
||||
},
|
||||
choose = {
|
||||
name = L["choose"],
|
||||
desc = L["choose_sub"],
|
||||
type = "select",
|
||||
order = 40,
|
||||
get = "GetCurrentProfile",
|
||||
set = "SetProfile",
|
||||
values = "ListProfiles",
|
||||
arg = "common",
|
||||
},
|
||||
copydesc = {
|
||||
order = 50,
|
||||
type = "description",
|
||||
name = "\n" .. L["copy_desc"],
|
||||
},
|
||||
copyfrom = {
|
||||
order = 60,
|
||||
type = "select",
|
||||
name = L["copy"],
|
||||
desc = L["copy_desc"],
|
||||
get = false,
|
||||
set = "CopyProfile",
|
||||
values = "ListProfiles",
|
||||
disabled = "HasNoProfiles",
|
||||
arg = "nocurrent",
|
||||
},
|
||||
deldesc = {
|
||||
order = 70,
|
||||
type = "description",
|
||||
name = "\n" .. L["delete_desc"],
|
||||
},
|
||||
delete = {
|
||||
order = 80,
|
||||
type = "select",
|
||||
name = L["delete"],
|
||||
desc = L["delete_sub"],
|
||||
get = false,
|
||||
set = "DeleteProfile",
|
||||
values = "ListProfiles",
|
||||
disabled = "HasNoProfiles",
|
||||
arg = "nocurrent",
|
||||
confirm = true,
|
||||
confirmText = L["delete_confirm"],
|
||||
},
|
||||
}
|
||||
|
||||
--- Get/Create a option table that you can use in your addon to control the profiles of AceDB-3.0.
|
||||
-- @param db The database object to create the options table for.
|
||||
-- @return The options table to be used in AceConfig-3.0
|
||||
-- @usage
|
||||
-- -- Assuming `options` is your top-level options table and `self.db` is your database:
|
||||
-- options.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
|
||||
function AceDBOptions:GetOptionsTable(db, noDefaultProfiles)
|
||||
local tbl = AceDBOptions.optionTables[db] or {
|
||||
type = "group",
|
||||
name = L["profiles"],
|
||||
desc = L["profiles_sub"],
|
||||
}
|
||||
|
||||
tbl.handler = getOptionsHandler(db, noDefaultProfiles)
|
||||
tbl.args = optionsTable
|
||||
|
||||
AceDBOptions.optionTables[db] = tbl
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- upgrade existing tables
|
||||
for db,tbl in pairs(AceDBOptions.optionTables) do
|
||||
tbl.handler = getOptionsHandler(db)
|
||||
tbl.args = optionsTable
|
||||
end
|
||||
@@ -0,0 +1,286 @@
|
||||
local E, L, V, P, G = unpack(ElvUI)
|
||||
local CoA = E:GetModule("CoA")
|
||||
|
||||
local FRAMES = {
|
||||
{name = "CoAResourceSegmentBar", moverText = "Resource Segment Bar", hideKey = "hideResourceSegmentBar"},
|
||||
{name = "CoAResourceOrb", moverText = "Resource Orb", hideKey = "hideResourceOrb"},
|
||||
{name = "CoAResourceBar", moverText = "Resource Bar", hideKey = "hideResourceBar"},
|
||||
{name = "CoAMultiCastActionBarFrame", moverText = "Multi Cast Action Bar", hideKey = "hideMultiCastActionBar"},
|
||||
}
|
||||
|
||||
local FRAME_NAMES = {}
|
||||
for _, def in ipairs(FRAMES) do
|
||||
FRAME_NAMES[def.name] = true
|
||||
end
|
||||
|
||||
-- These frames' native right-click dropdown offers a "Lock/Unlock Frame"
|
||||
-- entry that's meaningless now that dragging is permanently disabled (see
|
||||
-- DisableDrag below), so strip it. Matched loosely (case-insensitive,
|
||||
-- requiring both words) since the exact wording isn't confirmed, and scoped
|
||||
-- to only our tracked frames' dropdowns so it can't affect unrelated menus.
|
||||
do
|
||||
local function IsTrackedDropdown(menu)
|
||||
if not menu then return false end
|
||||
|
||||
local name = menu.GetName and menu:GetName()
|
||||
local ownerName = name and name:match("^(.-)DropDown$")
|
||||
if ownerName and FRAME_NAMES[ownerName] then return true end
|
||||
|
||||
local parent = menu.GetParent and menu:GetParent()
|
||||
local parentName = parent and parent.GetName and parent:GetName()
|
||||
return parentName and FRAME_NAMES[parentName] or false
|
||||
end
|
||||
|
||||
local orig_AddButton = UIDropDownMenu_AddButton
|
||||
UIDropDownMenu_AddButton = function(info, level)
|
||||
if info and info.text and IsTrackedDropdown(UIDROPDOWNMENU_INIT_MENU) then
|
||||
local text = info.text:lower()
|
||||
if text:find("lock") and text:find("frame") then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return orig_AddButton(info, level)
|
||||
end
|
||||
end
|
||||
|
||||
-- Replace the native "Drag to move" tooltip hint with one that reflects how
|
||||
-- these frames are actually repositioned now (native drag is disabled).
|
||||
-- Scoped by IsMouseOver rather than GameTooltip:GetOwner(), since the owner
|
||||
-- passed to SetOwner may be a child region rather than the tracked frame
|
||||
-- itself. Matched loosely (case-insensitive "drag" + "move") since the exact
|
||||
-- wording isn't confirmed. Hooked on both OnShow and OnUpdate since some
|
||||
-- tooltips keep refreshing their lines while visible.
|
||||
local tooltipFrames = {}
|
||||
local tooltipHooked = false
|
||||
|
||||
local function ReplaceTooltipLines()
|
||||
local hovering = false
|
||||
for frame in pairs(tooltipFrames) do
|
||||
if frame:IsMouseOver() then
|
||||
hovering = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not hovering then return end
|
||||
|
||||
for i = 1, GameTooltip:NumLines() do
|
||||
local line = _G["GameTooltipTextLeft"..i]
|
||||
local text = line and line:GetText()
|
||||
if text then
|
||||
-- Some lines combine the drag hint with other text (e.g. a
|
||||
-- trailing "Right-click for options"), so replace just the
|
||||
-- matched span instead of the whole line.
|
||||
local replaced, count = text:gsub("[Dd]rag.-[Mm]ove", "Toggle ElvUI's anchors to move")
|
||||
if count > 0 then
|
||||
line:SetText(replaced)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function FixTooltip(frame)
|
||||
if tooltipFrames[frame] then return end
|
||||
tooltipFrames[frame] = true
|
||||
|
||||
if not tooltipHooked then
|
||||
tooltipHooked = true
|
||||
GameTooltip:HookScript("OnShow", ReplaceTooltipLines)
|
||||
GameTooltip:HookScript("OnUpdate", ReplaceTooltipLines)
|
||||
end
|
||||
end
|
||||
|
||||
-- A single nil-out isn't enough: the frame's own update logic re-attaches
|
||||
-- OnDragStart/OnDragStop (and re-registers drag buttons) on refresh, and
|
||||
-- native dragging ends with the engine calling SetPoint directly on the
|
||||
-- frame, severing the live anchor to our mover holder. So we don't just
|
||||
-- clear drag once, we permanently intercept any future attempt to turn it
|
||||
-- back on. The CoAClearing* guards stop our own corrective calls from
|
||||
-- re-triggering these same hooks.
|
||||
local function DisableDrag(frame)
|
||||
if frame.CoADragDisabled then return end
|
||||
frame.CoADragDisabled = true
|
||||
|
||||
frame:SetScript("OnDragStart", nil)
|
||||
frame:SetScript("OnDragStop", nil)
|
||||
frame:RegisterForDrag()
|
||||
|
||||
hooksecurefunc(frame, "SetScript", function(self, script, handler)
|
||||
if handler and (script == "OnDragStart" or script == "OnDragStop") and not self.CoAClearingDragScript then
|
||||
self.CoAClearingDragScript = true
|
||||
self:SetScript(script, nil)
|
||||
self.CoAClearingDragScript = false
|
||||
end
|
||||
end)
|
||||
|
||||
hooksecurefunc(frame, "RegisterForDrag", function(self, ...)
|
||||
if select("#", ...) > 0 and not self.CoAClearingDrag then
|
||||
self.CoAClearingDrag = true
|
||||
self:RegisterForDrag()
|
||||
self.CoAClearingDrag = false
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Anchoring a frame while in combat lockdown can taint it, so anchors
|
||||
-- queued during combat are batched and applied together on the next
|
||||
-- PLAYER_REGEN_ENABLED instead of each frame registering its own handler
|
||||
-- (which would stomp on each other, since AceEvent keeps only the most
|
||||
-- recently registered callback per event for a given object).
|
||||
local pendingAnchors = {}
|
||||
local combatHandlerRegistered = false
|
||||
|
||||
local function QueueAnchor(fn)
|
||||
if not InCombatLockdown() then
|
||||
fn()
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(pendingAnchors, fn)
|
||||
|
||||
if not combatHandlerRegistered then
|
||||
combatHandlerRegistered = true
|
||||
|
||||
CoA:RegisterEvent("PLAYER_REGEN_ENABLED", function()
|
||||
for i = 1, #pendingAnchors do
|
||||
pendingAnchors[i]()
|
||||
end
|
||||
|
||||
wipe(pendingAnchors)
|
||||
CoA:UnregisterEvent("PLAYER_REGEN_ENABLED")
|
||||
combatHandlerRegistered = false
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local function AnchorToHolder(frame, holder)
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("CENTER", holder, "CENTER")
|
||||
end
|
||||
|
||||
-- Class resource frames re-anchor themselves (e.g. relative to the player/
|
||||
-- target frame) whenever they refresh, which stomps our mover anchor. Since
|
||||
-- there's no event that fires only for that self-repositioning, we hook
|
||||
-- SetPoint itself and snap back to the holder any time something else moves
|
||||
-- the frame. CoARepositioning guards against the corrective SetPoint call
|
||||
-- re-triggering this same hook.
|
||||
local function LockPosition(frame, holder)
|
||||
if frame.CoAPositionLocked then return end
|
||||
frame.CoAPositionLocked = true
|
||||
|
||||
hooksecurefunc(frame, "SetPoint", function(self)
|
||||
if self.CoARepositioning then return end
|
||||
self.CoARepositioning = true
|
||||
|
||||
QueueAnchor(function()
|
||||
AnchorToHolder(self, holder)
|
||||
self.CoARepositioning = false
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local function SetupMover(frame, name, moverText)
|
||||
if frame.CoAMoverCreated then return true end
|
||||
|
||||
local width, height = frame:GetSize()
|
||||
if width == 0 or height == 0 then return false end
|
||||
|
||||
local left, bottom = frame:GetLeft(), frame:GetBottom()
|
||||
if not left or not bottom then return false end
|
||||
|
||||
frame.CoAMoverCreated = true
|
||||
|
||||
local holder = CreateFrame("Frame", "CoA_"..name.."Holder", E.UIParent)
|
||||
holder:Size(width, height)
|
||||
holder:Point("BOTTOMLEFT", E.UIParent, "BOTTOMLEFT", left, bottom)
|
||||
|
||||
E:CreateMover(holder, "CoA_"..name.."Mover", moverText, nil, nil, nil, "ALL,COA", nil, "CoA,skin,classResources")
|
||||
holder:SetAllPoints(_G["CoA_"..name.."Mover"])
|
||||
|
||||
QueueAnchor(function()
|
||||
AnchorToHolder(frame, holder)
|
||||
LockPosition(frame, holder)
|
||||
end)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- The server never Show()s a frame the current class doesn't use, so its
|
||||
-- data (powerType, maxValue, ...) is never populated. Forcing Show() on it
|
||||
-- exposes that uninitialized state and renders as a garbage white bar
|
||||
-- texture (same issue and same fix as HideCoAUI). Only force-show a frame
|
||||
-- we've already seen the game display naturally at least once.
|
||||
local seenNaturalShow = {}
|
||||
|
||||
-- CoAForceHidden distinguishes frames we hid ourselves (via the options
|
||||
-- checkbox) from frames the game itself decided to hide, so unchecking the
|
||||
-- box only restores frames we were suppressing.
|
||||
local function ApplyVisibility(frame, hideKey)
|
||||
if CoA.db.profile[hideKey] then
|
||||
frame.CoAForceHidden = true
|
||||
frame:Hide()
|
||||
elseif frame.CoAForceHidden and seenNaturalShow[frame] then
|
||||
frame.CoAForceHidden = false
|
||||
frame:Show()
|
||||
end
|
||||
end
|
||||
|
||||
local function SetupVisibility(frame, hideKey)
|
||||
if frame.CoAVisibilityHooked then return end
|
||||
frame.CoAVisibilityHooked = true
|
||||
|
||||
frame:HookScript("OnShow", function(self)
|
||||
seenNaturalShow[self] = true
|
||||
ApplyVisibility(self, hideKey)
|
||||
end)
|
||||
|
||||
ApplyVisibility(frame, hideKey)
|
||||
end
|
||||
|
||||
function CoA:UpdateClassResourceVisibility()
|
||||
for _, def in ipairs(FRAMES) do
|
||||
local frame = _G[def.name]
|
||||
if frame then
|
||||
ApplyVisibility(frame, def.hideKey)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local hooked = {}
|
||||
|
||||
local function TryHookAll()
|
||||
local allHooked = true
|
||||
|
||||
for _, def in ipairs(FRAMES) do
|
||||
if not hooked[def.name] then
|
||||
local frame = _G[def.name]
|
||||
|
||||
if frame then
|
||||
DisableDrag(frame)
|
||||
SetupVisibility(frame, def.hideKey)
|
||||
FixTooltip(frame)
|
||||
|
||||
if SetupMover(frame, def.name, def.moverText) then
|
||||
hooked[def.name] = true
|
||||
else
|
||||
allHooked = false
|
||||
end
|
||||
else
|
||||
allHooked = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return allHooked
|
||||
end
|
||||
|
||||
function CoA:InitializeClassResources()
|
||||
if TryHookAll() then return end
|
||||
|
||||
self.classResourcesTimer = self:ScheduleRepeatingTimer(function()
|
||||
if TryHookAll() then
|
||||
self:CancelTimer(self.classResourcesTimer)
|
||||
self.classResourcesTimer = nil
|
||||
end
|
||||
end, 0.5)
|
||||
end
|
||||
@@ -0,0 +1,86 @@
|
||||
local E, L, V, P, G = unpack(ElvUI)
|
||||
local UF = E:GetModule("UnitFrames")
|
||||
local CoA = E:GetModule("CoA")
|
||||
|
||||
-- Base dispel types each custom class can remove, independent of talents.
|
||||
-- CHRONOMANCER is a special case: its dispel removes the last debuff applied
|
||||
-- to the target regardless of type, so it's never filtered out here -- we
|
||||
-- can only approximate this as "treat every typed debuff as dispellable",
|
||||
-- since the underlying oUF scan only ever surfaces typed debuffs anyway.
|
||||
local CLASS_DISPEL_TYPES = {
|
||||
CHRONOMANCER = true,
|
||||
SUNCLERIC = {Magic = true, Poison = true, Disease = true},
|
||||
STARCALLER = {Poison = true, Disease = true},
|
||||
PROPHET = {Poison = true},
|
||||
WITCHDOCTOR = {Curse = true},
|
||||
CULTIST = {Magic = true},
|
||||
PYROMANCER = {},
|
||||
}
|
||||
|
||||
-- Extra dispel types unlocked by a talent choice. There's no reliable way to
|
||||
-- auto-detect the talent on this server, so these are gated by a manual
|
||||
-- checkbox in the options panel instead.
|
||||
local TALENT_DISPEL_TYPES = {
|
||||
PROPHET = {flag = "hasBlightAntidote", types = {Curse = true}},
|
||||
CULTIST = {flag = "hasDevourCurse", types = {Curse = true}},
|
||||
PYROMANCER = {flag = "hasBurnImpurities", types = {Magic = true, Disease = true, Bleed = true}},
|
||||
}
|
||||
|
||||
function CoA:CanDispel(debuffType)
|
||||
local _, class = UnitClass("player")
|
||||
local baseTypes = CLASS_DISPEL_TYPES[class]
|
||||
|
||||
if baseTypes == true then return true end
|
||||
if baseTypes and baseTypes[debuffType] then return true end
|
||||
|
||||
local talent = TALENT_DISPEL_TYPES[class]
|
||||
if talent and CoA.db.profile[talent.flag] and talent.types[debuffType] then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function SuppressHighlight(object)
|
||||
if object.DebuffHighlightBackdrop and object.DBHGlow then
|
||||
object.DBHGlow:Hide()
|
||||
elseif object.DebuffHighlightUseTexture then
|
||||
object.DebuffHighlight:SetTexture(nil)
|
||||
else
|
||||
object.DebuffHighlight:SetVertexColor(0, 0, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local origPostUpdate = UF.PostUpdate_DebuffHighlight
|
||||
|
||||
local function DispelAwarePostUpdate(dbh, object, debuffType, texture, wasFiltered, style, color)
|
||||
origPostUpdate(dbh, object, debuffType, texture, wasFiltered, style, color)
|
||||
|
||||
if CoA.db.profile.dispelHighlightOnlyMine and debuffType and not wasFiltered and not CoA:CanDispel(debuffType) then
|
||||
SuppressHighlight(object)
|
||||
end
|
||||
end
|
||||
|
||||
UF.PostUpdate_DebuffHighlight = DispelAwarePostUpdate
|
||||
|
||||
hooksecurefunc(UF, "Configure_DebuffHighlight", function(_, frame)
|
||||
local dbh = frame.DebuffHighlight
|
||||
if dbh then
|
||||
dbh.PostUpdate = UF.PostUpdate_DebuffHighlight
|
||||
end
|
||||
end)
|
||||
|
||||
function CoA:UpdateDispelHighlight()
|
||||
UF:Update_AllFrames()
|
||||
end
|
||||
|
||||
-- On Ascension, UnitClass("player")'s second return only reliably reports the
|
||||
-- real custom class (CULTIST, PYROMANCER, ...) a short while after login --
|
||||
-- immediately at ADDON_LOADED/PLAYER_LOGIN it can still read back the generic
|
||||
-- "HERO" base class. If a debuff highlight gets evaluated before that data
|
||||
-- syncs, CoA:CanDispel wrongly returns false and the highlight stays wrongly
|
||||
-- suppressed until the next aura change. Force one extra refresh shortly
|
||||
-- after entering the world so the very first debuff isn't judged too early.
|
||||
function CoA:InitializeDispelHighlight()
|
||||
CoA:RegisterEvent("PLAYER_ENTERING_WORLD", function()
|
||||
CoA:ScheduleTimer("UpdateDispelHighlight", 2)
|
||||
end)
|
||||
end
|
||||
+85
-13
@@ -1,8 +1,12 @@
|
||||
local E, L, V, P, G = unpack(ElvUI)
|
||||
local LSM = E.Libs.LSM
|
||||
local CoA = E:GetModule("CoA")
|
||||
|
||||
local CONTAINER_NAME = "ExtraActionBar"
|
||||
local BUTTON_NAME = "ExtraActionBarPoolFrameExtraActionButtonTemplate1"
|
||||
local BINDING_ACTION = "CLICK "..BUTTON_NAME..":LeftButton"
|
||||
|
||||
_G["BINDING_NAME_"..BINDING_ACTION] = "Extra Action Button"
|
||||
|
||||
local function SkinGlow(button, name)
|
||||
local glow = _G[name.."SpellActivationAlert"]
|
||||
@@ -16,10 +20,72 @@ local function SkinGlow(button, name)
|
||||
end
|
||||
end
|
||||
|
||||
local function SkinButton(button)
|
||||
local RIM_EDGE_RATIO = 6 / 52 -- 6px confirmed empirically at the native 52px size to fully hide the icon's baked-in vanilla border
|
||||
|
||||
local function UpdateRimEdge(button, size)
|
||||
local rim = button and button.CoARim
|
||||
if not rim then return end
|
||||
|
||||
rim:SetBackdrop({edgeFile = E.media.blankTex, edgeSize = size * RIM_EDGE_RATIO})
|
||||
rim:SetBackdropBorderColor(unpack(E.media.bordercolor))
|
||||
end
|
||||
|
||||
local HOTKEY_MARGIN = 3
|
||||
|
||||
local function UpdateHotkeyPosition(button, size)
|
||||
local hotkey = button and _G[button:GetName().."HotKey"]
|
||||
if not hotkey then return end
|
||||
|
||||
local inset = -(size * RIM_EDGE_RATIO + HOTKEY_MARGIN)
|
||||
hotkey:ClearAllPoints()
|
||||
hotkey:Point("TOPRIGHT", inset, inset)
|
||||
end
|
||||
|
||||
local function UpdateSize(button)
|
||||
button = button or _G[BUTTON_NAME]
|
||||
if button then
|
||||
local size = CoA.db.profile.extraActionButtonSize or 52
|
||||
button:SetSize(size, size)
|
||||
UpdateRimEdge(button, size)
|
||||
UpdateHotkeyPosition(button, size)
|
||||
end
|
||||
end
|
||||
|
||||
function CoA:UpdateExtraActionButtonSize()
|
||||
UpdateSize()
|
||||
end
|
||||
|
||||
local function UpdateHotkeyText(button)
|
||||
button = button or _G[BUTTON_NAME]
|
||||
local hotkey = button and _G[button:GetName().."HotKey"]
|
||||
if not hotkey then return end
|
||||
|
||||
local key = GetBindingKey(BINDING_ACTION)
|
||||
if key then
|
||||
hotkey:SetText(GetBindingText(key, "KEY_"))
|
||||
hotkey:Show()
|
||||
else
|
||||
hotkey:SetText("")
|
||||
hotkey:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function RecenterAnchor(button, container)
|
||||
local x, y = button:GetCenter()
|
||||
local cx, cy = container:GetCenter()
|
||||
if not (x and y and cx and cy) then return end
|
||||
|
||||
button:ClearAllPoints()
|
||||
button:SetPoint("CENTER", container, "CENTER", x - cx, y - cy)
|
||||
end
|
||||
|
||||
local function SkinButton(button, container)
|
||||
if button.CoASkinned then return end
|
||||
button.CoASkinned = true
|
||||
|
||||
RecenterAnchor(button, container)
|
||||
UpdateSize(button)
|
||||
|
||||
local name = button:GetName()
|
||||
local icon = _G[name.."Icon"]
|
||||
local hotkey = _G[name.."HotKey"]
|
||||
@@ -57,14 +123,18 @@ local function SkinButton(button)
|
||||
local rim = CreateFrame("Frame", nil, button.backdrop)
|
||||
rim:SetAllPoints(icon)
|
||||
rim:SetFrameLevel(button.backdrop:GetFrameLevel() + 10)
|
||||
rim:SetBackdrop({edgeFile = E.media.blankTex, edgeSize = 6})
|
||||
rim:SetBackdropBorderColor(unpack(E.media.bordercolor))
|
||||
button.CoARim = rim
|
||||
UpdateRimEdge(button, CoA.db.profile.extraActionButtonSize or 52)
|
||||
|
||||
button:SetFrameLevel(rim:GetFrameLevel() + 1)
|
||||
end
|
||||
|
||||
if hotkey then
|
||||
hotkey:ClearAllPoints()
|
||||
hotkey:Point("TOPRIGHT", 0, 0)
|
||||
hotkey:FontTemplate()
|
||||
hotkey:FontTemplate(LSM:Fetch("font", E.db.actionbar.font), E.db.actionbar.fontSize, E.db.actionbar.fontOutline)
|
||||
hotkey:SetTextColor(1, 1, 1)
|
||||
|
||||
UpdateHotkeyText(button)
|
||||
CoA:RegisterEvent("UPDATE_BINDINGS", function() UpdateHotkeyText(button) end)
|
||||
end
|
||||
|
||||
if macroText then
|
||||
@@ -88,13 +158,13 @@ local function SkinButton(button)
|
||||
end
|
||||
|
||||
local function SetupMover(container, button)
|
||||
if CoA.extraActionBarMoverCreated then return end
|
||||
if CoA.extraActionBarMoverCreated then return true end
|
||||
|
||||
local width, height = button:GetSize()
|
||||
if width == 0 or height == 0 then return end
|
||||
if width == 0 or height == 0 then return false end
|
||||
|
||||
local left, bottom = button:GetLeft(), button:GetBottom()
|
||||
if not left or not bottom then return end
|
||||
if not left or not bottom then return false end
|
||||
|
||||
CoA.extraActionBarMoverCreated = true
|
||||
|
||||
@@ -105,7 +175,7 @@ local function SetupMover(container, button)
|
||||
holder:Size(width, height)
|
||||
holder:Point("BOTTOMLEFT", E.UIParent, "BOTTOMLEFT", left, bottom)
|
||||
|
||||
E:CreateMover(holder, "CoA_ExtraActionBarMover", "Extra Action Bar", nil, nil, nil, "ALL,ACTIONBARS", nil, "CoA,skin,extraActionBar")
|
||||
E:CreateMover(holder, "CoA_ExtraActionBarMover", "Extra Action Bar", nil, nil, nil, "ALL,COA", nil, "CoA,skin,extraActionBar")
|
||||
holder:SetAllPoints(_G["CoA_ExtraActionBarMover"])
|
||||
|
||||
local function Anchor()
|
||||
@@ -121,6 +191,8 @@ local function SetupMover(container, button)
|
||||
else
|
||||
Anchor()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function TryHook()
|
||||
@@ -128,11 +200,11 @@ local function TryHook()
|
||||
local button = _G[BUTTON_NAME]
|
||||
|
||||
if container and button then
|
||||
SetupMover(container, button)
|
||||
SkinButton(button)
|
||||
SkinButton(button, container)
|
||||
return SetupMover(container, button)
|
||||
end
|
||||
|
||||
return container ~= nil and button ~= nil
|
||||
return false
|
||||
end
|
||||
|
||||
function CoA:InitializeExtraActionBar()
|
||||
|
||||
+51
-12
@@ -1,7 +1,27 @@
|
||||
local E, L, V, P, G = unpack(ElvUI)
|
||||
local LSM = E.Libs.LSM
|
||||
local CoA = E:GetModule("CoA")
|
||||
|
||||
local BUTTON_NAME = "LayerPickerFrame"
|
||||
local MIN_WIDTH, MIN_HEIGHT = 66, 14
|
||||
local PAD_X, PAD_Y = 10, 10
|
||||
|
||||
local function UpdateFont(button)
|
||||
button = button or _G[BUTTON_NAME]
|
||||
local text = button and _G[button:GetName().."Text"]
|
||||
if not text then return end
|
||||
|
||||
text:FontTemplate(LSM:Fetch("font", CoA.db.profile.instanceButtonFont), CoA.db.profile.instanceButtonFontSize, CoA.db.profile.instanceButtonFontOutline)
|
||||
|
||||
button:SetSize(
|
||||
math.max(MIN_WIDTH, text:GetStringWidth() + PAD_X),
|
||||
math.max(MIN_HEIGHT, text:GetStringHeight() + PAD_Y)
|
||||
)
|
||||
end
|
||||
|
||||
function CoA:UpdateInstanceButtonFont()
|
||||
UpdateFont()
|
||||
end
|
||||
|
||||
do
|
||||
local orig_AddButton = UIDropDownMenu_AddButton
|
||||
@@ -14,22 +34,44 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
-- A single nil-out isn't enough: native dragging ends with the engine
|
||||
-- calling SetPoint directly on the frame, severing the live anchor to our
|
||||
-- mover holder. So we don't just clear drag once, we permanently intercept
|
||||
-- any future attempt to turn it back on. The CoAClearing* guards stop our
|
||||
-- own corrective calls from re-triggering these same hooks.
|
||||
local function DisableDrag(button)
|
||||
if button.CoADragDisabled then return end
|
||||
button.CoADragDisabled = true
|
||||
|
||||
button:SetScript("OnDragStart", nil)
|
||||
button:SetScript("OnDragStop", nil)
|
||||
button:RegisterForDrag()
|
||||
|
||||
hooksecurefunc(button, "SetScript", function(self, script, handler)
|
||||
if handler and (script == "OnDragStart" or script == "OnDragStop") and not self.CoAClearingDragScript then
|
||||
self.CoAClearingDragScript = true
|
||||
self:SetScript(script, nil)
|
||||
self.CoAClearingDragScript = false
|
||||
end
|
||||
end)
|
||||
|
||||
hooksecurefunc(button, "RegisterForDrag", function(self, ...)
|
||||
if select("#", ...) > 0 and not self.CoAClearingDrag then
|
||||
self.CoAClearingDrag = true
|
||||
self:RegisterForDrag()
|
||||
self.CoAClearingDrag = false
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function SetupMover(button)
|
||||
if CoA.layerPickerMoverCreated then return end
|
||||
if CoA.layerPickerMoverCreated then return true end
|
||||
|
||||
local width, height = button:GetSize()
|
||||
if width == 0 or height == 0 then return end
|
||||
if width == 0 or height == 0 then return false end
|
||||
|
||||
local left, bottom = button:GetLeft(), button:GetBottom()
|
||||
if not left or not bottom then return end
|
||||
if not left or not bottom then return false end
|
||||
|
||||
CoA.layerPickerMoverCreated = true
|
||||
|
||||
@@ -37,7 +79,7 @@ local function SetupMover(button)
|
||||
holder:Size(width, height)
|
||||
holder:Point("BOTTOMLEFT", E.UIParent, "BOTTOMLEFT", left, bottom)
|
||||
|
||||
E:CreateMover(holder, "CoA_LayerPickerMover", "Instance", nil, nil, nil, nil, nil, "CoA,skin,instance")
|
||||
E:CreateMover(holder, "CoA_LayerPickerMover", "Instance", nil, nil, nil, "ALL,COA", nil, "CoA,skin,instance")
|
||||
holder:SetAllPoints(_G["CoA_LayerPickerMover"])
|
||||
_G["CoA_LayerPickerMover"]:SetFrameStrata("FULLSCREEN")
|
||||
|
||||
@@ -54,22 +96,19 @@ local function SetupMover(button)
|
||||
else
|
||||
Anchor()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function SkinButton(button)
|
||||
if button.CoASkinned then return end
|
||||
button.CoASkinned = true
|
||||
|
||||
local name = button:GetName()
|
||||
local text = _G[name.."Text"]
|
||||
|
||||
button:StripTextures(true)
|
||||
button:CreateBackdrop("Default")
|
||||
button:StyleButton()
|
||||
|
||||
if text then
|
||||
text:FontTemplate()
|
||||
end
|
||||
UpdateFont(button)
|
||||
end
|
||||
|
||||
local function TryHook()
|
||||
@@ -77,11 +116,11 @@ local function TryHook()
|
||||
|
||||
if button then
|
||||
DisableDrag(button)
|
||||
SetupMover(button)
|
||||
SkinButton(button)
|
||||
return SetupMover(button)
|
||||
end
|
||||
|
||||
return button ~= nil
|
||||
return false
|
||||
end
|
||||
|
||||
function CoA:InitializeLayerPicker()
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
# ElvUI_CoA
|
||||
# ElvUI_CoA (WIP)
|
||||
|
||||
ElvUI plugin that hides and skins the custom Conquest of Azeroth (Ascension WoW, client 3.3.5) server frames not covered by stock ElvUI.
|
||||
ElvUI plugin that hides and skins the custom CoA (3.3.5) frames not covered by stock ElvUI.
|
||||
|
||||
## Implemented
|
||||
|
||||
- Class resource frames: hiding
|
||||
- Extra action button frame: skin, anchor, size
|
||||
- Instance swap frame: skin, anchor, font settings
|
||||
- Only highlight unitframes if dispellable by your CoA class, with manual talent switch.
|
||||
|
||||
## Installation
|
||||
|
||||
Download the repo as .zip, extract in `Interface\AddOns`, remove `-master` from the folder name.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
||||
@@ -1,39 +1,352 @@
|
||||
local E, L, V, P, G = unpack(ElvUI)
|
||||
local EP = E.Libs.EP
|
||||
local ACH = LibStub("LibAceConfigHelper")
|
||||
local AceDB = LibStub("AceDB-3.0")
|
||||
local AceDBOptions = LibStub("AceDBOptions-3.0")
|
||||
|
||||
local AddOnName = ...
|
||||
|
||||
BINDING_HEADER_COA = "Conquest of Azeroth"
|
||||
|
||||
local CoA = E:NewModule("CoA", "AceEvent-3.0", "AceTimer-3.0")
|
||||
E.CoA = CoA
|
||||
|
||||
V.CoA = {}
|
||||
local defaults = {
|
||||
profile = {
|
||||
extraActionButtonSize = 52,
|
||||
instanceButtonFont = "PT Sans Narrow",
|
||||
instanceButtonFontSize = 12,
|
||||
instanceButtonFontOutline = "OUTLINE",
|
||||
dispelHighlightOnlyMine = false,
|
||||
hasBlightAntidote = false,
|
||||
hasDevourCurse = false,
|
||||
hasBurnImpurities = false,
|
||||
hideResourceSegmentBar = false,
|
||||
hideResourceOrb = false,
|
||||
hideResourceBar = false,
|
||||
hideMultiCastActionBar = false,
|
||||
},
|
||||
}
|
||||
|
||||
function CoA:RefreshConfig()
|
||||
if self.UpdateExtraActionButtonSize then self:UpdateExtraActionButtonSize() end
|
||||
if self.UpdateInstanceButtonFont then self:UpdateInstanceButtonFont() end
|
||||
if self.UpdateDispelHighlight then self:UpdateDispelHighlight() end
|
||||
if self.UpdateClassResourceVisibility then self:UpdateClassResourceVisibility() end
|
||||
end
|
||||
|
||||
CoA:RegisterEvent("ADDON_LOADED", function(_, addon)
|
||||
if addon ~= AddOnName then return end
|
||||
CoA:UnregisterEvent("ADDON_LOADED")
|
||||
|
||||
CoA.db = AceDB:New("ElvUI_CoADB", defaults, true)
|
||||
|
||||
CoA.db.RegisterCallback(CoA, "OnProfileChanged", "RefreshConfig")
|
||||
CoA.db.RegisterCallback(CoA, "OnProfileCopied", "RefreshConfig")
|
||||
CoA.db.RegisterCallback(CoA, "OnProfileReset", "RefreshConfig")
|
||||
end)
|
||||
|
||||
local function getOptions()
|
||||
local profiles = AceDBOptions:GetOptionsTable(CoA.db)
|
||||
profiles.order = 5
|
||||
|
||||
local options = {
|
||||
order = 55,
|
||||
type = "group",
|
||||
childGroups = "tab",
|
||||
name = string.format("|cff1784d1%s|r", "Conquest of Azeroth"),
|
||||
args = {
|
||||
extraActionButton = {
|
||||
classResources = {
|
||||
order = 1,
|
||||
type = "group",
|
||||
name = "Extra Action Button",
|
||||
args = {},
|
||||
name = "Class Resources",
|
||||
args = {
|
||||
header = {
|
||||
order = 1,
|
||||
type = "header",
|
||||
name = "Class Resources",
|
||||
},
|
||||
desc = {
|
||||
order = 2,
|
||||
type = "description",
|
||||
name = "You can move these elements with Toggle Anchors.\n",
|
||||
},
|
||||
hideResourceSegmentBar = {
|
||||
order = 3,
|
||||
type = "toggle",
|
||||
name = "Hide CoAResourceSegmentBar",
|
||||
width = "full",
|
||||
get = function() return CoA.db.profile.hideResourceSegmentBar end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hideResourceSegmentBar = value
|
||||
|
||||
if CoA.UpdateClassResourceVisibility then
|
||||
CoA:UpdateClassResourceVisibility()
|
||||
end
|
||||
end,
|
||||
},
|
||||
hideResourceOrb = {
|
||||
order = 4,
|
||||
type = "toggle",
|
||||
name = "Hide CoAResourceOrb",
|
||||
width = "full",
|
||||
get = function() return CoA.db.profile.hideResourceOrb end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hideResourceOrb = value
|
||||
|
||||
if CoA.UpdateClassResourceVisibility then
|
||||
CoA:UpdateClassResourceVisibility()
|
||||
end
|
||||
end,
|
||||
},
|
||||
hideResourceBar = {
|
||||
order = 5,
|
||||
type = "toggle",
|
||||
name = "Hide CoAResourceBar",
|
||||
width = "full",
|
||||
get = function() return CoA.db.profile.hideResourceBar end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hideResourceBar = value
|
||||
|
||||
if CoA.UpdateClassResourceVisibility then
|
||||
CoA:UpdateClassResourceVisibility()
|
||||
end
|
||||
end,
|
||||
},
|
||||
hideMultiCastActionBar = {
|
||||
order = 6,
|
||||
type = "toggle",
|
||||
name = "Hide CoAMultiCastActionBarFrame",
|
||||
width = "full",
|
||||
get = function() return CoA.db.profile.hideMultiCastActionBar end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hideMultiCastActionBar = value
|
||||
|
||||
if CoA.UpdateClassResourceVisibility then
|
||||
CoA:UpdateClassResourceVisibility()
|
||||
end
|
||||
end,
|
||||
},
|
||||
hideAll = {
|
||||
order = 7,
|
||||
type = "execute",
|
||||
name = "Hide All",
|
||||
func = function()
|
||||
CoA.db.profile.hideResourceSegmentBar = true
|
||||
CoA.db.profile.hideResourceOrb = true
|
||||
CoA.db.profile.hideResourceBar = true
|
||||
CoA.db.profile.hideMultiCastActionBar = true
|
||||
|
||||
if CoA.UpdateClassResourceVisibility then
|
||||
CoA:UpdateClassResourceVisibility()
|
||||
end
|
||||
end,
|
||||
},
|
||||
showAll = {
|
||||
order = 8,
|
||||
type = "execute",
|
||||
name = "Show All",
|
||||
func = function()
|
||||
CoA.db.profile.hideResourceSegmentBar = false
|
||||
CoA.db.profile.hideResourceOrb = false
|
||||
CoA.db.profile.hideResourceBar = false
|
||||
CoA.db.profile.hideMultiCastActionBar = false
|
||||
|
||||
if CoA.UpdateClassResourceVisibility then
|
||||
CoA:UpdateClassResourceVisibility()
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
},
|
||||
instanceSwap = {
|
||||
extraActionButton = {
|
||||
order = 2,
|
||||
type = "group",
|
||||
name = "Instance Swap",
|
||||
args = {},
|
||||
name = "Extra Action Button",
|
||||
args = {
|
||||
header = {
|
||||
order = 1,
|
||||
type = "header",
|
||||
name = "Extra Action Button",
|
||||
},
|
||||
desc = {
|
||||
order = 2,
|
||||
type = "description",
|
||||
name = "You can move this element with Toggle Anchors.\n",
|
||||
},
|
||||
size = {
|
||||
order = 3,
|
||||
type = "range",
|
||||
name = "Size",
|
||||
desc = "Adjust the width/height of the Extra Action Button, in pixels.",
|
||||
min = 30,
|
||||
max = 100,
|
||||
step = 1,
|
||||
get = function() return CoA.db.profile.extraActionButtonSize end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.extraActionButtonSize = value
|
||||
|
||||
if CoA.UpdateExtraActionButtonSize then
|
||||
CoA:UpdateExtraActionButtonSize()
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
},
|
||||
instanceSwap = {
|
||||
order = 3,
|
||||
type = "group",
|
||||
name = "Instance Swap",
|
||||
args = {
|
||||
header = {
|
||||
order = 1,
|
||||
type = "header",
|
||||
name = "Instance Swap",
|
||||
},
|
||||
desc = {
|
||||
order = 2,
|
||||
type = "description",
|
||||
name = "You can move this element with Toggle Anchors.\n",
|
||||
},
|
||||
instanceFont = {
|
||||
order = 3,
|
||||
type = "group",
|
||||
inline = true,
|
||||
name = "Instance Font",
|
||||
args = {
|
||||
font = ACH:SharedMediaFont("Font", nil, 1, nil,
|
||||
function() return CoA.db.profile.instanceButtonFont end,
|
||||
function(_, value)
|
||||
CoA.db.profile.instanceButtonFont = value
|
||||
|
||||
if CoA.UpdateInstanceButtonFont then
|
||||
CoA:UpdateInstanceButtonFont()
|
||||
end
|
||||
end),
|
||||
fontSize = {
|
||||
order = 2,
|
||||
type = "range",
|
||||
name = "Font Size",
|
||||
min = 8,
|
||||
max = 32,
|
||||
step = 1,
|
||||
get = function() return CoA.db.profile.instanceButtonFontSize end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.instanceButtonFontSize = value
|
||||
|
||||
if CoA.UpdateInstanceButtonFont then
|
||||
CoA:UpdateInstanceButtonFont()
|
||||
end
|
||||
end,
|
||||
},
|
||||
fontOutline = ACH:FontFlags("Font Outline", nil, 3, nil,
|
||||
function() return CoA.db.profile.instanceButtonFontOutline end,
|
||||
function(_, value)
|
||||
CoA.db.profile.instanceButtonFontOutline = value
|
||||
|
||||
if CoA.UpdateInstanceButtonFont then
|
||||
CoA:UpdateInstanceButtonFont()
|
||||
end
|
||||
end),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dispelHighlight = {
|
||||
order = 4,
|
||||
type = "group",
|
||||
name = "Debuff Highlighting",
|
||||
args = {
|
||||
header = {
|
||||
order = 1,
|
||||
type = "header",
|
||||
name = "Debuff Highlighting",
|
||||
},
|
||||
onlyMine = {
|
||||
order = 2,
|
||||
type = "toggle",
|
||||
name = "Only Highlight If Dispellable By Me",
|
||||
desc = "Suppress the debuff highlight on unitframes for debuff types your class cannot dispel.",
|
||||
get = function() return CoA.db.profile.dispelHighlightOnlyMine end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.dispelHighlightOnlyMine = value
|
||||
|
||||
if CoA.UpdateDispelHighlight then
|
||||
CoA:UpdateDispelHighlight()
|
||||
end
|
||||
end,
|
||||
},
|
||||
talents = {
|
||||
order = 3,
|
||||
type = "group",
|
||||
inline = true,
|
||||
name = "Talents",
|
||||
args = {
|
||||
hasBlightAntidote = {
|
||||
order = 1,
|
||||
type = "toggle",
|
||||
name = string.format("Blight Antidote (%s)", LOCALIZED_CLASS_NAMES_MALE.PROPHET),
|
||||
desc = "Grants Curse dispel.",
|
||||
get = function() return CoA.db.profile.hasBlightAntidote end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hasBlightAntidote = value
|
||||
|
||||
if CoA.UpdateDispelHighlight then
|
||||
CoA:UpdateDispelHighlight()
|
||||
end
|
||||
end,
|
||||
},
|
||||
hasDevourCurse = {
|
||||
order = 2,
|
||||
type = "toggle",
|
||||
name = string.format("Devour Curse (%s)", LOCALIZED_CLASS_NAMES_MALE.CULTIST),
|
||||
desc = "Grants Curse dispel.",
|
||||
get = function() return CoA.db.profile.hasDevourCurse end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hasDevourCurse = value
|
||||
|
||||
if CoA.UpdateDispelHighlight then
|
||||
CoA:UpdateDispelHighlight()
|
||||
end
|
||||
end,
|
||||
},
|
||||
hasBurnImpurities = {
|
||||
order = 3,
|
||||
type = "toggle",
|
||||
name = string.format("Burn Impurities (%s)", LOCALIZED_CLASS_NAMES_MALE.PYROMANCER),
|
||||
desc = "Grants Magic, Disease, and Bleed dispel.",
|
||||
get = function() return CoA.db.profile.hasBurnImpurities end,
|
||||
set = function(_, value)
|
||||
CoA.db.profile.hasBurnImpurities = value
|
||||
|
||||
if CoA.UpdateDispelHighlight then
|
||||
CoA:UpdateDispelHighlight()
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
profiles = profiles,
|
||||
},
|
||||
}
|
||||
|
||||
E.Options.args.CoA = options
|
||||
end
|
||||
|
||||
local function RegisterMoverCategory()
|
||||
for _, layout in ipairs(E.ConfigModeLayouts) do
|
||||
if layout == "COA" then return end
|
||||
end
|
||||
|
||||
tinsert(E.ConfigModeLayouts, "COA")
|
||||
E.ConfigModeLocalizedStrings.COA = "CoA"
|
||||
end
|
||||
|
||||
function CoA:Initialize()
|
||||
RegisterMoverCategory()
|
||||
|
||||
EP:RegisterPlugin(AddOnName, getOptions)
|
||||
|
||||
if self.InitializeExtraActionBar then
|
||||
@@ -43,6 +356,14 @@ function CoA:Initialize()
|
||||
if self.InitializeLayerPicker then
|
||||
self:InitializeLayerPicker()
|
||||
end
|
||||
|
||||
if self.InitializeDispelHighlight then
|
||||
self:InitializeDispelHighlight()
|
||||
end
|
||||
|
||||
if self.InitializeClassResources then
|
||||
self:InitializeClassResources()
|
||||
end
|
||||
end
|
||||
|
||||
local function InitializeCallback()
|
||||
|
||||
Reference in New Issue
Block a user