commit e70861fff1276f9fda7706481b49858bdc3a4be8 Author: Robin Date: Wed Jan 13 15:54:35 2021 +0100 Revision 25 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ea295cb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "Common"] + path = Common + url = https://git.grml.de/rilgamon/Common.git +[submodule "Moduls/Clickzz"] + path = Moduls/Clickzz + url = https://git.grml.de/rilgamon/Clickzz.git diff --git a/Common b/Common new file mode 160000 index 0000000..762c59c --- /dev/null +++ b/Common @@ -0,0 +1 @@ +Subproject commit 762c59c6c6abc6ecaa71f3e36bb2c6dd16b8e0ed diff --git a/Moduls/Clickzz b/Moduls/Clickzz new file mode 160000 index 0000000..8bdf839 --- /dev/null +++ b/Moduls/Clickzz @@ -0,0 +1 @@ +Subproject commit 8bdf839b68b524ba5e72d8f200f2db6aff41e647 diff --git a/core.lua b/core.lua new file mode 100644 index 0000000..c830689 --- /dev/null +++ b/core.lua @@ -0,0 +1,534 @@ +local name, addon = ... +addon['name'] = name +addon['parentName'] = name +addon['modsDir'] = 'Mods' +local hooked = {} +--[[*** Settings Start***]] +local HideBorder = true -- Hides the editboxborder when not active +local UnClamp = true -- Make the window movable (take care! you can move it out of your screen) +local HideFriendButton = true -- Hide the friendsbutton +local Unsticky = true -- Dont remember whispers for next enter +local NoSay = true -- Dont default chat to say +local realm +local defaults = { + ['cvars'] = { + ['autoLootDefault'] = "1", +-- ['consolidateBuffs'] = "0", + }, + ['dests'] = { + ["Tirion"] = "Ritalein", + ["Arygos"] = "Pastorella", + ["Thrall"] = "Gurnsey", + } +} + +defaults['dests'][GetRealmName()] = defaults['dests'][GetRealmName()] or UnitName("player") + +local dest = defaults['dests'][GetRealmName()] +local options = { +} +local rep = ERR_FRIEND_ONLINE_SS:gsub("%%s", "(.+)"):gsub("[%[%]]", "%%%1") +local rosterOfficer = {} +local rosterNotice = {} +local db +local ldbicon = LibStub:GetLibrary("LibDBIcon-1.0") +local function OnClick(self, button) + if(IsShiftKeyDown() and button == "LeftButton") then + addon['db']['global']['ldbicons'][name]['hide'] = not addon['db']['global']['ldbicons'][name]['hide'] + if(ldbicon) then + if(addon['db']['global']['ldbicons'][name]['hide']) then + ldbicon:Hide(name) + else + ldbicon:Show(name) + end + end + else + if(InterfaceOptionsFrame:IsVisible() and not InCombatLockdown()) then + InterfaceOptionsFrame:Hide() + else + InterfaceOptionsFrame_OpenToCategory(name) + InterfaceOptionsFrame_OpenToCategory(name) -- Twice because of a bug in InterfaceOptionsFrame_OpenToCategory + end + end +end +local postmaster = { + ['Der Postmeister'] = true, + ['The Postmaster'] = true, +} +local itemList = { + "52306","52307","52308","52309","43248","52492", -- cata + "83793","83794", -- mop + "115990","115993", -- wod +} + +local function itemCount() + local cnt = 0 + for _,v in ipairs(itemList) do + cnt = cnt + GetItemCount(v) + end + return cnt +end + +local function sendItem(id) + for _,v in ipairs(itemList) do + if(tonumber(v) == tonumber(id)) then + return true + end + end + return false +end + +local function guildRoster(nameC) + for i=1, GetNumGuildMembers() do + local name, _, _, _, _, _, note, officernote = GetGuildRosterInfo(i) +-- local name, rank, rankIndex, level, classDisplayName, zone, note, officernote, isOnline, status, class, achievementPoints, achievementRank, isMobile, canSoR, repStanding = GetGuildRosterInfo(index) + local n = strsplit("-",name) + if(name == nameC or n == nameC) then + rosterOfficer[nameC] = officernote + rosterNotice[nameC] = note + return (rosterOfficer[nameC] and rosterOfficer[nameC]~="") and rosterOfficer[nameC] or (rosterNotice[nameC] or "") + else +-- + end + end + for i=1,C_FriendList.GetNumFriends() do + local name, _, _, _, _, _, note = C_FriendList.GetFriendInfo(i) + if(name==nameC) then + return note and note or "" + end + end + for i=1,BNGetNumFriends() do + local _, characterName, messageText, noteText + if(BNGetFriendInfo) then + _, _, _, _, characterName, _, _, _, _, _, _, messageText, noteText = BNGetFriendInfo(i) + else + local res = C_BattleNet.GetFriendAccountInfo(i) + if(res and res['gameAccountInfo']) then + characterName, messageText, noteText = res['gameAccountInfo']['characterName'],res['customMessage'],res['note'] + end + end + if(characterName==nameC) then + local line = "" + if(noteText) then + line = line .. noteText + end + if(messageText) then + if(noteText) then + line = line .. " | " + end + line = line .. messageText + end + return line + end + end + print(format("Line 68-Debug zz_UI (%s)",nameC)) + return "" +end + +local function addNoticeR(link) + return guildRoster(select(2,strsplit(":",select(3,string.find(link, "^|H(.+)|h%[.*%]"))))) +end + +local function addNotice(self, event, msg, author, ...) + if(msg and rep and string.find(msg,rep)) then + return false, gsub(msg, " ", format(" (%s) ",addNoticeR(msg)) or " ",1), author, ... + end + return false +end + +local function focusToggle(self) + local name = self:GetName() + if(self:HasFocus()) then + _G[name..'Left']:Show() + _G[name..'Right']:Show() + _G[name..'Mid']:Show() + else + _G[name..'Left']:Hide() + _G[name..'Right']:Hide() + _G[name..'Mid']:Hide() + end +end + +local function hookFrame(self) + if(not hooked[self]) then + self:HookScript("OnEditFocusGained",focusToggle) + self:HookScript("OnEditFocusLost",focusToggle) + hooked[self] = true + end + return +end + +local garrisonMaps = { + [1152] = true, -- FW Horde Garrison Level 1 + [1330] = true, -- FW Horde Garrison Level 2 + [1153] = true, -- FW Horde Garrison Level 3 + [1154] = true, -- FW Horde Garrison Level 4 + [1158] = true, -- SMV Alliance Garrison Level 1 + [1331] = true, -- SMV Alliance Garrison Level 2 + [1159] = true, -- SMV Alliance Garrison Level 3 + [1160] = true, -- SMV Alliance Garrison Level 4 +} + +local function InGarrison() + local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() + if(garrisonMaps[instanceMapID]) then + return true + end + return false +end +local function apply(eb) + local d = 'PARTY' + local i, t = IsInInstance() + if (t=='raid') then + d = 'RAID' + elseif ((t=='party' and not InGarrison()) or t=='pvp') then + d = 'INSTANCE_CHAT' + elseif (not i and GetNumGroupMembers()>0) then + d = 'PARTY' + elseif IsInGuild() then + d = 'GUILD' + end + eb:SetAttribute("chatType", d) +-- eb:SetAttribute("stickyType", d) +end + +local function chatHoverIn(cf,link,text) + local typ = string.match(link, "^(.-):") +-- print(typ,link) +--[[ print(typ,link) + local tbl = { strsplit(":", link) } + for k,v in pairs(tbl) do + print(v) + end +]]-- + if(typ == "item" or typ == "enchant" or typ == "spell" or typ == "quest" or typ == "currency") then + GameTooltip:SetOwner(UIParent, "ANCHOR_CURSOR") + GameTooltip:SetHyperlink(link) + ShowUIPanel(GameTooltip) + elseif(typ == "player") then + local typ, name = string.match(link, "^(.-):(.*):(.*):") + if(name) then + GameTooltip:SetOwner(UIParent, "ANCHOR_CURSOR") + GameTooltip:SetUnit(name) + ShowUIPanel(GameTooltip) + end + end +end + +local function chatHoverOut(cf,link,text) + HideUIPanel(GameTooltip) +end + +local function OnText(message) + addon:OnText(name, message) +end +local ClassColorUnits = { + ["target"] = { + ["name"] = "TargetFrame", + ["back"] = "TargetFrame", + ["x-offset"] = -104, + ["y-offset"] = 60, + ["x-offset2"] = 6, + ["y-offset2"] = -22, + }, + ["player"] = { + ["name"] = "PlayerFrame", + ["back"] = "PlayerFrameBackground", + ["x-offset"] = 0, + ["y-offset"] = 22, + ["x-offset2"] = 0, + ["y-offset2"] = 0, + }, + ["focus"] = { + ["name"] = "FocusFrame", + ["back"] = "FocusFrame", + ["x-offset"] = -104, + ["y-offset"] = 60, + ["x-offset2"] = 6, + ["y-offset2"] = -22, + }, +} +local function updateClassColor(unit) + if(unit and UnitExists(unit)) then + local _,class = UnitClass(unit) + if(class and RAID_CLASS_COLORS[class]) then + if(ClassColorUnits[unit]) then + local uf = _G[ClassColorUnits[unit]['name']] + local bg = _G[ClassColorUnits[unit]['back']] + if(not uf['unitClassColorBack']) then + uf['unitClassColorBack'] = uf:CreateTexture(nil, "ARTWORK") + uf['unitClassColorBack']:SetPoint("TOPLEFT", bg, ClassColorUnits[unit]['x-offset2'], ClassColorUnits[unit]['y-offset2']) + uf['unitClassColorBack']:SetPoint("BOTTOMRIGHT", bg, ClassColorUnits[unit]['x-offset'], ClassColorUnits[unit]['y-offset']) + uf['unitClassColorBack']:SetTexture("Interface\\TargetingFrame\\UI-StatusBar") + end + local col = RAID_CLASS_COLORS[class] + if(not UnitIsPlayer(unit)) then + uf['unitClassColorBack']:SetVertexColor(0, 0, 0, 0) + else + uf['unitClassColorBack']:SetVertexColor(col['r'], col['g'], col['b'],1) + end + end + end + end +end + +local function OnEvent(self, event, arg1, ...) + if(arg1 == name and event=="ADDON_LOADED") then + if(PlayerFrame:IsUserPlaced()) then + PlayerFrame:SetMoveable(false) + else + PlayerFrame:SetUserPlaced(true) + PlayerFrame:ClearAllPoints() + PlayerFrame:SetPoint("RIGHT", UIParent, "CENTER", 0, -140) + end + + if(not TargetFrame:IsUserPlaced()) then + TargetFrame:SetUserPlaced(true) + TargetFrame:ClearAllPoints() + TargetFrame:SetPoint("TOPLEFT", PlayerFrame, "TOPRIGHT",0,0) + else + TargetFrame:SetMoveable(false) + end + self:UnregisterEvent(event) + + -- Remove the cancel button (Reduce the taint caused by InterfaceOptionsFrame src: http://www.wowinterface.com/forums/showpost.php?p=275119&postcount=17 ) + InterfaceOptionsFrameCancel:Hide() + InterfaceOptionsFrameOkay:SetAllPoints(InterfaceOptionsFrameCancel) + -- Make clicking cancel the same as clicking okay + InterfaceOptionsFrameCancel:SetScript("OnClick", function() + InterfaceOptionsFrameOkay:Click() + end) + if(HideFriendButton) then + local fb = _G['QuickJoinToastButton'] + if(fb) then + fb:UnregisterAllEvents() + if(not hooked[fb]) then + fb:HookScript("OnShow", function(self) self:Hide() end) + hooked[fb] = true + end + fb:Hide() + end + end + if(Unsticky) then + ChatTypeInfo['WHISPER']['sticky'] = 0 + ChatTypeInfo['BN_WHISPER']['sticky'] = 0 + end + elseif(event == "PLAYER_TARGET_CHANGED") then + updateClassColor("target") + elseif(event == "PLAYER_FOCUS_CHANGED") then + updateClassColor("focus") + elseif(event == "MAIL_SHOW") then +-- sf_new() + elseif(event == "UNIT_INVENTORY_CHANGED") then + if(itemCount()<12) then return end + if(UnitName("player") == dest) then + self:UnregisterEvent(event) + return + end + if(SendMailFrame:IsVisible())then + local a,b = 0,0 + local link,numberOfSlots + local info,id = {},0 + local full = 0 + while(a<=NUM_BAG_SLOTS) do + numberOfSlots = GetContainerNumSlots(a) + b = 1 + while(b<=numberOfSlots) do + link = GetContainerItemLink(a, b) + if(link) then + id = addon:getItemId(link) + -- print(id,link) + if(sendItem(id)) then + local _, itemCount, locked, _, _ = GetContainerItemInfo(a, b) + if(not locked) then + UseContainerItem(a, b) + full = full + 1 + if(full >= 12) then + SendMail(dest,"Post",nil) + full = 0 + SELECTED_CHAT_FRAME:AddMessage("Sending") + break + end + end + end + end + b = b + 1 + end + a = a + 1 + end + end + elseif(event == "UPDATE_CHAT_WINDOWS") then + ChatFrame1:SetUserPlaced(true) + ChatFrame1:SetClampedToScreen(false) + ChatFrame1:ClearAllPoints() + ChatFrame1:SetWidth(353) + ChatFrame1:SetHeight(148) + ChatFrame1:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT",32,30) + local iname = "Friendlog" + local ifound = false + for i = 1, NUM_CHAT_WINDOWS do + local cf = _G['ChatFrame'..i] + cf.oldAlpha = cf.oldAlpha or 0 -- Fix 'max-bug' in FCF.lua + cf:HookScript("OnHyperlinkEnter",chatHoverIn) + cf:HookScript("OnHyperlinkLeave",chatHoverOut) + local cfname, _, _, _, _, _, shown, _, _, _ = GetChatWindowInfo(i) + if(cfname == iname) then + ifound = true + end + if(UnClamp) then + local cf = _G['ChatFrame'..i] + cf:SetClampedToScreen(false) + cf:Raise() + end + if(HideBorder) then + local ef = _G['ChatFrame'..i..'EditBox'] + focusToggle(ef) + hookFrame(ef) + end + end + if(not ifound) then + local frame = FCF_OpenNewWindow(iname) + FCF_CopyChatSettings(frame, DEFAULT_CHAT_FRAME) + FCF_DockUpdate() + end + self:UnregisterEvent(event) + elseif(event == 'PLAYER_ENTERING_WORLD') then + for i = 1, NUM_CHAT_WINDOWS do + if(NoSay) then + local eb = _G['ChatFrame'..i..'EditBox'] + apply(eb) + if(not hooked['NoSay']) then + hooked['NoSay'] = true + end + end + end + updateClassColor("player") + elseif(event == 'MAIL_INBOX_UPDATE') then + for i=GetInboxNumItems(),1,-1 do + local packageIcon, stationeryIcon, sender, subject, money, CODAmount, daysLeft, hasItem, wasRead, wasReturned, textCreated, canReply, isGM =GetInboxHeaderInfo(i) + if(postmaster[sender]) then + if(money and money >0) then + TakeInboxMoney(i) + end + if(hasItem) then + for j = 1, ATTACHMENTS_MAX_RECEIVE do + local name, texture, count, quality, canUse = GetInboxItem(i, j) + if(name) then + TakeInboxItem(i,j) + end + end + end + C_Timer.After(5, function() DeleteInboxItem(i) end) + end + end + elseif(event == 'CHAT_MSG_SYSTEM') then -- http://www.wowinterface.com/downloads/info23035-AFKQuit.html + if(arg1 and arg1 == IDLE_MESSAGE) then + ForceQuit() + end + end +end + +--[[ +Start Healthbar color Phanx +http://www.wowinterface.com/forums/showthread.php?p=302918#post302918 +--]] +local function OnMinMaxChanged(bar, minValue, maxValue) + bar.maxValue = maxValue +end + +local function SetValue(bar, value) + bar:real_SetValue(value) + local percent = value / bar.maxValue + if percent > 0.5 then + bar:SetStatusBarColor(2 * (1 - percent), 1, 0) + else + bar:SetStatusBarColor(1, 2 * percent, 0) + end +end + +local function HandleBar(bar) + local _, maxValue = bar:GetMinMaxValues() + bar.maxValue = maxValue + bar.lockColor = true + bar:HookScript("OnMinMaxChanged", OnMinMaxChanged) + bar.real_SetValue = bar.SetValue + bar.SetValue = SetValue +end +--[[ +End Healthbar color Phanx +http://www.wowinterface.com/forums/showthread.php?p=302918#post302918 +--]] + +local function UpdateChar() + local name = UnitName('player') + local uname = name.."-"..realm + local _,class = UnitClass("player") + db['chardb'] = db['chardb'] or {} + db['chardb'][uname] = db['chardb'][uname] or {} + db['chardb'][uname]['level'] = UnitLevel("player") + db['chardb'][uname]['class'] = class +end + +local function getPref(pref) + return db[pref[#pref]] +end + +local function setPref(pref,value) + db[pref[#pref]] = value +end + +local function getUNames() + local n = {} + for k,v in pairs(db['chardb']) do + n[#n+1] = k + end + return n +end + +local brokers = {} + +function addon:RegisterBroker(brokerName, brokerFunc, default, defaults) + brokers[brokerName] = { + ['func'] = brokerFunc, + ['default'] = default, + ['defaults'] = defaults, + } +end +do + addon['preloads'][#addon['preloads'] + 1] = function(...) + db = addon['db']['profile'][name] + for broker, tab in pairs(brokers) do + if(type(tab['defaults']) == 'table') then + addon['db']['profile'][broker] = addon['db']['profile'][broker] or tab['defaults'] + else + addon['db']['profile'][broker] = addon['db']['profile'][broker] or {} + end + end + for k, v in pairs(db['cvars']) do + C_CVar.SetCVar(k,v) + end + end + local function init(self, ...) + realm = GetRealmName() + options = addon:InitConfig(name, true, { + ['type'] = "launcher", + ['OnClick'] = OnClick + }, getPref, setPref) + for broker, tab in pairs(brokers) do + addon:AddConfigEntry(name,"toggle",broker.."toggle",broker,"Enable/Disable "..broker.." on next reload",1,nil,nil,nil,nil,addon['options']['args'][name]) + if(addon['db']['profile'][broker..'toggle'] or addon['db']['profile'][broker..'toggle'] == nil) then + tab['func']() + end + end + ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", addNotice) + + + HandleBar(PlayerFrameHealthBar) + HandleBar(TargetFrameHealthBar) + UpdateChar() + + end + addon:startup(name, name, init, nil, defaults) + addon:RegisterFunc(addon:IsClassic() and {'MAIL_INBOX_UPDATE','PLAYER_TARGET_CHANGED','UPDATE_CHAT_WINDOWS','ADDON_LOADED','PLAYER_ENTERING_WORLD','CHAT_MSG_SYSTEM','MAIL_SHOW','MAIL_CLOSED'} or {'MAIL_INBOX_UPDATE','PLAYER_TARGET_CHANGED','PLAYER_FOCUS_CHANGED','UPDATE_CHAT_WINDOWS','ADDON_LOADED','PLAYER_ENTERING_WORLD','CHAT_MSG_SYSTEM','MAIL_SHOW','MAIL_CLOSED'},"OnEvent", OnEvent) +end +-- addon:RegisterEventThrottle(name,'UNIT_INVENTORY_CHANGED', 0.07, OnEvent) diff --git a/icon2.tga b/icon2.tga new file mode 100644 index 0000000..f675e12 Binary files /dev/null and b/icon2.tga differ diff --git a/layout.lua b/layout.lua new file mode 100644 index 0000000..0c4e180 --- /dev/null +++ b/layout.lua @@ -0,0 +1,88 @@ +local name, addon = ... +local lpanels = addon['lpanels'] +if(not lpanels) then return end +local rilgamonHeight = 200 +local rilgamonWidth = rilgamonHeight * 2 +local rilgamonTopHeight = 22 + +lpanels:CreateLayout("Rilgamon", { + -- Create a global layout right here + { + name = "TopGradient", + strata = "BACKGROUND", level = 0, + height = rilgamonTopHeight, width = "100%", + anchor_to = "TOP", + bg_color = "0 0 0 1", + bg_alpha = 1, + gradient = "V", + gradient_color = "CLASS", + gradient_alpha = .8, + }, { + name = "ClassLine", + strata = "BACKGROUND", + x_off = 0, y_off = rilgamonHeight, + level = 0, + bg_color = "CLASS", + bg_alpha = 1, + anchor_to = "BOTTOM", + width = "100%", + height = 1, + }, { + name = "ClassGradient", parent = "ClassLine", + strata = "BACKGROUND", level = 0, + width = "100%", + height = rilgamonHeight, + y_off = -1, + anchor_to = "TOP", + bg_color = "CLASS", + bg_alpha = .5, + gradient = "V", + gradient_color = "0 0 0 .5", + gradient_alpha = .5, + }, { + name = "BlackBoxLeft", parent = "ClassLine", + strata = "BACKGROUND", level = 2, + width = rilgamonWidth, height = rilgamonHeight, + y_off = -1, + bg_color = "0 0 0 1", + bg_alpha = 1, + anchor_to = "TOPLEFT", + }, { + name = "BlackBoxRight", parent = "ClassLine", + strata = "BACKGROUND", level = 2, + width = rilgamonWidth, height = rilgamonHeight, + y_off = -1, + bg_color = "0 0 0 1", + bg_alpha = 1, + anchor_to = "TOPRIGHT", + }, { + name = "GradientLeft", parent = "BlackBoxLeft", + strata = "BACKGROUND", level = 1, + width = rilgamonWidth, height = rilgamonHeight, + anchor_to = "LEFT", + anchor_from = "RIGHT", + bg_color = "0 0 0 1", + bg_alpha = 1, + gradient = "H", + gradient_color = "CLASS", + gradient_alpha = 0, + }, { + name = "GradientRight", parent = "BlackBoxRight", + strata = "BACKGROUND", level = 1, + width = rilgamonWidth, height = rilgamonHeight, + anchor_to = "RIGHT", + anchor_from = "LEFT", + bg_color = "CLASS", + bg_alpha = 0, + gradient = "H", + gradient_color = "0 0 0 1", + gradient_alpha = 1, + } +} +-- , { bottom = rilgamonHeight, top = rilgamonTopHeight } -- Viewport - delete this line for no viewport + +) + +lpanels:ApplyLayout(nil, "Rilgamon") + + diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..b9e992f --- /dev/null +++ b/license.txt @@ -0,0 +1,6 @@ +The following license excludes the libraries (Libs) included. See the libraries directory or website. + +This AddOn is public domain. That means you can change it, rename it or paint it yellow. +My name (Rilgamon) is valid only for WoWInterface.com and curse.com. +If you use/offer this addon on another website please remove my name. +If you want to give me credit you can replace it with a link to my profile on WoWInterface.com. \ No newline at end of file diff --git a/litepanels.lua b/litepanels.lua new file mode 100644 index 0000000..b1f7f3a --- /dev/null +++ b/litepanels.lua @@ -0,0 +1,395 @@ +local name, addon = ... +-- LitePanels +------------------------------------------------------------------------------------------ +local lp, hidden, deps = CreateFrame('Frame','lp_C'), {}, {} lp:Hide() + +lpanels = { + profile = {}, temp = {}, + cinfo = { + n = strlower(UnitName'player'), + r = strlower(gsub(GetRealmName()," ","")), + c = strlower(select(2,UnitClass'player')) + }, + defaults = { + parent = "UIParent", strata = "BACKGROUND", + anchor_to = "BOTTOMLEFT", anchor_from = "BOTTOMLEFT", + x_off = 0, y_off = 0, height = 0, width = 0, + bg_color = "0 0 0", bg_alpha = 1, gradient_color = "1 1 1", + border = "Interface\\Tooltips\\UI-Tooltip-Border", + border_size = 16, border_color = "1 1 1", border_alpha = 1, + text = { + string = "", font = "Fonts\\FRIZQT__.TTF", size = 12, + justify_h = "CENTER", justify_v = "MIDDLE", + anchor_to = "CENTER", anchor_from = "CENTER", + x_off = 0, y_off = 0, color = "1 1 1", alpha = 1, + shadow = { color = "0 0 0", alpha = 1, y = -1, x = 1 }, + } + }, + media = [[Interface\AddOns\LitePanels\media\]], + sides = {'left','top','right','bottom'}, + events = {'OnEvent','OnUpdate','OnResize','OnClick','OnDoubleClick', + 'OnMouseUp','OnMouseDown','OnEnter','OnLeave','OnHide','OnShow'}, + defaultv = {'anchor_to','x_off','y_off','width','height','strata'}, +} +addon['lpanels'] = lpanels +local gsub = string.gsub +local strmatch = string.match +local type = type +local floor = math.floor +local unpack = unpack +local pairs = pairs +local ipairs = ipairs +local IsAddOnLoaded = IsAddOnLoaded +local hooksecurefunc = hooksecurefunc + +local r, is = function(n, dec) return floor(n * (10 ^ (dec or 0)) + 0.5) end, function(v,t) return type(v) == t end +local dummy, d = function() end, lpanels.defaults + +local class = (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[strupper(lpanels.cinfo.c)] + +local function setcolor(color) + if color == "CLASS" then + return tonumber(class.r), tonumber(class.g), tonumber(class.b) + elseif is(color,'string') then + return strmatch(color, "([%d%.]+)%s+([%d%.]+)%s+([%d%.]+)") + else return unpack(color) end +end + +------------------------------------------------------------------------------------------ +-- API: lpanels:CreateLayout("Layout Name", { _layout code_ }, { _viewport_ }) +-- » arg1 - Layout name. Name can be anything at all. +-- » arg2 - Table with your layout code, can also be set as a variable. +-- » arg3 - Viewport table. ie: {top=10, bottom=100, left=5, right=5} or {bottom=100} +function lpanels:CreateLayout(name, layout, viewport) + if name and (layout or viewport) then + self.profile[name] = layout or {} + if viewport then self.profile[name].Viewport = viewport end + end +end + +------------------------------------------------------------------------------------------ +-- API: lpanels:ApplyLayout("n:Character r:Realm c:Class", "layout1", "layout2") +-- » arg1 - Profile string: May be set to any combination of n:ame, r:ealm, c:lass, +-- separated by a single space. If nil, applies the specified layouts to all +-- characters. Additionally, a dash may be applied to front of the +-- condition (-n:ame -r:ealm -c:lass) to make the layout NOT load on the specified +-- value. The profile string is *not* CASE-sensitive. +-- » arg2,... - Layout names created by CreateLayout() to apply to this profile. No set +-- limit to how many layouts can be applied per profile. +function lpanels:MatchProf(profile,AND) + local apply = false + for a, str in gmatch(profile,'(%-?[nrc]):([^%s]+)') do + if strmatch(self.cinfo[strlower(strsub(a,-1,-1))], strlower(str)) then + if strmatch(a,'^%-') then return false else apply = true end + elseif strmatch(a,'^%-') then apply = true elseif AND then return false end + end + return apply +end +function lpanels:ApplyLayout(profile, ...) + if not profile or profile == "" then profile = "n:"..self.cinfo.n end + while strfind(profile,'%(.-%)') do + for prof in gmatch(profile,'%(([^%(]-)%)') do + profile = gsub(profile,'%('..gsub(prof,'%-','%%%-')..'%)', self:MatchProf(prof,1) and 'n:'..self.cinfo.n or 'n:x') + end + end + if self:MatchProf(profile) then + for _, name in ipairs{...} do + if self.profile[name] then + if self.profile[name].Viewport then self.profile.vp = self.profile[name].Viewport end + for _, f in ipairs(self.profile[name]) do self.profile[#self.profile+1] = f end + end + self.profile[name] = nil + end + end +end + +------------------------------------------------------------------------------------------ +-- Core +function lpanels.RegisterEvent(self, event) + if event == "PLAYER_LOGIN" then lpanels.temp.OnEvent(self,"PLAYER_LOGIN") end +end + +local function Resize(panel, width, height) + if width and height and (strmatch(width,"%d+%%") or strmatch(height,"%d+%%")) then + -- resize based on the panel's anchor frame or parent if no anchor + local _, parent = panel:GetPoint() + local function hook(_, _width, _height) + if strmatch(width,"%%") then panel:SetWidth(_width * strmatch(width,"(%d+)%%") * .01 + (strmatch(width,"%%%s([%+%-]%d+)") or 0)) end + if strmatch(height,"%%") then panel:SetHeight(_height * strmatch(height,"(%d+)%%") * .01 + (strmatch(height,"%%.*([%+%-]%d+)") or 0)) end + end + parent:HookScript("OnSizeChanged", hook) + end +end + +function lpanels:MakePanel(f) + -- setting a few defaults + for _, attr in ipairs(self.defaultv) do if not f[attr] then f[attr] = d[attr] end end + + -- setting parent/anchor + local origparent, origanchor + for _, frame in pairs{f.parent, f.anchor_frame} do + if is(frame,'string') and not _G[frame] then + if not hidden[frame] then hidden[frame] = {} end tinsert(hidden[frame], f.name) + f.parent, origparent, origanchor, f.anchor_frame = "lp_C", f.parent, f.anchor_frame + end + end + f.parent = _G[f.parent] or _G[d.parent] + + -- create frame; object name will be LP_PanelName or LP_i if anonymous + local panel = CreateFrame("Frame", f.name, f.parent, BackdropTemplateMixin and "BackdropTemplate" or nil) + panel.bg = panel:CreateTexture(nil, "BACKGROUND") + + -- inserting some data into frame table + if f.parent == lp_C then panel.parent, panel.anchor_frame, panel.strata, panel.level = origparent, origanchor, f.strata, f.level end + panel.width, panel.height = f.width, f.height + + -- inset/outset; pos = inset, neg = outset + if f.inset or f.border then + if not f.inset then f.inset = f.border == "SOLID" and (f.border_size or 1) or (f.border_size or d.border_size) * 0.25 end + local inset = {} for _,s in ipairs(self.sides) do + inset[s] = is(f.inset,'table') and f.inset[s] or is(f.inset,'number') and f.inset or 0 + end + panel.bg:SetPoint("TOPLEFT", inset.left, -inset.top) + panel.bg:SetPoint("BOTTOMRIGHT", -inset.right, inset.bottom) + else + panel.bg:SetAllPoints(panel) + end + + -- hide dependant frames, will show later for late loading addons + if f.require and not IsAddOnLoaded(f.require) then deps[f.name] = f.require panel:Hide() end + + -- set positions + panel:SetParent(f.parent) + panel:ClearAllPoints() + if strmatch(f.x_off, "%%") then f.x_off = (_G[f.anchor_frame] or f.parent):GetWidth() * strmatch(f.x_off,"(%d+)%%") * .01 end + if strmatch(f.y_off, "%%") then f.y_off = (_G[f.anchor_frame] or f.parent):GetHeight() * strmatch(f.y_off,"(%d+)%%") * .01 end + panel:SetPoint(strupper(f.anchor_to), _G[f.anchor_frame] or f.parent, strupper(f.anchor_from or f.anchor_to), f.x_off, f.y_off) + if f.scale then panel:SetScale(f.scale) end + + -- height, width + if f.parent ~= lp then Resize(panel, f.width, f.height) end + panel:SetWidth(strmatch(f.width,"%%") and (_G[f.anchor_frame] or f.parent):GetWidth() * strmatch(f.width,"(%d+)%%") * .01 or f.width) + panel:SetHeight(strmatch(f.height,"%%") and (_G[f.anchor_frame] or f.parent):GetHeight() * strmatch(f.height,"(%d+)%%") * .01 or f.height) + + -- strata, level + panel:SetFrameStrata(f.strata or d.strata) + if f.level then panel:SetFrameLevel(f.level) end + + -- default texts bg alpha, blend mode + if f.text and not f.bg_alpha and not f.tex_file and not f.bg_color then f.bg_alpha = 0 end + if f.bg_blend then panel.bg:SetBlendMode(f.bg_blend) end + + -- texture art + if f.tex_file then + -- panel.bg:SetTexCoordModifiesRect(false) -- method removed in 3.3.3 + panel.bg:SetTexture(not strmatch(f.tex_file,"[/\\]") and self.media..f.tex_file or f.tex_file) + if f.tex_coord then panel.bg:SetTexCoord(unpack(f.tex_coord)) end + if f.tex_rotate then + local coords = {} + for i,c in ipairs{225,225,135,135,-45,-45,45,45} do + coords[i] = 0.5+math[mod(i*0.5,2)==1 and "cos" or "sin"](rad(f.tex_rotate+c))*sqrt(0.5) + end + panel.bg:SetTexCoord(unpack(coords)) + end + if f.flip_v or f.flip_h then + local ULx,ULy,LLx,LLy,URx,URy,LRx,LRy = panel.bg:GetTexCoord() + if f.flip_v then panel.bg:SetTexCoord(LLx,LLy,ULx,ULy,LRx,LRy,URx,URy) end + if f.flip_h then panel.bg:SetTexCoord(URx,URy,LRx,LRy,ULx,ULy,LLx,LLy) end + end + if f.bg_color then panel.bg:SetVertexColor(setcolor(f.bg_color)) end + if f.bg_alpha then panel.bg:SetAlpha(f.bg_alpha) end + + -- gradient texture + elseif f.gradient and strmatch(f.gradient, "^[HV]") then + local bg_r, bg_g, bg_b = setcolor(f.bg_color or d.bg_color) + local gr_r, gr_g, gr_b = setcolor(f.gradient_color or d.gradient_color) + local gradient = gsub(gsub(f.gradient,"^H.*","HORIZONTAL"),"^V.*","VERTICAL") + panel.bg:SetGradientAlpha(gradient,bg_r,bg_g,bg_b,f.bg_alpha or d.bg_alpha,gr_r,gr_g,gr_b,f.gradient_alpha or f.bg_alpha or d.bg_alpha) + panel.bg:SetColorTexture(1,1,1,1) + -- solid texture + else + local bg_r, bg_g, bg_b = setcolor(f.bg_color or d.bg_color) + panel.bg:SetColorTexture(bg_r,bg_g,bg_b,f.bg_alpha or d.bg_alpha) + end + + -- borders + if f.border then + panel:SetBackdrop({ + edgeFile = f.border == true and d.border or f.border == "SOLID" and "Interface\\Buttons\\WHITE8X8" or not strmatch(f.border,"[/\\]") and self.media..f.border or f.border, + edgeSize = (f.border == "SOLID" and not f.border_size) and 1 or f.border_size or d.border_size }) + local bo_r, bo_g, bo_b = setcolor(f.border_color or d.border_color) + panel:SetBackdropBorderColor(bo_r,bo_g,bo_b,f.border_alpha or d.border_alpha) + end + + -- texts + if f.text then + -- if f.text contains multiple tables, treat those tables as multiple text objects; name them self.text1,text2,text3,... + f.text = is(f.text[1],'table') and f.text or {f.text} + for i,t in ipairs(f.text) do + if #f.text == 1 then i = "" end + if is(t,'string') then t = {string=t} end + panel["text"..i] = panel:CreateFontString(nil, "OVERLAY") + local text = panel["text"..i] + + -- keep text string and frame height/width the same if nil + if i == "" and (not f.width or not f.height or f.width == 0 or f.height == 0) then + local function settext() + panel:SetWidth(text:GetStringWidth()) + panel:SetHeight(text:GetStringHeight()) + end + hooksecurefunc(text, "SetText", settext) + end + + -- texts font + if t.font and not strmatch(t.font,"[/\\]") then t.font = self.media..t.font end + local flags = t.outline == 1 and "OUTLINE" or t.outline == 2 and "THICKOUTLINE" + if t.mono then flags = (flags and flags..", " or "").."MONOCHROME" end + text:SetFont(t.font or d.text.font, t.size or d.text.size, flags) + if not text:GetFont() then -- handle invalid font error + text:SetFont(d.text.font, t.size or d.text.size, flags) + print("|cffffffffLite|cff66C6FFPanels |cffff5555Font invalid:", strmatch(t.font, "([^/\\]+)$")) + end + + -- texts string + if not t.string then t.string = d.text.string end + text:SetText(is(t.string,'function') and t.string(text) or t.string) + + -- texts color + local tx_r, tx_g, tx_b = setcolor(t.color or d.text.color) + text:SetTextColor(tx_r,tx_g,tx_b,t.alpha or d.text.alpha) + + -- texts shadow + if (not t.shadow and not t.outline) or (t.shadow and t.shadow ~= 0) then + if not t.shadow then t.shadow = d.text.shadow end + if is(t.shadow,'number') then t.shadow = {x=t.shadow,y=-t.shadow,alpha=d.text.shadow.alpha} end + if is(t.shadow,'table') then + local sh_r, sh_g, sh_b = setcolor(t.shadow.color or d.text.shadow.color) + text:SetShadowOffset(t.shadow.x or d.text.shadow.x, t.shadow.y or d.text.shadow.y) + text:SetShadowColor(sh_r,sh_g,sh_b,t.shadow.alpha or d.text.shadow.alpha) + end + end + + -- texts positioning within panel + text:SetJustifyH(t.justify_h or d.text.justify_h) + text:SetJustifyV(t.justify_v or d.text.justify_v) + text:SetPoint(strupper(t.anchor_to or d.text.anchor_to), panel, strupper(t.anchor_to or t.anchor_from or d.text.anchor_from), t.x_off or d.text.x_off, t.y_off or d.text.y_off) + + -- if it exists, hook text function to the frame's OnUpdate script + if is(t.string,'function') and t.update ~= 0 then + text.elapsed = 0 + local update, string = t.update or 1, t.string + local function OnUpdate(self, u) + text.elapsed = text.elapsed + u + if text.elapsed > update then text:SetText(string(text)) text.elapsed = 0 end + end + if not f.OnUpdate then f.OnUpdate = OnUpdate else hooksecurefunc(f, "OnUpdate", OnUpdate) end + end + end + end + + -- OnLoad handler + if f.OnLoad then + if f.OnEvent then -- fake PLAYER_LOGIN; it should have already fired + hooksecurefunc(panel, "RegisterEvent", self.RegisterEvent) + self.temp.OnEvent = f.OnEvent + end + f.OnLoad(panel) + end + + -- scripting functions, args passed to layout's function + if f.mouse ~= false and (f.mouse or f.OnClick or f.OnMouseDown or f.OnMouseUp or f.OnDoubleClick or f.OnEnter or f.OnLeave) then panel:EnableMouse(true) end + for _, action in ipairs(lpanels.events) do + local func = f[action] + if is(func,'function') then + if action == "OnDoubleClick" then + panel.timer, panel.button = 0, nil + local function hook(self, button) + if self.timer and self.timer < time() then self.startTimer = false end + if self.timer == time() and self.button == button and self.startTimer then + self.startTimer = false + func(self, button) + else + self.startTimer, self.timer, self.button = true, time(), button + end + end + panel:HookScript("OnMouseUp", hook) + else + if action == "OnUpdate" then panel.elapsed = 0 end + panel:HookScript(gsub(gsub(action,'OnClick','OnMouseUp'),'Resize','SizeChanged'), func) + end + end + end +end + +function lpanels:Init() + if #self.profile == 0 then return end + + -- check if parent/anchor names are the names of other panels, then tag with 'LP_' + for i, f in ipairs(self.profile) do for _, p in ipairs(self.profile) do + if f.name ~= nil then + if f.name == p.parent then p.parent = "LP_"..p.parent end + if f.name == p.anchor_frame then p.anchor_frame = "LP_"..p.anchor_frame end + end + end f.name = f.name and "LP_"..f.name or "LP_"..i end + + -- set viewport + if is(self.profile.vp,'table') then + WorldFrame:ClearAllPoints() + local vp = {} for _, s in ipairs(self.sides) do + vp[s] = self.profile.vp[s] and self.profile.vp[s] * UIParent:GetScale() or 0 + end + WorldFrame:SetPoint("TOPLEFT", vp.left, -vp.top) + WorldFrame:SetPoint("BOTTOMRIGHT", -vp.right, vp.bottom) + end + + -- begin to cycle through user profile and create panels + for i,f in ipairs(self.profile) do self:MakePanel(f) end +end + +function lpanels:Exit() + r,is,class,setcolor,dummy = nil + for k in pairs(self) do self[k] = nil end + self.reset = 1 self.reset = nil +end + +function lpanels.OnEvent(self, event) + if event == "PLAYER_LOGIN" then + -- pre-1.5 layout compatibility + if LPanels then for name, profile in pairs(LPanels) do + if name == "Default" or name == format("%s - %s", UnitName'player', GetRealmName()) then + lpanels:CreateLayout(name, profile) + lpanels:ApplyLayout(name ~= "Default" and "n:"..lpanels.cinfo.n.." r:"..lpanels.cinfo.r, name) + end LPanels = nil + end end + -- run addon + lpanels:Init() + lpanels = lpanels:Exit() + elseif event == "ADDON_LOADED" and deps then + for frame, addon in pairs(deps) do + if IsAddOnLoaded(addon) then _G[frame]:Show() frame = nil end + end + end +end + +function lpanels.CreateFrame(_, frame) + if not hidden[frame] then return end + for i, child in ipairs(hidden[frame]) do + local panel = _G[child] + if not panel.anchor_frame or _G[panel.anchor_frame] then + local points = {panel:GetPoint()} points[2] = _G[panel.anchor_frame] or frame + panel:SetParent(frame or UIParent) + panel:ClearAllPoints() panel:SetPoint(unpack(points)) + Resize(panel, panel.width, panel.height) + panel:SetFrameStrata(panel.strata) + if panel.level then panel:SetFrameLevel(panel.level) end + hidden[frame][i] = nil + end + end + if #hidden[frame] == 0 then hidden[frame] = nil end +end + +hooksecurefunc("CreateFrame", lpanels.CreateFrame) + +lp:RegisterEvent'ADDON_LOADED' +lp:RegisterEvent'PLAYER_LOGIN' +lp:SetScript("OnEvent", lpanels.OnEvent) diff --git a/pack.xml b/pack.xml new file mode 100644 index 0000000..ebf9214 --- /dev/null +++ b/pack.xml @@ -0,0 +1,6 @@ + +