/
--[[ |
Original codebase: |
oUF_Castbar by starlon. |
http://svn.wowace.com/wowace/trunk/oUF_Castbar/ |
Elements handled: .Castbar |
Sub-elements: .Text, .Icon, .Time, .SafeZone, .Spark |
Notes: This element will not work on units that require a OnUpdate. |
(eventless units). |
Functions that can be overridden from within a layout: |
- :CustomDelayText(duration) |
- :CustomTimeText(duration) |
--]] |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local noop = function() end |
local UnitName = UnitName |
local GetTime = GetTime |
local UnitCastingInfo = UnitCastingInfo |
local UnitChannelInfo = UnitChannelInfo |
local UNIT_SPELLCAST_START = function(self, event, unit, spell, spellrank) |
if(self.unit ~= unit) then return end |
local castbar = self.Castbar |
local name, rank, text, texture, startTime, endTime, _, castid = UnitCastingInfo(unit) |
if(not name) then |
castbar:Hide() |
return |
end |
endTime = endTime / 1e3 |
startTime = startTime / 1e3 |
local max = endTime - startTime |
castbar.castid = castid |
castbar.duration = GetTime() - startTime |
castbar.max = max |
castbar.delay = 0 |
castbar.casting = true |
castbar:SetMinMaxValues(0, max) |
castbar:SetValue(0) |
if(castbar.Text) then castbar.Text:SetText(text) end |
if(castbar.Icon) then castbar.Icon:SetTexture(texture) end |
if(castbar.Time) then castbar.Time:SetText() end |
local sf = castbar.SafeZone |
if(sf) then |
sf:ClearAllPoints() |
sf:SetPoint'RIGHT' |
sf:SetPoint'TOP' |
sf:SetPoint'BOTTOM' |
end |
if(self.PostCastStart) then self:PostCastStart(event, unit, name, rank, text, castid) end |
castbar:Show() |
end |
local UNIT_SPELLCAST_FAILED = function(self, event, unit, spellname, spellrank, castid) |
if(self.unit ~= unit) then return end |
local castbar = self.Castbar |
if(castbar.castid ~= castid) then |
return |
end |
castbar.casting = nil |
castbar:SetValue(0) |
castbar:Hide() |
if(self.PostCastFailed) then self:PostCastFailed(event, unit, spellname, spellrank, castid) end |
end |
local UNIT_SPELLCAST_INTERRUPTED = function(self, event, unit, spellname, spellrank, castid) |
if(self.unit ~= unit) then return end |
local castbar = self.Castbar |
if(castbar.castid ~= castid) then |
return |
end |
castbar.casting = nil |
castbar.channeling = nil |
castbar:SetValue(0) |
castbar:Hide() |
if(self.PostCastInterrupted) then self:PostCastInterrupted(event, unit, spellname, spellrank, castid) end |
end |
local UNIT_SPELLCAST_DELAYED = function(self, event, unit, spellname, spellrank) |
if(self.unit ~= unit) then return end |
local name, rank, text, texture, startTime, endTime = UnitCastingInfo(unit) |
if(not startTime) then return end |
local castbar = self.Castbar |
local duration = GetTime() - (startTime / 1000) |
if(duration < 0) then duration = 0 end |
castbar.delay = castbar.delay + castbar.duration - duration |
castbar.duration = duration |
castbar:SetValue(duration) |
if(self.PostCastDelayed) then self:PostCastDelayed(event, unit, name, rank, text) end |
end |
local UNIT_SPELLCAST_STOP = function(self, event, unit, spellname, spellrank, castid) |
if(self.unit ~= unit) then return end |
local castbar = self.Castbar |
if(castbar.castid ~= castid) then |
return |
end |
castbar.casting = nil |
castbar:SetValue(0) |
castbar:Hide() |
if(self.PostCastStop) then self:PostCastStop(event, unit, spellname, spellrank, castid) end |
end |
local UNIT_SPELLCAST_CHANNEL_START = function(self, event, unit, spellname, spellrank) |
if(self.unit ~= unit) then return end |
local castbar = self.Castbar |
local name, rank, text, texture, startTime, endTime = UnitChannelInfo(unit) |
if(not name) then |
return |
end |
endTime = endTime / 1e3 |
startTime = startTime / 1e3 |
local max = (endTime - startTime) |
local duration = endTime - GetTime() |
castbar.duration = duration |
castbar.max = max |
castbar.delay = 0 |
castbar.channeling = true |
castbar:SetMinMaxValues(0, max) |
castbar:SetValue(duration) |
if(castbar.Text) then castbar.Text:SetText(name) end |
if(castbar.Icon) then castbar.Icon:SetTexture(texture) end |
if(castbar.Time) then castbar.Time:SetText() end |
local sf = castbar.SafeZone |
if(sf) then |
sf:ClearAllPoints() |
sf:SetPoint'LEFT' |
sf:SetPoint'TOP' |
sf:SetPoint'BOTTOM' |
end |
if(self.PostChannelStart) then self:PostChannelStart(event, unit, name, rank, text) end |
castbar:Show() |
end |
local UNIT_SPELLCAST_CHANNEL_UPDATE = function(self, event, unit, spellname, spellrank) |
if(self.unit ~= unit) then return end |
local name, rank, text, texture, startTime, endTime, oldStart = UnitChannelInfo(unit) |
if(not name) then |
return |
end |
local castbar = self.Castbar |
local duration = (endTime / 1000) - GetTime() |
castbar.delay = castbar.delay + castbar.duration - duration |
castbar.duration = duration |
castbar.max = (endTime - startTime) / 1000 |
castbar:SetMinMaxValues(0, castbar.max) |
castbar:SetValue(duration) |
if(self.PostChannelUpdate) then self:PostChannelUpdate(event, unit, name, rank, text) end |
end |
local UNIT_SPELLCAST_CHANNEL_STOP = function(self, event, unit, spellname, spellrank) |
if(self.unit ~= unit) then return end |
local castbar = self.Castbar |
if(castbar:IsShown()) then |
castbar.channeling = nil |
castbar:SetValue(castbar.max) |
castbar:Hide() |
if(self.PostChannelStop) then self:PostChannelStop(event, unit, spellname, spellrank) end |
end |
end |
local onUpdate = function(self, elapsed) |
if self.casting then |
local duration = self.duration + elapsed |
if (duration >= self.max) then |
self.casting = nil |
self:Hide() |
end |
if self.SafeZone then |
local width = self:GetWidth() |
local _, _, ms = GetNetStats() |
-- MADNESS! |
local safeZonePercent = (width / self.max) * (ms / 1e5) |
if(safeZonePercent > 1) then safeZonePercent = 1 end |
self.SafeZone:SetWidth(width * safeZonePercent) |
end |
if self.Time then |
if self.delay ~= 0 then |
if(self.CustomDelayText) then |
self:CustomDelayText(duration) |
else |
self.Time:SetFormattedText("%.1f|cffff0000-%.1f|r", duration, self.delay) |
end |
else |
if(self.CustomTimeText) then |
self:CustomTimeText(duration) |
else |
self.Time:SetFormattedText("%.1f", duration) |
end |
end |
end |
self.duration = duration |
self:SetValue(duration) |
if self.Spark then |
self.Spark:SetPoint("CENTER", self, "LEFT", (duration / self.max) * self:GetWidth(), 0) |
end |
elseif self.channeling then |
local duration = self.duration - elapsed |
if(duration <= 0) then |
self.channeling = nil |
self:Hide() |
return |
end |
if(self.SafeZone) then |
local width = self:GetWidth() |
local _, _, ms = GetNetStats() |
-- MADNESS! |
local safeZonePercent = (width / self.max) * (ms / 1e5) |
if(safeZonePercent > 1) then safeZonePercent = 1 end |
self.SafeZone:SetWidth(width * safeZonePercent) |
end |
if self.Time then |
if self.delay ~= 0 then |
if(self.CustomDelayText) then |
self:CustomDelayText(duration) |
else |
self.Time:SetFormattedText("%.1f|cffff0000-%.1f|r", duration, self.delay) |
end |
else |
if(self.CustomTimeText) then |
self:CustomTimeText(duration) |
else |
self.Time:SetFormattedText("%.1f", duration) |
end |
end |
end |
self.duration = duration |
self:SetValue(duration) |
if self.Spark then |
self.Spark:SetPoint("CENTER", self, "LEFT", (duration / self.max) * self:GetWidth(), 0) |
end |
else |
self.unitName = nil |
self.channeling = nil |
self:SetValue(1) |
self:Hide() |
end |
end |
local Enable = function(object, unit) |
local castbar = object.Castbar |
if(castbar) then |
if(not (unit and unit:match'%wtarget$')) then |
object:RegisterEvent("UNIT_SPELLCAST_START", UNIT_SPELLCAST_START) |
object:RegisterEvent("UNIT_SPELLCAST_FAILED", UNIT_SPELLCAST_FAILED) |
object:RegisterEvent("UNIT_SPELLCAST_STOP", UNIT_SPELLCAST_STOP) |
object:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED", UNIT_SPELLCAST_INTERRUPTED) |
object:RegisterEvent("UNIT_SPELLCAST_DELAYED", UNIT_SPELLCAST_DELAYED) |
object:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START", UNIT_SPELLCAST_CHANNEL_START) |
object:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", UNIT_SPELLCAST_CHANNEL_UPDATE) |
object:RegisterEvent("UNIT_SPELLCAST_CHANNEL_INTERRUPTED", 'UNIT_SPELLCAST_INTERRUPTED') |
object:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP", UNIT_SPELLCAST_CHANNEL_STOP) |
end |
castbar:SetScript("OnUpdate", object.OnCastbarUpdate or onUpdate) |
if object.unit == "player" then |
CastingBarFrame:UnregisterAllEvents() |
CastingBarFrame.Show = noop |
CastingBarFrame:Hide() |
elseif(object.unit == 'pet') then |
PetCastingBarFrame:UnregisterAllEvents() |
PetCastingBarFrame.Show = noop |
PetCastingBarFrame:Hide() |
end |
if(not castbar:GetStatusBarTexture()) then |
castbar:SetStatusBarTexture[[Interface\TargetingFrame\UI-StatusBar]] |
end |
local spark = castbar.Spark |
if(spark and spark:IsObjectType'Texture' and not spark:GetTexture()) then |
spark:SetTexture[[Interface\CastingBar\UI-CastingBar-Spark]] |
end |
local sz = castbar.SafeZone |
if(sz and sz:IsObjectType'Texture' and not sz:GetTexture()) then |
sz:SetTexture(1, 0, 0) |
end |
castbar:Hide() |
return true |
end |
end |
local Disable = function(object, unit) |
local castbar = object.Castbar |
if(castbar) then |
object:UnregisterEvent("UNIT_SPELLCAST_START", UNIT_SPELLCAST_START) |
object:UnregisterEvent("UNIT_SPELLCAST_FAILED", UNIT_SPELLCAST_FAILED) |
object:UnregisterEvent("UNIT_SPELLCAST_STOP", UNIT_SPELLCAST_STOP) |
object:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED", UNIT_SPELLCAST_INTERRUPTED) |
object:UnregisterEvent("UNIT_SPELLCAST_DELAYED", UNIT_SPELLCAST_DELAYED) |
object:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START", UNIT_SPELLCAST_CHANNEL_START) |
object:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE", UNIT_SPELLCAST_CHANNEL_UPDATE) |
object:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_INTERRUPTED", UNIT_SPELLCAST_CHANNEL_INTERRUPTED) |
object:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_STOP", UNIT_SPELLCAST_CHANNEL_STOP) |
castbar:SetScript("OnUpdate", nil) |
end |
end |
oUF:AddElement('Castbar', function(...) |
UNIT_SPELLCAST_START(...) |
UNIT_SPELLCAST_CHANNEL_START(...) |
end, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local GetComboPoints = GetComboPoints |
local MAX_COMBO_POINTS = MAX_COMBO_POINTS |
local Update = function(self, event, unit) |
local cpoints = self.CPoints |
if(self.unit ~= unit and (cpoints.unit and cpoints.unit ~= unit)) then return end |
local cp = GetComboPoints(cpoints.unit or unit, 'target') |
if(#cpoints == 0) then |
cpoints:SetText((cp > 0) and cp) |
else |
for i=1, MAX_COMBO_POINTS do |
if(i <= cp) then |
cpoints[i]:Show() |
else |
cpoints[i]:Hide() |
end |
end |
end |
end |
local Enable = function(self) |
if(self.CPoints) then |
self:RegisterEvent('UNIT_COMBO_POINTS', Update) |
return true |
end |
end |
local Disable = function(self) |
if(self.CPoints) then |
self:UnregisterEvent('UNIT_COMBO_POINTS', Update) |
end |
end |
oUF:AddElement('CPoints', Update, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local objects = oUF.objects |
local VehicleDriverFrame |
local UpdateVehicleSwitch = function(self, attr, value) |
if attr == "unit" then |
self.unit = value |
if self:GetAttribute("normalUnit") == "player" then |
PlayerFrame.unit = self.unit |
BuffFrame_Update() |
end |
end |
end |
local Enable = function(self, unit) |
if self.disallowVehicleSwap or (unit ~= "player" and unit ~= "pet") then return end |
if not VehicleDriverFrame then |
VehicleDriverFrame = CreateFrame("Frame", nil, UIParent, "SecureHandlerStateTemplate") |
RegisterStateDriver(VehicleDriverFrame, "vehicle", "[target=vehicle,exists,bonusbar:5]vehicle;novehicle") |
VehicleDriverFrame:SetAttribute("_onstate-vehicle", [[ |
if newstate == "vehicle" then |
for idx, frame in pairs(VEHICLE_FRAMES) do |
frame:SetAttribute("unit", frame:GetAttribute("vehicleUnit")) |
end |
else |
for idx, frame in pairs(VEHICLE_FRAMES) do |
frame:SetAttribute("unit", frame:GetAttribute("normalUnit")) |
end |
end |
]]) |
VehicleDriverFrame:Execute([[ |
VEHICLE_FRAMES = newtable() |
]]) |
end |
self:SetAttribute("normalUnit", unit) |
if unit == "player" then |
self:SetAttribute("vehicleUnit", "pet") |
elseif unit == "pet" then |
self:SetAttribute("vehicleUnit", "player") |
end |
VehicleDriverFrame:SetFrameRef("vehicleFrame", self) |
VehicleDriverFrame:Execute([[ |
local frame = self:GetFrameRef("vehicleFrame") |
table.insert(VEHICLE_FRAMES, frame) |
]]) |
self:HookScript("OnAttributeChanged", UpdateVehicleSwitch) |
end |
local Disable = function(self) |
self:SetAttribute("unit", self:GetAttribute("normalUnit")) |
VehicleDriverFrame:SetFrameRef("vehicleFrame", self) |
VehicleDriverFrame:Execute([[ |
local frame = self:GetFrameRef("vehicleFrame") |
for idx, value in pairs(VEHICLE_FRAMES) do |
if value == frame then |
table.remove(VEHICLE_FRAMES, idx) |
return |
end |
end |
]]) |
end |
oUF:AddElement("VehicleSwitch", nil, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local function Update(self, event) |
local unit |
local method, pid, rid = GetLootMethod() |
if(method == 'master') then |
if(pid) then |
if(pid == 0) then |
unit = 'player' |
else |
unit = 'party'..pid |
end |
elseif(rid) then |
unit = 'raid'..rid |
end |
if(UnitIsUnit(unit, self.unit)) then |
self.MasterLooter:Show() |
elseif(self.MasterLooter:IsShown()) then |
self.MasterLooter:Hide() |
end |
elseif(self.MasterLooter:IsShown()) then |
self.MasterLooter:Hide() |
end |
end |
local function Enable(self, unit) |
local masterlooter = self.MasterLooter |
if(masterlooter) then |
self:RegisterEvent('PARTY_LOOT_METHOD_CHANGED', Update) |
self:RegisterEvent('PARTY_MEMBERS_CHANGED', Update) |
if(masterlooter:IsObjectType('Texture') and not masterlooter:GetTexture()) then |
masterlooter:SetTexture([[Interface\GroupFrame\UI-Group-MasterLooter]]) |
end |
return true |
end |
end |
local function Disable(self) |
if(self.MasterLooter) then |
self:UnregisterEvent('PARTY_LOOT_METHOD_CHANGED', Update) |
self:UnregisterEvent('PARTY_MEMBERS_CHANGED', Update) |
end |
end |
oUF:AddElement('MasterLooter', Update, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
do |
local Update = function(self, event) |
if(IsResting()) then |
self.Resting:Show() |
else |
self.Resting:Hide() |
end |
end |
local Enable = function(self, unit) |
if(self.Resting and unit == 'player') then |
self:RegisterEvent("PLAYER_UPDATE_RESTING", Update) |
if(self.Resting:IsObjectType"Texture" and not self.Resting:GetTexture()) then |
self.Resting:SetTexture[[Interface\CharacterFrame\UI-StateIcon]] |
self.Resting:SetTexCoord(0, .5, 0, .421875) |
end |
return true |
end |
end |
local Disable = function(self) |
if(self.Resting) then |
self:UnregisterEvent("PLAYER_UPDATE_RESTING", Update) |
end |
end |
oUF:AddElement('Resting', Update, Enable, Disable) |
end |
do |
local Update = function(self, event) |
if(UnitAffectingCombat"player") then |
self.Combat:Show() |
else |
self.Combat:Hide() |
end |
end |
local Enable = function(self, unit) |
if(self.Combat and unit == 'player') then |
self:RegisterEvent("PLAYER_REGEN_DISABLED", Update) |
self:RegisterEvent("PLAYER_REGEN_ENABLED", Update) |
if(self.Combat:IsObjectType"Texture" and not self.Combat:GetTexture()) then |
self.Combat:SetTexture[[Interface\CharacterFrame\UI-StateIcon]] |
self.Combat:SetTexCoord(.5, 1, 0, .5) |
end |
return true |
end |
end |
local Disable = function(self) |
if(self.Combat) then |
self:UnregisterEvent("PLAYER_REGEN_DISABLED", Update) |
self:UnregisterEvent("PLAYER_REGEN_ENABLED", Update) |
end |
end |
oUF:AddElement('Combat', Update, Enable, Disable) |
end |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local Update = function(self, event, unit) |
if(not UnitIsUnit(self.unit, unit)) then return end |
local portrait = self.Portrait |
if(portrait:IsObjectType'Model') then |
local name = UnitName(unit) |
if(not UnitExists(unit) or not UnitIsConnected(unit) or not UnitIsVisible(unit)) then |
portrait:SetModelScale(4.25) |
portrait:SetPosition(0, 0, -1.5) |
portrait:SetModel"Interface\\Buttons\\talktomequestionmark.mdx" |
elseif(portrait.name ~= name or event == 'UNIT_MODEL_CHANGED') then |
portrait:SetUnit(unit) |
portrait:SetCamera(0) |
portrait.name = name |
else |
portrait:SetCamera(0) |
end |
else |
SetPortraitTexture(portrait, unit) |
end |
end |
local Enable = function(self) |
if(self.Portrait) then |
self:RegisterEvent("UNIT_PORTRAIT_UPDATE", Update) |
self:RegisterEvent("UNIT_MODEL_CHANGED", Update) |
return true |
end |
end |
local Disable = function(self) |
if(self.Portrait) then |
self:UnregisterEvent("UNIT_PORTRAIT_UPDATE", Update) |
self:UnregisterEvent("UNIT_MODEL_CHANGED", Update) |
end |
end |
oUF:AddElement('Portrait', Update, Enable, Disable) |
--[[ |
Elements handled: .Threat |
Functions that can be overridden from within a layout: |
- :PreUpdateThreat(event, unit) |
- :OverrideUpdateThreat(event, unit, status) |
- :PostUpdateThreat(event, unit, status) |
--]] |
if(select(4, GetBuildInfo()) < 3e4) then return end |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local Update = function(self, event, unit) |
if(unit ~= self.unit) then return end |
if(self.PreUpdateThreat) then self:PreUpdateThreat(event, unit) end |
unit = unit or self.unit |
local threat = self.Threat |
local status = UnitThreatSituation(unit) |
if(not self.OverrideUpdateThreat) then |
if(status > 0) then |
local r, g, b = GetThreatStatusColor(status) |
threat:SetVertexColor(r, g, b) |
threat:Show() |
else |
threat:Hide() |
end |
else |
self:OverrideUpdateThreat(event, unit, status) |
end |
if(self.PostUpdateThreat) then self:PostUpdateThreat(event, unit, status) end |
end |
local Enable = function(self) |
local threat = self.Threat |
if(threat) then |
self:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE", Update) |
threat:Hide() |
if(threat:IsObjectType"Texture" and not threat:GetTexture()) then |
threat:SetTexture[[Interface\Minimap\ObjectIcons]] |
threat:SetTexCoord(6/8, 7/8, 1/2, 1) |
end |
return true |
end |
end |
local Disable = function(self) |
local threat = self.Threat |
if(threat) then |
self:UnregisterEvent("UNIT_THREAT_SITUATION_UPDATE", Update) |
end |
end |
oUF:AddElement('Threat', Update, Enable, Disable) |
--[[ |
Elements handled: .Auras, .Buffs, .Debuffs |
Shared: |
- spacing: Padding between aura icons. (Default: 0) |
- size: Size of the aura icons. (Default: 16) |
- initialAnchor: Initial anchor in the aura frame. (Default: "BOTTOMLEFT") |
- onlyShowPlayer: Only display icons casted by the player. (Default: nil) |
- growth-x: Growth direction, affected by initialAnchor. (Default: "UP") |
- growth-y: Growth direction, affected by initialAnchor. (Default: "RIGHT") |
- disableCooldown: Disable the Cooldown Spiral on the Aura Icons. (Default: nil) |
- filter: Expects a string with filter. See the UnitAura[1] documentation for |
more information. |
.Auras only: |
- gap: Adds a empty icon to separate buffs and debuffs. (Default: nil) |
- numBuffs: The maximum number of buffs that should be shown. (Default: 32) |
- numDebuffs: The maximum number of debuffs that should be shown. (Default: 40) |
- buffFilter: See filter on Shared. (Default: "HELPFUL") |
- debuffFilter: See filter on Shared. (Default: "HARMFUL") |
- Variables set by .Auras: |
- visibleBuffs: Number of currently visible buff icons. |
- visibleDebuffs: Number of currently visible debuff icons. |
- visibleAuras: Total number of currently visible buffs + debuffs. |
.Buffs only: |
- num: The maximum number of buffs that should be shown. (Default: 32) |
- Variables set by .Buffs: |
- visibleBuffs: Number of currently visible buff icons. |
.Debuffs only: |
- num: The maximum number of debuffs that should be shown. (Default: 40) |
- Variables set by .Debuffs: |
- visibleDebuffs: Number of currently visible debuff icons. |
Functions that can be overridden from within a layout: |
- :PostCreateAuraIcon(icon, icons, index, isDebuff) |
- :CreateAuraIcon(icons, index, isDebuff) |
- :PostUpdateAuraIcon(icons, unit, icon, index, offset, filter, isDebuff) |
- :PreUpdateAura(event, unit) |
- :SetAuraPosition(auras, max) |
- :PostUpdateAura(event, unit) |
[1] http://www.wowwiki.com/API_UnitAura |
--]] |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local ulduar = select(4, GetBuildInfo()) >= 30100 |
local OnEnter = function(self) |
if(not self:IsVisible()) then return end |
GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT") |
GameTooltip:SetUnitAura(self.frame.unit, self:GetID(), self.filter) |
end |
local OnLeave = function() |
GameTooltip:Hide() |
end |
local createAuraIcon = function(self, icons, index, debuff) |
local button = CreateFrame("Frame", nil, icons) |
button:EnableMouse(true) |
button:SetWidth(icons.size or 16) |
button:SetHeight(icons.size or 16) |
local cd = CreateFrame("Cooldown", nil, button) |
cd:SetAllPoints(button) |
local icon = button:CreateTexture(nil, "BACKGROUND") |
icon:SetAllPoints(button) |
local count = button:CreateFontString(nil, "OVERLAY") |
count:SetFontObject(NumberFontNormal) |
count:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -1, 0) |
local overlay = button:CreateTexture(nil, "OVERLAY") |
overlay:SetTexture"Interface\\Buttons\\UI-Debuff-Overlays" |
overlay:SetAllPoints(button) |
overlay:SetTexCoord(.296875, .5703125, 0, .515625) |
button.overlay = overlay |
button:SetScript("OnEnter", OnEnter) |
button:SetScript("OnLeave", OnLeave) |
table.insert(icons, button) |
button.parent = icons |
button.frame = self |
button.debuff = debuff |
button.icon = icon |
button.count = count |
button.cd = cd |
if(self.PostCreateAuraIcon) then self:PostCreateAuraIcon(button, icons, index, debuff) end |
return button |
end |
local customFilter = function(icons, unit, icon, name, rank, texture, count, dtype, duration, timeLeft, caster) |
local isPlayer = caster |
if(ulduar) then |
isPlayer = caster == unit |
else |
caster = nil |
end |
if((icons.onlyShowPlayer and isPlayer) or (not icons.onlyShowPlayer and name)) then |
icon.isPlayer = isPlayer |
icon.owner = caster |
return true |
end |
end |
local updateIcon = function(self, unit, icons, index, offset, filter, isDebuff, max) |
if(index == 0) then index = max end |
local icon = icons[index + offset] |
if(not icon) then |
icon = (self.CreateAuraIcon or createAuraIcon) (self, icons, index, isDebuff) |
end |
local name, rank, texture, count, dtype, duration, timeLeft, caster = UnitAura(unit, index, filter) |
local show = (self.CustomAuraFilter or customFilter) (icons, unit, icon, name, rank, texture, count, dtype, duration, timeLeft, caster) |
if(show) then |
if(not icons.disableCooldown and duration and duration > 0) then |
icon.cd:SetCooldown(timeLeft - duration, duration) |
icon.cd:Show() |
icon.duration = duration |
icon.timeLeft = timeLeft |
else |
icon.cd:Hide() |
end |
if((isDebuff and icons.showDebuffType) or (not isDebuff and icons.showBuffType) or icons.showType) then |
local color = DebuffTypeColor[dtype] or DebuffTypeColor.none |
icon.overlay:SetVertexColor(color.r, color.g, color.b) |
icon.overlay:Show() |
else |
icon.overlay:Hide() |
end |
icon.icon:SetTexture(texture) |
icon.count:SetText((count > 1 and count)) |
icon.filter = filter |
icon.debuff = isDebuff |
icon:SetID(index) |
icon:Show() |
if(self.PostUpdateAuraIcon) then |
self:PostUpdateAuraIcon(icons, unit, icon, index, offset, filter, isDebuff) |
end |
return true |
else |
icon:Hide() |
end |
end |
local SetAuraPosition = function(self, icons, x) |
if(icons and x > 0) then |
local col = 0 |
local row = 0 |
local spacing = icons.spacing or 0 |
local gap = icons.gap |
local size = (icons.size or 16) + spacing |
local anchor = icons.initialAnchor or "BOTTOMLEFT" |
local growthx = (icons["growth-x"] == "LEFT" and -1) or 1 |
local growthy = (icons["growth-y"] == "DOWN" and -1) or 1 |
local cols = math.floor(icons:GetWidth() / size + .5) |
local rows = math.floor(icons:GetHeight() / size + .5) |
for i = 1, x do |
local button = icons[i] |
if(button and button:IsShown()) then |
if(gap and button.debuff) then |
if(col > 0) then |
col = col + 1 |
end |
gap = false |
end |
if(col >= cols) then |
col = 0 |
row = row + 1 |
end |
button:ClearAllPoints() |
button:SetPoint(anchor, icons, anchor, col * size * growthx, row * size * growthy) |
col = col + 1 |
end |
end |
end |
end |
local Update = function(self, event, unit) |
if(self.unit ~= unit) then return end |
if(self.PreUpdateAura) then self:PreUpdateAura(event, unit) end |
local auras, buffs, debuffs = self.Auras, self.Buffs, self.Debuffs |
if(auras) then |
local buffs = auras.numBuffs or 32 |
local debuffs = auras.numDebuffs or 40 |
local max = debuffs + buffs |
local visibleBuffs, visibleDebuffs = 0, 0 |
for index = 1, max do |
if(index > buffs) then |
if(updateIcon(self, unit, auras, index % debuffs, visibleBuffs, auras.debuffFilter or auras.filter or 'HARMFUL', true, debuffs)) then |
visibleDebuffs = visibleDebuffs + 1 |
end |
else |
if(updateIcon(self, unit, auras, index, 0, auras.buffFilter or auras.filter or 'HELPFUL')) then |
visibleBuffs = visibleBuffs + 1 |
end |
end |
end |
auras.visibleBuffs = visibleBuffs |
auras.visibleDebuffs = visibleDebuffs |
auras.visibleAuras = visibleBuffs + visibleDebuffs |
self:SetAuraPosition(auras, max) |
end |
if(buffs) then |
local filter = buffs.filter or 'HELPFUL' |
local max = buffs.num or 32 |
local visibleBuffs = 0 |
for index = 1, max do |
if(not updateIcon(self, unit, buffs, index, 0, filter)) then |
max = index - 1 |
while(buffs[index]) do |
buffs[index]:Hide() |
index = index + 1 |
end |
break |
end |
visibleBuffs = visibleBuffs + 1 |
end |
buffs.visibleBuffs = visibleBuffs |
self:SetAuraPosition(buffs, max) |
end |
if(debuffs) then |
local filter = debuffs.filter or 'HARMFUL' |
local max = debuffs.num or 40 |
local visibleDebuffs = 0 |
for index = 1, max do |
if(not updateIcon(self, unit, debuffs, index, 0, filter, true)) then |
max = index - 1 |
while(debuffs[index]) do |
debuffs[index]:Hide() |
index = index + 1 |
end |
break |
end |
visibleDebuffs = visibleDebuffs + 1 |
end |
debuffs.visibleDebuffs = visibleDebuffs |
self:SetAuraPosition(debuffs, max) |
end |
if(self.PostUpdateAura) then self:PostUpdateAura(event, unit) end |
end |
local Enable = function(self) |
if(self.Buffs or self.Debuffs or self.Auras) then |
if(not self.SetAuraPosition) then |
self.SetAuraPosition = SetAuraPosition |
end |
self:RegisterEvent("UNIT_AURA", Update) |
return true |
end |
end |
local Disable = function(self) |
if(self.Buffs or self.Debuffs or self.Auras) then |
self:UnregisterEvent("UNIT_AURA", Update) |
end |
end |
oUF:AddElement('Aura', Update, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local Update = function(self, event, unit) |
if(self.unit ~= unit) then return end |
if(self.Happiness) then |
local happiness = GetPetHappiness() |
local hunterPet = select(2, HasPetUI()) |
if(not (happiness or hunterPet)) then |
return self.Happiness:Hide() |
end |
self.Happiness:Show() |
if(happiness == 1) then |
self.Happiness:SetTexCoord(0.375, 0.5625, 0, 0.359375) |
elseif(happiness == 2) then |
self.Happiness:SetTexCoord(0.1875, 0.375, 0, 0.359375) |
elseif(happiness == 3) then |
self.Happiness:SetTexCoord(0, 0.1875, 0, 0.359375) |
end |
if(self.PostUpdateHappiness) then self:PostUpdateHappiness(event, unit, happiness) end |
end |
end |
local Enable = function(self) |
local happiness = self.Happiness |
if(happiness) then |
self:RegisterEvent("UNIT_HAPPINESS", Update) |
if(happiness:IsObjectType"Texture" and not happiness:GetTexture()) then |
happiness:SetTexture[[Interface\PetPaperDollFrame\UI-PetHappiness]] |
end |
return true |
end |
end |
local Disable = function(self) |
local happiness = self.Happiness |
if(happiness) then |
self:UnregisterEvent("UNIT_HAPPINESS", Update) |
end |
end |
oUF:AddElement('Happiness', Update, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local Update = function(self, event) |
local unit = self.unit |
if((UnitInParty(unit) or UnitInRaid(unit)) and UnitIsPartyLeader(unit)) then |
self.Leader:Show() |
else |
self.Leader:Hide() |
end |
end |
local Enable = function(self) |
local leader = self.Leader |
if(leader) then |
self:RegisterEvent("PARTY_LEADER_CHANGED", Update) |
self:RegisterEvent("PARTY_MEMBERS_CHANGED", Update) |
if(leader:IsObjectType"Texture" and not leader:GetTexture()) then |
leader:SetTexture[[Interface\GroupFrame\UI-Group-LeaderIcon]] |
end |
return true |
end |
end |
local Disable = function(self) |
local leader = self.Leader |
if(leader) then |
self:UnregisterEvent("PARTY_LEADER_CHANGED", Update) |
self:UnregisterEvent("PARTY_MEMBERS_CHANGED", Update) |
end |
end |
oUF:AddElement('Leader', Update, Enable, Disable) |
--[[ |
Elements handled: .Power |
Shared: |
The following settings are listed by priority: |
- colorTapping |
- colorDisconnected |
- colorHappiness |
- colorPower |
- colorClass (Colors player units based on class) |
- colorClassPet (Colors pet units based on class) |
- colorClassNPC (Colors non-player units based on class) |
- colorReaction |
- colorSmooth - will use smoothGradient instead of the internal gradient if set. |
Background: |
- multiplier - number used to manipulate the power background. (default: 1) |
This option will only enable for player and pet. |
- frequentUpdates - do OnUpdate polling of power data. |
Functions that can be overridden from within a layout: |
- :PreUpdatePower(event, unit) |
- :OverrideUpdatePower(event, unit, bar, min, max) - Setting this function |
will disable the above color settings. |
- :PostUpdatePower(event, unit, bar, min, max) |
--]] |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local UnitManaMax = UnitManaMax |
local UnitPowerType = UnitPowerType |
local min, max, bar |
local OnPowerUpdate |
do |
local UnitMana = UnitMana |
OnPowerUpdate = function(self) |
if(self.disconnected) then return end |
local power = UnitMana(self.unit) |
if(power ~= self.min) then |
self.min = power |
self:GetParent():UNIT_MAXMANA("OnPowerUpdate", self.unit) |
end |
end |
end |
local Update = function(self, event, unit) |
if(self.unit ~= unit) then return end |
if(self.PreUpdatePower) then self:PreUpdatePower(event, unit) end |
min, max = UnitMana(unit), UnitManaMax(unit) |
bar = self.Power |
bar:SetMinMaxValues(0, max) |
bar:SetValue(min) |
bar.disconnected = not UnitIsConnected(unit) |
bar.unit = unit |
if(not self.OverrideUpdatePower) then |
local r, g, b, t |
if(bar.colorTapping and UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit)) then |
t = self.colors.tapped |
elseif(bar.colorDisconnected and not UnitIsConnected(unit)) then |
t = self.colors.disconnected |
elseif(bar.colorHappiness and unit == "pet" and GetPetHappiness()) then |
t = self.colors.happiness[GetPetHappiness()] |
elseif(bar.colorPower) then |
local _, ptype = UnitPowerType(unit) |
t = self.colors.power[ptype] |
elseif(bar.colorClass and UnitIsPlayer(unit)) or |
(bar.colorClassNPC and not UnitIsPlayer(unit)) or |
(bar.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then |
local _, class = UnitClass(unit) |
t = self.colors.class[class] |
elseif(bar.colorReaction) then |
t = self.colors.reaction[UnitReaction(unit, "player")] |
elseif(bar.colorSmooth) then |
r, g, b = self.ColorGradient(min / max, unpack(bar.smoothGradient or self.colors.smooth)) |
end |
if(t) then |
r, g, b = t[1], t[2], t[3] |
end |
if(b) then |
bar:SetStatusBarColor(r, g, b) |
local bg = bar.bg |
if(bg) then |
local mu = bg.multiplier or 1 |
bg:SetVertexColor(r * mu, g * mu, b * mu) |
end |
end |
else |
self:OverrideUpdatePower(event, unit, bar, min, max) |
end |
if(self.PostUpdatePower) then self:PostUpdatePower(event, unit, bar, min, max) end |
end |
local Enable = function(self, unit) |
local power = self.Power |
if(power) then |
if(power.frequentUpdates and (unit == 'player' or unit == 'pet')) then |
power.disconnected = true |
power:SetScript("OnUpdate", OnPowerUpdate) |
else |
self:RegisterEvent("UNIT_MANA", Update) |
self:RegisterEvent("UNIT_RAGE", Update) |
self:RegisterEvent("UNIT_FOCUS", Update) |
self:RegisterEvent("UNIT_ENERGY", Update) |
self:RegisterEvent("UNIT_RUNIC_POWER", Update) |
end |
self:RegisterEvent("UNIT_MAXMANA", Update) |
self:RegisterEvent("UNIT_MAXRAGE", Update) |
self:RegisterEvent("UNIT_MAXFOCUS", Update) |
self:RegisterEvent("UNIT_MAXENERGY", Update) |
self:RegisterEvent("UNIT_DISPLAYPOWER", Update) |
self:RegisterEvent("UNIT_MAXRUNIC_POWER", Update) |
self:RegisterEvent('UNIT_HAPPINESS', Update) |
-- For tapping. |
self:RegisterEvent('UNIT_FACTION', Update) |
if(not power:GetStatusBarTexture()) then |
power:SetStatusBarTexture[[Interface\TargetingFrame\UI-StatusBar]] |
end |
return true |
end |
end |
local Disable = function(self) |
local power = self.Power |
if(power) then |
if(power:GetScript'OnUpdate') then |
power:SetScript("OnUpdate", nil) |
else |
self:UnregisterEvent("UNIT_MANA", Update) |
self:UnregisterEvent("UNIT_RAGE", Update) |
self:UnregisterEvent("UNIT_FOCUS", Update) |
self:UnregisterEvent("UNIT_ENERGY", Update) |
self:UnregisterEvent("UNIT_RUNIC_POWER", Update) |
end |
self:UnregisterEvent("UNIT_MAXMANA", Update) |
self:UnregisterEvent("UNIT_MAXRAGE", Update) |
self:UnregisterEvent("UNIT_MAXFOCUS", Update) |
self:UnregisterEvent("UNIT_MAXENERGY", Update) |
self:UnregisterEvent("UNIT_DISPLAYPOWER", Update) |
self:UnregisterEvent("UNIT_MAXRUNIC_POWER", Update) |
self:UnregisterEvent('UNIT_HAPPINESS', Update) |
self:UnregisterEvent('UNIT_FACTION', Update) |
end |
end |
oUF:AddElement('Power', Update, Enable, Disable) |
--[[ |
Elements handled: .Range |
Settings: |
- inRangeAlpha - A number for frame alpha when unit is within player range. |
Required. |
- outsideRangeAlpha - A number for frame alpha when unit is outside player |
range. Required. |
--]] |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local objects = oUF.objects |
local OnRangeFrame |
local UnitInRange, UnitIsConnected = |
UnitInRange, UnitIsConnected |
-- updating of range. |
local timer = 0 |
local OnRangeUpdate = function(self, elapsed) |
timer = timer + elapsed |
if(timer >= .25) then |
for _, object in ipairs(objects) do |
if(object:IsShown() and object.Range) then |
if(UnitIsConnected(object.unit) and not UnitInRange(object.unit)) then |
if(object:GetAlpha() == object.inRangeAlpha) then |
object:SetAlpha(object.outsideRangeAlpha) |
end |
elseif(object:GetAlpha() ~= object.inRangeAlpha) then |
object:SetAlpha(object.inRangeAlpha) |
end |
end |
end |
timer = 0 |
end |
end |
local Enable = function(self) |
if(self.Range and not OnRangeFrame) then |
OnRangeFrame = CreateFrame"Frame" |
OnRangeFrame:SetScript("OnUpdate", OnRangeUpdate) |
end |
end |
oUF:AddElement('Range', nil, Enable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local GetRaidTargetIndex = GetRaidTargetIndex |
local SetRaidTargetIconTexture = SetRaidTargetIconTexture |
local Update = function(self, event) |
local index = GetRaidTargetIndex(self.unit) |
local icon = self.RaidIcon |
if(index) then |
SetRaidTargetIconTexture(icon, index) |
icon:Show() |
else |
icon:Hide() |
end |
end |
local Enable = function(self) |
local ricon = self.RaidIcon |
if(ricon) then |
self:RegisterEvent("RAID_TARGET_UPDATE", Update) |
if(ricon:IsObjectType"Texture" and not ricon:GetTexture()) then |
ricon:SetTexture[[Interface\TargetingFrame\UI-RaidTargetingIcons]] |
end |
return true |
end |
end |
local Disable = function(self) |
local ricon = self.RaidIcon |
if(ricon) then |
self:UnregisterEvent("RAID_TARGET_UPDATE", Update) |
end |
end |
oUF:AddElement('RaidIcon', Update, Enable, Disable) |
--[[ |
-- Experimental oUF tags |
-- Status: Incomplete |
-- |
-- Credits: Vika, Cladhaire, Tekkub |
-- |
-- TODO: |
-- - Tag and Untag should be able to handle more than one fontstring at a time. |
]] |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local function Hex(r, g, b) |
if type(r) == "table" then |
if r.r then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end |
end |
return string.format("|cff%02x%02x%02x", r*255, g*255, b*255) |
end |
local tags |
tags = { |
["[class]"] = function(u) return UnitClass(u) end, |
["[creature]"] = function(u) return UnitCreatureFamily(u) or UnitCreatureType(u) end, |
["[curhp]"] = UnitHealth, |
["[curpp]"] = UnitPower, |
["[dead]"] = function(u) return UnitIsDead(u) and "Dead" or UnitIsGhost(u) and "Ghost" end, |
["[difficulty]"] = function(u) if UnitCanAttack("player", u) then local l = UnitLevel(u); return Hex(GetDifficultyColor((l > 0) and l or 99)) end end, |
["[faction]"] = function(u) return UnitFactionGroup(u) end, |
["[leader]"] = function(u) return UnitIsPartyLeader(u) and "(L)" end, |
["[leaderlong]"] = function(u) return UnitIsPartyLeader(u) and "(Leader)" end, |
["[level]"] = function(u) local l = UnitLevel(u) return (l > 0) and l or "??" end, |
["[maxhp]"] = UnitHealthMax, |
["[maxpp]"] = UnitPowerMax, |
["[missinghp]"] = function(u) return UnitHealthMax(u) - UnitHealth(u) end, |
["[missingpp]"] = function(u) return UnitPowerMax(u) - UnitPower(u) end, |
["[name]"] = function(u, r) return UnitName(r or u) end, |
["[offline]"] = function(u) return (not UnitIsConnected(u) and "Offline") end, |
["[perhp]"] = function(u) local m = UnitHealthMax(u); return m == 0 and 0 or math.floor(UnitHealth(u)/m*100+0.5) end, |
["[perpp]"] = function(u) local m = UnitPowerMax(u); return m == 0 and 0 or math.floor(UnitPower(u)/m*100+0.5) end, |
["[plus]"] = function(u) local c = UnitClassification(u); return (c == "elite" or c == "rareelite") and "+" end, |
["[pvp]"] = function(u) return UnitIsPVP(u) and "PvP" end, |
["[race]"] = function(u) return UnitRace(u) end, |
["[raidcolor]"] = function(u) local _, x = UnitClass(u); return x and Hex(RAID_CLASS_COLORS[x]) end, |
["[rare]"] = function(u) local c = UnitClassification(u); return (c == "rare" or c == "rareelite") and "Rare" end, |
["[resting]"] = function(u) return u == "player" and IsResting() and "zzz" end, |
["[sex]"] = function(u) local s = UnitSex(u) return s == 2 and "Male" or s == 3 and "Female" end, |
["[smartclass]"] = function(u) return UnitIsPlayer(u) and tags["[class]"](u) or tags["[creature]"](u) end, |
["[status]"] = function(u) return UnitIsDead(u) and "Dead" or UnitIsGhost(u) and "Ghost" or not UnitIsConnected(u) and "Offline" or tags["[resting]"](u) end, |
["[threat]"] = function(u) local s = UnitThreatSituation(u) return s == 1 and "++" or s == 2 and "--" or s == 3 and "Aggro" end, |
["[threatcolor]"] = function(u) return Hex(GetThreatStatusColor(UnitThreatSituation(u))) end, |
["[cpoints]"] = function(u) local cp = GetComboPoints(u, 'target') return (cp > 0) and cp end, |
['[smartlevel]'] = function(u) |
local c = UnitClassification(u) |
if(c == 'worldboss') then |
return 'Boss' |
else |
local plus = tags['[plus]'](u) |
local level = tags['[level]'](u) |
if(plus) then |
return level .. plus |
else |
return level |
end |
end |
end, |
["[classification]"] = function(u) |
local c = UnitClassification(u) |
return c == "rare" and "Rare" or c == "eliterare" and "Rare Elite" or c == "elite" and "Elite" or c == "worldboss" and "Boss" |
end, |
["[shortclassification]"] = function(u) |
local c = UnitClassification(u) |
return c == "rare" and "R" or c == "eliterare" and "R+" or c == "elite" and "+" or c == "worldboss" and "B" |
end, |
} |
local tagEvents = { |
["[curhp]"] = "UNIT_HEALTH", |
["[curpp]"] = "UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_RUNIC_POWER", |
["[dead]"] = "UNIT_HEALTH", |
["[leader]"] = "PARTY_LEADER_CHANGED", |
["[leaderlong]"] = "PARTY_LEADER_CHANGED", |
["[level]"] = "UNIT_LEVEL PLAYER_LEVEL_UP", |
["[maxhp]"] = "UNIT_MAXHEALTH", |
["[maxpp]"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_MAXRUNIC_POWER", |
["[missinghp]"] = "UNIT_HEALTH UNIT_MAXHEALTH", |
["[missingpp]"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER", |
["[name]"] = "UNIT_NAME_UPDATE", |
["[offline]"] = "UNIT_HEALTH", |
["[perhp]"] = "UNIT_HEALTH UNIT_MAXHEALTH", |
["[perpp]"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER", |
["[pvp]"] = "UNIT_FACTION", |
["[resting]"] = "PLAYER_UPDATE_RESTING", |
["[status]"] = "UNIT_HEALTH PLAYER_UPDATE_RESTING", |
["[smartlevel]"] = "UNIT_LEVEL PLAYER_LEVEL_UP UNIT_CLASSIFICATION_CHANGED", |
["[threat]"] = "UNIT_THREAT_SITUATION_UPDATE", |
["[threatcolor]"] = "UNIT_THREAT_SITUATION_UPDATE", |
['[cpoints]'] = 'UNIT_COMBO_POINTS UNIT_TARGET', |
['[rare]'] = 'UNIT_CLASSIFICATION_CHANGED', |
['[classification]'] = 'UNIT_CLASSIFICATION_CHANGED', |
['[shortclassification]'] = 'UNIT_CLASSIFICATION_CHANGED', |
} |
local unitlessEvents = { |
PLAYER_TARGET_CHANGED = true, |
PLAYER_FOCUS_CHANGED = true, |
PLAYER_LEVEL_UP = true, |
} |
local events = {} |
local frame = CreateFrame"Frame" |
frame:SetScript('OnEvent', function(self, event, unit) |
local strings = events[event] |
if(strings) then |
for k, fontstring in next, strings do |
if(not unitlessEvents[event] and fontstring.parent.unit == unit and fontstring:IsVisible()) then |
fontstring:UpdateTag() |
end |
end |
end |
end) |
local eventlessUnits = {} |
local timer = .5 |
local lowestTimer = .5 |
local OnUpdate = function(self, elapsed) |
if(timer >= lowestTimer) then |
for k, fs in next, eventlessUnits do |
if(fs.parent:IsShown() and UnitExists(fs.parent.unit)) then |
fs:UpdateTag() |
end |
end |
timer = 0 |
end |
timer = timer + elapsed |
end |
local OnShow = function(self) |
for _, fs in next, self.__tags do |
fs:UpdateTag() |
end |
end |
local RegisterEvent = function(fontstr, event) |
if(not events[event]) then events[event] = {} end |
frame:RegisterEvent(event) |
table.insert(events[event], fontstr) |
end |
local RegisterEvents = function(fontstr, tagstr) |
-- Forcefully strip away any parentheses and the characters in them. |
tagstr = tagstr:gsub('%b()', '') |
for tag in tagstr:gmatch'[%[]%w+[%]]' do |
local tagevents = tagEvents[tag] |
if(tagevents) then |
for event in tagevents:gmatch'%S+' do |
RegisterEvent(fontstr, event) |
end |
end |
end |
end |
local UnregisterEvents = function(fontstr) |
for events, data in pairs(events) do |
for k, tagfsstr in pairs(data) do |
if(tagfsstr == fontstr) then |
if(#data[k] == 1) then frame:UnregisterEvent(event) end |
data[k] = nil |
end |
end |
end |
end |
local tagPool = {} |
local funcPool = {} |
local tmp = {} |
local Tag = function(self, fs, tagstr) |
if(not fs or not tagstr or self == oUF) then return end |
if(not self.__tags) then |
self.__tags = {} |
table.insert(self.__elements, OnShow) |
else |
-- Since people ignore everything that's good practice - unregister the tag |
-- if it already exists. |
for _, tag in pairs(self.__tags) do |
if(fs == tag) then |
-- We don't need to remove it from the __tags table as Untag handles |
-- that for us. |
self:Untag(fs) |
end |
end |
end |
fs.parent = self |
local func = tagPool[tagstr] |
if(not func) then |
-- Using .- in the match prevents use from supporting [] as prepend/append |
-- characters. Supporting these and having a single pattern here is a real |
-- headache however. |
local format = tagstr:gsub('%%', '%%%%'):gsub('[[].-[]]', '%%s') |
local args = {} |
for bracket in tagstr:gmatch'([[](.-)[]])' do |
local tfunc = funcPool[bracket] or tags[bracket] |
if(not tfunc) then |
-- ... |
local pre, tag, ap = bracket:match'[%[](%b())([%w]+)(%b())[%]]' |
if(not pre) then pre, tag = bracket:match'[%[](%b())([%w]+)[%]]' end |
if(not pre) then tag, ap = bracket:match'[%[]([%w]+)(%b())[%]]' end |
tag = (tag and '['.. tag ..']') |
tag = tags[tag] |
if(tag) then |
if(pre and ap) then |
pre = pre:sub(2,-2) |
ap = ap:sub(2,-2) |
tfunc = function(u) |
local str = tag(u) |
if(str) then |
return pre..str..ap |
end |
end |
elseif(pre) then |
pre = pre:sub(2,-2) |
tfunc = function(u) |
local str = tag(u) |
if(str) then |
return pre..str |
end |
end |
elseif(ap) then |
ap = ap:sub(2,-2) |
tfunc = function(u) |
local str = tag(u) |
if(str) then |
return str..ap |
end |
end |
end |
funcPool[bracket] = tfunc |
end |
end |
if(tfunc) then |
table.insert(args,tfunc) |
else |
return error(('Attempted to use invalid tag %s.'):format(bracket), 3) |
end |
end |
func = function(self) |
local unit = self.parent.unit |
local __unit = self.parent.__unit |
for i, func in next, args do |
tmp[i] = func(unit, __unit) or '' |
end |
self:SetFormattedText(format, unpack(tmp)) |
end |
tagPool[tagstr] = func |
end |
fs.UpdateTag = func |
local unit = self.unit |
if((unit and unit:match'%w+target') or fs.frequentUpdates) then |
if(type(fs.frequentUpdates) == 'number') then |
lowestTimer = math.min(fs.frequentUpdates, lowestTimer) |
end |
table.insert(eventlessUnits, fs) |
if(not frame:GetScript'OnUpdate') then |
frame:SetScript('OnUpdate', OnUpdate) |
end |
else |
RegisterEvents(fs, tagstr) |
if(unit == 'focus') then |
RegisterEvent(fs, 'PLAYER_FOCUS_CHANGED') |
elseif(unit == 'target') then |
RegisterEvent(fs, 'PLAYER_TARGET_CHANGED') |
elseif(unit == 'mouseover') then |
RegisterEvent(fs, 'UPDATE_MOUSEOVER_UNIT') |
end |
end |
table.insert(self.__tags, fs) |
end |
local Untag = function(self, fs) |
if(not fs or self == oUF) then return end |
UnregisterEvents(fs) |
for k, fontstr in next, eventlessUnits do |
if(fs == fontstr) then |
table.remove(eventlessUnits, k) |
end |
end |
for k, fontstr in next, self.__tags do |
if(fontstr == fs) then |
table.remove(self.__tags, k) |
end |
end |
fs.UpdateTag = nil |
end |
oUF.Tags = tags |
oUF.TagEvents = tagEvents |
oUF.UnitlessTagEvents = unitlessEvents |
oUF.Tag = Tag |
oUF.Untag = Untag |
--[[ |
Elements handled: .Health |
Shared: |
The following settings are listed by priority: |
- colorTapping |
- colorDisconnected |
- colorHappiness |
- colorClass (Colors player units based on class) |
- colorClassPet (Colors pet units based on class) |
- colorClassNPC (Colors non-player units based on class) |
- colorReaction |
- colorSmooth - will use smoothGradient instead of the internal gradient if set. |
- colorHealth |
Background: |
- multiplier - number used to manipulate the power background. (default: 1) |
WotLK only: |
- frequentUpdates - do OnUpdate polling of health data. |
Functions that can be overridden from within a layout: |
- :PreUpdateHealth(event, unit) |
- :OverrideUpdateHealth(event, unit, bar, min, max) - Setting this function |
will disable the above color settings. |
- :PostUpdateHealth(event, unit, bar, min, max) |
--]] |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local OnHealthUpdate |
do |
local UnitHealth = UnitHealth |
OnHealthUpdate = function(self) |
if(self.disconnected) then return end |
local health = UnitHealth(self.unit) |
if(health ~= self.min) then |
self.min = health |
self:GetParent():UNIT_MAXHEALTH("OnHealthUpdate", self.unit) |
end |
end |
end |
local Update = function(self, event, unit) |
if(self.unit ~= unit) then return end |
if(self.PreUpdateHealth) then self:PreUpdateHealth(event, unit) end |
local min, max = UnitHealth(unit), UnitHealthMax(unit) |
local bar = self.Health |
bar:SetMinMaxValues(0, max) |
bar:SetValue(min) |
bar.disconnected = not UnitIsConnected(unit) |
bar.unit = unit |
if(not self.OverrideUpdateHealth) then |
local r, g, b, t |
if(bar.colorTapping and UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit)) then |
t = self.colors.tapped |
elseif(bar.colorDisconnected and not UnitIsConnected(unit)) then |
t = self.colors.disconnected |
elseif(bar.colorHappiness and unit == "pet" and GetPetHappiness()) then |
t = self.colors.happiness[GetPetHappiness()] |
elseif(bar.colorClass and UnitIsPlayer(unit)) or |
(bar.colorClassNPC and not UnitIsPlayer(unit)) or |
(bar.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then |
local _, class = UnitClass(unit) |
t = self.colors.class[class] |
elseif(bar.colorReaction) then |
t = self.colors.reaction[UnitReaction(unit, "player")] |
elseif(bar.colorSmooth and max ~= 0) then |
r, g, b = self.ColorGradient(min / max, unpack(bar.smoothGradient or self.colors.smooth)) |
elseif(bar.colorHealth) then |
t = self.colors.health |
end |
if(t) then |
r, g, b = t[1], t[2], t[3] |
end |
if(b) then |
bar:SetStatusBarColor(r, g, b) |
local bg = bar.bg |
if(bg) then |
local mu = bg.multiplier or 1 |
bg:SetVertexColor(r * mu, g * mu, b * mu) |
end |
end |
else |
self:OverrideUpdateHealth(event, unit, bar, min, max) |
end |
if(self.PostUpdateHealth) then self:PostUpdateHealth(event, unit, bar, min, max) end |
end |
local Enable = function(self) |
local health = self.Health |
if(health) then |
if(health.frequentUpdates and (self.unit and not self.unit:match'%w+target$') or not self.unit) then |
health.disconnected = true |
health:SetScript('OnUpdate', OnHealthUpdate) |
else |
self:RegisterEvent("UNIT_HEALTH", Update) |
end |
self:RegisterEvent("UNIT_MAXHEALTH", Update) |
self:RegisterEvent('UNIT_HAPPINESS', Update) |
-- For tapping. |
self:RegisterEvent('UNIT_FACTION', Update) |
if(not health:GetStatusBarTexture()) then |
health:SetStatusBarTexture[[Interface\TargetingFrame\UI-StatusBar]] |
end |
return true |
end |
end |
local Disable = function(self) |
local health = self.Health |
if(health) then |
if(self:GetScript'OnUpdate') then |
health:SetScript('OnUpdate', nil) |
else |
self:UnregisterEvent('UNIT_HEALTH', Update) |
end |
self:UnregisterEvent('UNIT_MAXHEALTH', Update) |
self:UnregisterEvent('UNIT_HAPPINESS', Update) |
self:UnregisterEvent('UNIT_FACTION', Update) |
end |
end |
oUF:AddElement('Health', Update, Enable, Disable) |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local oUF = _G[global] |
local Update = function(self, event, unit) |
if(unit ~= self.unit) then return end |
if(self.PvP) then |
local factionGroup = UnitFactionGroup(unit) |
if(UnitIsPVPFreeForAll(unit)) then |
self.PvP:SetTexture[[Interface\TargetingFrame\UI-PVP-FFA]] |
self.PvP:Show() |
elseif(factionGroup and UnitIsPVP(unit)) then |
self.PvP:SetTexture([[Interface\TargetingFrame\UI-PVP-]]..factionGroup) |
self.PvP:Show() |
else |
self.PvP:Hide() |
end |
end |
end |
local Enable = function(self) |
if(self.PvP) then |
self:RegisterEvent("UNIT_FACTION", Update) |
return true |
end |
end |
local Disable = function(self) |
if(self.PvP) then |
self:UnregisterEvent("UNIT_FACTION", Update) |
end |
end |
oUF:AddElement('PvP', Update, Enable, Disable) |
## Interface: 30100 |
## Title: oUF |
## Author: Haste |
## Version: 1.3.9 |
## X-eMail: troeks@gmail.com |
## X-oUF: oUF |
## Notes: Unit frame framework. Does nothing by itself. |
oUF.xml |
Copyright (c) 2006-2009 Trond A Ekseth |
Permission is hereby granted, free of charge, to any person |
obtaining a copy of this software and associated documentation |
files (the "Software"), to deal in the Software without |
restriction, including without limitation the rights to use, |
copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the |
Software is furnished to do so, subject to the following |
conditions: |
The above copyright notice and this permission notice shall be |
included in all copies or substantial portions of the Software. |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
OTHER DEALINGS IN THE SOFTWARE. |
<Ui xmlns="http://www.blizzard.com/wow/ui/"> |
<Script file='ouf.lua' /> |
<Script file='elements\power.lua' /> |
<Script file='elements\aura.lua' /> |
<Script file='elements\health.lua' /> |
<Script file='elements\cpoints.lua' /> |
<Script file='elements\ricons.lua' /> |
<Script file='elements\leader.lua' /> |
<Script file='elements\status.lua' /> |
<Script file='elements\pvp.lua' /> |
<Script file='elements\portraits.lua' /> |
<Script file='elements\range.lua' /> |
<Script file='elements\happiness.lua' /> |
<Script file='elements\castbar.lua' /> |
<Script file='elements\threat.lua' /> |
<Script file='elements\tags.lua' /> |
<Script file='elements\vehicle.lua' /> |
<Script file='elements\masterlooter.lua' /> |
<!-- |
This template requires the layout to set the anchoring positions from the |
sub-frame(s). These are feed to the layouts style functions as any other unit |
spawned by a header. The main difference is that they have the unitsuffix |
attribute set (and they are parented to their respective owener). |
--> |
<Button name="oUF_HeaderTargetTemplate" inherits="SecureUnitButtonTemplate" hidden="true" virtual="true"> |
<Frames> |
<Button name="$parentTarget" inherits="SecureUnitButtonTemplate"> |
<Attributes> |
<Attribute name="unitsuffix" type="string" value="target"/> |
<Attribute name="useparent-unit" type="boolean" value="true"/> |
<Attribute name="type1" type="string" value="target"/> |
<Attribute name="initial-unitWatch" type="boolean" value="true"/> |
</Attributes> |
</Button> |
</Frames> |
</Button> |
</Ui> |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
assert(global, 'X-oUF needs to be defined in the parent add-on.') |
local _VERSION = GetAddOnMetadata(parent, 'version') |
local function argcheck(value, num, ...) |
assert(type(num) == 'number', "Bad argument #2 to 'argcheck' (number expected, got "..type(num)..")") |
for i=1,select("#", ...) do |
if type(value) == select(i, ...) then return end |
end |
local types = strjoin(", ", ...) |
local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]") |
error(("Bad argument #%d to '%s' (%s expected, got %s"):format(num, name, types, type(value)), 3) |
end |
local print = function(a) ChatFrame1:AddMessage("|cff33ff99oUF:|r "..tostring(a)) end |
local error = function(...) print("|cffff0000Error:|r "..string.format(...)) end |
local dummy = function() end |
local function SetManyAttributes(self, ...) |
for i=1,select("#", ...),2 do |
local att,val = select(i, ...) |
if not att then return end |
self:SetAttribute(att,val) |
end |
end |
-- Colors |
local colors = { |
health = {49/255, 207/255, 37/255}, -- Health |
happiness = { |
[1] = {1, 0, 0}, -- need.... | unhappy |
[2] = {1, 1, 0}, -- new..... | content |
[3] = {0, 1, 0}, -- colors.. | happy |
}, |
smooth = { |
1, 0, 0, |
1, 1, 0, |
0, 1, 0 |
}, |
disconnected = {.6, .6, .6}, |
tapped = {.6,.6,.6}, |
class = {}, |
reaction = {}, |
power = {}, |
} |
-- We do this because people edit the vars directly, and changing the default |
-- globals makes SPICE FLOW! |
for eclass, color in next, RAID_CLASS_COLORS do |
colors.class[eclass] = {color.r, color.g, color.b} |
end |
for power, color in next, PowerBarColor do |
if(type(power) == 'string') then |
colors.power[power] = {color.r, color.g, color.b} |
end |
end |
for eclass, color in next, FACTION_BAR_COLORS do |
colors.reaction[eclass] = {color.r, color.g, color.b} |
end |
-- add-on object |
local oUF = CreateFrame"Button" |
local frame_metatable = {__index = oUF} |
local event_metatable = { |
__call = function(funcs, self, ...) |
for _, func in ipairs(funcs) do |
func(self, ...) |
end |
end, |
} |
local styles, style = {} |
local callback, units, objects = {}, {}, {} |
local _G, select, type, tostring, math_modf = |
_G, select, type, tostring, math.modf |
local UnitExists, UnitName = |
UnitExists, UnitName |
local conv = { |
['playerpet'] = 'pet', |
['playertarget'] = 'target', |
} |
local elements = {} |
local enableTargetUpdate = function(object) |
-- updating of "invalid" units. |
local OnTargetUpdate |
do |
local timer = 0 |
OnTargetUpdate = function(self, elapsed) |
if(not self.unit) then |
return |
elseif(timer >= .5) then |
self:PLAYER_ENTERING_WORLD'OnTargetUpdate' |
timer = 0 |
end |
timer = timer + elapsed |
end |
end |
object:SetScript("OnUpdate", OnTargetUpdate) |
end |
-- Events |
local OnEvent = function(self, event, ...) |
if(not self:IsShown() and not self.vehicleUnit) then return end |
self[event](self, event, ...) |
end |
local OnAttributeChanged = function(self, name, value) |
if(name == "unit" and value) then |
units[value] = self |
if(self.unit and self.unit == value) then |
return |
else |
if(self.hasChildren) then |
for _, object in next, objects do |
local unit = SecureButton_GetModifiedUnit(object) |
object.unit = conv[unit] or unit |
object:PLAYER_ENTERING_WORLD() |
end |
end |
self.unit = value |
self.id = value:match"^.-(%d+)" |
self:PLAYER_ENTERING_WORLD() |
end |
end |
end |
-- Gigantic function of doom |
local HandleUnit = function(unit, object) |
if(unit == "player") then |
-- Hide the blizzard stuff |
PlayerFrame:UnregisterAllEvents() |
PlayerFrame.Show = dummy |
PlayerFrame:Hide() |
PlayerFrameHealthBar:UnregisterAllEvents() |
PlayerFrameManaBar:UnregisterAllEvents() |
elseif(unit == "pet")then |
-- Hide the blizzard stuff |
PetFrame:UnregisterAllEvents() |
PetFrame.Show = dummy |
PetFrame:Hide() |
PetFrameHealthBar:UnregisterAllEvents() |
PetFrameManaBar:UnregisterAllEvents() |
elseif(unit == "target") then |
-- Hide the blizzard stuff |
TargetFrame:UnregisterAllEvents() |
TargetFrame.Show = dummy |
TargetFrame:Hide() |
TargetFrameHealthBar:UnregisterAllEvents() |
TargetFrameManaBar:UnregisterAllEvents() |
TargetFrameSpellBar:UnregisterAllEvents() |
ComboFrame:UnregisterAllEvents() |
ComboFrame.Show = dummy |
ComboFrame:Hide() |
-- Enable our shit |
object:RegisterEvent("PLAYER_TARGET_CHANGED", 'PLAYER_ENTERING_WORLD') |
elseif(unit == "focus") then |
FocusFrame:UnregisterAllEvents() |
FocusFrame.Show = dummy |
FocusFrame:Hide() |
FocusFrameHealthBar:UnregisterAllEvents() |
FocusFrameManaBar:UnregisterAllEvents() |
FocusFrameSpellBar:UnregisterAllEvents() |
object:RegisterEvent("PLAYER_FOCUS_CHANGED", 'PLAYER_ENTERING_WORLD') |
elseif(unit == "mouseover") then |
object:RegisterEvent("UPDATE_MOUSEOVER_UNIT", 'PLAYER_ENTERING_WORLD') |
elseif(unit:match"target") then |
-- Hide the blizzard stuff |
if(unit == "targettarget") then |
TargetofTargetFrame:UnregisterAllEvents() |
TargetofTargetFrame.Show = dummy |
TargetofTargetFrame:Hide() |
TargetofTargetHealthBar:UnregisterAllEvents() |
TargetofTargetManaBar:UnregisterAllEvents() |
end |
enableTargetUpdate(object) |
elseif(unit == "party") then |
for i=1,4 do |
local party = "PartyMemberFrame"..i |
local frame = _G[party] |
frame:UnregisterAllEvents() |
frame.Show = dummy |
frame:Hide() |
_G[party..'HealthBar']:UnregisterAllEvents() |
_G[party..'ManaBar']:UnregisterAllEvents() |
end |
end |
end |
local initObject = function(unit, style, ...) |
local num = select('#', ...) |
for i=1, num do |
local object = select(i, ...) |
object.__elements = {} |
object = setmetatable(object, frame_metatable) |
style(object, unit) |
local mt = type(style) == 'table' |
local height = object:GetAttribute'initial-height' or (mt and style['initial-height']) |
local width = object:GetAttribute'initial-width' or (mt and style['initial-width']) |
local scale = object:GetAttribute'initial-scale' or (mt and style['initial-scale']) |
local suffix = object:GetAttribute'unitsuffix' |
if(height) then |
object:SetAttribute('initial-height', height) |
if(unit) then object:SetHeight(height) end |
end |
if(width) then |
object:SetAttribute("initial-width", width) |
if(unit) then object:SetWidth(width) end |
end |
if(scale) then |
object:SetAttribute("initial-scale", scale) |
if(unit) then object:SetScale(scale) end |
end |
if(suffix == 'target') then |
enableTargetUpdate(object) |
end |
if(num > 1 and i == 1) then |
object.hasChildren = true |
end |
object:SetAttribute("*type1", "target") |
object:SetScript("OnEvent", OnEvent) |
object:SetScript("OnAttributeChanged", OnAttributeChanged) |
object:SetScript("OnShow", object.PLAYER_ENTERING_WORLD) |
object:RegisterEvent"PLAYER_ENTERING_WORLD" |
for element in next, elements do |
object:EnableElement(element, unit) |
end |
for _, func in next, callback do |
func(object) |
end |
-- We could use ClickCastFrames only, but it will probably contain frames that |
-- we don't care about. |
table.insert(objects, object) |
_G.ClickCastFrames = ClickCastFrames or {} |
ClickCastFrames[object] = true |
end |
end |
local walkObject = function(object, unit) |
local style = object:GetParent().style or styles[style] |
initObject(unit, style, object, object:GetChildren()) |
end |
function oUF:RegisterInitCallback(func) |
table.insert(callback, func) |
end |
function oUF:RegisterStyle(name, func) |
argcheck(name, 2, 'string') |
argcheck(func, 3, 'function', 'table') |
if(styles[name]) then return error("Style [%s] already registered.", name) end |
if(not style) then style = name end |
styles[name] = func |
end |
function oUF:SetActiveStyle(name) |
argcheck(name, 2, 'string') |
if(not styles[name]) then return error("Style [%s] does not exist.", name) end |
style = name |
end |
function oUF:Spawn(unit, name, template, disableBlizz) |
argcheck(unit, 2, 'string') |
if(not style) then return error("Unable to create frame. No styles have been registered.") end |
local style = styles[style] |
local object |
if(unit == "header") then |
if(not template) then |
template = "SecureGroupHeaderTemplate" |
end |
HandleUnit(disableBlizz or 'party') |
local header = CreateFrame("Frame", name, UIParent, template) |
header:SetAttribute("template", "SecureUnitButtonTemplate") |
header.initialConfigFunction = walkObject |
header.style = style |
header.SetManyAttributes = SetManyAttributes |
return header |
else |
object = CreateFrame("Button", name, UIParent, "SecureUnitButtonTemplate") |
object:SetAttribute("unit", unit) |
object.unit = unit |
object.id = unit:match"^.-(%d+)" |
units[unit] = object |
walkObject(object, unit) |
HandleUnit(unit, object) |
RegisterUnitWatch(object) |
end |
return object |
end |
local RegisterEvent = oUF.RegisterEvent |
function oUF:RegisterEvent(event, func) |
argcheck(event, 2, 'string') |
if(type(func) == 'string' and type(self[func]) == 'function') then |
func = self[func] |
end |
local curev = self[event] |
if(curev and func) then |
if(type(curev) == 'function') then |
self[event] = setmetatable({curev, func}, event_metatable) |
else |
for _, infunc in ipairs(curev) do |
if(infunc == func) then return end |
end |
table.insert(curev, func) |
end |
elseif(self:IsEventRegistered(event)) then |
return |
else |
if(func) then |
self[event] = func |
elseif(not self[event]) then |
return error("Handler for event [%s] on unit [%s] does not exist.", event, self.unit or 'unknown') |
end |
RegisterEvent(self, event) |
end |
end |
local UnregisterEvent = oUF.UnregisterEvent |
function oUF:UnregisterEvent(event, func) |
argcheck(event, 2, 'string') |
local curev = self[event] |
if(type(curev) == 'table' and func) then |
for k, infunc in ipairs(curev) do |
if(infunc == func) then |
curev[k] = nil |
if(#curev == 0) then |
table.remove(curev, k) |
UnregisterEvent(self, event) |
end |
end |
end |
else |
self[event] = nil |
UnregisterEvent(self, event) |
end |
end |
function oUF:AddElement(name, update, enable, disable) |
argcheck(name, 2, 'string') |
argcheck(update, 3, 'function', 'nil') |
argcheck(enable, 4, 'function', 'nil') |
argcheck(disable, 5, 'function', 'nil') |
if(elements[name]) then return error('Element [%s] is already registered.', name) end |
elements[name] = { |
update = update; |
enable = enable; |
disable = disable; |
} |
end |
function oUF:EnableElement(name, unit) |
argcheck(name, 2, 'string') |
argcheck(unit, 3, 'string', 'nil') |
local element = elements[name] |
if(not element) then return end |
if(element.enable(self, unit or self.unit)) then |
table.insert(self.__elements, element.update) |
end |
end |
function oUF:DisableElement(name) |
argcheck(name, 2, 'string') |
local element = elements[name] |
if(not element) then return end |
for k, update in ipairs(self.__elements) do |
if(update == element.update) then |
table.remove(self.__elements, k) |
element.disable(self) |
break |
end |
end |
end |
function oUF:UpdateElement(name) |
argcheck(name, 2, 'string') |
local element = elements[name] |
if(not element) then return end |
element.update(self, 'UpdateElement', self.unit) |
end |
oUF.Enable = RegisterUnitWatch |
function oUF:Disable() |
UnregisterUnitWatch(self) |
self:Hide() |
end |
--[[ |
--:PLAYER_ENTERING_WORLD() |
-- Notes: |
-- - Does a full update of all elements on the object. |
--]] |
function oUF:PLAYER_ENTERING_WORLD(event) |
local unit = self.unit |
if(not UnitExists(unit)) then return end |
for _, func in next, self.__elements do |
func(self, event, unit) |
end |
end |
-- http://www.wowwiki.com/ColorGradient |
function oUF.ColorGradient(perc, ...) |
if perc >= 1 then |
local r, g, b = select(select('#', ...) - 2, ...) |
return r, g, b |
elseif perc <= 0 then |
local r, g, b = ... |
return r, g, b |
end |
local num = select('#', ...) / 3 |
local segment, relperc = math.modf(perc*(num-1)) |
local r1, g1, b1, r2, g2, b2 = select((segment*3)+1, ...) |
return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc |
end |
oUF.version = _VERSION |
oUF.units = units |
oUF.objects = objects |
oUF.colors = colors |
_G[global] = oUF |
## Interface: 30100 |
## Title: ouF_|cff336633Smee2|r_Config |
## Notes: layout for oUF. |
## Author: Smee |
## X-eMail: airtonix@gmail.com |
## Version: 30100.0081 |
## RequiredDeps: oUF, oUF_Smee2, oUF_TagEditor |
## OptionalDeps: SharedMedia |
## LoadOnDemand: 1 |
embeds.xml |
core.lua |
methods.lua |
config.lua |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="Libs\LibStub\LibStub.lua"/> |
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/> |
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/> |
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/> |
<Include file="Libs\AceGUI-3.0-SharedMediaWidgets\widget.xml"/> |
</Ui> |
local layoutName = 'oUF_Smee2' |
local modulus = mod |
local mod = _G[layoutName] |
local configModName = layoutName..'_Config' |
local configMod = _G[configModName] |
local tinsert = table.insert |
local db = mod.db.profile |
GlobalObject = {} |
configMod.growthYDirections= { |
UP = "UP", |
DOWN = "DOWN", |
} |
configMod.growthXDirections= { |
LEFT = "LEFT", |
RIGHT = "RIGHT", |
} |
configMod.frameAnchorPoints = { |
TOPLEFT = "TOPLEFT", TOP = "TOP", TOPRIGHT = "TOPRIGHT", |
LEFT = "LEFT", CENTER = "CENTER", RIGHT = "RIGHT", |
BOTTOMLEFT = "BOTTOMLEFT", BOTTOM = "BOTTOM", BOTTOMRIGHT = "BOTTOMRIGHT" |
} |
configMod.textHorizontalAlignmentPoints={ |
LEFT = "LEFT", CENTER = "CENTER", RIGHT ="RIGHT" |
} |
configMod.textVerticalAlignmentPoints={ |
TOP = "TOP", MIDDLE = "MIDDLE", BOTTOM = "BOTTOM" |
} |
configMod.fontOutlineTypes={ |
NONE = "None", |
OUTLINE = "OUTLINE", |
THICKOUTLINE = "THICKOUTLINE", |
MONOCHROME = "MONOCHROME" |
} |
configMod.resizeRules = { |
["Height"] = { |
['Power'] = function(obj,num) |
return ((obj:GetParent().db.height - 3) / 100 ) * num |
end, |
['Health'] = function(obj,num) |
return ((obj:GetParent().db.height - 3) / 100 ) * num |
end, |
['Castbar'] = function(obj,num) |
return num |
end, |
['RuneBar'] = function(obj,num) |
return ((obj:GetParent().db.height - 3) / 100 ) * num |
end, |
['TotemBar'] = function(obj,num) |
return num |
end, |
}, |
["Width"] = { |
['Power'] = function(obj,num) |
return obj:GetWidth() |
end, |
['Health'] = function(obj,num) |
return obj:GetWidth() |
end, |
['Castbar'] = function(obj,num) |
return num |
end, |
['RuneBar'] = function(obj,num) |
return ((obj:GetParent().db.width - 3) / 100 ) * num |
end, |
['Totembar'] = function(obj,num) |
return num |
end, |
} |
} |
function configMod:UpdateTextures(object,data) |
local textures = mod.db.profile.textures |
for bar,obj in pairs(object.bars)do |
obj:SetStatusBarTexture(mod.LSM:Fetch('statusbar',data.statusbar)) |
end |
end |
function configMod:PlayerFramesToAnchorTo() |
local AnchorToFrames = {} |
for frame, object in pairs(mod.units)do |
AnchorToFrames[frame] = frame |
end |
AnchorToFrames['UIParent'] = 'UIParent' |
return AnchorToFrames |
end |
function configMod:concatLeaves(branch) |
local picture = "" |
for index,value in pairs(branch) do |
picture = picture .. "["..index.."] - "..tostring(value).."\n" |
end |
return picture |
end |
function configMod:SetupUnitOptions(table) |
for index,frame in pairs(table)do |
if frame.unit ~= nil then |
self.options.args['frames'].args['units'].args[index] = self:AddUnitOptionSet(frame) |
self:Debug("Inserting Option Config for : "..frame.unit) |
end |
end |
end |
-- MANUPILATORS |
function configMod:ScaleObject(obj,value) |
if obj~=nil then |
obj:SetScale(db.frames.scale) |
else |
db.frames.scale = value |
for index,frame in pairs(oUF.objects)do |
if(frame.unit ~= nil) then |
self:ScaleObject(frame) |
end |
end |
end |
end |
function configMod:MoveObject(object,setting) |
if(object ~= nil) then |
local anchorTo = setting.anchorTo |
if(anchorTo == nil or anchorTo == 'parent')then |
anchorTo = object:GetParent() |
elseif(oUF.units[anchorTo])then |
anchorTo = oUF.units[anchorTo] |
else |
anchorTo = UIParent |
end |
object:ClearAllPoints() |
object:SetPoint(setting.anchorFromPoint,anchorTo,setting.anchorToPoint,setting.anchorX,setting.anchorY) |
end |
end |
function configMod:SizeObject(object,settings,parent) |
if(object ~= nil) then |
local widthRule = configMod.resizeRules["Width"][parent] and configMod.resizeRules["Width"][parent](object,settings.width) or settings.width |
local heightRule = configMod.resizeRules["Height"][parent] and configMod.resizeRules["Height"][parent](object,settings.height) or settings.height |
object:SetWidth(widthRule) |
object:SetHeight(heightRule) |
if(object.OnSizeChange) then object:OnSizeChange(object) end |
end |
end |
function configMod:SetFontType(obj,size,name,outline) |
mod:UpdateFontObjects(obj,size,name,outline) |
end |
function configMod:SetAuraTimeFormat(value) |
db.auras.timers.UsingMMSS = value |
end |
function configMod:ToggleAuraTimers(value) |
db.auras.timers.enabled = value |
end |
function configMod:GetAuraRows(obj) |
local extra = modulus(obj.num,obj.Colomns) > 0 and 1 or 0 |
local rows = math.floor(obj.num/obj.Colomns) |
return rows + extra |
end |
function configMod:SetAuraFontOptions(obj,size,name,outline) |
local fontDb = mod.db.profile.auras.font |
self:Debug(size,name,outline) |
fontDb.size = size or fontDb.size |
fontDb.name = name or fontDb.name |
fontDb.outline = outline or fontDb.outline |
-- for index,unit in pairs(mod.units)do |
-- if unit.Buffs then end |
-- end |
end |
function configMod:adjustAuraFrame(object,setting,value) |
self:Debug("adjustAuraFrame("..tostring(object)..","..setting..","..value..")") |
object[setting]=value |
if(setting == "count") then |
if(object.num ~= value) then |
for index,obj in pairs({object:GetChildren()})do |
obj:Hide() |
end |
end |
object.num = value |
elseif(setting == "growth-x" or setting == "growth-y")then |
-- |
end |
object:GetParent():UpdateElement('Aura') |
--background helper sizing. |
object:SetHeight(object.size * self:GetAuraRows(object)) |
object:SetWidth(object.size * object.Colomns) |
end |
function configMod:ToggleFrameLock(obj,value) |
if obj ~= nil then |
if value == false then |
obj:SetBackdropColor(.2,1,.2,.5) |
obj:EnableMouse(true); |
obj:SetMovable(true); |
obj:RegisterForDrag("LeftButton"); |
obj:SetUserPlaced(true) |
obj:SetScript("OnDragStart", function() |
if(db.frames.locked == false)then |
this.isMoving = true; |
this:StartMoving() |
end |
end); |
obj:SetScript("OnDragStop", function() |
if(this.isMoving == true)then |
this:StopMovingOrSizing() |
end |
local from, obj, to,x,y = this:GetPoint(); |
this.db.anchorFromPoint = from; |
this.db.anchorTo = obj or 'UIParent'; |
this.db.anchorToPoint = to; |
this.db.anchorX = x; |
this.db.anchorY = y; |
end); |
else |
obj:SetUserPlaced(false) |
obj:SetMovable(false); |
obj:RegisterForDrag(""); |
obj:SetBackdropColor(unpack(db.colors.backdropColors)) |
end |
else |
db.frames.locked = value |
for index,frame in pairs(oUF.objects)do |
if(frame.unit ~= nil) then |
self:ToggleFrameLock(frame,value) |
end |
end |
end |
end |
function configMod:Orientation(obj,value) |
if(obj.SetChildOrientation) then |
obj:SetChildOrientation(value) |
else |
obj:SetOrientation(value) |
end |
end |
function configMod:EnableObject(obj,value) |
if value == true then |
obj:Show() |
if obj.SetupEnabled then obj.SetupEnabled(obj) end |
else |
obj:Hide() |
if obj.SetupDisabled then obj.SetupDisabled(obj) end |
end |
end |
function configMod:ToggleConfigAssist(object) |
if(object:GetBackdrop()~=nil)then |
self:Print("Hiding Backdrop") |
object:SetBackdrop(nil) |
else |
self:Print("Showing Backdrop") |
object:SetBackdrop(mod.db.profile.textures.backgrounds.default) |
object:SetBackdropColor(0,0,0,.5) |
end |
end |
function configMod:PositionFontObject(object,settings) |
object:ClearAllPoints() |
object:SetJustifyH(settings.justifyH) |
object:SetJustifyV(settings.justifyV) |
-- object:SetFont(db.fonts['default'].name, db.fonts['default'].size, db.fonts['default'].outline) |
object:SetPoint(settings.anchorFromPoint, object.parent.elements[settings.anchorTo], settings.anchorToPoint, settings.anchorX, settings.anchorY) |
self:Debug(' tag : '..settings.tag) |
object.parent:Tag(object, settings.tag) |
object:UpdateTag() |
end |
function configMod:UpdateTagEvents(object,tagstr) |
-- Forcefully strip away any parentheses and the characters in them. |
self:Print("Attempting : "..tagstr) |
local tagEvents = oUF.TagEvents |
tagstr = tagstr:gsub('%b()', '') |
for tag in tagstr:gmatch'[%[]%w+[%]]' do |
local tagevents = tagEvents[tag] |
if(tagevents) then |
for event in tagevents:gmatch'%S+' do |
self:Print(event) |
oUF.RegisterTagEvent(object, event) |
end |
end |
end |
end |
-- GETTERS & SETTERS |
function configMod:getOptionValue(info) |
local value = '' |
local key = info[#info] |
local parentKey = info[#info-1] |
local section = 'default' |
self:Debug(parentKey,key,info['arg']) |
if(key == "enabledDebugMessages")then |
value = db.enabledDebugMessages |
end |
if(parentKey == "font")then |
if(info['arg']=='global-aura')then |
value = db.auras.font[key] |
else |
value = db.frames.font[key] |
end |
elseif(parentKey == "units")then |
if(key == "lock")then |
value = db.frames.locked |
elseif(key == "scale") then |
value = db.frames.scale |
end |
elseif(parentKey == "auras")then |
if(key == "timers")then |
value = db.auras.timers.enabled |
elseif(key == "format")then |
value = db.auras.timers.UsingMMSS |
end |
end |
self:Debug("get : "..self:concatLeaves(info) .. " : "..tostring(args)) |
return value |
end |
function configMod:setOptionValue(info,value) |
local key = info[#info] |
local parentKey = info[#info-1] |
self:Debug("\nset : " .. self:concatLeaves(info) ) |
if(key == "enabledDebugMessages")then |
db.enabledDebugMessages = value |
end |
if(parentKey)then |
if(parentKey == "font")then |
-- (obj,size,name,outline) |
if(key == "size")then |
--change fontsize |
if(info['arg']=='global-aura')then |
self:SetAuraFontOptions(nil,value,nil,nil) |
else |
self:SetFontType(nil,value,nil,nil) |
end |
elseif(key == "name")then |
--change fonttype |
if(info['arg']=='global-aura') |
then self:SetAuraFontOptions(nil,nil,value,nil) |
else |
self:SetFontType(nil,nil,value,nil) |
end |
elseif(key == "outline")then |
--change fontoutline |
if(info['arg']=='global-aura')then |
self:SetAuraFontOptions(nil,nil,nil,value) |
else |
self:SetFontType(nil,nil,nil,value) |
end |
end |
elseif(parentKey == "units")then |
if(key == "lock") then |
self:ToggleFrameLock(nil,value) |
elseif(key == "scale") then |
self:ScaleObject(nil,value) |
end |
elseif(parentKey == "auras")then |
if(key == "timers")then |
self:ToggleAuraTimers(value) |
elseif(key == "format")then |
self:SetAuraTimeFormat(value) |
end |
end |
else |
-- |
end |
end |
-- Handling of the frames.units[unitName] |
-- GET-- |
function configMod:GetUnitFrameOption(info) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
local output |
if(#info >= 4)then output = profile end |
if(#info >= 5)then output = output[info[4]] end |
if(#info >= 6)then output = output[info[5]] end |
if(#info == 7)then output = output[info[6]] end |
if(#info == 8)then output = output[info[7]] end |
output = output[setting] |
self:Debug("\nGetUnitFrameOption : "..self:concatLeaves(info)) |
return output |
end |
-- SET-- |
function configMod:SetUnitFrameOption(info,value) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
local output |
self:Debug("\nSetUnitFrameOption : "..self:concatLeaves(info)) |
local parent = info[#info-1] |
if(#info >= 4)then output = profile; end |
if(#info >= 5)then output = output[info[4]];object = object[info[4]]; end |
if(#info >= 6)then output = output[info[5]];object = object[info[5]]; end |
if(#info >= 7)then output = output[info[6]];object = object[info[6]]; end |
if(#info >= 8)then output = output[info[7]];object = object[info[7]]; end |
output[setting] = value |
if(setting == "height" or setting == "width" )then |
self:SizeObject(object,output,parent) |
elseif(setting == "scale" or setting == "anchorX" or setting == "anchorY" or setting == "anchorFromPoint" or setting == "anchorToPoint" )then |
if setting == "scale" or parent == "Timer" then |
info['arg']:UpdateTotemBar() |
else |
self:MoveObject(object,output) |
end |
elseif(setting == "growth-x" or setting == "growth-y") or (setting=="Colomns" or setting =="Rows" or setting =="size" or setting =="playerSize" or setting=="spacing")then |
self:adjustAuraFrame(object,setting,value) |
elseif(setting == "orientation")then |
self:Orientation(value) |
elseif(setting == "count")then |
self:adjustAuraFrame(object,setting,value) |
elseif setting == "enabled" then |
self:EnableObject(object,value) |
elseif setting == "inRangeAlpha" or setting == "outsideRangeAlpha" then |
object.setting = value |
elseif setting == "setup" then |
self:ToggleConfigAssist(object) |
elseif parent =="textures" then |
self:UpdateTextures(info['arg'],output) |
end |
end |
function configMod:GetUnitFrameFontObjectOption(info) |
end |
function configMod:SetUnitFrameFontObjectOption(info,value) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
local output |
self:Debug("\nSetUnitFrameFontObjectOption :\n "..self:concatLeaves(info)) |
if(#info >= 4)then output = profile; end |
if(#info >= 5)then output = output[info[4]];object = object[info[4]]; end |
if(#info >= 6)then output = output[info[5]];object = object[info[5]]; end |
if(#info >= 7)then output = output[info[6]];object = object[info[6]]; end |
if(#info >= 8)then output = output[info[7]];object = object[info[7]]; end |
output[setting] = value |
self:PositionFontObject(object.object, output) |
end |
-- Handling of the frames.units[unitName] |
-- GET-- |
function configMod:GetColourOption(info) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
local output |
if(#info >= 4)then output = profile end |
if(#info >= 5)then output = output[info[4]] end |
if(#info >= 6)then output = output[info[5]] end |
if(#info >= 7)then output = output[info[6]] end |
if(#info >= 8)then output = output[info[7]] end |
local r, g, b,a = unpack( output[setting] ) |
self:Debug("\nGetColourOption : "..self:concatLeaves(info) .. " : " ..tostring(r, g, b,a)) |
return r,g,b,a |
end |
-- SET-- |
function configMod:SetColourOption(info,r,g,b,a) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
local parent = info[#info-1] |
local output |
if(#info >= 4)then output = profile; end |
if(#info >= 5)then output = output[info[4]];object = object[info[4]]; end |
if(#info >= 6)then output = output[info[5]];object = object[info[5]]; end |
if(#info >= 7)then output = output[info[6]];object = object[info[6]]; end |
if(#info >= 8)then output = output[info[7]];object = object[info[7]]; end |
output[setting]={r,g,b,a} |
if setting == 'bgColor' then |
object.bg:SetTexture(r,g,b,a) |
elseif setting == 'BackdropColor' then |
object:SetBackdropColor(r,g,b,a) |
elseif setting == 'StatusBarColor' then |
object:SetStatusBarColor(r,g,b,a) |
elseif setting == 'colour' and parent=='SafeZone' then |
object:SetVertexColor(r,g,b,a) |
end |
self:Debug("\nsetColourOption : "..self:concatLeaves(info) ..tostring(r, g, b,a)) |
end |
--FontTags-- |
--GET-- |
function configMod:GetFontObjectTag(info) |
local object = info['arg'] |
local setting = info[#info] |
self:Debug("\nGetFontObjectTag :\n "..self:concatLeaves(info)) |
return "text" |
end |
--SET-- |
function configMod:SetFontObjectTag(info,value) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
local output |
if(#info >= 4)then output = profile; end |
if(#info >= 5)then output = output[info[4]];object = object[info[4]]; end |
if(#info >= 6)then output = output[info[5]];object = object[info[5]]; end |
if(#info >= 7)then output = output[info[6]];object = object[info[6]]; end |
if(#info >= 8)then output = output[info[7]];object = object[info[7]]; end |
self:Debug("\nSetFontObjectTag :\n "..self:concatLeaves(info)) |
output[setting] = value |
end |
--SET-- |
function configMod:GetTagOption(info) |
self:Debug("\nGetTagOption :\n "..self:concatLeaves(info)) |
local arg,setting, hash = info['arg'],info[#info],{ |
['inputTagString'] = function(tag) return tag end, |
['inputTagFunc'] = function(tag) return oUF.TagsLogicStrings[tag] end, |
['inputTagEvents'] = function(tag) return oUF.TagEvents[tag] end, |
} |
return hash[setting](arg) |
end |
--SET-- |
function configMod:SetTagOption(info,value) |
self:Debug("\nSetTagOption :\n "..self:concatLeaves(info)) |
local tag,setting, hash =info[#info-1],info[#info],{ |
['inputTagString'] = function(tag,value) |
self:Debug(" tag moniker : "..tag.." = ".. value .." \"this is supposed to rename the tagstring or create a new one\" ") |
end, |
['inputTagFunc'] = function(tag,value) |
oUF.TagsLogicStrings[tag] = value |
oUF:ReWriteTag(tag,nil,value) |
end, |
['inputTagEvents'] = function(tag,value) |
oUF.TagEvents[tag] = value |
oUF:ReWriteTag(tag,value,nil) |
end, |
} |
hash[setting](tag,value) |
end |
-- VALIDATE-- |
function configMod:CheckUnitFrameOption(info) |
return false |
end |
local tableExtend = function(array,table) |
for index,data in pairs(table)do |
array[index] = data |
end |
end |
local config=oUF_Smee2_Config |
config.options = { |
--[[ |
name = "oUF_Smee2", handler = config, |
type = 'group', |
args = { |
["frames"] = { |
--]] |
name = "Global", type = 'group', |
childGroups = "select", |
handler = config, |
args = { |
["enabledDebugMessages"] = { |
name = "Enable Debug Messages in ChatFrame1",desc = "Toggles on/off output of debug messages.", |
type = 'toggle', |
get = "getOptionValue", set = "setOptionValue", |
}, |
["tags"] = { |
name = "Tags", desc = "Text Status Tags ", |
type = 'group',childGroups = "select", |
args = { |
}, |
}, |
["frames"] = { |
name = "Frames", desc = "Frame Options ", |
type = 'group', |
args = { |
["font"] = { |
name = "Font", desc = "Global controls for fonts ", |
type = 'group', |
args = { |
["size"] = { |
name = "Font Size",desc = "Change the font size, note this is affected by your ui-scale in video settings.", |
type = "range", min = 1,max = 48.0, step = 0.1, |
order = 103, |
get = "getOptionValue", set = "setOptionValue", |
}, |
["name"] = { |
type = "select", |
name = "Fontface", |
dialogControl = 'LSM30_Font', + disabled = not config.mod.SharedMediaActive, + desc = "Fontface to use on the bars.", |
get = 'getOptionValue', set = 'setOptionValue', |
disabled ='CheckUnitFrameOption', |
values = AceGUIWidgetLSMlists.font, |
order=26, |
}, |
["outline"] = { |
type = "select", |
name = "Outline", desc = "font options, typically outline types", |
get = 'getOptionValue', set = 'setOptionValue', |
disabled ='CheckUnitFrameOption', |
values = config.fontOutlineTypes, |
order = 7, |
}, |
}, |
}, |
["auras"] = { |
name = "Aura", desc = "Global controls for aura icons ", |
type = 'group', |
args = { |
["timers"] = { |
name = "Countdown Timers", desc = "Toggles on/off display of time remaining on each buff/debuff icon", |
type = 'toggle', |
get = "getOptionValue", set = "setOptionValue", |
}, |
["format"] = { |
name = "Short Format", desc = "Toggles between single digit or minutes : seconds time format display for timer countdown", |
type = 'toggle', |
get = "getOptionValue", set = "setOptionValue", |
}, |
["font"] = { |
name = "Font", desc = "Global controls for fonts ", |
type = 'group',guiInline=true, |
args = { |
["size"] = { |
name = "Font Size",desc = "Change the font size, note this is affected by your ui-scale in video settings.", |
type = "range", min = 1,max = 48.0, step = 0.1, |
order = 103,arg = 'global-aura', |
get = "getOptionValue", set = "setOptionValue", |
}, |
["name"] = { |
type = "select", |
name = "Fontface", |
dialogControl = 'LSM30_Font', + disabled = not config.mod.SharedMediaActive, + desc = "Fontface to use on the bars.", |
get = 'getOptionValue', set = 'setOptionValue', |
disabled ='CheckUnitFrameOption', |
values = AceGUIWidgetLSMlists.font, |
order=26,arg = 'global-aura', |
}, |
["outline"] = { |
type = "select", |
name = "Outline", desc = "font options, typically outline types", |
get = 'getOptionValue', set = 'setOptionValue', |
disabled ='CheckUnitFrameOption', |
values = config.fontOutlineTypes, |
order = 7,arg = 'global-aura', |
}, |
}, |
}, |
}, |
}, |
["units"] = { |
name = "Units", desc = "Individual frame controls ", |
type = 'group', |
args = { |
["lock"] = { |
name = "Lock Frame Positions",desc = "Toggles on/off Frame Lock, allowing you to drag the frames around.", |
type = 'toggle', order = 1, |
get = "getOptionValue", set = "setOptionValue", |
}, |
["scale"] = { |
type = "range", order = 2, |
name = "Frame Scale", desc = "Global frame scale.", |
min = 0.1, max = 2.0, step = 0.01, |
get = "getOptionValue", set = "setOptionValue", |
}, |
}, |
}, |
}, |
}, |
}, |
-- }, |
-- }, |
} |
function config:CreateAuraOptions(groupName,frame) |
local optionSet={ |
type = 'group', |
name = groupName, |
order=60, |
args = { |
["setup"] = { |
name = "Show Setup Background", |
desc = "Toggles on/off the displaying of the frame background to help with layout configuration", |
type = 'toggle', |
get = "GetUnitFrameOption", set = "SetUnitFrameOption", |
arg = frame, |
order = 1, |
}, |
["position"] = { |
type = "header", |
name = "Position", |
order = 2, |
}, |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the horizontal position.", |
min = -400, max = 400, step = 1, |
get = "GetUnitFrameOption", set = "SetUnitFrameOption", |
disabled ="CheckUnitFrameOption", |
arg = frame, |
order = 5, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the vertical position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = frame, |
order = 5, |
}, |
["anchorToPoint"] = { |
type = "select", |
name = "To edge...", |
desc = "Which edge on the "..frame.unit.." frame to attach To", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values=config.frameAnchorPoints, |
arg = frame, |
order=6, |
}, |
["anchorFromPoint"] = { |
type = "select", |
name = "From edge...", desc = "Which edge to attach from on the "..frame.unit.." frame.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values = config.frameAnchorPoints, |
arg = frame, |
order = 7, |
}, |
["arrangement"] = { |
type = "header", |
name = "Arrangement", |
order=1, |
}, |
["Colomns"] = { |
type = "range", |
name = "Colomns", desc = "Set amount of icons per row.", |
min = 1, max = 40, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled = 'CheckUnitFrameOption', |
arg = frame, |
order = 8, |
}, |
["count"] = { |
type = "range", |
name = "Count", desc = "Set total amount of icons. For now this setting only takes effect after a UIReload", |
min = 1, max = 40, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled = 'CheckUnitFrameOption', |
arg = frame, |
order = 9, |
}, |
["spacing"] = { |
type = "range", |
name = "Spacing", desc = "Set distance between each icon", |
min = 1, max = 40, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled = 'CheckUnitFrameOption', |
arg = frame, |
order = 10, |
}, |
["growth-x"] = { |
type = "select", |
name = "horizontal growth direction", |
desc = "Aura icons grow left or right", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values=config.growthXDirections, |
arg = frame, |
order=11, |
}, |
["growth-y"] = { |
type = "select", |
name = "vertical growth direction", |
desc = "Aura icons grow up or down", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values=config.growthYDirections, |
arg = frame, |
order=12, |
}, |
["size"] = { |
type = "header", |
name = "Size", |
order=13, |
}, |
["size"] = { |
type = "range", |
name = "icon size", desc = "Set the size of the aura icons.", |
min = 0.1, max = 48, step = 0.1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = frame, |
order = 20, |
}, |
["playerSize"] = { |
type = "range", |
name = "Your Icons Scale", desc = "Set the size of auras that belong to you, compared to the normal size. expressed as a fraction", |
min = 0.1, max = 4.0, step = 0.1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = frame, |
order = 20, |
} |
} |
} |
return optionSet |
end |
function config:CreateBarOptions(groupName,frame) |
local optionGroup = { |
type = 'group', |
name = groupName, |
order = 52, |
args = { |
["enabled"] = { |
type = "toggle", |
name = "enable", desc = "Enable this bar.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=1, |
}, |
["reverse"] = { |
type = "toggle", |
name = "reverse", desc = "Reverse the direction in which this statusbar grows.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=1, |
}, |
["headerSize"] = { |
type = "header", |
name = "Size", |
order=10, |
}, |
["height"] = { |
type = "range", |
name = "Height", desc = "Set the bar height.", |
min = 1, max = 100, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=11, |
}, |
["bgColor"] = { |
type = "color", |
name = "bgColor Colour", desc = "choose the bgColor for the bar.", |
get = 'GetColourOption', set = 'SetColourOption', |
arg = frame, |
hasAlpha = true, |
order=32, |
} |
} |
} |
if not (groupName=="Health" or groupName=="Power") then |
tableExtend(optionGroup.args,{ |
["width"] = { |
type = "range", |
name = "Width", desc = "Set the bar width.", |
min = 1, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=12, |
}, |
["height"] = { |
type = "range", |
name = "Height", desc = "Set the bar height.", |
min = 1, max = 200, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=11, |
}, |
["headerPosition"] = { |
type = "header", |
name = "Position", |
order=20, |
}, |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the Vertical position.", |
min = -400, max = 400, |
step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=21, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the Horizontal position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=22, |
}, |
}) |
end |
if(groupName == 'Castbar') then |
tableExtend(optionGroup.args,{ |
["headerColour"] = { |
type = "header", |
name = "Colours", |
order=30, |
}, |
["StatusBarColor"] = { |
type = "color", |
name = "StatusBarColor Colour", desc = "choose the StatusBarColor the casting bar.", |
get = 'GetColourOption', set = 'SetColourOption', |
arg = frame, |
hasAlpha = true, |
order=31, |
}, |
["BackdropColor"] = { |
type = "color", |
name = "BackdropColor Colour", desc = "choose the BackdropColor for the casting bar.", |
get = 'GetColourOption', set = 'SetColourOption', |
arg = frame, |
hasAlpha = true, |
order=33, |
}, |
['Text'] = { |
type = "group", |
name = "CastName", |
order=40, |
args={ |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the Vertical position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=21, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the Horizontal position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=22, |
}, |
} |
}, |
['Time'] = { |
type = "group", |
name = "CastTime", |
order=50, |
args={ |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the Vertical position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=21, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the Horizontal position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=22, |
}, |
} |
} |
}) |
if frame.unit == 'player' then |
tableExtend(optionGroup.args,{ |
["SafeZone"] = { |
type = 'group', |
name = 'Latency SafeZone', |
order = 52, |
args = { |
["enabled"] = { |
type = "toggle", |
name = "enable", desc = "Enable latency safezone overlay.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=1, |
}, |
["accurate"] = { |
type = "toggle", |
name = "accurate latency", desc = "turn on accurate latency measurement.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=1, |
}, |
["colour"] = { |
type = "color", |
name = "Colour", desc = "choose the colour for the safezone.", |
get = 'GetColourOption', set = 'SetColourOption', |
arg = frame, |
hasAlpha = true, |
order=32, |
} |
} |
} |
}) |
end |
elseif groupName == "TotemBar"then |
tableExtend(optionGroup.args,{ |
["scale"] = { |
type = "range", |
name = "Scale", desc = "Set scale of the totem icon.", |
min = 1, max = 4, step = .1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=22, |
}, |
['Timer'] = { |
type = "group", |
name = "Timer", |
order=40, |
args={ |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the Vertical position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=21, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the Horizontal position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=22, |
}, |
["anchorToPoint"] = { |
type = "select", |
name = "To edge...", |
desc = "Which edge on the "..frame.unit.." frame to attach To", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values=config.frameAnchorPoints, |
arg = frame, |
order=26, |
}, |
["anchorFromPoint"] = { |
type = "select", |
name = "From edge...", desc = "Which edge to attach from on the "..frame.unit.." frame.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values = config.frameAnchorPoints, |
arg = frame, |
order = 28, |
}, |
} |
} |
}) |
elseif(groupName == 'RuneBar') then |
optionGroup.args["orientation"] = { |
type = "select", |
name = "Rune Orientation", desc = "Which axis do the runes deplete?", |
get = 'GetUnitFrameOption', set = 'GetUnitFrameOption', |
arg = frame, |
values = { HORIZONTAL ="Horizontally", VERTICAL = "Vertically"}, |
order=4, |
} |
end |
return optionGroup |
end |
function config:SetupTagOptions(tags) |
local tagOptions = self.options.args['tags'].args |
for tag,logic in pairs(tags)do |
tagOptions[tag] = self:AddTagOptionSet(tag,logic) |
end |
end |
function config:AddTagOptionSet(tag,logic) |
local tagOption = { |
type = 'group',childGroups = 'tab', |
name = tag, |
args = { |
["inputTagString"] = { |
type = "input", |
get = 'GetTagOption', set = 'SetTagOption', |
name = "tag string / moniker", width = 'full', |
desc = "The tag that you can use as a placeholder for this logic", |
arg = tag, |
order=12, |
}, |
["inputTagFunc"] = { |
type = "input",multiline=true, |
get = 'GetTagOption', set = 'SetTagOption', |
name = "tag logic / function", arg = tag, width = 'full', |
desc = "The logic that this tags uses to produce the resulting text", |
order=32, |
}, |
["inputTagEvents"] = { |
type = "input",multiline=true, |
get = 'GetTagOption', set = 'SetTagOption', |
name = 'tag events', arg = tag, width = 'full', |
desc = "A space delimited set of event names that will cause this tag to execute and update a fontstring using it. specifiying no events means that it only updates when the frame spawns.", |
order=42, |
}, |
}, |
} |
return tagOption |
end |
function config:AddUnitOptionSet(frame) |
local screenHeight = GetScreenHeight() |
local screenWidth = GetScreenWidth() |
--[[----------------------------------------- |
Unitframe |
frame.unit = 'player' |
frame = oUF_Player |
--------------------------------------------]] |
local optionSet = { |
type = 'group', |
name = frame.unit, |
args = { |
["headerSize"] = { |
type = "header", |
name = "Size", |
order=10, |
}, |
["height"] = { |
type = "range", |
name = "Height", desc = "Set the height.", |
min = 1, max = 200, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = frame, |
order=11, |
}, |
["width"] = { |
type = "range", |
name = "Width", desc = "Set the width.", |
min = 1, max = 600, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = frame, |
order=11, |
}, |
["headerPosition"] = { |
type = "header", |
name = "Position", |
order=20, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the Vertical position.", |
min = -config:round(screenHeight/2,0), max = config:round(screenHeight/2,0), step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=21, |
}, |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the Horizontal position.", |
min = -config:round(screenWidth/2,0), max = config:round(screenWidth/2,0), step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=22, |
}, |
["anchorToPoint"] = { |
type = "select", |
name = "To edge...", |
desc = "Which edge on the "..frame.unit.." frame to attach To", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values=config.frameAnchorPoints, |
arg = frame, |
order=26, |
}, |
["anchorTo"] = { |
type = "select", |
name = "Anchor To Frame...", |
desc = "On which frame to anchor "..frame.unit.." to.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values=config.PlayerFramesToAnchorTo, |
arg = frame, |
order=27, |
}, |
["anchorFromPoint"] = { |
type = "select", |
name = "From edge...", desc = "Which edge to attach from on the "..frame.unit.." frame.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values = config.frameAnchorPoints, |
arg = frame, |
order = 28, |
}, |
["headerRangeFading"] = { |
type = "header", |
name = "RangeFading", |
order=30, |
}, |
["Range"] = { |
type = "toggle", |
name = "Enabled", desc = "Fading this frame based on your proximity to the unit.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = frame, |
order=31, |
}, |
["inRangeAlpha"] = { |
type = "range", |
name = "In range opacity", desc = "Set the opacity level of the frame for when this unit is within your range.", |
min = 0, max = 1, |
step = 0.05, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=32, |
}, |
["outsideRangeAlpha"] = { |
type = "range", |
name = "Out of range opacity", desc = "Set the opacity level of the frame for when this unit is out of your range.", |
min = 0, max = 1, step = 0.05, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
arg = frame, |
order=33, |
}, |
}, |
} |
--[[----------------------------------------- |
Textures |
--------------------------------------------]] |
optionSet.args["textures"] = { |
type = 'group', |
name = "Textures", |
order = 30, |
args = { |
["statusbar"] = { |
type = "select", |
name = "Statusbar", |
dialogControl = 'LSM30_Statusbar', + disabled = not self.mod.SharedMediaActive, + desc = "Texture to use on the bars.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
values = AceGUIWidgetLSMlists.statusbar, |
arg = frame, |
order=26, |
}, |
}, |
} |
--[[----------------------------------------- |
FontObjects |
--------------------------------------------]] |
optionSet.args["FontObjects"] = { |
type = 'group', |
name = "Texts", |
order = 30, |
args = { |
}, |
} |
for index,data in pairs(frame.db.FontObjects)do |
local key = tostring(index) |
if(key) then |
optionSet.args["FontObjects"].args[key] = { |
type = 'group', |
name = data.desc, order = 30, |
args = { |
["header"] = { |
type = "header", |
name = data.desc, |
order=1, |
}, |
["anchorToPoint"] = { |
type = "select", |
name = "To edge...", |
desc = "Which edge on the "..frame.unit.." frame to attach To", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
disabled ='CheckUnitFrameOption', |
values=config.frameAnchorPoints, |
arg = frame, |
order=6, |
}, |
["anchorFromPoint"] = { |
type = "select", |
name = "From edge...", desc = "Which edge to attach from on the "..frame.unit.." frame.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
disabled ='CheckUnitFrameOption', |
values = config.frameAnchorPoints, |
arg = frame, |
order = 7, |
}, |
["justifyH"] = { |
type = "select", |
name = "Horizontal Alignment", desc = "text alignment.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
disabled ='CheckUnitFrameOption', |
values = config.textHorizontalAlignmentPoints, |
arg = frame, |
order = 7, |
}, |
["justifyV"] = { |
type = "select", |
name = "Vertical Alignment", desc = "text alignment.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
disabled ='CheckUnitFrameOption', |
values = config.textVerticalAlignmentPoints, |
arg = frame, |
order = 7, |
}, |
["anchorX"] = { |
type = "range", |
name = "Horizontal Position", desc = "Set the Vertical position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
arg = frame, |
order=21, |
}, |
["anchorY"] = { |
type = "range", |
name = "Vertical Position", desc = "Set the Horizontal position.", |
min = -400, max = 400, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
arg = frame, |
order=22, |
}, |
["tag"] = { |
type = "input", |
name = "oUF Tag", desc = "Tag representing logic to display.", |
get = 'GetUnitFrameOption', set = 'SetUnitFrameFontObjectOption', |
usage = "[tagname] [tagname]", |
arg = frame, |
order=32, |
}, |
} |
} |
else |
optionSet.args["FontObjects"].args[key] = { |
type = 'group', |
name = 'Error', order = 30, |
args = { |
["header"] = { |
type = "header", |
name = 'Error', |
order=1, |
}, |
['reason'] = { |
type = 'description', |
name = 'FontObject : '..key, |
order = 2, |
} |
}, |
} |
end |
end |
--[[----------------------------------------- |
Bars |
--------------------------------------------]] |
optionSet.args["bars"] = { |
type = 'group', |
name = "Bars", |
order = 30, |
args = { |
}, |
} |
for index,data in pairs(frame.db.bars)do |
local key = tostring(index) |
if(key) then |
if(data.classFilter)then |
if data.classFilter == frame.unitClass then -- bars like totembar and runebar should only apply to a particular class |
optionSet.args["bars"].args[key] = self:CreateBarOptions(key,frame) |
end |
else |
optionSet.args["bars"].args[key] = self:CreateBarOptions(key,frame) |
end |
end |
end |
--[[----------------------------------------- |
Auras |
--------------------------------------------]] |
optionSet.args.Buffs = self:CreateAuraOptions("Buffs",frame) |
optionSet.args.Debuffs = self:CreateAuraOptions("Debuffs",frame) |
return optionSet |
end |
function config:debug(info,value) |
local object = info['arg'] |
local profile = object.db |
local setting = info[#info] |
if setting == "height" then |
return object:GetHeight() |
end |
GlobalObject[#GlobalObject] = info |
self:Debug("\nGetUnitFrameOption : "..self:concatLeaves(info)) |
return info[#info] |
end |
function config:CreateStatusBarOptions(frame,bar,barName,insertOrder,settings) |
local options = { |
type = 'group', |
name = barName, |
order = insertOrder, |
args = { |
["height"] = { |
type = "range", |
name = "Height", desc = "Set the height, as a percentage of the unit frame.", |
min = 1, max = 100, step = 1, |
get = 'GetUnitFrameOption', set = 'SetUnitFrameOption', |
disabled ='CheckUnitFrameOption', |
arg = bar, |
order=11, |
}, |
}, |
} |
return options |
end |
local layoutName = 'oUF_Smee2' |
local mod = _G[layoutName] |
local configModName = layoutName..'_Config' |
_G[configModName] = LibStub("AceAddon-3.0"):NewAddon(configModName, "AceConsole-3.0") |
local configMod = _G[configModName] |
configMod.mod = mod |
--======================-- |
--==<< ACE3 SETUP >>==-- |
--======================-- |
function configMod:round(num, idp) |
if idp and idp>0 then return math.floor(num * mult + 0.5) / (10^idp) end |
return math.floor(num + 0.5) |
end |
function configMod:numberize(val) |
if(val >= 1e3) then |
return ("%.1fk"):format(val / 1e3) |
elseif (val >= 1e6) then |
return ("%.1fm"):format(val / 1e6) |
else |
return val |
end |
end |
function configMod:Debug(msg) |
if not mod.db.profile.enabledDebugMessages then return end |
self:Print("|cFFFFFF00Debug : |r"..tostring(msg)) |
end |
function configMod:OnInitialize() |
end |
function configMod:OnEnable() |
self:Debug("Enabling") |
self:SetupUnitOptions(mod.units) |
self:SetupTagOptions(oUF.TagsLogicStrings) |
LibStub("AceConfig-3.0"):RegisterOptionsTable(configModName, self.options,layoutName) |
-- RegisterOptions("Profiles", LibStub('AceDBOptions-3.0'):GetOptionsTable(addon.db)) |
self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions(configModName, layoutName) |
self:Debug("Enabled") |
end |
function configMod:OnDisable() |
self:Debug("Disabling") |
-- Called when the addon is disabled |
db = self.db.profile |
if db.enabled then |
db.enabled = false |
return |
end |
self:Debug("Disabled") |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceGUI-3.0.lua"/> |
<Script file="widgets\AceGUIWidget-Button.lua"/> |
<Script file="widgets\AceGUIWidget-CheckBox.lua"/> |
<Script file="widgets\AceGUIWidget-ColorPicker.lua"/> |
<Script file="widgets\AceGUIWidget-DropDownGroup.lua"/> |
<Script file="widgets\AceGUIWidget-DropDown.lua"/> |
<Script file="widgets\AceGUIWidget-DropDown-Items.lua"/> |
<Script file="widgets\AceGUIWidget-EditBox.lua"/> |
<Script file="widgets\AceGUIWidget-Frame.lua"/> |
<Script file="widgets\AceGUIWidget-Heading.lua"/> |
<Script file="widgets\AceGUIWidget-InlineGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Keybinding.lua"/> |
<Script file="widgets\AceGUIWidget-ScrollFrame.lua"/> |
<Script file="widgets\AceGUIWidget-SimpleGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Slider.lua"/> |
<Script file="widgets\AceGUIWidget-TabGroup.lua"/> |
<Script file="widgets\AceGUIWidget-TreeGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Label.lua"/> |
<Script file="widgets\AceGUIWidget-MultiLineEditBox.lua"/> |
<Script file="widgets\AceGUIWidget-BlizOptionsGroup.lua"/> |
<Script file="widgets\AceGUIWidget-Icon.lua"/> |
</Ui> |
--[[ $Id: AceGUI-3.0.lua 681 2008-09-06 13:01:59Z nargiddley $ ]] |
local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 16 |
local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR) |
if not AceGUI then return end -- No upgrade needed |
--local con = LibStub("AceConsole-3.0",true) |
AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {} |
AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {} |
AceGUI.WidgetBase = AceGUI.WidgetBase or {} |
AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {} |
AceGUI.WidgetVersions = AceGUI.WidgetVersions or {} |
-- local upvalues |
local WidgetRegistry = AceGUI.WidgetRegistry |
local LayoutRegistry = AceGUI.LayoutRegistry |
local WidgetVersions = AceGUI.WidgetVersions |
local pcall = pcall |
local select = select |
local pairs = pairs |
local ipairs = ipairs |
local type = type |
local assert = assert |
local tinsert = tinsert |
local tremove = tremove |
local CreateFrame = CreateFrame |
local UIParent = UIParent |
--[[ |
xpcall safecall implementation |
]] |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local xpcall, eh = ... |
local method, ARGS |
local function call() return method(ARGS) end |
local function dispatch(func, ...) |
method = func |
if not method then return end |
ARGS = ... |
return xpcall(call, eh) |
end |
return dispatch |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", table.concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
Dispatchers[0] = function(func) |
return xpcall(func, errorhandler) |
end |
local function safecall(func, ...) |
return Dispatchers[select('#', ...)](func, ...) |
end |
-- Recycling functions |
local new, del |
do |
AceGUI.objPools = AceGUI.objPools or {} |
local objPools = AceGUI.objPools |
--Returns a new instance, if none are available either returns a new table or calls the given contructor |
function new(type,constructor,...) |
if not type then |
type = "table" |
end |
if not objPools[type] then |
objPools[type] = {} |
end |
local newObj = tremove(objPools[type]) |
if not newObj then |
newObj = constructor and constructor(...) or {} |
end |
return newObj |
end |
-- Releases an instance to the Pool |
function del(obj,type) |
if not type then |
type = "table" |
end |
if not objPools[type] then |
objPools[type] = {} |
end |
for i,v in ipairs(objPools[type]) do |
if v == obj then |
error("Attempt to Release Widget that is already released") |
return |
end |
end |
tinsert(objPools[type],obj) |
end |
end |
------------------- |
-- API Functions -- |
------------------- |
-- Gets a widget Object |
function AceGUI:Create(type) |
local reg = WidgetRegistry |
if reg[type] then |
local widget = new(type,reg[type]) |
if rawget(widget,'Acquire') then |
widget.OnAcquire = widget.Acquire |
widget.Acquire = nil |
elseif rawget(widget,'Aquire') then |
widget.OnAcquire = widget.Aquire |
widget.Aquire = nil |
end |
if rawget(widget,'Release') then |
widget.OnRelease = rawget(widget,'Release') |
widget.Release = nil |
end |
if widget.OnAcquire then |
widget:OnAcquire() |
else |
error(("Widget type %s doesn't supply an OnAcquire Function"):format(type)) |
end |
safecall(widget.ResumeLayout, widget) |
return widget |
end |
end |
-- Releases a widget Object |
function AceGUI:Release(widget) |
safecall( widget.PauseLayout, widget ) |
widget:Fire("OnRelease") |
safecall( widget.ReleaseChildren, widget ) |
if widget.OnRelease then |
widget:OnRelease() |
else |
error(("Widget type %s doesn't supply an OnRelease Function"):format(type)) |
end |
for k in pairs(widget.userdata) do |
widget.userdata[k] = nil |
end |
for k in pairs(widget.events) do |
widget.events[k] = nil |
end |
widget.width = nil |
--widget.frame:SetParent(nil) |
widget.frame:ClearAllPoints() |
widget.frame:Hide() |
widget.frame:SetParent(nil) |
if widget.content then |
widget.content.width = nil |
widget.content.height = nil |
end |
del(widget,widget.type) |
end |
----------- |
-- Focus -- |
----------- |
----- |
-- Called when a widget has taken focus |
-- e.g. Dropdowns opening, Editboxes gaining kb focus |
----- |
function AceGUI:SetFocus(widget) |
if self.FocusedWidget and self.FocusedWidget ~= widget then |
safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget) |
end |
self.FocusedWidget = widget |
end |
----- |
-- Called when something has happened that could cause widgets with focus to drop it |
-- e.g. titlebar of a frame being clicked |
----- |
function AceGUI:ClearFocus() |
if self.FocusedWidget then |
safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget) |
self.FocusedWidget = nil |
end |
end |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
OnAcquire() - Called when the object is acquired, should set everything to a default hidden state |
OnRelease() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
:OnWidthSet(width) - Called when the width of the widget is changed |
:OnHeightSet(height) - Called when the height of the widget is changed |
Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead |
AceGUI already sets a handler to the event |
:OnLayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the |
area used for controls. These can be nil if the layout used the existing size to layout the controls. |
]] |
-------------------------- |
-- Widget Base Template -- |
-------------------------- |
do |
local function fixlevels(parent,...) |
local i = 1 |
local child = select(i, ...) |
while child do |
child:SetFrameLevel(parent:GetFrameLevel()+1) |
fixlevels(child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
local WidgetBase = AceGUI.WidgetBase |
WidgetBase.SetParent = function(self, parent) |
local frame = self.frame |
frame:SetParent(nil) |
frame:SetParent(parent.content) |
self.parent = parent |
fixlevels(parent.frame,parent.frame:GetChildren()) |
end |
WidgetBase.SetCallback = function(self, name, func) |
if type(func) == "function" then |
self.events[name] = func |
end |
end |
WidgetBase.Fire = function(self, name, ...) |
if self.events[name] then |
local success, ret = safecall(self.events[name], self, name, ...) |
if success then |
return ret |
end |
end |
end |
WidgetBase.SetWidth = function(self, width) |
self.frame:SetWidth(width) |
self.frame.width = width |
if self.OnWidthSet then |
self:OnWidthSet(width) |
end |
end |
WidgetBase.SetHeight = function(self, height) |
self.frame:SetHeight(height) |
self.frame.height = height |
if self.OnHeightSet then |
self:OnHeightSet(height) |
end |
end |
WidgetBase.IsVisible = function(self) |
return self.frame:IsVisible() |
end |
WidgetBase.IsShown= function(self) |
return self.frame:IsShown() |
end |
WidgetBase.Release = function(self) |
AceGUI:Release(self) |
end |
WidgetBase.SetPoint = function(self, ...) |
return self.frame:SetPoint(...) |
end |
WidgetBase.ClearAllPoints = function(self) |
return self.frame:ClearAllPoints() |
end |
WidgetBase.GetNumPoints = function(self) |
return self.frame:GetNumPoints() |
end |
WidgetBase.GetPoint = function(self, ...) |
return self.frame:GetPoint(...) |
end |
WidgetBase.GetUserDataTable = function(self) |
return self.userdata |
end |
WidgetBase.SetUserData = function(self, key, value) |
self.userdata[key] = value |
end |
WidgetBase.GetUserData = function(self, key) |
return self.userdata[key] |
end |
WidgetBase.IsFullHeight = function(self) |
return self.height == "fill" |
end |
WidgetBase.SetFullHeight = function(self, isFull) |
if isFull then |
self.height = "fill" |
else |
self.height = nil |
end |
end |
WidgetBase.IsFullWidth = function(self) |
return self.width == "fill" |
end |
WidgetBase.SetFullWidth = function(self, isFull) |
if isFull then |
self.width = "fill" |
else |
self.width = nil |
end |
end |
-- local function LayoutOnUpdate(this) |
-- this:SetScript("OnUpdate",nil) |
-- this.obj:PerformLayout() |
-- end |
local WidgetContainerBase = AceGUI.WidgetContainerBase |
WidgetContainerBase.PauseLayout = function(self) |
self.LayoutPaused = true |
end |
WidgetContainerBase.ResumeLayout = function(self) |
self.LayoutPaused = nil |
end |
WidgetContainerBase.PerformLayout = function(self) |
if self.LayoutPaused then |
return |
end |
safecall(self.LayoutFunc,self.content, self.children) |
end |
--call this function to layout, makes sure layed out objects get a frame to get sizes etc |
WidgetContainerBase.DoLayout = function(self) |
self:PerformLayout() |
-- if not self.parent then |
-- self.frame:SetScript("OnUpdate", LayoutOnUpdate) |
-- end |
end |
WidgetContainerBase.AddChild = function(self, child) |
tinsert(self.children,child) |
child:SetParent(self) |
child.frame:Show() |
self:DoLayout() |
end |
WidgetContainerBase.ReleaseChildren = function(self) |
local children = self.children |
for i in ipairs(children) do |
AceGUI:Release(children[i]) |
children[i] = nil |
end |
end |
WidgetContainerBase.SetLayout = function(self, Layout) |
self.LayoutFunc = AceGUI:GetLayout(Layout) |
end |
local function FrameResize(this) |
local self = this.obj |
if this:GetWidth() and this:GetHeight() then |
if self.OnWidthSet then |
self:OnWidthSet(this:GetWidth()) |
end |
if self.OnHeightSet then |
self:OnHeightSet(this:GetHeight()) |
end |
end |
end |
local function ContentResize(this) |
if this:GetWidth() and this:GetHeight() then |
this.width = this:GetWidth() |
this.height = this:GetHeight() |
this.obj:DoLayout() |
end |
end |
setmetatable(WidgetContainerBase,{__index=WidgetBase}) |
--One of these function should be called on each Widget Instance as part of its creation process |
function AceGUI:RegisterAsContainer(widget) |
widget.children = {} |
widget.userdata = {} |
widget.events = {} |
widget.base = WidgetContainerBase |
widget.content.obj = widget |
widget.frame.obj = widget |
widget.content:SetScript("OnSizeChanged",ContentResize) |
widget.frame:SetScript("OnSizeChanged",FrameResize) |
setmetatable(widget,{__index=WidgetContainerBase}) |
widget:SetLayout("List") |
end |
function AceGUI:RegisterAsWidget(widget) |
widget.userdata = {} |
widget.events = {} |
widget.base = WidgetBase |
widget.frame.obj = widget |
widget.frame:SetScript("OnSizeChanged",FrameResize) |
setmetatable(widget,{__index=WidgetBase}) |
end |
end |
------------------ |
-- Widget API -- |
------------------ |
-- Registers a widget Constructor, this function returns a new instance of the Widget |
function AceGUI:RegisterWidgetType(Name, Constructor, Version) |
assert(type(Constructor) == "function") |
assert(type(Version) == "number") |
local oldVersion = WidgetVersions[Name] |
if oldVersion and oldVersion >= Version then return end |
WidgetVersions[Name] = Version |
WidgetRegistry[Name] = Constructor |
end |
-- Registers a Layout Function |
function AceGUI:RegisterLayout(Name, LayoutFunc) |
assert(type(LayoutFunc) == "function") |
if type(Name) == "string" then |
Name = Name:upper() |
end |
LayoutRegistry[Name] = LayoutFunc |
end |
function AceGUI:GetLayout(Name) |
if type(Name) == "string" then |
Name = Name:upper() |
end |
return LayoutRegistry[Name] |
end |
AceGUI.counts = AceGUI.counts or {} |
function AceGUI:GetNextWidgetNum(type) |
if not self.counts[type] then |
self.counts[type] = 0 |
end |
self.counts[type] = self.counts[type] + 1 |
return self.counts[type] |
end |
--[[ Widget Template |
-------------------------- |
-- Widget Name -- |
-------------------------- |
do |
local Type = "Type" |
local function OnAcquire(self) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.frame = frame |
frame.obj = self |
--Container Support |
--local content = CreateFrame("Frame",nil,frame) |
--self.content = content |
--AceGUI:RegisterAsContainer(self) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor) |
end |
]] |
------------- |
-- Layouts -- |
------------- |
--[[ |
A Layout is a func that takes 2 parameters |
content - the frame that widgets will be placed inside |
children - a table containing the widgets to layout |
]] |
-- Very simple Layout, Children are stacked on top of each other down the left side |
AceGUI:RegisterLayout("List", |
function(content, children) |
local height = 0 |
local width = content.width or content:GetWidth() or 0 |
for i, child in ipairs(children) do |
local frame = child.frame |
frame:ClearAllPoints() |
frame:Show() |
if i == 1 then |
frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0) |
else |
frame:SetPoint("TOPLEFT",children[i-1].frame,"BOTTOMLEFT",0,0) |
end |
if child.width == "fill" then |
child:SetWidth(width) |
frame:SetPoint("RIGHT",content,"RIGHT") |
if child.OnWidthSet then |
child:OnWidthSet(content.width or content:GetWidth()) |
end |
if child.DoLayout then |
child:DoLayout() |
end |
end |
height = height + (frame.height or frame:GetHeight() or 0) |
end |
safecall( content.obj.LayoutFinished, content.obj, nil, height ) |
end |
) |
-- A single control fills the whole content area |
AceGUI:RegisterLayout("Fill", |
function(content, children) |
if children[1] then |
children[1]:SetWidth(content:GetWidth() or 0) |
children[1]:SetHeight(content:GetHeight() or 0) |
children[1].frame:SetAllPoints(content) |
children[1].frame:Show() |
safecall( content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight() ) |
end |
end |
) |
AceGUI:RegisterLayout("Flow", |
function(content, children) |
--used height so far |
local height = 0 |
--width used in the current row |
local usedwidth = 0 |
--height of the current row |
local rowheight = 0 |
local rowoffset = 0 |
local lastrowoffset |
local width = content.width or content:GetWidth() or 0 |
--control at the start of the row |
local rowstart |
local rowstartoffset |
local lastrowstart |
local isfullheight |
local frameoffset |
local lastframeoffset |
local oversize |
for i, child in ipairs(children) do |
oversize = nil |
local frame = child.frame |
local frameheight = frame.height or frame:GetHeight() or 0 |
local framewidth = frame.width or frame:GetWidth() or 0 |
lastframeoffset = frameoffset |
frameoffset = child.alignoffset or (frameheight / 2) |
frame:Show() |
frame:ClearAllPoints() |
if i == 1 then |
-- anchor the first control to the top left |
--frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0) |
rowheight = frameheight |
rowoffset = frameoffset |
rowstart = frame |
rowstartoffset = frameoffset |
usedwidth = framewidth |
if usedwidth > width then |
oversize = true |
end |
else |
-- if there isn't available width for the control start a new row |
-- if a control is "fill" it will be on a row of its own full width |
if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then |
--anchor the previous row, we will now know its height and offset |
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3)) |
height = height + rowheight + 3 |
--save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it |
rowstart = frame |
rowstartoffset = frameoffset |
rowheight = frameheight |
rowoffset = frameoffset |
usedwidth = frame.width or frame:GetWidth() |
if usedwidth > width then |
oversize = true |
end |
-- put the control on the current row, adding it to the width and checking if the height needs to be increased |
else |
--handles cases where the new height is higher than either control because of the offsets |
--math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset) |
--offset is always the larger of the two offsets |
rowoffset = math.max(rowoffset, frameoffset) |
rowheight = math.max(rowheight,rowoffset+(frameheight/2)) |
frame:SetPoint("TOPLEFT",children[i-1].frame,"TOPRIGHT",0,frameoffset-lastframeoffset) |
usedwidth = framewidth + usedwidth |
end |
end |
if child.width == "fill" then |
child:SetWidth(width) |
frame:SetPoint("RIGHT",content,"RIGHT",0,0) |
usedwidth = 0 |
rowstart = frame |
rowstartoffset = frameoffset |
if child.DoLayout then |
child:DoLayout() |
end |
rowheight = frame.height or frame:GetHeight() or 0 |
rowoffset = child.alignoffset or (rowheight / 2) |
rowstartoffset = rowoffset |
elseif oversize then |
if width > 1 then |
frame:SetPoint("RIGHT",content,"RIGHT",0,0) |
end |
end |
if child.height == "fill" then |
frame:SetPoint("BOTTOM",content,"BOTTOM") |
isfullheight = true |
break |
end |
end |
--anchor the last row, if its full height needs a special case since its height has just been changed by the anchor |
if isfullheight then |
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-height) |
elseif rowstart then |
rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3)) |
end |
height = height + rowheight + 3 |
safecall( content.obj.LayoutFinished, content.obj, nil, height ) |
end |
) |
local AceGUI = LibStub("AceGUI-3.0") |
---------------- |
-- Main Frame -- |
---------------- |
--[[ |
Events : |
OnClose |
]] |
do |
local Type = "Frame" |
local Version = 7 |
local FrameBackdrop = { |
bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", |
edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", |
tile = true, tileSize = 32, edgeSize = 32, |
insets = { left = 8, right = 8, top = 8, bottom = 8 } |
} |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function frameOnClose(this) |
this.obj:Fire("OnClose") |
end |
local function closeOnClick(this) |
this.obj:Hide() |
end |
local function frameOnMouseDown(this) |
AceGUI:ClearFocus() |
end |
local function titleOnMouseDown(this) |
this:GetParent():StartMoving() |
AceGUI:ClearFocus() |
end |
local function frameOnMouseUp(this) |
local frame = this:GetParent() |
frame:StopMovingOrSizing() |
local self = frame.obj |
local status = self.status or self.localstatus |
status.width = frame:GetWidth() |
status.height = frame:GetHeight() |
status.top = frame:GetTop() |
status.left = frame:GetLeft() |
end |
local function sizerseOnMouseDown(this) |
this:GetParent():StartSizing("BOTTOMRIGHT") |
AceGUI:ClearFocus() |
end |
local function sizersOnMouseDown(this) |
this:GetParent():StartSizing("BOTTOM") |
AceGUI:ClearFocus() |
end |
local function sizereOnMouseDown(this) |
this:GetParent():StartSizing("RIGHT") |
AceGUI:ClearFocus() |
end |
local function sizerOnMouseUp(this) |
this:GetParent():StopMovingOrSizing() |
end |
local function SetTitle(self,title) |
self.titletext:SetText(title) |
end |
local function SetStatusText(self,text) |
self.statustext:SetText(text) |
end |
local function Hide(self) |
self.frame:Hide() |
end |
local function Show(self) |
self.frame:Show() |
end |
local function OnAcquire(self) |
self.frame:SetParent(UIParent) |
self.frame:SetFrameStrata("FULLSCREEN_DIALOG") |
self:ApplyStatus() |
end |
local function OnRelease(self) |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
self:ApplyStatus() |
end |
local function ApplyStatus(self) |
local status = self.status or self.localstatus |
local frame = self.frame |
self:SetWidth(status.width or 700) |
self:SetHeight(status.height or 500) |
if status.top and status.left then |
frame:SetPoint("TOP",UIParent,"BOTTOM",0,status.top) |
frame:SetPoint("LEFT",UIParent,"LEFT",status.left,0) |
else |
frame:SetPoint("CENTER",UIParent,"CENTER") |
end |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 34 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 57 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = "Frame" |
self.Hide = Hide |
self.Show = Show |
self.SetTitle = SetTitle |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetStatusText = SetStatusText |
self.SetStatusTable = SetStatusTable |
self.ApplyStatus = ApplyStatus |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.localstatus = {} |
self.frame = frame |
frame.obj = self |
frame:SetWidth(700) |
frame:SetHeight(500) |
frame:SetPoint("CENTER",UIParent,"CENTER",0,0) |
frame:EnableMouse() |
frame:SetMovable(true) |
frame:SetResizable(true) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
frame:SetScript("OnMouseDown", frameOnMouseDown) |
frame:SetBackdrop(FrameBackdrop) |
frame:SetBackdropColor(0,0,0,1) |
frame:SetScript("OnHide",frameOnClose) |
frame:SetMinResize(400,200) |
frame:SetToplevel(true) |
local closebutton = CreateFrame("Button",nil,frame,"UIPanelButtonTemplate") |
closebutton:SetScript("OnClick", closeOnClick) |
closebutton:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-27,17) |
closebutton:SetHeight(20) |
closebutton:SetWidth(100) |
closebutton:SetText("Close") |
self.closebutton = closebutton |
closebutton.obj = self |
local statusbg = CreateFrame("Frame",nil,frame) |
statusbg:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",15,15) |
statusbg:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-132,15) |
statusbg:SetHeight(24) |
statusbg:SetBackdrop(PaneBackdrop) |
statusbg:SetBackdropColor(0.1,0.1,0.1) |
statusbg:SetBackdropBorderColor(0.4,0.4,0.4) |
self.statusbg = statusbg |
local statustext = statusbg:CreateFontString(nil,"OVERLAY","GameFontNormal") |
self.statustext = statustext |
statustext:SetPoint("TOPLEFT",statusbg,"TOPLEFT",7,-2) |
statustext:SetPoint("BOTTOMRIGHT",statusbg,"BOTTOMRIGHT",-7,2) |
statustext:SetHeight(20) |
statustext:SetJustifyH("LEFT") |
statustext:SetText("") |
local title = CreateFrame("Frame",nil,frame) |
self.title = title |
title:EnableMouse() |
title:SetScript("OnMouseDown",titleOnMouseDown) |
title:SetScript("OnMouseUp", frameOnMouseUp) |
local titlebg = frame:CreateTexture(nil,"OVERLAY") |
titlebg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") |
titlebg:SetTexCoord(0.31,0.67,0,0.63) |
titlebg:SetPoint("TOP",frame,"TOP",0,12) |
titlebg:SetWidth(100) |
titlebg:SetHeight(40) |
local titlebg_l = frame:CreateTexture(nil,"OVERLAY") |
titlebg_l:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") |
titlebg_l:SetTexCoord(0.21,0.31,0,0.63) |
titlebg_l:SetPoint("RIGHT",titlebg,"LEFT",0,0) |
titlebg_l:SetWidth(30) |
titlebg_l:SetHeight(40) |
local titlebg_right = frame:CreateTexture(nil,"OVERLAY") |
titlebg_right:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") |
titlebg_right:SetTexCoord(0.67,0.77,0,0.63) |
titlebg_right:SetPoint("LEFT",titlebg,"RIGHT",0,0) |
titlebg_right:SetWidth(30) |
titlebg_right:SetHeight(40) |
title:SetAllPoints(titlebg) |
local titletext = title:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOP",titlebg,"TOP",0,-14) |
self.titletext = titletext |
local sizer_se = CreateFrame("Frame",nil,frame) |
sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
sizer_se:SetWidth(25) |
sizer_se:SetHeight(25) |
sizer_se:EnableMouse() |
sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown) |
sizer_se:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_se = sizer_se |
local line1 = sizer_se:CreateTexture(nil, "BACKGROUND") |
self.line1 = line1 |
line1:SetWidth(14) |
line1:SetHeight(14) |
line1:SetPoint("BOTTOMRIGHT", -8, 8) |
line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
local x = 0.1 * 14/17 |
line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5) |
local line2 = sizer_se:CreateTexture(nil, "BACKGROUND") |
self.line2 = line2 |
line2:SetWidth(8) |
line2:SetHeight(8) |
line2:SetPoint("BOTTOMRIGHT", -8, 8) |
line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
local x = 0.1 * 8/17 |
line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5) |
local sizer_s = CreateFrame("Frame",nil,frame) |
sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0) |
sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) |
sizer_s:SetHeight(25) |
sizer_s:EnableMouse() |
sizer_s:SetScript("OnMouseDown",sizersOnMouseDown) |
sizer_s:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_s = sizer_s |
local sizer_e = CreateFrame("Frame",nil,frame) |
sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25) |
sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
sizer_e:SetWidth(25) |
sizer_e:EnableMouse() |
sizer_e:SetScript("OnMouseDown",sizereOnMouseDown) |
sizer_e:SetScript("OnMouseUp", sizerOnMouseUp) |
self.sizer_e = sizer_e |
--Container Support |
local content = CreateFrame("Frame",nil,frame) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",frame,"TOPLEFT",17,-27) |
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-17,40) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Inline Group -- |
-------------------------- |
--[[ |
This is a simple grouping container, no selection |
It will resize automatically to the height of the controls added to it |
]] |
do |
local Type = "InlineGroup" |
local Version = 4 |
local function OnAcquire(self) |
self:SetWidth(300) |
self:SetHeight(100) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function SetTitle(self,title) |
self.titletext:SetText(title) |
end |
local function LayoutFinished(self, width, height) |
self:SetHeight((height or 0) + 40) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 20 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 20 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetTitle = SetTitle |
self.frame = frame |
self.LayoutFinished = LayoutFinished |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
titletext:SetJustifyH("LEFT") |
titletext:SetHeight(18) |
self.titletext = titletext |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-17) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-3,3) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
--[[ $Id: AceGUIWidget-DropDown.lua 679 2008-09-06 12:51:18Z nargiddley $ ]]-- |
local min, max, floor = math.min, math.max, math.floor |
local AceGUI = LibStub("AceGUI-3.0") |
local function fixlevels(parent,...) |
local i = 1 |
local child = select(i, ...) |
while child do |
child:SetFrameLevel(parent:GetFrameLevel()+1) |
fixlevels(child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
local function fixstrata(strata, parent, ...) |
local i = 1 |
local child = select(i, ...) |
parent:SetFrameStrata(strata) |
while child do |
fixstrata(strata, child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
do |
local widgetType = "Dropdown-Pullout" |
local widgetVersion = 2 |
--[[ Static data ]]-- |
local backdrop = { |
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", |
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", |
edgeSize = 32, |
tileSize = 32, |
tile = true, |
insets = { left = 11, right = 12, top = 12, bottom = 11 }, |
} |
local sliderBackdrop = { |
bgFile = "Interface\\Buttons\\UI-SliderBar-Background", |
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", |
tile = true, tileSize = 8, edgeSize = 8, |
insets = { left = 3, right = 3, top = 3, bottom = 3 } |
} |
local defaultWidth = 200 |
local defaultMaxHeight = 600 |
--[[ UI Event Handlers ]]-- |
-- HACK: This should be no part of the pullout, but there |
-- is no other 'clean' way to response to any item-OnEnter |
-- Used to close Submenus when an other item is entered |
local function OnEnter(item) |
local self = item.pullout |
for k, v in ipairs(self.items) do |
if v.CloseMenu and v ~= item then |
v:CloseMenu() |
end |
end |
end |
-- See the note in Constructor() for each scroll related function |
local function OnMouseWheel(this, value) |
this.obj:MoveScroll(value) |
end |
local function OnScrollValueChanged(this, value) |
this.obj:SetScroll(value) |
end |
local function OnSizeChanged(this) |
this.obj:FixScroll() |
end |
--[[ Exported methods ]]-- |
-- exported |
local function SetScroll(self, value) |
local status = self.scrollStatus |
local frame, child = self.scrollFrame, self.itemFrame |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
local offset |
if height > viewheight then |
offset = 0 |
else |
offset = floor((viewheight - height) / 1000 * value) |
end |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset) |
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset) |
status.offset = offset |
status.scrollvalue = value |
end |
-- exported |
local function MoveScroll(self, value) |
local status = self.scrollStatus |
local frame, child = self.scrollFrame, self.itemFrame |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
if height > viewheight then |
self.slider:Hide() |
else |
self.slider:Show() |
local diff = height - viewheight |
local delta = 1 |
if value < 0 then |
delta = -1 |
end |
self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000)) |
end |
end |
-- exported |
local function FixScroll(self) |
local status = self.scrollStatus |
local frame, child = self.scrollFrame, self.itemFrame |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
local offset = status.offset or 0 |
if viewheight < height then |
self.slider:Hide() |
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset) |
self.slider:SetValue(0) |
else |
self.slider:Show() |
local value = (offset / (viewheight - height) * 1000) |
if value > 1000 then value = 1000 end |
self.slider:SetValue(value) |
self:SetScroll(value) |
if value < 1000 then |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset) |
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset) |
status.offset = offset |
end |
end |
end |
-- exported, AceGUI callback |
local function OnAcquire(self) |
self.frame:SetParent(UIParent) |
--self.itemFrame:SetToplevel(true) |
end |
-- exported, AceGUI callback |
local function OnRelease(self) |
self:Clear() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
-- exported |
local function AddItem(self, item) |
self.items[#self.items + 1] = item |
local h = #self.items * 16 |
self.itemFrame:SetHeight(h) |
self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement |
item.frame:SetPoint("LEFT", self.itemFrame, "LEFT") |
item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT") |
item:SetPullout(self) |
item:SetOnEnter(OnEnter) |
end |
-- exported |
local function Open(self, point, relFrame, relPoint, x, y) |
local items = self.items |
local frame = self.frame |
local itemFrame = self.itemFrame |
frame:SetPoint(point, relFrame, relPoint, x, y) |
local height = 8 |
for i, item in pairs(items) do |
if i == 1 then |
item:SetPoint("TOP", itemFrame, "TOP", 0, -2) |
else |
item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1) |
end |
item:Show() |
height = height + 16 |
end |
itemFrame:SetHeight(height) |
fixstrata("TOOLTIP", frame, frame:GetChildren()) |
frame:Show() |
self:Fire("OnOpen") |
end |
-- exported |
local function Close(self) |
self.frame:Hide() |
self:Fire("OnClose") |
end |
-- exported |
local function Clear(self) |
local items = self.items |
for i, item in pairs(items) do |
AceGUI:Release(item) |
items[i] = nil |
end |
end |
-- exported |
local function IterateItems(self) |
return ipairs(self.items) |
end |
-- exported |
local function SetHideOnLeave(self, val) |
self.hideOnLeave = val |
end |
-- exported |
local function SetMaxHeight(self, height) |
self.maxHeight = height or defaultMaxHeight |
if self.frame:GetHeight() > height then |
self.frame:SetHeight(height) |
elseif (self.itemFrame:GetHeight() + 34) < height then |
self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem |
end |
end |
-- exported |
local function GetRightBorderWidth(self) |
return 6 + (self.slider:IsShown() and 12 or 0) |
end |
-- exported |
local function GetLeftBorderWidth(self) |
return 6 |
end |
--[[ Constructor ]]-- |
local function Constructor() |
local count = AceGUI:GetNextWidgetNum(widgetType) |
local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent) |
local self = {} |
self.count = count |
self.type = widgetType |
self.frame = frame |
frame.obj = self |
self.OnAcquire = OnAcquire |
self.OnRelease = OnRelease |
self.AddItem = AddItem |
self.Open = Open |
self.Close = Close |
self.Clear = Clear |
self.IterateItems = IterateItems |
self.SetHideOnLeave = SetHideOnLeave |
self.SetScroll = SetScroll |
self.MoveScroll = MoveScroll |
self.FixScroll = FixScroll |
self.SetMaxHeight = SetMaxHeight |
self.GetRightBorderWidth = GetRightBorderWidth |
self.GetLeftBorderWidth = GetLeftBorderWidth |
self.items = {} |
self.scrollStatus = { |
scrollvalue = 0, |
} |
self.maxHeight = defaultMaxHeight |
frame:SetBackdrop(backdrop) |
frame:SetBackdropColor(0, 0, 0) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
frame:SetClampedToScreen(true) |
frame:SetWidth(defaultWidth) |
frame:SetHeight(self.maxHeight) |
--frame:SetToplevel(true) |
-- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame |
local scrollFrame = CreateFrame("ScrollFrame", nil, frame) |
local itemFrame = CreateFrame("Frame", nil, scrollFrame) |
self.scrollFrame = scrollFrame |
self.itemFrame = itemFrame |
scrollFrame.obj = self |
itemFrame.obj = self |
local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame) |
slider:SetOrientation("VERTICAL") |
slider:SetHitRectInsets(0, 0, -10, 0) |
slider:SetBackdrop(sliderBackdrop) |
slider:SetWidth(8) |
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") |
slider:SetFrameStrata("FULLSCREEN_DIALOG") |
self.slider = slider |
slider.obj = self |
scrollFrame:SetScrollChild(itemFrame) |
scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12) |
scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12) |
scrollFrame:EnableMouseWheel(true) |
scrollFrame:SetScript("OnMouseWheel", OnMouseWheel) |
scrollFrame:SetScript("OnSizeChanged", OnSizeChanged) |
scrollFrame:SetToplevel(true) |
scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG") |
itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0) |
itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0) |
itemFrame:SetHeight(400) |
itemFrame:SetToplevel(true) |
itemFrame:SetFrameStrata("FULLSCREEN_DIALOG") |
slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0) |
slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0) |
slider:SetScript("OnValueChanged", OnScrollValueChanged) |
slider:SetMinMaxValues(0, 1000) |
slider:SetValueStep(1) |
slider:SetValue(0) |
scrollFrame:Show() |
itemFrame:Show() |
slider:Hide() |
self:FixScroll() |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
do |
local widgetType = "Dropdown" |
local widgetVersion = 18 |
--[[ Static data ]]-- |
--[[ UI event handler ]]-- |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function Dropdown_OnHide(this) |
local self = this.obj |
if self.open then |
self.pullout:Close() |
end |
end |
local function Dropdown_TogglePullout(this) |
local self = this.obj |
if self.open then |
self.open = nil |
self.pullout:Close() |
AceGUI:ClearFocus() |
else |
self.open = true |
self.pullout:SetWidth(self.frame:GetWidth()) |
self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0) |
AceGUI:SetFocus(self) |
end |
end |
local function OnPulloutOpen(this) |
local self = this.userdata.obj |
local value = self.value |
if not self.multiselect then |
for i, item in this:IterateItems() do |
item:SetValue(item.userdata.value == value) |
end |
end |
self.open = true |
end |
local function OnPulloutClose(this) |
local self = this.userdata.obj |
self.open = nil |
self:Fire("OnClosed") |
end |
local function ShowMultiText(self) |
local text |
for i, widget in self.pullout:IterateItems() do |
if widget.type == "Dropdown-Item-Toggle" then |
if widget:GetValue() then |
if text then |
text = text..", "..widget:GetText() |
else |
text = widget:GetText() |
end |
end |
end |
end |
self:SetText(text) |
end |
local function OnItemValueChanged(this, event, checked) |
local self = this.userdata.obj |
if self.multiselect then |
self:Fire("OnValueChanged", this.userdata.value, checked) |
ShowMultiText(self) |
else |
if checked then |
self:SetValue(this.userdata.value) |
self:Fire("OnValueChanged", this.userdata.value) |
else |
this:SetValue(true) |
end |
if self.open then |
self.pullout:Close() |
end |
end |
end |
--[[ Exported methods ]]-- |
-- exported, AceGUI callback |
local function OnAcquire(self) |
local pullout = AceGUI:Create("Dropdown-Pullout") |
self.pullout = pullout |
pullout.userdata.obj = self |
pullout:SetCallback("OnClose", OnPulloutClose) |
pullout:SetCallback("OnOpen", OnPulloutOpen) |
self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1) |
fixlevels(self.pullout.frame, self.pullout.frame:GetChildren()) |
end |
-- exported, AceGUI callback |
local function OnRelease(self) |
if self.open then |
self.pullout:Close() |
end |
AceGUI:Release(self.pullout) |
self:SetText("") |
self:SetLabel("") |
self:SetDisabled(false) |
self:SetMultiselect(false) |
self.value = nil |
self.list = nil |
self.open = nil |
self.hasClose = nil |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
-- exported |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
self.button:Disable() |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.button:Enable() |
self.label:SetTextColor(1,.82,0) |
self.text:SetTextColor(1,1,1) |
end |
end |
-- exported |
local function ClearFocus(self) |
if self.open then |
self.pullout:Close() |
end |
end |
-- exported |
local function SetText(self, text) |
self.text:SetText(text or "") |
end |
-- exported |
local function SetLabel(self, text) |
if text and text ~= "" then |
self.label:SetText(text) |
self.label:Show() |
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-18) |
self.frame:SetHeight(44) |
else |
self.label:SetText("") |
self.label:Hide() |
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0) |
self.frame:SetHeight(26) |
end |
end |
-- exported |
local function SetValue(self, value) |
if self.list then |
self:SetText(self.list[value] or "") |
end |
self.value = value |
end |
-- exported |
local function SetItemValue(self, item, value) |
if not self.multiselect then return end |
for i, widget in self.pullout:IterateItems() do |
if widget.userdata.value == item then |
if widget.SetValue then |
widget:SetValue(value) |
end |
end |
end |
ShowMultiText(self) |
end |
-- exported |
local function SetItemDisabled(self, item, disabled) |
for i, widget in self.pullout:IterateItems() do |
if widget.userdata.value == item then |
widget:SetDisabled(disabled) |
end |
end |
end |
local function AddListItem(self, value, text) |
local item = AceGUI:Create("Dropdown-Item-Toggle") |
item:SetText(text) |
item.userdata.obj = self |
item.userdata.value = value |
item:SetCallback("OnValueChanged", OnItemValueChanged) |
self.pullout:AddItem(item) |
end |
local function AddCloseButton(self) |
if not self.hasClose then |
local close = AceGUI:Create("Dropdown-Item-Execute") |
close:SetText(CLOSE) |
self.pullout:AddItem(close) |
self.hasClose = true |
end |
end |
-- exported |
local sortlist = {} |
local function SetList(self, list) |
self.list = list |
self.pullout:Clear() |
self.hasClose = nil |
if not list then return end |
for v in pairs(list) do |
sortlist[#sortlist + 1] = v |
end |
table.sort(sortlist) |
for i, value in pairs(sortlist) do |
AddListItem(self, value, list[value]) |
sortlist[i] = nil |
end |
if self.multiselect then |
ShowMultiText(self) |
AddCloseButton(self) |
end |
end |
-- exported |
local function AddItem(self, value, text) |
if self.list then |
self.list[value] = text |
AddListItem(self, value, text) |
end |
end |
-- exported |
local function SetMultiselect(self, multi) |
self.multiselect = multi |
if multi then |
ShowMultiText(self) |
AddCloseButton(self) |
end |
end |
-- exported |
local function GetMultiselect(self) |
return self.multiselect |
end |
--[[ Constructor ]]-- |
local function Constructor() |
local count = AceGUI:GetNextWidgetNum(widgetType) |
local frame = CreateFrame("Frame", nil, UIParent) |
local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate") |
local self = {} |
self.type = widgetType |
self.frame = frame |
self.dropdown = dropdown |
self.count = count |
frame.obj = self |
dropdown.obj = self |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.ClearFocus = ClearFocus |
self.SetText = SetText |
self.SetValue = SetValue |
self.SetList = SetList |
self.SetLabel = SetLabel |
self.SetDisabled = SetDisabled |
self.AddItem = AddItem |
self.SetMultiselect = SetMultiselect |
self.GetMultiselect = GetMultiselect |
self.SetItemValue = SetItemValue |
self.SetItemDisabled = SetItemDisabled |
self.alignoffset = 31 |
frame:SetHeight(44) |
frame:SetWidth(200) |
frame:SetScript("OnHide",Dropdown_OnHide) |
dropdown:ClearAllPoints() |
dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0) |
dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0) |
dropdown:SetScript("OnHide", nil) |
local left = _G[dropdown:GetName() .. "Left"] |
local middle = _G[dropdown:GetName() .. "Middle"] |
local right = _G[dropdown:GetName() .. "Right"] |
middle:ClearAllPoints() |
right:ClearAllPoints() |
middle:SetPoint("LEFT", left, "RIGHT", 0, 0) |
middle:SetPoint("RIGHT", right, "LEFT", 0, 0) |
right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17) |
local button = _G[dropdown:GetName() .. "Button"] |
self.button = button |
button.obj = self |
button:SetScript("OnEnter",Control_OnEnter) |
button:SetScript("OnLeave",Control_OnLeave) |
button:SetScript("OnClick",Dropdown_TogglePullout) |
local text = _G[dropdown:GetName() .. "Text"] |
self.text = text |
text.obj = self |
text:ClearAllPoints() |
text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2) |
text:SetPoint("LEFT", left, "LEFT", 25, 2) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
label:Hide() |
self.label = label |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Edit box -- |
-------------------------- |
--[[ |
Events : |
OnTextChanged |
OnEnterPressed |
]] |
do |
local Type = "EditBox" |
local Version = 8 |
local function OnAcquire(self) |
self:SetDisabled(false) |
self.showbutton = true |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetDisabled(false) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function EditBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
local function ShowButton(self) |
if self.showbutton then |
self.button:Show() |
self.editbox:SetTextInsets(0,20,3,3) |
end |
end |
local function HideButton(self) |
self.button:Hide() |
self.editbox:SetTextInsets(0,0,3,3) |
end |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
local cancel = self:Fire("OnEnterPressed",value) |
if not cancel then |
HideButton(self) |
end |
end |
local function Button_OnClick(this) |
local editbox = this.obj.editbox |
editbox:ClearFocus() |
EditBox_OnEnterPressed(editbox) |
end |
local function EditBox_OnReceiveDrag(this) |
local self = this.obj |
local type, id, info = GetCursorInfo() |
if type == "item" then |
self:SetText(info) |
self:Fire("OnEnterPressed",info) |
ClearCursor() |
elseif type == "spell" then |
local name, rank = GetSpellName(id, info) |
if rank and rank:match("%d") then |
name = name.."("..rank..")" |
end |
self:SetText(name) |
self:Fire("OnEnterPressed",name) |
ClearCursor() |
end |
HideButton(self) |
AceGUI:ClearFocus() |
end |
local function EditBox_OnTextChanged(this) |
local self = this.obj |
local value = this:GetText() |
if value ~= self.lasttext then |
self:Fire("OnTextChanged",value) |
self.lasttext = value |
ShowButton(self) |
end |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(0.5,0.5,0.5) |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.editbox:EnableMouse(true) |
self.editbox:SetTextColor(1,1,1) |
self.label:SetTextColor(1,.82,0) |
end |
end |
local function SetText(self, text) |
self.lasttext = text or "" |
self.editbox:SetText(text or "") |
self.editbox:SetCursorPosition(0) |
HideButton(self) |
end |
local function SetWidth(self, width) |
self.frame:SetWidth(width) |
end |
local function SetLabel(self, text) |
if text and text ~= "" then |
self.label:SetText(text) |
self.label:Show() |
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,-18) |
self.frame:SetHeight(44) |
else |
self.label:SetText("") |
self.label:Hide() |
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0) |
self.frame:SetHeight(26) |
end |
end |
local function Constructor() |
local num = AceGUI:GetNextWidgetNum(Type) |
local frame = CreateFrame("Frame",nil,UIParent) |
local editbox = CreateFrame("EditBox","AceGUI-3.0EditBox"..num,frame,"InputBoxTemplate") |
local self = {} |
self.type = Type |
self.num = num |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetDisabled = SetDisabled |
self.SetText = SetText |
self.SetWidth = SetWidth |
self.SetLabel = SetLabel |
self.frame = frame |
frame.obj = self |
self.editbox = editbox |
editbox.obj = self |
self.alignoffset = 30 |
frame:SetHeight(44) |
frame:SetWidth(200) |
editbox:SetScript("OnEnter",Control_OnEnter) |
editbox:SetScript("OnLeave",Control_OnLeave) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(ChatFontNormal) |
editbox:SetScript("OnEscapePressed",EditBox_OnEscapePressed) |
editbox:SetScript("OnEnterPressed",EditBox_OnEnterPressed) |
editbox:SetScript("OnTextChanged",EditBox_OnTextChanged) |
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag) |
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag) |
editbox:SetTextInsets(0,0,3,3) |
editbox:SetMaxLetters(256) |
editbox:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",6,0) |
editbox:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
editbox:SetHeight(19) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-2) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-2) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
self.label = label |
local button = CreateFrame("Button",nil,editbox,"UIPanelButtonTemplate") |
button:SetWidth(40) |
button:SetHeight(20) |
button:SetPoint("RIGHT",editbox,"RIGHT",-2,0) |
button:SetText(OKAY) |
button:SetScript("OnClick", Button_OnClick) |
button:Hide() |
self.button = button |
button.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Heading -- |
-------------------------- |
do |
local Type = "Heading" |
local Version = 3 |
local function OnAcquire(self) |
self:SetText("") |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetText(self, text) |
self.label:SetText(text or "") |
if (text or "") == "" then |
self.left:SetPoint("RIGHT",self.frame,"RIGHT",-3,0) |
self.right:Hide() |
else |
self.left:SetPoint("RIGHT",self.label,"LEFT",-5,0) |
self.right:Show() |
end |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetText = SetText |
self.frame = frame |
frame.obj = self |
frame:SetHeight(18) |
local label = frame:CreateFontString(nil,"BACKGROUND","GameFontNormal") |
label:SetPoint("TOP",frame,"TOP",0,0) |
label:SetPoint("BOTTOM",frame,"BOTTOM",0,0) |
label:SetJustifyH("CENTER") |
label:SetHeight(18) |
self.label = label |
local left = frame:CreateTexture(nil, "BACKGROUND") |
self.left = left |
left:SetHeight(8) |
left:SetPoint("LEFT",frame,"LEFT",3,0) |
left:SetPoint("RIGHT",label,"LEFT",-5,0) |
left:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
left:SetTexCoord(0.81, 0.94, 0.5, 1) |
local right = frame:CreateTexture(nil, "BACKGROUND") |
self.right = right |
right:SetHeight(8) |
right:SetPoint("RIGHT",frame,"RIGHT",-3,0) |
right:SetPoint("LEFT",label,"RIGHT",5,0) |
right:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border") |
right:SetTexCoord(0.81, 0.94, 0.5, 1) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Simple Group -- |
-------------------------- |
--[[ |
This is a simple grouping container, no selection, no borders |
It will resize automatically to the height of the controls added to it |
]] |
do |
local Type = "SimpleGroup" |
local Version = 4 |
local function OnAcquire(self) |
self:SetWidth(300) |
self:SetHeight(100) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function LayoutFinished(self, width, height) |
self:SetHeight(height or 0) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
content:SetWidth(width) |
content.width = width |
end |
local function OnHeightSet(self, height) |
local content = self.content |
content:SetHeight(height) |
content.height = height |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.frame = frame |
self.LayoutFinished = LayoutFinished |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
--Container Support |
local content = CreateFrame("Frame",nil,frame) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
--[[ |
Selection Group controls all have an interface to select a group for thier contents |
None of them will auto size to thier contents, and should usually be used with a scrollframe |
unless you know that the controls will fit inside |
]] |
-------------------------- |
-- Dropdown Group -- |
-------------------------- |
--[[ |
Events : |
OnGroupSelected |
]] |
do |
local Type = "DropdownGroup" |
local Version = 9 |
local function OnAcquire(self) |
self.dropdown:SetText("") |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.dropdown.list = nil |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function SetTitle(self,title) |
self.titletext:SetText(title) |
end |
local function SelectedGroup(self,event,value) |
local group = self.parentgroup |
local status = group.status or group.localstatus |
status.selected = value |
self.parentgroup:Fire("OnGroupSelected", value) |
end |
local function SetGroupList(self,list) |
self.dropdown:SetList(list) |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
end |
local function SetGroup(self,group) |
self.dropdown:SetValue(group) |
local status = self.status or self.localstatus |
status.selected = group |
self:Fire("OnGroupSelected", group) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 26 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 63 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame") |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetTitle = SetTitle |
self.SetGroupList = SetGroupList |
self.SetGroup = SetGroup |
self.SetStatusTable = SetStatusTable |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.localstatus = {} |
self.frame = frame |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
titletext:SetJustifyH("LEFT") |
titletext:SetHeight(18) |
self.titletext = titletext |
local dropdown = AceGUI:Create("Dropdown") |
self.dropdown = dropdown |
dropdown.frame:SetParent(frame) |
dropdown.parentgroup = self |
dropdown:SetCallback("OnValueChanged",SelectedGroup) |
dropdown.frame:SetPoint("TOPLEFT",titletext,"BOTTOMLEFT",-7,3) |
dropdown.frame:Show() |
dropdown:SetLabel("") |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-40) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-3,3) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Scroll Frame -- |
-------------------------- |
do |
local Type = "ScrollFrame" |
local Version = 3 |
local function OnAcquire(self) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
end |
local function SetScroll(self, value) |
local status = self.status or self.localstatus |
local frame, child = self.scrollframe, self.content |
local viewheight = frame:GetHeight() |
local height = child:GetHeight() |
local offset |
if viewheight > height then |
offset = 0 |
else |
offset = floor((height - viewheight) / 1000.0 * value) |
end |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) |
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) |
status.offset = offset |
status.scrollvalue = value |
end |
local function MoveScroll(self, value) |
local status = self.status or self.localstatus |
local frame, child = self.scrollframe, self.content |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
if height > viewheight then |
self.scrollbar:Hide() |
else |
self.scrollbar:Show() |
local diff = height - viewheight |
local delta = 1 |
if value < 0 then |
delta = -1 |
end |
self.scrollbar:SetValue(math.min(math.max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000)) |
end |
end |
local function FixScroll(self) |
local status = self.status or self.localstatus |
local frame, child = self.scrollframe, self.content |
local height, viewheight = frame:GetHeight(), child:GetHeight() |
local offset = status.offset |
if not offset then |
offset = 0 |
end |
local curvalue = self.scrollbar:GetValue() |
if viewheight < height then |
self.scrollbar:Hide() |
self.scrollbar:SetValue(0) |
--self.scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,0) |
else |
self.scrollbar:Show() |
--self.scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-16,0) |
local value = (offset / (viewheight - height) * 1000) |
if value > 1000 then value = 1000 end |
self.scrollbar:SetValue(value) |
self:SetScroll(value) |
if value < 1000 then |
child:ClearAllPoints() |
child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) |
child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) |
status.offset = offset |
end |
end |
end |
local function OnMouseWheel(this,value) |
this.obj:MoveScroll(value) |
end |
local function OnScrollValueChanged(this, value) |
this.obj:SetScroll(value) |
end |
local function FixScrollOnUpdate(this) |
this:SetScript("OnUpdate", nil) |
this.obj:FixScroll() |
end |
local function OnSizeChanged(this) |
--this:SetScript("OnUpdate", FixScrollOnUpdate) |
this.obj:FixScroll() |
end |
local function LayoutFinished(self,width,height) |
self.content:SetHeight(height or 0 + 20) |
self:FixScroll() |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
if not status.scrollvalue then |
status.scrollvalue = 0 |
end |
end |
local createdcount = 0 |
local function OnWidthSet(self, width) |
local content = self.content |
content.width = width |
end |
local function OnHeightSet(self, height) |
local content = self.content |
content.height = height |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.MoveScroll = MoveScroll |
self.FixScroll = FixScroll |
self.SetScroll = SetScroll |
self.LayoutFinished = LayoutFinished |
self.SetStatusTable = SetStatusTable |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.localstatus = {} |
self.frame = frame |
frame.obj = self |
--Container Support |
local scrollframe = CreateFrame("ScrollFrame",nil,frame) |
local content = CreateFrame("Frame",nil,scrollframe) |
createdcount = createdcount + 1 |
local scrollbar = CreateFrame("Slider",("AceConfigDialogScrollFrame%dScrollBar"):format(createdcount),scrollframe,"UIPanelScrollBarTemplate") |
local scrollbg = scrollbar:CreateTexture(nil,"BACKGROUND") |
scrollbg:SetAllPoints(scrollbar) |
scrollbg:SetTexture(0,0,0,0.4) |
self.scrollframe = scrollframe |
self.content = content |
self.scrollbar = scrollbar |
scrollbar.obj = self |
scrollframe.obj = self |
content.obj = self |
scrollframe:SetScrollChild(content) |
scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
scrollframe:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-16,0) |
scrollframe:EnableMouseWheel(true) |
scrollframe:SetScript("OnMouseWheel", OnMouseWheel) |
scrollframe:SetScript("OnSizeChanged", OnSizeChanged) |
content:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) |
content:SetPoint("TOPRIGHT",scrollframe,"TOPRIGHT",0,0) |
content:SetHeight(400) |
scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) |
scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) |
scrollbar:SetScript("OnValueChanged", OnScrollValueChanged) |
scrollbar:SetMinMaxValues(0,1000) |
scrollbar:SetValueStep(1) |
scrollbar:SetValue(0) |
scrollbar:SetWidth(16) |
self.localstatus.scrollvalue = 0 |
self:FixScroll() |
AceGUI:RegisterAsContainer(self) |
--AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Button -- |
-------------------------- |
do |
local Type = "Button" |
local Version = 7 |
local function OnAcquire(self) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetDisabled(false) |
end |
local function Button_OnClick(this) |
this.obj:Fire("OnClick") |
AceGUI:ClearFocus() |
end |
local function Button_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Button_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function SetText(self, text) |
self.text:SetText(text or "") |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.frame:Disable() |
else |
self.frame:Enable() |
end |
end |
local function Constructor() |
local num = AceGUI:GetNextWidgetNum(Type) |
local frame = CreateFrame("Button","AceGUI30Button"..num,UIParent,"UIPanelButtonTemplate2") |
local self = {} |
self.num = num |
self.type = Type |
self.frame = frame |
local text = frame:GetFontString() |
self.text = text |
text:SetPoint("LEFT",frame,"LEFT",15,0) |
text:SetPoint("RIGHT",frame,"RIGHT",-15,0) |
frame:SetScript("OnClick",Button_OnClick) |
frame:SetScript("OnEnter",Button_OnEnter) |
frame:SetScript("OnLeave",Button_OnLeave) |
self.SetText = SetText |
self.SetDisabled = SetDisabled |
frame:EnableMouse(true) |
frame:SetHeight(24) |
frame:SetWidth(200) |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.frame = frame |
frame.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
--[[ $Id: AceGUIWidget-DropDown-Items.lua 656 2008-05-31 11:47:08Z nargiddley $ ]]-- |
local AceGUI = LibStub("AceGUI-3.0") |
local function fixlevels(parent,...) |
local i = 1 |
local child = select(i, ...) |
while child do |
child:SetFrameLevel(parent:GetFrameLevel()+1) |
fixlevels(child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
local function fixstrata(strata, parent, ...) |
local i = 1 |
local child = select(i, ...) |
parent:SetFrameStrata(strata) |
while child do |
fixstrata(strata, child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
-- ItemBase is the base "class" for all dropdown items. |
-- Each item has to use ItemBase.Create(widgetType) to |
-- create an initial 'self' value. |
-- ItemBase will add common functions and ui event handlers. |
-- Be sure to keep basic usage when you override functions. |
local ItemBase = { |
-- NOTE: The ItemBase version is added to each item's version number |
-- to ensure proper updates on ItemBase changes. |
-- Use at least 1000er steps. |
version = 1000, |
counter = 0, |
} |
function ItemBase.Frame_OnEnter(this) |
local self = this.obj |
if self.useHighlight then |
self.highlight:Show() |
end |
self:Fire("OnEnter") |
if self.specialOnEnter then |
self.specialOnEnter(self) |
end |
end |
function ItemBase.Frame_OnLeave(this) |
local self = this.obj |
self.highlight:Hide() |
self:Fire("OnLeave") |
if self.specialOnLeave then |
self.specialOnLeave(self) |
end |
end |
-- exported, AceGUI callback |
function ItemBase.OnAcquire(self) |
self.frame:SetToplevel(true) |
self.frame:SetFrameStrata("FULLSCREEN_DIALOG") |
end |
-- exported, AceGUI callback |
function ItemBase.OnRelease(self) |
self:SetDisabled(false) |
self.pullout = nil |
self.frame:SetParent(nil) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
-- exported |
-- NOTE: this is called by a Dropdown-Pullout. |
-- Do not call this method directly |
function ItemBase.SetPullout(self, pullout) |
self.pullout = pullout |
self.frame:SetParent(nil) |
self.frame:SetParent(pullout.itemFrame) |
self.parent = pullout.itemFrame |
fixlevels(pullout.itemFrame, pullout.itemFrame:GetChildren()) |
end |
-- exported |
function ItemBase.SetText(self, text) |
self.text:SetText(text or "") |
end |
-- exported |
function ItemBase.GetText(self) |
return self.text:GetText() |
end |
-- exported |
function ItemBase.SetPoint(self, ...) |
self.frame:SetPoint(...) |
end |
-- exported |
function ItemBase.Show(self) |
self.frame:Show() |
end |
-- exported |
function ItemBase.Hide(self) |
self.frame:Hide() |
end |
-- exported |
function ItemBase.SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.useHighlight = false |
self.text:SetTextColor(.5, .5, .5) |
else |
self.useHighlight = true |
self.text:SetTextColor(1, 1, 1) |
end |
end |
-- exported |
-- NOTE: this is called by a Dropdown-Pullout. |
-- Do not call this method directly |
function ItemBase.SetOnLeave(self, func) |
self.specialOnLeave = func |
end |
-- exported |
-- NOTE: this is called by a Dropdown-Pullout. |
-- Do not call this method directly |
function ItemBase.SetOnEnter(self, func) |
self.specialOnEnter = func |
end |
function ItemBase.Create(type) |
-- NOTE: Most of the following code is copied from AceGUI-3.0/Dropdown widget |
local count = AceGUI:GetNextWidgetNum(type) |
local frame = CreateFrame("Button", "AceGUI30DropDownItem"..count) |
local self = {} |
self.frame = frame |
frame.obj = self |
self.type = type |
self.useHighlight = true |
frame:SetHeight(17) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
text:SetTextColor(1,1,1) |
text:SetJustifyH("LEFT") |
text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0) |
text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0) |
self.text = text |
local highlight = frame:CreateTexture(nil, "OVERLAY") |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetHeight(14) |
highlight:ClearAllPoints() |
highlight:SetPoint("RIGHT",frame,"RIGHT",-3,0) |
highlight:SetPoint("LEFT",frame,"LEFT",5,0) |
highlight:Hide() |
self.highlight = highlight |
local check = frame:CreateTexture("OVERLAY") |
check:SetWidth(16) |
check:SetHeight(16) |
check:SetPoint("LEFT",frame,"LEFT",3,-1) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
check:Hide() |
self.check = check |
local sub = frame:CreateTexture("OVERLAY") |
sub:SetWidth(16) |
sub:SetHeight(16) |
sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1) |
sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow") |
sub:Hide() |
self.sub = sub |
frame:SetScript("OnEnter", ItemBase.Frame_OnEnter) |
frame:SetScript("OnLeave", ItemBase.Frame_OnLeave) |
self.OnAcquire = ItemBase.OnAcquire |
self.OnRelease = ItemBase.OnRelease |
self.SetPullout = ItemBase.SetPullout |
self.GetText = ItemBase.GetText |
self.SetText = ItemBase.SetText |
self.SetDisabled = ItemBase.SetDisabled |
self.SetPoint = ItemBase.SetPoint |
self.Show = ItemBase.Show |
self.Hide = ItemBase.Hide |
self.SetOnLeave = ItemBase.SetOnLeave |
self.SetOnEnter = ItemBase.SetOnEnter |
return self |
end |
--[[ |
Template for items: |
-- Item: |
-- |
do |
local widgetType = "Dropdown-Item-" |
local widgetVersion = 1 |
local function Constructor() |
local self = ItemBase.Create(widgetType) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version) |
end |
--]] |
-- Item: Header |
-- A single text entry. |
-- Special: Different text color and no highlight |
do |
local widgetType = "Dropdown-Item-Header" |
local widgetVersion = 1 |
local function OnEnter(this) |
local self = this.obj |
self:Fire("OnEnter") |
if self.specialOnEnter then |
self.specialOnEnter(self) |
end |
end |
local function OnLeave(this) |
local self = this.obj |
self:Fire("OnLeave") |
if self.specialOnLeave then |
self.specialOnLeave(self) |
end |
end |
-- exported, override |
local function SetDisabled(self, disabled) |
ItemBase.SetDisabled(self, disabled) |
if not disabled then |
self.text:SetTextColor(1, 1, 0) |
end |
end |
local function Constructor() |
local self = ItemBase.Create(widgetType) |
self.SetDisabled = SetDisabled |
self.frame:SetScript("OnEnter", OnEnter) |
self.frame:SetScript("OnLeave", OnLeave) |
self.text:SetTextColor(1, 1, 0) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version) |
end |
-- Item: Execute |
-- A simple button |
do |
local widgetType = "Dropdown-Item-Execute" |
local widgetVersion = 1 |
local function Frame_OnClick(this, button) |
local self = this.obj |
if self.disabled then return end |
self:Fire("OnClick") |
if self.pullout then |
self.pullout:Close() |
end |
end |
local function Constructor() |
local self = ItemBase.Create(widgetType) |
self.frame:SetScript("OnClick", Frame_OnClick) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version) |
end |
-- Item: Toggle |
-- Some sort of checkbox for dropdown menus. |
-- Does not close the pullout on click. |
do |
local widgetType = "Dropdown-Item-Toggle" |
local widgetVersion = 2 |
local function UpdateToggle(self) |
if self.value then |
self.check:Show() |
else |
self.check:Hide() |
end |
end |
local function OnRelease(self) |
ItemBase.OnRelease(self) |
self:SetValue(nil) |
end |
local function Frame_OnClick(this, button) |
local self = this.obj |
if self.disabled then return end |
self.value = not self.value |
UpdateToggle(self) |
self:Fire("OnValueChanged", self.value) |
end |
-- exported |
local function SetValue(self, value) |
self.value = value |
UpdateToggle(self) |
end |
-- exported |
local function GetValue(self) |
return self.value |
end |
local function Constructor() |
local self = ItemBase.Create(widgetType) |
self.frame:SetScript("OnClick", Frame_OnClick) |
self.SetValue = SetValue |
self.GetValue = GetValue |
self.OnRelease = OnRelease |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version) |
end |
-- Item: Menu |
-- Shows a submenu on mouse over |
-- Does not close the pullout on click |
do |
local widgetType = "Dropdown-Item-Menu" |
local widgetVersion = 1 |
local function OnEnter(this) |
local self = this.obj |
self:Fire("OnEnter") |
if self.specialOnEnter then |
self.specialOnEnter(self) |
end |
self.highlight:Show() |
if not self.disabled and self.submenu then |
self.submenu:Open("TOPLEFT", self.frame, "TOPRIGHT", self.pullout:GetRightBorderWidth(), 0, self.frame:GetFrameLevel() + 100) |
end |
end |
local function OnHide(this) |
local self = this.obj |
if self.submenu then |
self.submenu:Close() |
end |
end |
-- exported |
function SetMenu(self, menu) |
assert(menu.type == "Dropdown-Pullout") |
self.submenu = menu |
end |
-- exported |
function CloseMenu(self) |
self.submenu:Close() |
end |
local function Constructor() |
local self = ItemBase.Create(widgetType) |
self.sub:Show() |
self.frame:SetScript("OnEnter", OnEnter) |
self.frame:SetScript("OnHide", OnHide) |
self.SetMenu = SetMenu |
self.CloseMenu = CloseMenu |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version) |
end |
-- Item: Separator |
-- A single line to separate items |
do |
local widgetType = "Dropdown-Item-Separator" |
local widgetVersion = 1 |
-- exported, override |
local function SetDisabled(self, disabled) |
ItemBase.SetDisabled(self, disabled) |
self.useHighlight = false |
end |
local function Constructor() |
local self = ItemBase.Create(widgetType) |
self.SetDisabled = SetDisabled |
local line = self.frame:CreateTexture(nil, "OVERLAY") |
line:SetHeight(1) |
line:SetTexture(.5, .5, .5) |
line:SetPoint("LEFT", self.frame, "LEFT", 10, 0) |
line:SetPoint("RIGHT", self.frame, "RIGHT", -10, 0) |
self.text:Hide() |
self.useHighlight = false |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-- Recycling functions |
local new, del |
do |
local pool = setmetatable({},{__mode='k'}) |
function new() |
local t = next(pool) |
if t then |
pool[t] = nil |
return t |
else |
return {} |
end |
end |
function del(t) |
for k in pairs(t) do |
t[k] = nil |
end |
pool[t] = true |
end |
end |
-------------- |
-- TreeView -- |
-------------- |
do |
local Type = "TreeGroup" |
local Version = 16 |
local DEFAULT_TREE_WIDTH = 175 |
local DEFAULT_TREE_SIZABLE = true |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local DraggerBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = nil, |
tile = true, tileSize = 16, edgeSize = 0, |
insets = { left = 3, right = 3, top = 7, bottom = 7 } |
} |
local function OnAcquire(self) |
self:SetTreeWidth(DEFAULT_TREE_WIDTH,DEFAULT_TREE_SIZABLE) |
self:EnableButtonTooltips(true) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.status = nil |
for k, v in pairs(self.localstatus) do |
if k == "groups" then |
for k2 in pairs(v) do |
v[k2] = nil |
end |
else |
self.localstatus[k] = nil |
end |
end |
self.localstatus.scrollvalue = 0 |
self.localstatus.treewidth = DEFAULT_TREE_WIDTH |
self.localstatus.treesizable = DEFAULT_TREE_SIZABLE |
end |
local function GetButtonParents(line) |
local parent = line.parent |
if parent and parent.value then |
return parent.value, GetButtonParents(parent) |
end |
end |
local function GetButtonUniqueValue(line) |
local parent = line.parent |
if parent and parent.value then |
return GetButtonUniqueValue(parent).."\001"..line.value |
else |
return line.value |
end |
end |
local function ButtonOnClick(this) |
local self = this.obj |
self:Fire("OnClick",this.uniquevalue, this.selected) |
if not this.selected then |
self:SetSelected(this.uniquevalue) |
this.selected = true |
this:LockHighlight() |
self:RefreshTree() |
end |
AceGUI:ClearFocus() |
end |
local function ExpandOnClick(this) |
local button = this.button |
local self = button.obj |
local status = (self.status or self.localstatus).groups |
status[button.uniquevalue] = not status[button.uniquevalue] |
self:RefreshTree() |
end |
local function ButtonOnDoubleClick(button) |
local self = button.obj |
local status = self.status or self.localstatus |
local status = (self.status or self.localstatus).groups |
status[button.uniquevalue] = not status[button.uniquevalue] |
self:RefreshTree() |
end |
local function EnableButtonTooltips(self, enable) |
self.enabletooltips = enable |
end |
local function Button_OnEnter(this) |
local self = this.obj |
self:Fire("OnButtonEnter", this.uniquevalue, this) |
if self.enabletooltips then |
GameTooltip:SetOwner(this, "ANCHOR_NONE") |
GameTooltip:SetPoint("LEFT",this,"RIGHT") |
GameTooltip:SetText(this.text:GetText(), 1, .82, 0, 1) |
GameTooltip:Show() |
end |
end |
local function Button_OnLeave(this) |
local self = this.obj |
self:Fire("OnButtonLeave", this.uniquevalue, this) |
if self.enabletooltips then |
GameTooltip:Hide() |
end |
end |
local buttoncount = 1 |
local function CreateButton(self) |
local button = CreateFrame("Button",("AceGUI30TreeButton%d"):format(buttoncount),self.treeframe, "OptionsListButtonTemplate") |
buttoncount = buttoncount + 1 |
button.obj = self |
button:SetScript("OnClick",ButtonOnClick) |
button:SetScript("OnDoubleClick", ButtonOnDoubleClick) |
button:SetScript("OnEnter",Button_OnEnter) |
button:SetScript("OnLeave",Button_OnLeave) |
button.toggle.button = button |
button.toggle:SetScript("OnClick",ExpandOnClick) |
return button |
end |
local function UpdateButton(button, treeline, selected, canExpand, isExpanded) |
local self = button.obj |
local toggle = button.toggle |
local frame = self.frame |
local text = treeline.text or "" |
local level = treeline.level |
local value = treeline.value |
local uniquevalue = treeline.uniquevalue |
local disabled = treeline.disabled |
button.treeline = treeline |
button.value = value |
button.uniquevalue = uniquevalue |
if selected then |
button:LockHighlight() |
button.selected = true |
else |
button:UnlockHighlight() |
button.selected = false |
end |
local normalText = button.text |
local normalTexture = button:GetNormalTexture() |
local line = button.line |
button.level = level |
if ( level == 1 ) then |
button:SetNormalFontObject("GameFontNormal") |
button:SetHighlightFontObject("GameFontHighlight") |
button.text:SetPoint("LEFT", 8, 2) |
else |
button:SetNormalFontObject("GameFontHighlightSmall") |
button:SetHighlightFontObject("GameFontHighlightSmall") |
button.text:SetPoint("LEFT", 8 * level, 2) |
end |
if disabled then |
button:EnableMouse(false) |
button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE) |
else |
button.text:SetText(text) |
button:EnableMouse(true) |
end |
if canExpand then |
if not isExpanded then |
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP") |
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN") |
else |
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP") |
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN") |
end |
toggle:Show() |
else |
toggle:Hide() |
end |
end |
local function OnScrollValueChanged(this, value) |
if this.obj.noupdate then return end |
local self = this.obj |
local status = self.status or self.localstatus |
status.scrollvalue = value |
self:RefreshTree() |
AceGUI:ClearFocus() |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
if not status.groups then |
status.groups = {} |
end |
if not status.scrollvalue then |
status.scrollvalue = 0 |
end |
if not status.treewidth then |
status.treewidth = DEFAULT_TREE_WIDTH |
end |
if not status.treesizable then |
status.treesizable = DEFAULT_TREE_SIZABLE |
end |
self:SetTreeWidth(status.treewidth,status.treesizable) |
self:RefreshTree() |
end |
--sets the tree to be displayed |
--[[ |
example tree |
Alpha |
Bravo |
-Charlie |
-Delta |
-Echo |
Foxtrot |
tree = { |
{ |
value = "A", |
text = "Alpha" |
}, |
{ |
value = "B", |
text = "Bravo", |
children = { |
{ |
value = "C", |
text = "Charlie" |
}, |
{ |
value = "D", |
text = "Delta" |
children = { |
{ |
value = "E", |
text = "Echo" |
} |
} |
} |
} |
}, |
{ |
value = "F", |
text = "Foxtrot" |
}, |
} |
]] |
local function SetTree(self, tree) |
if tree then |
assert(type(tree) == "table") |
end |
self.tree = tree |
self:RefreshTree() |
end |
local function BuildLevel(self, tree, level, parent) |
local lines = self.lines |
local status = (self.status or self.localstatus) |
local groups = status.groups |
local hasChildren = self.hasChildren |
for i, v in ipairs(tree) do |
local line = new() |
lines[#lines+1] = line |
line.value = v.value |
line.text = v.text |
line.disabled = v.disabled |
line.tree = tree |
line.level = level |
line.parent = parent |
line.uniquevalue = GetButtonUniqueValue(line) |
if v.children then |
line.hasChildren = true |
else |
line.hasChildren = nil |
end |
if v.children then |
if groups[line.uniquevalue] then |
self:BuildLevel(v.children, level+1, line) |
end |
end |
end |
end |
--fire an update after one frame to catch the treeframes height |
local function FirstFrameUpdate(this) |
local self = this.obj |
this:SetScript("OnUpdate",nil) |
self:RefreshTree() |
end |
local function ResizeUpdate(this) |
this.obj:RefreshTree() |
end |
local function RefreshTree(self) |
local buttons = self.buttons |
local lines = self.lines |
for i, v in ipairs(buttons) do |
v:Hide() |
end |
while lines[1] do |
local t = tremove(lines) |
for k in pairs(t) do |
t[k] = nil |
end |
del(t) |
end |
if not self.tree then return end |
--Build the list of visible entries from the tree and status tables |
local status = self.status or self.localstatus |
local groupstatus = status.groups |
local tree = self.tree |
local treeframe = self.treeframe |
self:BuildLevel(tree, 1) |
local numlines = #lines |
local maxlines = (math.floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18)) |
local first, last |
if numlines <= maxlines then |
--the whole tree fits in the frame |
status.scrollvalue = 0 |
self:ShowScroll(false) |
first, last = 1, numlines |
else |
self:ShowScroll(true) |
--scrolling will be needed |
self.noupdate = true |
self.scrollbar:SetMinMaxValues(0, numlines - maxlines) |
--check if we are scrolled down too far |
if numlines - status.scrollvalue < maxlines then |
status.scrollvalue = numlines - maxlines |
self.scrollbar:SetValue(status.scrollvalue) |
end |
self.noupdate = nil |
first, last = status.scrollvalue+1, status.scrollvalue + maxlines |
end |
local buttonnum = 1 |
for i = first, last do |
local line = lines[i] |
local button = buttons[buttonnum] |
if not button then |
button = self:CreateButton() |
buttons[buttonnum] = button |
button:SetParent(treeframe) |
button:SetFrameLevel(treeframe:GetFrameLevel()+1) |
button:ClearAllPoints() |
if i == 1 then |
if self.showscroll then |
button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10) |
button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10) |
else |
button:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10) |
button:SetPoint("TOPLEFT", self.treeframe, "TOPLEFT", 0, -10) |
end |
else |
button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0) |
button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0) |
end |
end |
UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] ) |
button:Show() |
buttonnum = buttonnum + 1 |
end |
end |
local function SetSelected(self, value) |
local status = self.status or self.localstatus |
if status.selected ~= value then |
status.selected = value |
self:Fire("OnGroupSelected", value) |
end |
end |
local function BuildUniqueValue(...) |
local n = select('#', ...) |
if n == 1 then |
return ... |
else |
return (...).."\001"..BuildUniqueValue(select(2,...)) |
end |
end |
local function Select(self, uniquevalue, ...) |
local status = self.status or self.localstatus |
local groups = status.groups |
for i = 1, select('#', ...) do |
groups[BuildUniqueValue(select(i, ...))] = true |
end |
status.selected = uniquevalue |
self:RefreshTree() |
self:Fire("OnGroupSelected", uniquevalue) |
end |
local function SelectByPath(self, ...) |
self:Select(BuildUniqueValue(...), ...) |
end |
--Selects a tree node by UniqueValue |
local function SelectByValue(self, uniquevalue) |
self:Select(uniquevalue,string.split("\001", uniquevalue)) |
end |
local function ShowScroll(self, show) |
self.showscroll = show |
if show then |
self.scrollbar:Show() |
if self.buttons[1] then |
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10) |
end |
else |
self.scrollbar:Hide() |
if self.buttons[1] then |
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10) |
end |
end |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local treeframe = self.treeframe |
local status = self.status or self.localstatus |
local contentwidth = width - status.treewidth - 20 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
local maxtreewidth = math.min(400, width - 50) |
if maxtreewidth > 100 and status.treewidth > maxtreewidth then |
self:SetTreeWidth(maxtreewidth, status.treesizable) |
end |
treeframe:SetMaxResize(maxtreewidth,1600) |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 20 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function TreeOnMouseWheel(this, delta) |
local self = this.obj |
if self.showscroll then |
local scrollbar = self.scrollbar |
local min, max = scrollbar:GetMinMaxValues() |
local value = scrollbar:GetValue() |
local newvalue = math.min(max,math.max(min,value - delta)) |
if value ~= newvalue then |
scrollbar:SetValue(newvalue) |
end |
end |
end |
local function SetTreeWidth(self, treewidth, resizable) |
if not resizable then |
if type(treewidth) == 'number' then |
resizable = false |
elseif type(treewidth) == 'boolean' then |
resizable = treewidth |
treewidth = DEFAULT_TREE_WIDTH |
else |
resizable = false |
treewidth = DEFAULT_TREE_WIDTH |
end |
end |
self.treeframe:SetWidth(treewidth) |
self.dragger:EnableMouse(resizable) |
local status = self.status or self.localstatus |
status.treewidth = treewidth |
status.treesizable = resizable |
end |
local function draggerLeave(this) |
this:SetBackdropColor(1, 1, 1, 0) |
end |
local function draggerEnter(this) |
this:SetBackdropColor(1, 1, 1, 0.8) |
end |
local function draggerDown(this) |
local treeframe = this:GetParent() |
treeframe:StartSizing("RIGHT") |
end |
local function draggerUp(this) |
local treeframe = this:GetParent() |
local self = treeframe.obj |
local frame = treeframe:GetParent() |
treeframe:StopMovingOrSizing() |
--treeframe:SetScript("OnUpdate", nil) |
treeframe:SetUserPlaced(false) |
--Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize |
treeframe:SetHeight(0) |
treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) |
treeframe.obj:Fire("OnTreeResize",treeframe:GetWidth()) |
local status = self.status or self.localstatus |
status.treewidth = treeframe:GetWidth() |
end |
local createdcount = 0 |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.lines = {} |
self.levels = {} |
self.buttons = {} |
self.hasChildren = {} |
self.localstatus = {} |
self.localstatus.groups = {} |
local treeframe = CreateFrame("Frame",nil,frame) |
treeframe.obj = self |
treeframe:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
treeframe:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) |
treeframe:SetWidth(DEFAULT_TREE_WIDTH) |
treeframe:SetScript("OnUpdate",FirstFrameUpdate) |
treeframe:SetScript("OnSizeChanged",ResizeUpdate) |
treeframe:EnableMouseWheel(true) |
treeframe:SetScript("OnMouseWheel", TreeOnMouseWheel) |
treeframe:SetBackdrop(PaneBackdrop) |
treeframe:SetBackdropColor(0.1,0.1,0.1,0.5) |
treeframe:SetBackdropBorderColor(0.4,0.4,0.4) |
treeframe:SetResizable(true) |
treeframe:SetMinResize(100, 1) |
treeframe:SetMaxResize(400,1600) |
local dragger = CreateFrame("Frame", nil, treeframe) |
dragger:SetWidth(8) |
dragger:SetPoint("TOP", treeframe, "TOPRIGHT") |
dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT") |
dragger:SetBackdrop(DraggerBackdrop) |
dragger:SetBackdropColor(1, 1, 1, 0) |
dragger:SetScript("OnMouseDown", draggerDown) |
dragger:SetScript("OnMouseUp", draggerUp) |
dragger:SetScript("OnEnter", draggerEnter) |
dragger:SetScript("OnLeave", draggerLeave) |
self.dragger = dragger |
self.treeframe = treeframe |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetTree = SetTree |
self.SetTreeWidth = SetTreeWidth |
self.RefreshTree = RefreshTree |
self.SetStatusTable = SetStatusTable |
self.BuildLevel = BuildLevel |
self.CreateButton = CreateButton |
self.SetSelected = SetSelected |
self.ShowScroll = ShowScroll |
self.SetStatusTable = SetStatusTable |
self.Select = Select |
self.SelectByValue = SelectByValue |
self.SelectByPath = SelectByPath |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.EnableButtonTooltips = EnableButtonTooltips |
self.frame = frame |
frame.obj = self |
createdcount = createdcount + 1 |
local scrollbar = CreateFrame("Slider",("AceConfigDialogTreeGroup%dScrollBar"):format(createdcount),treeframe,"UIPanelScrollBarTemplate") |
self.scrollbar = scrollbar |
local scrollbg = scrollbar:CreateTexture(nil,"BACKGROUND") |
scrollbg:SetAllPoints(scrollbar) |
scrollbg:SetTexture(0,0,0,0.4) |
scrollbar.obj = self |
self.noupdate = true |
scrollbar:SetPoint("TOPRIGHT",treeframe,"TOPRIGHT",-10,-26) |
scrollbar:SetPoint("BOTTOMRIGHT",treeframe,"BOTTOMRIGHT",-10,26) |
scrollbar:SetScript("OnValueChanged", OnScrollValueChanged) |
scrollbar:SetMinMaxValues(0,0) |
self.localstatus.scrollvalue = 0 |
scrollbar:SetValueStep(1) |
scrollbar:SetValue(0) |
scrollbar:SetWidth(16) |
self.noupdate = nil |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
border:SetPoint("TOPLEFT",treeframe,"TOPRIGHT", 0,0) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
--AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- ColorPicker -- |
-------------------------- |
do |
local Type = "ColorPicker" |
local Version = 9 |
local function OnAcquire(self) |
self.HasAlpha = false |
self:SetColor(0,0,0,1) |
end |
local function SetLabel(self, text) |
self.text:SetText(text) |
end |
local function SetColor(self,r,g,b,a) |
self.r = r |
self.g = g |
self.b = b |
self.a = a or 1 |
self.colorSwatch:SetVertexColor(r,g,b,a) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function SetHasAlpha(self, HasAlpha) |
self.HasAlpha = HasAlpha |
end |
local function ColorCallback(self,r,g,b,a,isAlpha) |
if not self.HasAlpha then |
a = 1 |
end |
self:SetColor(r,g,b,a) |
if ColorPickerFrame:IsVisible() then |
--colorpicker is still open |
self:Fire("OnValueChanged",r,g,b,a) |
else |
--colorpicker is closed, color callback is first, ignore it, |
--alpha callback is the final call after it closes so confirm now |
if isAlpha then |
self:Fire("OnValueConfirmed",r,g,b,a) |
end |
end |
end |
local function ColorSwatch_OnClick(this) |
HideUIPanel(ColorPickerFrame) |
local self = this.obj |
if not self.disabled then |
ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG") |
ColorPickerFrame.func = function() |
local r,g,b = ColorPickerFrame:GetColorRGB() |
local a = 1 - OpacitySliderFrame:GetValue() |
ColorCallback(self,r,g,b,a) |
end |
ColorPickerFrame.hasOpacity = self.HasAlpha |
ColorPickerFrame.opacityFunc = function() |
local r,g,b = ColorPickerFrame:GetColorRGB() |
local a = 1 - OpacitySliderFrame:GetValue() |
ColorCallback(self,r,g,b,a,true) |
end |
local r, g, b, a = self.r, self.g, self.b, self.a |
if self.HasAlpha then |
ColorPickerFrame.opacity = 1 - (a or 0) |
end |
ColorPickerFrame:SetColorRGB(r, g, b) |
ColorPickerFrame.cancelFunc = function() |
ColorCallback(self,r,g,b,a,true) |
end |
ShowUIPanel(ColorPickerFrame) |
end |
AceGUI:ClearFocus() |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if self.disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
else |
self.text:SetTextColor(1,1,1) |
end |
end |
local function Constructor() |
local frame = CreateFrame("Button",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetLabel = SetLabel |
self.SetColor = SetColor |
self.SetDisabled = SetDisabled |
self.SetHasAlpha = SetHasAlpha |
self.frame = frame |
frame.obj = self |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
self.text = text |
text:SetJustifyH("LEFT") |
text:SetTextColor(1,1,1) |
frame:SetHeight(24) |
frame:SetWidth(200) |
text:SetHeight(24) |
frame:SetScript("OnClick", ColorSwatch_OnClick) |
frame:SetScript("OnEnter",Control_OnEnter) |
frame:SetScript("OnLeave",Control_OnLeave) |
local colorSwatch = frame:CreateTexture(nil, "OVERLAY") |
self.colorSwatch = colorSwatch |
colorSwatch:SetWidth(19) |
colorSwatch:SetHeight(19) |
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch") |
local texture = frame:CreateTexture(nil, "BACKGROUND") |
colorSwatch.texture = texture |
texture:SetWidth(16) |
texture:SetHeight(16) |
texture:SetTexture(1,1,1) |
texture:Show() |
local checkers = frame:CreateTexture(nil, "BACKGROUND") |
colorSwatch.checkers = checkers |
checkers:SetTexture("Tileset\\Generic\\Checkers") |
checkers:SetDesaturated(true) |
checkers:SetVertexColor(1,1,1,0.75) |
checkers:SetTexCoord(.25,0,0.5,.25) |
checkers:SetWidth(14) |
checkers:SetHeight(14) |
checkers:Show() |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
self.highlight = highlight |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(frame) |
highlight:Hide() |
texture:SetPoint("CENTER", colorSwatch, "CENTER") |
checkers:SetPoint("CENTER", colorSwatch, "CENTER") |
colorSwatch:SetPoint("LEFT", frame, "LEFT", 0, 0) |
text:SetPoint("LEFT",colorSwatch,"RIGHT",2,0) |
text:SetPoint("RIGHT",frame,"RIGHT") |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Label -- |
-------------------------- |
do |
local Type = "Label" |
local Version = 8 |
local function OnAcquire(self) |
self:SetText("") |
self:SetImage(nil) |
self:SetColor() |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function UpdateImageAnchor(self) |
local width = self.frame.width or self.frame:GetWidth() or 0 |
local image = self.image |
local label = self.label |
local frame = self.frame |
local height |
label:ClearAllPoints() |
image:ClearAllPoints() |
if self.imageshown then |
local imagewidth = image:GetWidth() |
if (width - imagewidth) < 200 or (label:GetText() or "") == "" then |
--image goes on top centered when less than 200 width for the text, or if there is no text |
image:SetPoint("TOP",frame,"TOP",0,0) |
label:SetPoint("TOP",image,"BOTTOM",0,0) |
label:SetPoint("LEFT",frame,"LEFT",0,0) |
label:SetWidth(width) |
height = image:GetHeight() + label:GetHeight() |
else |
--image on the left |
image:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPLEFT",image,"TOPRIGHT",0,0) |
label:SetWidth(width - imagewidth) |
height = math.max(image:GetHeight(), label:GetHeight()) |
end |
else |
--no image shown |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetWidth(width) |
height = self.label:GetHeight() |
end |
self.resizing = true |
self.frame:SetHeight(height) |
self.frame.height = height |
self.resizing = nil |
end |
local function SetText(self, text) |
self.label:SetText(text or "") |
UpdateImageAnchor(self) |
end |
local function SetColor(self, r, g, b) |
if not (r and g and b) then |
r, g, b = 1, 1, 1 |
end |
self.label:SetVertexColor(r, g, b) |
end |
local function OnWidthSet(self, width) |
if self.resizing then return end |
UpdateImageAnchor(self) |
end |
local function SetImage(self, path, ...) |
local image = self.image |
image:SetTexture(path) |
if image:GetTexture() then |
self.imageshown = true |
local n = select('#', ...) |
if n == 4 or n == 8 then |
image:SetTexCoord(...) |
end |
else |
self.imageshown = nil |
end |
UpdateImageAnchor(self) |
end |
local function SetImageSize(self, width, height) |
self.image:SetWidth(width) |
self.image:SetHeight(height) |
UpdateImageAnchor(self) |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetText = SetText |
self.SetColor = SetColor |
self.frame = frame |
self.OnWidthSet = OnWidthSet |
self.SetImage = SetImage |
self.SetImageSize = SetImageSize |
frame.obj = self |
frame:SetHeight(18) |
frame:SetWidth(200) |
local label = frame:CreateFontString(nil,"BACKGROUND","GameFontHighlightSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetWidth(200) |
label:SetJustifyH("LEFT") |
label:SetJustifyV("TOP") |
self.label = label |
local image = frame:CreateTexture(nil,"BACKGROUND") |
self.image = image |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
--[[ |
--Multiline Editbox Widget, Originally by bam |
--]] |
local assert, error, ipairs, next, pairs, select, tonumber, tostring, type, unpack, pcall, xpcall = |
assert, error, ipairs, next, pairs, select, tonumber, tostring, type, unpack, pcall, xpcall |
local getmetatable, setmetatable, rawequal, rawget, rawset, getfenv, setfenv, loadstring, debugstack = |
getmetatable, setmetatable, rawequal, rawget, rawset, getfenv, setfenv, loadstring, debugstack |
local math, string, table = math, string, table |
local find, format, gmatch, gsub, tolower, match, toupper, join, split, trim = |
string.find, string.format, string.gmatch, string.gsub, string.lower, string.match, string.upper, string.join, string.split, string.trim |
local concat, insert, maxn, remove, sort = table.concat, table.insert, table.maxn, table.remove, table.sort |
local max, min, abs, ceil, floor = math.max, math.min, math.abs, math.ceil, math.floor |
local LibStub = assert(LibStub) |
local ChatFontNormal = ChatFontNormal |
local ClearCursor = ClearCursor |
local CreateFrame = CreateFrame |
local GetCursorInfo = GetCursorInfo |
local GetSpellName = GetSpellName |
local UIParent = UIParent |
local UISpecialFrames = UISpecialFrames |
-- No global variables after this! |
local _G = getfenv() |
local AceGUI = LibStub("AceGUI-3.0") |
local Version = 7 |
--------------------- |
-- Common Elements -- |
--------------------- |
local FrameBackdrop = { |
bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", |
edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", |
tile = true, tileSize = 32, edgeSize = 32, |
insets = { left = 8, right = 8, top = 8, bottom = 8 } |
} |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local ControlBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 3, bottom = 3 } |
} |
-------------------------- |
-- Edit box -- |
-------------------------- |
--[[ |
Events : |
OnTextChanged |
OnEnterPressed |
]] |
do |
local Type = "MultiLineEditBox" |
local MultiLineEditBox = {} |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
local cancel = self:Fire("OnEnterPressed",value) |
if not cancel then |
self.button:Disable() |
end |
end |
local function Button_OnClick(this) |
local editbox = this.obj.editbox |
editbox:ClearFocus() |
EditBox_OnEnterPressed(editbox) |
end |
local function EditBox_OnReceiveDrag(this) |
local self = this.obj |
local type, id, info = GetCursorInfo() |
if type == "item" then |
self:SetText(info) |
self:Fire("OnEnterPressed",info) |
ClearCursor() |
elseif type == "spell" then |
local name, rank = GetSpellName(id, info) |
if rank and rank:match("%d") then |
name = name.."("..rank..")" |
end |
self:SetText(name) |
self:Fire("OnEnterPressed",name) |
ClearCursor() |
end |
self.button:Disable() |
AceGUI:ClearFocus() |
end |
function MultiLineEditBox:OnAcquire() |
self:SetDisabled(false) |
self:ShowButton(true) |
end |
function MultiLineEditBox:OnRelease() |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetDisabled(false) |
end |
function MultiLineEditBox:SetDisabled(disabled) |
self.disabled = disabled |
if disabled then |
self.editbox:EnableMouse(false) |
self.scrollframe:EnableMouse(false) |
self.editbox:ClearFocus() |
self.editbox:SetTextColor(0.5, 0.5, 0.5) |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.editbox:EnableMouse(true) |
self.scrollframe:EnableMouse(true) |
self.editbox:SetTextColor(1, 1, 1) |
self.label:SetTextColor(1,.82,0) |
end |
end |
function MultiLineEditBox:SetText(text) |
text = text or "" |
local editbox = self.editbox |
local oldText = editbox:GetText() |
local dummy = format(" %s", text) |
self.lasttext = dummy -- prevents OnTextChanged from firing |
editbox:SetText(dummy) |
editbox:HighlightText(0, 1) |
self.lasttext = oldText |
editbox:Insert("") |
end |
function MultiLineEditBox:SetLabel(text) |
if (text or "") == "" then |
self.backdrop:SetPoint("TOPLEFT",self.frame,"TOPLEFT",0,0) |
self.label:Hide() |
self.label:SetText("") |
else |
self.backdrop:SetPoint("TOPLEFT",self.frame,"TOPLEFT",0,-20) |
self.label:Show() |
self.label:SetText(text) |
end |
end |
function MultiLineEditBox:GetText() |
return self.editbox:GetText() |
end |
function MultiLineEditBox:ShowButton(show) |
if show then |
self.backdrop:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,22) |
self.button:Show() |
else |
self.backdrop:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",0,0) |
self.button:Hide() |
end |
end |
local function Constructor() |
local frame = CreateFrame("Frame", nil, UIParent) |
local backdrop = CreateFrame("Frame", nil, frame) |
local self = {} |
for k, v in pairs(MultiLineEditBox) do self[k] = v end |
self.type = Type |
self.frame = frame |
self.backdrop = backdrop |
frame.obj = self |
backdrop:SetBackdrop(ControlBackdrop) |
backdrop:SetBackdropColor(0, 0, 0) |
backdrop:SetBackdropBorderColor(0.4, 0.4, 0.4) |
backdrop:SetPoint("TOPLEFT",frame,"TOPLEFT",0, -20) |
backdrop:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,22) |
local scrollframe = CreateFrame("ScrollFrame", format("%s@%s@%s", Type, "ScrollFrame", tostring(self)), backdrop, "UIPanelScrollFrameTemplate") |
scrollframe:SetPoint("TOPLEFT", 5, -6) |
scrollframe:SetPoint("BOTTOMRIGHT", -28, 6) |
scrollframe.obj = self |
self.scrollframe = scrollframe |
local scrollchild = CreateFrame("Frame", nil, scrollframe) |
scrollframe:SetScrollChild(scrollchild) |
scrollchild:SetHeight(2) |
scrollchild:SetWidth(2) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-2) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-2) |
label:SetJustifyH("LEFT") |
label:SetHeight(18) |
self.label = label |
local editbox = CreateFrame("EditBox", nil, scrollchild) |
self.editbox = editbox |
editbox.obj = self |
editbox:SetPoint("TOPLEFT") |
editbox:SetHeight(50) |
editbox:SetWidth(50) |
editbox:SetMultiLine(true) |
-- editbox:SetMaxLetters(7500) |
editbox:SetTextInsets(5, 5, 3, 3) |
editbox:EnableMouse(true) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(ChatFontNormal) |
local button = CreateFrame("Button",nil,scrollframe,"UIPanelButtonTemplate") |
button:SetWidth(80) |
button:SetHeight(20) |
button:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,2) |
button:SetText(ACCEPT) |
button:SetScript("OnClick", Button_OnClick) |
button:Disable() |
button:Hide() |
self.button = button |
button.obj = self |
scrollframe:EnableMouse(true) |
scrollframe:SetScript("OnMouseUp", function() editbox:SetFocus() end) |
scrollframe:SetScript("OnEnter", function(this) this.obj:Fire("OnEnter") end) |
scrollframe:SetScript("OnLeave", function(this) this.obj:Fire("OnLeave") end) |
editbox:SetScript("OnEnter", function(this) this.obj:Fire("OnEnter") end) |
editbox:SetScript("OnLeave", function(this) this.obj:Fire("OnLeave") end) |
local function FixSize() |
scrollchild:SetHeight(scrollframe:GetHeight()) |
scrollchild:SetWidth(scrollframe:GetWidth()) |
editbox:SetWidth(scrollframe:GetWidth()) |
end |
scrollframe:SetScript("OnShow", FixSize) |
scrollframe:SetScript("OnSizeChanged", FixSize) |
editbox:SetScript("OnEscapePressed", editbox.ClearFocus) |
editbox:SetScript("OnTextChanged", function(_, ...) |
scrollframe:UpdateScrollChildRect() |
local value = editbox:GetText() |
if value ~= self.lasttext then |
self:Fire("OnTextChanged", value) |
self.lasttext = value |
self.button:Enable() |
end |
end) |
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag) |
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag) |
do |
local cursorOffset, cursorHeight |
local idleTime |
local function FixScroll(_, elapsed) |
if cursorOffset and cursorHeight then |
idleTime = 0 |
local height = scrollframe:GetHeight() |
local range = scrollframe:GetVerticalScrollRange() |
local scroll = scrollframe:GetVerticalScroll() |
local size = height + range |
cursorOffset = -cursorOffset |
while cursorOffset < scroll do |
scroll = scroll - (height / 2) |
if scroll < 0 then scroll = 0 end |
scrollframe:SetVerticalScroll(scroll) |
end |
while cursorOffset + cursorHeight > scroll + height and scroll < range do |
scroll = scroll + (height / 2) |
if scroll > range then scroll = range end |
scrollframe:SetVerticalScroll(scroll) |
end |
elseif not idleTime or idleTime > 2 then |
frame:SetScript("OnUpdate", nil) |
idleTime = nil |
else |
idleTime = idleTime + elapsed |
end |
cursorOffset = nil |
end |
editbox:SetScript("OnCursorChanged", function(_, x, y, w, h) |
cursorOffset, cursorHeight = y, h |
if not idleTime then |
frame:SetScript("OnUpdate", FixScroll) |
end |
end) |
end |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type, Constructor, Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Slider -- |
-------------------------- |
do |
local Type = "Slider" |
local Version = 5 |
local function OnAcquire(self) |
self:SetDisabled(false) |
self:SetSliderValues(0,100,1) |
self:SetIsPercent(nil) |
self:SetValue(0) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.slider:EnableMouseWheel(false) |
self:SetDisabled(false) |
end |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function UpdateText(self) |
if self.ispercent then |
self.editbox:SetText((math.floor(self.value*1000+0.5)/10)..'%') |
else |
self.editbox:SetText(math.floor(self.value*100+0.5)/100) |
end |
end |
local function Slider_OnValueChanged(this) |
local self = this.obj |
if not this.setup then |
local newvalue |
newvalue = this:GetValue() |
if newvalue ~= self.value and not self.disabled then |
self.value = newvalue |
self:Fire("OnValueChanged", newvalue) |
end |
if self.value then |
local value = self.value |
UpdateText(self) |
end |
end |
end |
local function Slider_OnMouseUp(this) |
local self = this.obj |
self:Fire("OnMouseUp",this:GetValue()) |
end |
local function Slider_OnMouseWheel(this, v) |
local self = this.obj |
if not self.disabled then |
local value = self.value |
if v > 0 then |
value = math.min(value + (self.step or 1),self.max) |
else |
value = math.max(value - (self.step or 1), self.min) |
end |
self.slider:SetValue(value) |
end |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.slider:EnableMouse(false) |
self.label:SetTextColor(.5,.5,.5) |
self.hightext:SetTextColor(.5,.5,.5) |
self.lowtext:SetTextColor(.5,.5,.5) |
--self.valuetext:SetTextColor(.5,.5,.5) |
self.editbox:SetTextColor(.5,.5,.5) |
self.editbox:EnableMouse(false) |
self.editbox:ClearFocus() |
else |
self.slider:EnableMouse(true) |
self.label:SetTextColor(1,.82,0) |
self.hightext:SetTextColor(1,1,1) |
self.lowtext:SetTextColor(1,1,1) |
--self.valuetext:SetTextColor(1,1,1) |
self.editbox:SetTextColor(1,1,1) |
self.editbox:EnableMouse(true) |
end |
end |
local function SetValue(self, value) |
self.slider.setup = true |
self.slider:SetValue(value) |
self.value = value |
UpdateText(self) |
self.slider.setup = nil |
end |
local function SetLabel(self, text) |
self.label:SetText(text) |
end |
local function SetSliderValues(self,min, max, step) |
local frame = self.slider |
frame.setup = true |
self.min = min |
self.max = max |
self.step = step |
frame:SetMinMaxValues(min or 0,max or 100) |
self.lowtext:SetText(min or 0) |
self.hightext:SetText(max or 100) |
frame:SetValueStep(step or 1) |
frame.setup = nil |
end |
local function EditBox_OnEscapePressed(this) |
this:ClearFocus() |
end |
local function EditBox_OnEnterPressed(this) |
local self = this.obj |
local value = this:GetText() |
if self.ispercent then |
value = value:gsub('%%','') |
value = tonumber(value) / 100 |
else |
value = tonumber(value) |
end |
if value then |
self:Fire("OnMouseUp",value) |
end |
end |
local function SetIsPercent(self, value) |
self.ispercent = value |
end |
local function FrameOnMouseDown(this) |
this.obj.slider:EnableMouseWheel(true) |
AceGUI:ClearFocus() |
end |
local SliderBackdrop = { |
bgFile = "Interface\\Buttons\\UI-SliderBar-Background", |
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", |
tile = true, tileSize = 8, edgeSize = 8, |
insets = { left = 3, right = 3, top = 6, bottom = 6 } |
} |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.frame = frame |
frame.obj = self |
self.SetDisabled = SetDisabled |
self.SetValue = SetValue |
self.SetSliderValues = SetSliderValues |
self.SetLabel = SetLabel |
self.SetIsPercent = SetIsPercent |
self.alignoffset = 25 |
frame:EnableMouse(true) |
frame:SetScript("OnMouseDown",FrameOnMouseDown) |
self.slider = CreateFrame("Slider",nil,frame) |
local slider = self.slider |
slider:SetScript("OnEnter",Control_OnEnter) |
slider:SetScript("OnLeave",Control_OnLeave) |
slider:SetScript("OnMouseUp", Slider_OnMouseUp) |
slider.obj = self |
slider:SetOrientation("HORIZONTAL") |
slider:SetHeight(15) |
slider:SetHitRectInsets(0,0,-10,0) |
slider:SetBackdrop(SliderBackdrop) |
--slider:EnableMouseWheel(true) |
slider:SetScript("OnMouseWheel", Slider_OnMouseWheel) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
label:SetJustifyH("CENTER") |
label:SetHeight(15) |
self.label = label |
self.lowtext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") |
self.lowtext:SetPoint("TOPLEFT",slider,"BOTTOMLEFT",2,3) |
self.hightext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") |
self.hightext:SetPoint("TOPRIGHT",slider,"BOTTOMRIGHT",-2,3) |
local editbox = CreateFrame("EditBox",nil,frame) |
editbox:SetAutoFocus(false) |
editbox:SetFontObject(GameFontHighlightSmall) |
editbox:SetPoint("TOP",slider,"BOTTOM",0,0) |
editbox:SetHeight(14) |
editbox:SetWidth(70) |
editbox:SetJustifyH("CENTER") |
editbox:EnableMouse(true) |
editbox:SetScript("OnEscapePressed",EditBox_OnEscapePressed) |
editbox:SetScript("OnEnterPressed",EditBox_OnEnterPressed) |
self.editbox = editbox |
editbox.obj = self |
local bg = editbox:CreateTexture(nil,"BACKGROUND") |
editbox.bg = bg |
bg:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") |
bg:SetVertexColor(0,0,0,0.25) |
bg:SetAllPoints(editbox) |
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") |
frame:SetWidth(200) |
frame:SetHeight(44) |
slider:SetPoint("TOP",label,"BOTTOM",0,0) |
slider:SetPoint("LEFT",frame,"LEFT",3,0) |
slider:SetPoint("RIGHT",frame,"RIGHT",-3,0) |
slider:SetValue(self.value or 0) |
slider:SetScript("OnValueChanged",Slider_OnValueChanged) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
-------------------------- |
-- Tab Group -- |
-------------------------- |
do |
local Type = "TabGroup" |
local Version = 15 |
local PaneBackdrop = { |
bgFile = "Interface\\ChatFrame\\ChatFrameBackground", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 5, bottom = 3 } |
} |
local function OnAcquire(self) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.status = nil |
for k in pairs(self.localstatus) do |
self.localstatus[k] = nil |
end |
self.tablist = nil |
end |
local function Tab_SetText(self, text) |
self:_SetText(text) |
PanelTemplates_TabResize(self, 0) |
end |
local function UpdateTabLook(self) |
if self.disabled then |
PanelTemplates_SetDisabledTabState(self) |
elseif self.selected then |
PanelTemplates_SelectTab(self) |
else |
PanelTemplates_DeselectTab(self) |
end |
end |
local function Tab_SetSelected(self, selected) |
self.selected = selected |
UpdateTabLook(self) |
end |
local function Tab_OnClick(self) |
if not (self.selected or self.disabled) then |
self.obj:SelectTab(self.value) |
end |
end |
local function Tab_SetDisabled(self, disabled) |
self.disabled = disabled |
UpdateTabLook(self) |
end |
local function Tab_OnEnter(this) |
local self = this.obj |
self:Fire("OnTabEnter", self.tabs[this.id].value, this) |
end |
local function Tab_OnLeave(this) |
local self = this.obj |
self:Fire("OnTabLeave", self.tabs[this.id].value, this) |
end |
local function CreateTab(self, id) |
local tabname = "AceGUITabGroup"..self.num.."Tab"..id |
local tab = CreateFrame("Button",tabname,self.border,"OptionsFrameTabButtonTemplate") |
tab.obj = self |
tab.id = id |
tab:SetScript("OnClick",Tab_OnClick) |
tab:SetScript("OnEnter",Tab_OnEnter) |
tab:SetScript("OnLeave",Tab_OnLeave) |
tab._SetText = tab.SetText |
tab.SetText = Tab_SetText |
tab.SetSelected = Tab_SetSelected |
tab.SetDisabled = Tab_SetDisabled |
return tab |
end |
local function SetTitle(self, text) |
self.titletext:SetText(text or "") |
end |
-- called to set an external table to store status in |
local function SetStatusTable(self, status) |
assert(type(status) == "table") |
self.status = status |
end |
local function SelectTab(self, value) |
local status = self.status or self.localstatus |
local found |
for i, v in ipairs(self.tabs) do |
if v.value == value then |
v:SetSelected(true) |
found = true |
else |
v:SetSelected(false) |
end |
end |
status.selected = value |
if found then |
self:Fire("OnGroupSelected",value) |
end |
end |
local function SetTabs(self, tabs) |
self.tablist = tabs |
self:BuildTabs() |
end |
local widths = {} |
local rowwidths = {} |
local rowends = {} |
local function BuildTabs(self) |
local status = self.status or self.localstatus |
local tablist = self.tablist |
local tabs = self.tabs |
for i, v in ipairs(tabs) do |
v:Hide() |
end |
if not tablist then return end |
local width = self.frame.width or self.frame:GetWidth() or 0 |
for i = #widths, 1, -1 do |
widths[i] = nil |
end |
for i = #rowwidths, 1, -1 do |
rowwidths[i] = nil |
end |
for i = #rowends, 1, -1 do |
rowends[i] = nil |
end |
--Place Text into tabs and get thier initial width |
for i, v in ipairs(tablist) do |
local tab = tabs[i] |
if not tab then |
tab = self:CreateTab(i) |
tabs[i] = tab |
end |
tab:Show() |
tab:SetText(v.text) |
tab:SetDisabled(v.disabled) |
tab.value = v.value |
widths[i] = tab:GetWidth() - 10 --tabs are anchored 10 pixels from the right side of the previous one to reduce spacing |
end |
--First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout |
local numtabs = #tablist |
local numrows = 1 |
local usedwidth = 0 |
for i = 1, #tablist do |
--If this is not the first tab of a row and there isn't room for it |
if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then |
rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px |
rowends[numrows] = i - 1 |
numrows = numrows + 1 |
usedwidth = 0 |
end |
usedwidth = usedwidth + widths[i] |
end |
rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px |
rowends[numrows] = #tablist |
--Fix for single tabs being left on the last row, move a tab from the row above if applicable |
if numrows > 1 then |
--if the last row has only one tab |
if rowends[numrows-1] == numtabs-1 then |
--if there are more than 2 tabs in the 2nd last row |
if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then |
--move 1 tab from the second last row to the last |
rowends[numrows-1] = rowends[numrows-1] - 1 |
rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1] |
rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1] |
end |
end |
end |
--anchor the rows as defined and resize tabs to fill thier row |
local starttab = 1 |
for row, endtab in ipairs(rowends) do |
local first = true |
for tabno = starttab, endtab do |
local tab = tabs[tabno] |
tab:ClearAllPoints() |
if first then |
tab:SetPoint("TOPLEFT",self.frame,"TOPLEFT",0,-7-(row-1)*20 ) |
first = false |
else |
tab:SetPoint("LEFT",tabs[tabno-1],"RIGHT",-10,0) |
end |
end |
--equal padding for each tab to fill the available width |
local padding = (width - rowwidths[row]) / (endtab - starttab+1) |
for i = starttab, endtab do |
PanelTemplates_TabResize(tabs[i], padding) |
end |
starttab = endtab + 1 |
end |
self.borderoffset = 10+((numrows)*20) |
self.border:SetPoint("TOPLEFT",self.frame,"TOPLEFT",3,-self.borderoffset) |
end |
local function BuildTabsOnUpdate(this) |
BuildTabs(this.obj) |
this:SetScript("OnUpdate", nil) |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 60 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
BuildTabs(self) |
self.frame:SetScript("OnUpdate", BuildTabsOnUpdate) |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - (self.borderoffset + 23) |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function Constructor() |
local frame = CreateFrame("Frame",nil,UIParent) |
local self = {} |
self.type = Type |
self.num = AceGUI:GetNextWidgetNum(Type) |
self.localstatus = {} |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetTitle = SetTitle |
self.CreateTab = CreateTab |
self.SelectTab = SelectTab |
self.BuildTabs = BuildTabs |
self.SetStatusTable = SetStatusTable |
self.SetTabs = SetTabs |
self.frame = frame |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
frame.obj = self |
frame:SetHeight(100) |
frame:SetWidth(100) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") |
titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",14,0) |
titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-14,0) |
titletext:SetJustifyH("LEFT") |
titletext:SetHeight(18) |
self.titletext = titletext |
local border = CreateFrame("Frame",nil,frame) |
self.border = border |
self.borderoffset = 27 |
border:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-27) |
border:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-3,3) |
border:SetBackdrop(PaneBackdrop) |
border:SetBackdropColor(0.1,0.1,0.1,0.5) |
border:SetBackdropBorderColor(0.4,0.4,0.4) |
self.tabs = {} |
--Container Support |
local content = CreateFrame("Frame",nil,border) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",border,"TOPLEFT",10,-10) |
content:SetPoint("BOTTOMRIGHT",border,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Keybinding -- |
-------------------------- |
do |
local Type = "Keybinding" |
local Version = 11 |
local ControlBackdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 3, right = 3, top = 3, bottom = 3 } |
} |
local function Control_OnEnter(this) |
this.obj:Fire("OnEnter") |
end |
local function Control_OnLeave(this) |
this.obj:Fire("OnLeave") |
end |
local function keybindingMsgFixWidth(this) |
this:SetWidth(this.msg:GetWidth()+10) |
this:SetScript("OnUpdate",nil) |
end |
local function Keybinding_OnClick(this, button) |
if button == "LeftButton" or button == "RightButton" then |
local self = this.obj |
if self.waitingForKey then |
this:EnableKeyboard(false) |
self.msgframe:Hide() |
this:UnlockHighlight() |
self.waitingForKey = nil |
else |
this:EnableKeyboard(true) |
self.msgframe:Show() |
this:LockHighlight() |
self.waitingForKey = true |
end |
end |
AceGUI:ClearFocus() |
end |
local ignoreKeys = nil |
local function Keybinding_OnKeyDown(this, key) |
local self = this.obj |
if self.waitingForKey then |
local keyPressed = key |
if keyPressed == "ESCAPE" then |
keyPressed = "" |
else |
if not ignoreKeys then |
ignoreKeys = { |
["BUTTON1"] = true, ["BUTTON2"] = true, |
["UNKNOWN"] = true, |
["LSHIFT"] = true, ["LCTRL"] = true, ["LALT"] = true, |
["RSHIFT"] = true, ["RCTRL"] = true, ["RALT"] = true, |
} |
end |
if ignoreKeys[keyPressed] then return end |
if IsShiftKeyDown() then |
keyPressed = "SHIFT-"..keyPressed |
end |
if IsControlKeyDown() then |
keyPressed = "CTRL-"..keyPressed |
end |
if IsAltKeyDown() then |
keyPressed = "ALT-"..keyPressed |
end |
end |
this:EnableKeyboard(false) |
self.msgframe:Hide() |
this:UnlockHighlight() |
self.waitingForKey = nil |
if not self.disabled then |
self:SetKey(keyPressed) |
self:Fire("OnKeyChanged",keyPressed) |
end |
end |
end |
local function Keybinding_OnMouseDown(this, button) |
if button == "LeftButton" or button == "RightButton" then |
return |
elseif button == "MiddleButton" then |
button = "BUTTON3" |
elseif button == "Button4" then |
button = "BUTTON4" |
elseif button == "Button5" then |
button = "BUTTON5" |
end |
Keybinding_OnKeyDown(this, button) |
end |
local function OnAcquire(self) |
self:SetLabel("") |
self:SetKey("") |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.waitingForKey = nil |
self.msgframe:Hide() |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.button:Disable() |
self.label:SetTextColor(0.5,0.5,0.5) |
else |
self.button:Enable() |
self.label:SetTextColor(1,1,1) |
end |
end |
local function SetKey(self, key) |
if (key or "") == "" then |
self.button:SetText(NOT_BOUND) |
self.button:SetNormalFontObject("GameFontNormal") |
else |
self.button:SetText(key) |
self.button:SetNormalFontObject("GameFontHighlight") |
end |
end |
local function SetLabel(self, label) |
self.label:SetText(label or "") |
if (label or "") == "" then |
self.alignoffset = nil |
self:SetHeight(24) |
else |
self.alignoffset = 30 |
self:SetHeight(44) |
end |
end |
local function Constructor() |
local num = AceGUI:GetNextWidgetNum(Type) |
local frame = CreateFrame("Frame",nil,UIParent) |
local button = CreateFrame("Button","AceGUI-3.0 KeybindingButton"..num,frame,"UIPanelButtonTemplate2") |
local self = {} |
self.type = Type |
self.num = num |
local text = button:GetFontString() |
text:SetPoint("LEFT",button,"LEFT",7,0) |
text:SetPoint("RIGHT",button,"RIGHT",-7,0) |
button:SetScript("OnClick",Keybinding_OnClick) |
button:SetScript("OnKeyDown",Keybinding_OnKeyDown) |
button:SetScript("OnEnter",Control_OnEnter) |
button:SetScript("OnLeave",Control_OnLeave) |
button:SetScript("OnMouseDown",Keybinding_OnMouseDown) |
button:RegisterForClicks("AnyDown") |
button:EnableMouse() |
button:SetHeight(24) |
button:SetWidth(200) |
button:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT",0,0) |
button:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) |
frame:SetWidth(200) |
frame:SetHeight(44) |
self.alignoffset = 30 |
self.button = button |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) |
label:SetJustifyH("CENTER") |
label:SetHeight(18) |
self.label = label |
local msgframe = CreateFrame("Frame",nil,UIParent) |
msgframe:SetHeight(30) |
msgframe:SetBackdrop(ControlBackdrop) |
msgframe:SetBackdropColor(0,0,0) |
msgframe:SetFrameStrata("FULLSCREEN_DIALOG") |
msgframe:SetFrameLevel(1000) |
self.msgframe = msgframe |
local msg = msgframe:CreateFontString(nil,"OVERLAY","GameFontNormal") |
msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel") |
msgframe.msg = msg |
msg:SetPoint("TOPLEFT",msgframe,"TOPLEFT",5,-5) |
msgframe:SetScript("OnUpdate", keybindingMsgFixWidth) |
msgframe:SetPoint("BOTTOM",button,"TOP",0,0) |
msgframe:Hide() |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetLabel = SetLabel |
self.SetDisabled = SetDisabled |
self.SetKey = SetKey |
self.frame = frame |
frame.obj = self |
button.obj = self |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Check Box -- |
-------------------------- |
--[[ |
Events : |
OnValueChanged |
]] |
do |
local Type = "CheckBox" |
local Version = 4 |
local function OnAcquire(self) |
self:SetValue(false) |
self.tristate = nil |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self.check:Hide() |
self.highlight:Hide() |
self.down = nil |
self.checked = nil |
self:SetType() |
self:SetDisabled(false) |
end |
local function CheckBox_OnEnter(this) |
local self = this.obj |
if not self.disabled then |
self.highlight:Show() |
end |
self:Fire("OnEnter") |
end |
local function CheckBox_OnLeave(this) |
local self = this.obj |
if not self.down then |
self.highlight:Hide() |
end |
self:Fire("OnLeave") |
end |
local function CheckBox_OnMouseUp(this) |
local self = this.obj |
if not self.disabled then |
self:ToggleChecked() |
self:Fire("OnValueChanged",self.checked) |
self.text:SetPoint("LEFT",self.check,"RIGHT",0,0) |
end |
self.down = nil |
end |
local function CheckBox_OnMouseDown(this) |
local self = this.obj |
if not self.disabled then |
self.text:SetPoint("LEFT",self.check,"RIGHT",1,-1) |
self.down = true |
end |
AceGUI:ClearFocus() |
end |
local function SetDisabled(self,disabled) |
self.disabled = disabled |
if disabled then |
self.text:SetTextColor(0.5,0.5,0.5) |
SetDesaturation(self.check, true) |
else |
self.text:SetTextColor(1,1,1) |
if self.tristate and self.checked == nil then |
SetDesaturation(self.check, true) |
else |
SetDesaturation(self.check, false) |
end |
end |
end |
local function SetValue(self,value) |
local check = self.check |
self.checked = value |
if value then |
SetDesaturation(self.check, false) |
check:SetWidth(24) |
check:SetHeight(24) |
self.check:Show() |
else |
--Nil is the unknown tristate value |
if self.tristate and value == nil then |
SetDesaturation(self.check, true) |
check:SetWidth(20) |
check:SetHeight(20) |
self.check:Show() |
else |
SetDesaturation(self.check, false) |
check:SetWidth(24) |
check:SetHeight(24) |
self.check:Hide() |
end |
end |
end |
local function SetTriState(self, enabled) |
self.tristate = enabled |
self:SetValue(self:GetValue()) |
end |
local function GetValue(self) |
return self.checked |
end |
local function SetType(self, type) |
local checkbg = self.checkbg |
local check = self.check |
local highlight = self.highlight |
if type == "radio" then |
checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton") |
checkbg:SetTexCoord(0,0.25,0,1) |
check:SetTexture("Interface\\Buttons\\UI-RadioButton") |
check:SetTexCoord(0.5,0.75,0,1) |
check:SetBlendMode("ADD") |
highlight:SetTexture("Interface\\Buttons\\UI-RadioButton") |
highlight:SetTexCoord(0.5,0.75,0,1) |
else |
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") |
checkbg:SetTexCoord(0,1,0,1) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
check:SetTexCoord(0,1,0,1) |
check:SetBlendMode("BLEND") |
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight") |
highlight:SetTexCoord(0,1,0,1) |
end |
end |
local function ToggleChecked(self) |
local value = self:GetValue() |
if self.tristate then |
--cycle in true, nil, false order |
if value then |
self:SetValue(nil) |
elseif value == nil then |
self:SetValue(false) |
else |
self:SetValue(true) |
end |
else |
self:SetValue(not self:GetValue()) |
end |
end |
local function SetLabel(self, label) |
self.text:SetText(label) |
end |
local function Constructor() |
local frame = CreateFrame("Button",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetValue = SetValue |
self.GetValue = GetValue |
self.SetDisabled = SetDisabled |
self.SetType = SetType |
self.ToggleChecked = ToggleChecked |
self.SetLabel = SetLabel |
self.SetTriState = SetTriState |
self.frame = frame |
frame.obj = self |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight") |
self.text = text |
frame:SetScript("OnEnter",CheckBox_OnEnter) |
frame:SetScript("OnLeave",CheckBox_OnLeave) |
frame:SetScript("OnMouseUp",CheckBox_OnMouseUp) |
frame:SetScript("OnMouseDown",CheckBox_OnMouseDown) |
frame:EnableMouse() |
local checkbg = frame:CreateTexture(nil,"ARTWORK") |
self.checkbg = checkbg |
checkbg:SetWidth(24) |
checkbg:SetHeight(24) |
checkbg:SetPoint("LEFT",frame,"LEFT",0,0) |
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") |
local check = frame:CreateTexture(nil,"OVERLAY") |
self.check = check |
check:SetWidth(24) |
check:SetHeight(24) |
check:SetPoint("CENTER",checkbg,"CENTER",0,0) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
local highlight = frame:CreateTexture(nil, "BACKGROUND") |
self.highlight = highlight |
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight") |
highlight:SetBlendMode("ADD") |
highlight:SetAllPoints(checkbg) |
highlight:Hide() |
text:SetJustifyH("LEFT") |
frame:SetHeight(24) |
frame:SetWidth(200) |
text:SetHeight(18) |
text:SetPoint("LEFT",check,"RIGHT",0,0) |
text:SetPoint("RIGHT",frame,"RIGHT",0,0) |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
-------------------------- |
-- Label -- |
-------------------------- |
do |
local Type = "Icon" |
local Version = 4 |
local function OnAcquire(self) |
self:SetText("") |
self:SetImage(nil) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetText(self, text) |
self.label:SetText(text or "") |
end |
local function SetImage(self, path, ...) |
local image = self.image |
image:SetTexture(path) |
if image:GetTexture() then |
self.imageshown = true |
local n = select('#', ...) |
if n == 4 or n == 8 then |
image:SetTexCoord(...) |
end |
else |
self.imageshown = nil |
end |
end |
local function OnClick(this) |
this.obj:Fire("OnClick") |
AceGUI:ClearFocus() |
end |
local function OnEnter(this) |
this.obj.highlight:Show() |
end |
local function OnLeave(this) |
this.obj.highlight:Hide() |
end |
local function Constructor() |
local frame = CreateFrame("Button",nil,UIParent) |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.SetText = SetText |
self.frame = frame |
self.SetImage = SetImage |
frame.obj = self |
frame:SetHeight(110) |
frame:SetWidth(110) |
frame:EnableMouse(true) |
frame:SetScript("OnClick", OnClick) |
frame:SetScript("OnLeave", OnLeave) |
frame:SetScript("OnEnter", OnEnter) |
local label = frame:CreateFontString(nil,"BACKGROUND","GameFontHighlight") |
label:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,10) |
label:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,10) |
label:SetJustifyH("CENTER") |
label:SetJustifyV("TOP") |
label:SetHeight(18) |
self.label = label |
local image = frame:CreateTexture(nil,"BACKGROUND") |
self.image = image |
image:SetWidth(64) |
image:SetHeight(64) |
image:SetPoint("TOP",frame,"TOP",0,-10) |
local highlight = frame:CreateTexture(nil,"OVERLAY") |
self.highlight = highlight |
highlight:SetAllPoints(image) |
highlight:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") |
highlight:SetTexCoord(0,1,0.23,0.77) |
highlight:SetBlendMode("ADD") |
highlight:Hide() |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
local AceGUI = LibStub("AceGUI-3.0") |
------------- |
-- Widgets -- |
------------- |
--[[ |
Widgets must provide the following functions |
Acquire() - Called when the object is aquired, should set everything to a default hidden state |
Release() - Called when the object is Released, should remove any anchors and hide the Widget |
And the following members |
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes |
type - the type of the object, same as the name given to :RegisterWidget() |
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet |
It will be cleared automatically when a widget is released |
Placing values directly into a widget object should be avoided |
If the Widget can act as a container for other Widgets the following |
content - frame or derivitive that children will be anchored to |
The Widget can supply the following Optional Members |
]] |
---------------------------------- |
-- Blizzard Options Group -- |
---------------------------------- |
--[[ |
Group Designed to be added to the bliz interface options panel |
]] |
do |
local Type = "BlizOptionsGroup" |
local Version = 6 |
local function OnAcquire(self) |
end |
local function OnRelease(self) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
self:SetName() |
end |
local function okay(this) |
this.obj:Fire("okay") |
end |
local function cancel(this) |
this.obj:Fire("cancel") |
end |
local function defaults(this) |
this.obj:Fire("defaults") |
end |
local function SetName(self, name, parent) |
self.frame.name = name |
self.frame.parent = parent |
end |
local function OnShow(this) |
this.obj:Fire("OnShow") |
end |
local function OnHide(this) |
this.obj:Fire("OnHide") |
end |
local function OnWidthSet(self, width) |
local content = self.content |
local contentwidth = width - 63 |
if contentwidth < 0 then |
contentwidth = 0 |
end |
content:SetWidth(contentwidth) |
content.width = contentwidth |
end |
local function OnHeightSet(self, height) |
local content = self.content |
local contentheight = height - 26 |
if contentheight < 0 then |
contentheight = 0 |
end |
content:SetHeight(contentheight) |
content.height = contentheight |
end |
local function SetTitle(self, title) |
local content = self.content |
content:ClearAllPoints() |
if not title or title == "" then |
content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",15,-10) |
self.label:SetText("") |
else |
content:SetPoint("TOPLEFT",self.frame,"TOPLEFT",15,-40) |
self.label:SetText(title) |
end |
content:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-10,10) |
end |
local function Constructor() |
local frame = CreateFrame("Frame") |
local self = {} |
self.type = Type |
self.OnRelease = OnRelease |
self.OnAcquire = OnAcquire |
self.frame = frame |
self.SetName = SetName |
self.OnWidthSet = OnWidthSet |
self.OnHeightSet = OnHeightSet |
self.SetTitle = SetTitle |
frame.obj = self |
frame.okay = okay |
frame.cancel = cancel |
frame.defaults = defaults |
frame:Hide() |
frame:SetScript("OnHide",OnHide) |
frame:SetScript("OnShow",OnShow) |
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") |
self.label = label |
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 15, -15) |
label:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 10, -45) |
label:SetJustifyH("LEFT") |
label:SetJustifyV("TOP") |
--Container Support |
local content = CreateFrame("Frame",nil,frame) |
self.content = content |
content.obj = self |
content:SetPoint("TOPLEFT",frame,"TOPLEFT",15,-10) |
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-10,10) |
AceGUI:RegisterAsContainer(self) |
return self |
end |
AceGUI:RegisterWidgetType(Type,Constructor,Version) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConfigCmd-3.0.lua"/> |
</Ui> |
--[[ |
AceConfigCmd-3.0 |
Handles commandline optionstable access |
REQUIRES: AceConsole-3.0 for command registration (loaded on demand) |
]] |
-- TODO: handle disabled / hidden |
-- TODO: implement handlers for all types |
-- TODO: plugin args |
local MAJOR, MINOR = "AceConfigCmd-3.0", 6 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.commands = lib.commands or {} |
local commands = lib.commands |
local cfgreg = LibStub("AceConfigRegistry-3.0") |
local AceConsole -- LoD |
local AceConsoleName = "AceConsole-3.0" |
local L = setmetatable({}, { -- TODO: replace with proper locale |
__index = function(self,k) return k end |
}) |
local function print(msg) |
(SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME):AddMessage(msg) |
end |
-- pickfirstset() - picks the first non-nil value and returns it |
local function pickfirstset(...) |
for i=1,select("#",...) do |
if select(i,...)~=nil then |
return select(i,...) |
end |
end |
end |
-- err() - produce real error() regarding malformed options tables etc |
local function err(info,inputpos,msg ) |
local cmdstr=" "..strsub(info.input, 1, inputpos-1) |
error(MAJOR..": /" ..info[0] ..cmdstr ..": "..(msg or "malformed options table"), 2) |
end |
-- usererr() - produce chatframe message regarding bad slash syntax etc |
local function usererr(info,inputpos,msg ) |
local cmdstr=strsub(info.input, 1, inputpos-1); |
print("/" ..info[0] .. " "..cmdstr ..": "..(msg or "malformed options table")) |
end |
-- callmethod() - call a given named method (e.g. "get", "set") with given arguments |
local function callmethod(info, inputpos, tab, methodtype, ...) |
local method = info[methodtype] |
if not method then |
err(info, inputpos, "'"..methodtype.."': not set") |
end |
info.arg = tab.arg |
info.option = tab |
info.type = tab.type |
if type(method)=="function" then |
return method(info, ...) |
elseif type(method)=="string" then |
if type(info.handler[method])~="function" then |
err(info, inputpos, "'"..methodtype.."': '"..method.."' is not a member function of "..tostring(info.handler)) |
end |
return info.handler[method](info.handler, info, ...) |
else |
assert(false) -- type should have already been checked on read |
end |
end |
-- callfunction() - call a given named function (e.g. "name", "desc") with given arguments |
local function callfunction(info, tab, methodtype, ...) |
local method = tab[methodtype] |
info.arg = tab.arg |
info.option = tab |
info.type = tab.type |
if type(method)=="function" then |
return method(info, ...) |
else |
assert(false) -- type should have already been checked on read |
end |
end |
-- do_final() - do the final step (set/execute) along with validation and confirmation |
local function do_final(info, inputpos, tab, methodtype, ...) |
if info.validate then |
local res = callmethod(info,inputpos,tab,"validate",...) |
if type(res)=="string" then |
usererr(info, inputpos, "'"..strsub(info.input, inputpos).."' - "..res) |
return |
end |
end |
-- console ignores .confirm |
callmethod(info,inputpos,tab,methodtype, ...) |
end |
-- getparam() - used by handle() to retreive and store "handler", "get", "set", etc |
local function getparam(info, inputpos, tab, depth, paramname, types, errormsg) |
local old,oldat = info[paramname], info[paramname.."_at"] |
local val=tab[paramname] |
if val~=nil then |
if val==false then |
val=nil |
elseif not types[type(val)] then |
err(info, inputpos, "'" .. paramname.. "' - "..errormsg) |
end |
info[paramname] = val |
info[paramname.."_at"] = depth |
end |
return old,oldat |
end |
-- iterateargs(tab) - custom iterator that iterates both t.args and t.plugins.* |
local dummytable={} |
local function iterateargs(tab) |
if not tab.plugins then |
return pairs(tab.args) |
end |
local argtabkey,argtab=next(tab.plugins) |
local v |
return function(_, k) |
while argtab do |
k,v = next(argtab, k) |
if k then return k,v end |
if argtab==tab.args then |
argtab=nil |
else |
argtabkey,argtab = next(tab.plugins, argtabkey) |
if not argtabkey then |
argtab=tab.args |
end |
end |
end |
end |
end |
local function showhelp(info, inputpos, tab, noHead) |
if not noHead then |
print("|cff33ff99"..info.appName.."|r: Arguments to |cffffff78/"..info[0].."|r "..strsub(info.input,1,inputpos-1)..":") |
end |
local sortTbl = {} -- [1..n]=name |
local refTbl = {} -- [name]=tableref |
for k,v in iterateargs(tab) do |
if not refTbl[k] then -- a plugin overriding something in .args |
table.insert(sortTbl, k) |
refTbl[k] = v |
end |
end |
table.sort(sortTbl, function(one, two) |
local o1 = refTbl[one].order or 100 |
local o2 = refTbl[two].order or 100 |
if type(o1) == "function" or type(o1) == "string" then |
info.order = o1 |
info[#info+1] = one |
o1 = callmethod(info, inputpos, refTbl[one], "order") |
info[#info] = nil |
info.order = nil |
end |
if type(o2) == "function" or type(o1) == "string" then |
info.order = o2 |
info[#info+1] = two |
o2 = callmethod(info, inputpos, refTbl[two], "order") |
info[#info] = nil |
info.order = nil |
end |
if o1<0 and o2<0 then return o1<o2 end |
if o2<0 then return true end |
if o1<0 then return false end |
if o1==o2 then return tostring(one)<tostring(two) end -- compare names |
return o1<o2 |
end) |
for _,k in ipairs(sortTbl) do |
local v = refTbl[k] |
if not pickfirstset(v.cmdHidden, v.hidden, false) then |
-- recursively show all inline groups |
local name, desc = v.name, v.desc |
if type(name) == "function" then |
name = callfunction(info, v, 'name') |
end |
if type(desc) == "function" then |
desc = callfunction(info, v, 'desc') |
end |
if v.type == "group" and pickfirstset(v.cmdInline, v.inline, false) then |
print(" "..(desc or name)..":") |
showhelp(info, inputpos, v, true) |
else |
print(" |cffffff78"..k.."|r - "..(desc or name or "")) |
end |
end |
end |
end |
local function keybindingValidateFunc(text) |
if text == nil or text == "NONE" then |
return nil |
end |
text = text:upper() |
local shift, ctrl, alt |
local modifier |
while true do |
if text == "-" then |
break |
end |
modifier, text = strsplit('-', text, 2) |
if text then |
if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then |
return false |
end |
if modifier == "SHIFT" then |
if shift then |
return false |
end |
shift = true |
end |
if modifier == "CTRL" then |
if ctrl then |
return false |
end |
ctrl = true |
end |
if modifier == "ALT" then |
if alt then |
return false |
end |
alt = true |
end |
else |
text = modifier |
break |
end |
end |
if text == "" then |
return false |
end |
if not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] then |
return false |
end |
local s = text |
if shift then |
s = "SHIFT-" .. s |
end |
if ctrl then |
s = "CTRL-" .. s |
end |
if alt then |
s = "ALT-" .. s |
end |
return s |
end |
-- constants used by getparam() calls below |
local handlertypes = {["table"]=true} |
local handlermsg = "expected a table" |
local functypes = {["function"]=true, ["string"]=true} |
local funcmsg = "expected function or member name" |
-- handle() - selfrecursing function that processes input->optiontable |
-- - depth - starts at 0 |
-- - retfalse - return false rather than produce error if a match is not found (used by inlined groups) |
local function handle(info, inputpos, tab, depth, retfalse) |
if not(type(tab)=="table" and type(tab.type)=="string") then err(info,inputpos) end |
------------------------------------------------------------------- |
-- Grab hold of handler,set,get,func,etc if set (and remember old ones) |
-- Note that we do NOT validate if method names are correct at this stage, |
-- the handler may change before they're actually used! |
local oldhandler,oldhandler_at = getparam(info,inputpos,tab,depth,"handler",handlertypes,handlermsg) |
local oldset,oldset_at = getparam(info,inputpos,tab,depth,"set",functypes,funcmsg) |
local oldget,oldget_at = getparam(info,inputpos,tab,depth,"get",functypes,funcmsg) |
local oldfunc,oldfunc_at = getparam(info,inputpos,tab,depth,"func",functypes,funcmsg) |
local oldvalidate,oldvalidate_at = getparam(info,inputpos,tab,depth,"validate",functypes,funcmsg) |
--local oldconfirm,oldconfirm_at = getparam(info,inputpos,tab,depth,"confirm",functypes,funcmsg) |
------------------------------------------------------------------- |
-- Act according to .type of this table |
if tab.type=="group" then |
------------ group -------------------------------------------- |
if type(tab.args)~="table" then err(info, inputpos) end |
if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end |
-- grab next arg from input |
local _,nextpos,arg = string.find(info.input, " *([^ ]+) *", inputpos) |
if not arg then |
showhelp(info, inputpos, tab) |
return |
end |
nextpos=nextpos+1 |
-- loop .args and try to find a key with a matching name |
for k,v in iterateargs(tab) do |
if not(type(k)=="string" and type(v)=="table" and type(v.type)=="string") then err(info,inputpos, "options table child '"..tostring(k).."' is malformed") end |
-- is this child an inline group? if so, traverse into it |
if v.type=="group" and pickfirstset(v.cmdInline, v.inline, false) then |
info[depth+1] = k |
if handle(info, inputpos, v, depth+1, true)==false then |
info[depth+1] = nil |
-- wasn't found in there, but that's ok, we just keep looking down here |
else |
return -- done, name was found in inline group |
end |
-- matching name and not a inline group |
elseif strlower(arg)==strlower(k) then |
info[depth+1] = k |
return handle(info,nextpos,v,depth+1) |
end |
end |
-- no match |
if retfalse then |
-- restore old infotable members and return false to indicate failure |
info.handler,info.handler_at = oldhandler,oldhandler_at |
info.set,info.set_at = oldset,oldset_at |
info.get,info.get_at = oldget,oldget_at |
info.func,info.func_at = oldfunc,oldfunc_at |
info.validate,info.validate_at = oldvalidate,oldvalidate_at |
--info.confirm,info.confirm_at = oldconfirm,oldconfirm_at |
return false |
end |
-- couldn't find the command, display error |
usererr(info, inputpos, "'"..arg.."' - " .. L["unknown argument"]) |
return |
end |
local str = strsub(info.input,inputpos); |
if tab.type=="execute" then |
------------ execute -------------------------------------------- |
do_final(info, inputpos, tab, "func") |
elseif tab.type=="input" then |
------------ input -------------------------------------------- |
local res = true |
if tab.pattern then |
if not(type(tab.pattern)=="string") then err(info, inputpos, "'pattern' - expected a string") end |
if not strmatch(str, tab.pattern) then |
usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"]) |
return |
end |
end |
do_final(info, inputpos, tab, "set", str) |
elseif tab.type=="toggle" then |
------------ toggle -------------------------------------------- |
local b |
local str = strtrim(strlower(str)) |
if str=="" then |
b = callmethod(info, inputpos, tab, "get") |
if tab.tristate then |
--cycle in true, nil, false order |
if b then |
b = nil |
elseif b == nil then |
b = false |
else |
b = true |
end |
else |
b = not b |
end |
elseif str==L["on"] then |
b = true |
elseif str==L["off"] then |
b = false |
elseif tab.tristate and str==L["default"] then |
b = nil |
else |
if tab.tristate then |
usererr(info, inputpos, format(L["'%s' - expected 'on', 'off' or 'default', or no argument to toggle."], str)) |
else |
usererr(info, inputpos, format(L["'%s' - expected 'on' or 'off', or no argument to toggle."], str)) |
end |
return |
end |
do_final(info, inputpos, tab, "set", b) |
elseif tab.type=="range" then |
------------ range -------------------------------------------- |
local val = tonumber(str) |
if not val then |
usererr(info, inputpos, "'"..str.."' - "..L["expected number"]) |
return |
end |
if type(info.step)=="number" then |
val = val- (val % info.step) |
end |
if type(info.min)=="number" and val<info.min then |
usererr(info, inputpos, val.." - "..format(L["must be equal to or higher than %s"], tostring(info.min)) ) |
return |
end |
if type(info.max)=="number" and val>info.max then |
usererr(info, inputpos, val.." - "..format(L["must be equal to or lower than %s"], tostring(info.max)) ) |
return |
end |
do_final(info, inputpos, tab, "set", val) |
elseif tab.type=="select" then |
------------ select ------------------------------------ |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current selection and possible values |
return |
end |
local values = tab.values |
if type(values) == "function" or type(values) == "string" then |
info.values = values |
values = callmethod(info, inputpos, tab, "values") |
info.values = nil |
end |
local ok |
for k,v in pairs(values) do |
if strlower(k)==str then |
str = k -- overwrite with key (in case of case mismatches) |
ok = true |
break |
end |
end |
if not ok then |
usererr(info, inputpos, "'"..str.."' - "..L["unknown selection"]) |
return |
end |
do_final(info, inputpos, tab, "set", str) |
elseif tab.type=="multiselect" then |
------------ multiselect ------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current values |
return |
end |
local values = tab.values |
if type(values) == "function" or type(values) == "string" then |
info.values = values |
values = callmethod(info, inputpos, tab, "values") |
info.values = nil |
end |
--build a table of the selections, checking that they exist |
--parse for =on =off =default in the process |
--table will be key = true for options that should toggle, key = [on|off|default] for options to be set |
local sels = {} |
for v in string.gmatch(str, "[^ ]+") do |
--parse option=on etc |
local opt, val = string.match(v,'(.+)=(.+)') |
--get option if toggling |
if not opt then |
opt = v |
end |
--check that the opt is valid |
local ok |
for k,v in pairs(values) do |
if strlower(k)==opt then |
opt = k -- overwrite with key (in case of case mismatches) |
ok = true |
break |
end |
end |
if not ok then |
usererr(info, inputpos, "'"..opt.."' - "..L["unknown selection"]) |
return |
end |
--check that if val was supplied it is valid |
if val then |
if val == L["on"] or val == L["off"] or (tab.tristate and val == L["default"]) then |
--val is valid insert it |
sels[opt] = val |
else |
if tab.tristate then |
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on', 'off' or 'default', or no argument to toggle."], v, val)) |
else |
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on' or 'off', or no argument to toggle."], v, val)) |
end |
return |
end |
else |
-- no val supplied, toggle |
sels[opt] = true |
end |
end |
for opt, val in pairs(sels) do |
local newval |
if (val == true) then |
--toggle the option |
local b = callmethod(info, inputpos, tab, "get", opt) |
if tab.tristate then |
--cycle in true, nil, false order |
if b then |
b = nil |
elseif b == nil then |
b = false |
else |
b = true |
end |
else |
b = not b |
end |
newval = b |
else |
--set the option as specified |
if val==L["on"] then |
newval = true |
elseif val==L["off"] then |
newval = false |
elseif val==L["default"] then |
newval = nil |
end |
end |
do_final(info, inputpos, tab, "set", opt, newval) |
end |
elseif tab.type=="color" then |
------------ color -------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current value |
return |
end |
local r, g, b, a |
if tab.hasAlpha then |
if str:len() == 8 and str:find("^%x*$") then |
--parse a hex string |
r,g,b,a = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255, tonumber(str:sub(7, 8), 16) / 255 |
else |
--parse seperate values |
r,g,b,a = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+) ([%d%.]+)$") |
r,g,b,a = tonumber(r), tonumber(g), tonumber(b), tonumber(a) |
end |
if not (r and g and b and a) then |
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBBAA' or 'r g b a'."], str)) |
return |
end |
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 and a >= 0.0 and a <= 1.0 then |
--values are valid |
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 and a >= 0 and a <= 255 then |
--values are valid 0..255, convert to 0..1 |
r = r / 255 |
g = g / 255 |
b = b / 255 |
a = a / 255 |
else |
--values are invalid |
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0..1 or 0..255."], str)) |
end |
else |
a = 1.0 |
if str:len() == 6 and str:find("^%x*$") then |
--parse a hex string |
r,g,b = tonumber(str:sub(1, 2), 16) / 255, tonumber(str:sub(3, 4), 16) / 255, tonumber(str:sub(5, 6), 16) / 255 |
else |
--parse seperate values |
r,g,b = str:match("^([%d%.]+) ([%d%.]+) ([%d%.]+)$") |
r,g,b = tonumber(r), tonumber(g), tonumber(b) |
end |
if not (r and g and b) then |
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBB' or 'r g b'."], str)) |
return |
end |
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 then |
--values are valid |
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 then |
--values are valid 0..255, convert to 0..1 |
r = r / 255 |
g = g / 255 |
b = b / 255 |
else |
--values are invalid |
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0-1 or 0-255."], str)) |
end |
end |
do_final(info, inputpos, tab, "set", r,g,b,a) |
elseif tab.type=="keybinding" then |
------------ keybinding -------------------------------------------- |
local str = strtrim(strlower(str)) |
if str == "" then |
--TODO: Show current value |
return |
end |
local value = keybindingValidateFunc(str:upper()) |
if value == false then |
usererr(info, inputpos, format(L["'%s' - Invalid Keybinding."], str)) |
return |
end |
do_final(info, inputpos, tab, "set", value) |
elseif tab.type=="description" then |
------------ description -------------------- |
-- ignore description, GUI config only |
else |
err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'") |
end |
end |
----------------------------------------------------------------------- |
-- HandleCommand(slashcmd, appName, input) |
-- |
-- Call this from a chat command handler to parse the command input as operations on an aceoptions table |
-- |
-- slashcmd (string) - the slash command WITHOUT leading slash (only used for error output) |
-- appName (string) - the application name as given to AceConfigRegistry:RegisterOptionsTable() |
-- input (string) -- the commandline input (as given by the WoW handler, i.e. without the command itself) |
function lib:HandleCommand(slashcmd, appName, input) |
local optgetter = cfgreg:GetOptionsTable(appName) |
if not optgetter then |
error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'appName' - no options table "]]..tostring(appName)..[[" has been registered]], 2) |
end |
local options = assert( optgetter("cmd", MAJOR) ) |
local info = { -- Don't try to recycle this, it gets handed off to callbacks and whatnot |
[0] = slashcmd, |
appName = appName, |
options = options, |
input = input, |
self = self, |
handler = self, |
uiType = "cmd", |
uiName = MAJOR, |
} |
handle(info, 1, options, 0) -- (info, inputpos, table, depth) |
end |
----------------------------------------------------------------------- |
-- CreateChatCommand(slashcmd, appName) |
-- |
-- Utility function to create a slash command handler. |
-- Also registers tab completion with AceTab |
-- |
-- slashcmd (string) - the slash command WITHOUT leading slash (only used for error output) |
-- appName (string) - the application name as given to AceConfigRegistry:RegisterOptionsTable() |
function lib:CreateChatCommand(slashcmd, appName) |
if not AceConsole then |
AceConsole = LibStub(AceConsoleName) |
end |
if AceConsole.RegisterChatCommand(self, slashcmd, function(input) |
lib.HandleCommand(self, slashcmd, appName, input) -- upgradable |
end, |
true) then -- succesfully registered so lets get the command -> app table in |
commands[slashcmd] = appName |
end |
end |
-- GetChatCommandOptions(slashcmd) |
-- |
-- Utility function that returns the options table that belongs to a slashcommand |
-- mainly used by AceTab |
function lib:GetChatCommandOptions(slashcmd) |
return commands[slashcmd] |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConfigRegistry-3.0.lua"/> |
</Ui> |
--[[ |
AceConfigRegistry-3.0: |
Handle central registration of options tables in use by addons and modules. Do nothing else. |
Options tables can be registered as raw tables, or as function refs that return a table. |
These functions receive two arguments: "uiType" and "uiName". |
- Valid "uiTypes": "cmd", "dropdown", "dialog". This is verified by the library at call time. |
- The "uiName" field is expected to contain the full name of the calling addon, including version, e.g. "FooBar-1.0". This is verified by the library at call time. |
:IterateOptionsTables() and :GetOptionsTable() always return a function reference that the requesting config handling addon must call with the above arguments. |
]] |
local MAJOR, MINOR = "AceConfigRegistry-3.0", 6 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.tables = lib.tables or {} |
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") |
if not lib.callbacks then |
lib.callbacks = CallbackHandler:New(lib) |
end |
----------------------------------------------------------------------- |
-- Validating options table consistency: |
lib.validated = { |
-- list of options table names ran through :ValidateOptionsTable automatically. |
-- CLEARED ON PURPOSE, since newer versions may have newer validators |
cmd = {}, |
dropdown = {}, |
dialog = {}, |
} |
local function err(msg, errlvl, ...) |
local t = {} |
for i=select("#",...),1,-1 do |
tinsert(t, (select(i, ...))) |
end |
error(MAJOR..":ValidateOptionsTable(): "..table.concat(t,".")..msg, errlvl+2) |
end |
local isstring={["string"]=true, _="string"} |
local isstringfunc={["string"]=true,["function"]=true, _="string or funcref"} |
local istable={["table"]=true, _="table"} |
local ismethodtable={["table"]=true,["string"]=true,["function"]=true, _="methodname, funcref or table"} |
local optstring={["nil"]=true,["string"]=true, _="string"} |
local optstringfunc={["nil"]=true,["string"]=true,["function"]=true, _="string or funcref"} |
local optnumber={["nil"]=true,["number"]=true, _="number"} |
local optmethod={["nil"]=true,["string"]=true,["function"]=true, _="methodname or funcref"} |
local optmethodfalse={["nil"]=true,["string"]=true,["function"]=true,["boolean"]={[false]=true}, _="methodname, funcref or false"} |
local optmethodnumber={["nil"]=true,["string"]=true,["function"]=true,["number"]=true, _="methodname, funcref or number"} |
local optmethodtable={["nil"]=true,["string"]=true,["function"]=true,["table"]=true, _="methodname, funcref or table"} |
local optmethodbool={["nil"]=true,["string"]=true,["function"]=true,["boolean"]=true, _="methodname, funcref or boolean"} |
local opttable={["nil"]=true,["table"]=true, _="table"} |
local optbool={["nil"]=true,["boolean"]=true, _="boolean"} |
local optboolnumber={["nil"]=true,["boolean"]=true,["number"]=true, _="boolean or number"} |
local basekeys={ |
type=isstring, |
name=isstringfunc, |
desc=optstringfunc, |
order=optmethodnumber, |
validate=optmethodfalse, |
confirm=optmethodbool, |
confirmText=optstring, |
disabled=optmethodbool, |
hidden=optmethodbool, |
guiHidden=optmethodbool, |
dialogHidden=optmethodbool, |
dropdownHidden=optmethodbool, |
cmdHidden=optmethodbool, |
icon=optstringfunc, |
iconCoords=optmethodtable, |
handler=opttable, |
get=optmethodfalse, |
set=optmethodfalse, |
func=optmethodfalse, |
arg={["*"]=true}, |
width=optstring, |
} |
local typedkeys={ |
header={}, |
description={ |
image=optstringfunc, |
imageCoords=optmethodtable, |
imageHeight=optnumber, |
imageWidth=optnumber, |
}, |
group={ |
args=istable, |
plugins=opttable, |
inline=optbool, |
cmdInline=optbool, |
guiInline=optbool, |
dropdownInline=optbool, |
dialogInline=optbool, |
childGroups=optstring, |
}, |
execute={ |
-- func={ |
-- ["function"]=true, |
-- ["string"]=true, |
-- _="methodname or funcref" |
-- }, |
}, |
input={ |
pattern=optstring, |
usage=optstring, |
control=optstring, |
dialogControl=optstring, |
dropdownControl=optstring, |
multiline=optboolnumber, |
}, |
toggle={ |
tristate=optbool, |
}, |
tristate={ |
}, |
range={ |
min=optnumber, |
max=optnumber, |
step=optnumber, |
bigStep=optnumber, |
isPercent=optbool, |
}, |
select={ |
values=ismethodtable, |
style={ |
["nil"]=true, |
["string"]={dropdown=true,radio=true}, |
_="string: 'dropdown' or 'radio'" |
}, |
control=optstring, |
dialogControl=optstring, |
dropdownControl=optstring, |
}, |
multiselect={ |
values=ismethodtable, |
style=optstring, |
tristate=optbool, |
control=optstring, |
dialogControl=optstring, |
dropdownControl=optstring, |
}, |
color={ |
hasAlpha=optbool, |
}, |
keybinding={ |
-- TODO |
}, |
} |
local function validateKey(k,errlvl,...) |
errlvl=(errlvl or 0)+1 |
if type(k)~="string" then |
err("["..tostring(k).."] - key is not a string", errlvl,...) |
end |
if strfind(k, "[%c \127]") then |
err("["..tostring(k).."] - key name contained spaces (or control characters)", errlvl,...) |
end |
end |
local function validateVal(v, oktypes, errlvl,...) |
errlvl=(errlvl or 0)+1 |
local isok=oktypes[type(v)] or oktypes["*"] |
if not isok then |
err(": expected a "..oktypes._..", got '"..tostring(v).."'", errlvl,...) |
end |
if type(isok)=="table" then -- isok was a table containing specific values to be tested for! |
if not isok[v] then |
err(": did not expect "..type(v).." value '"..tostring(v).."'", errlvl,...) |
end |
end |
end |
local function validate(options,errlvl,...) |
errlvl=(errlvl or 0)+1 |
-- basic consistency |
if type(options)~="table" then |
err(": expected a table, got a "..type(options), errlvl,...) |
end |
if type(options.type)~="string" then |
err(".type: expected a string, got a "..type(options.type), errlvl,...) |
end |
-- get type and 'typedkeys' member |
local tk = typedkeys[options.type] |
if not tk then |
err(".type: unknown type '"..options.type.."'", errlvl,...) |
end |
-- make sure that all options[] are known parameters |
for k,v in pairs(options) do |
if not (tk[k] or basekeys[k]) then |
err(": unknown parameter", errlvl,tostring(k),...) |
end |
end |
-- verify that required params are there, and that everything is the right type |
for k,oktypes in pairs(basekeys) do |
validateVal(options[k], oktypes, errlvl,k,...) |
end |
for k,oktypes in pairs(tk) do |
validateVal(options[k], oktypes, errlvl,k,...) |
end |
-- extra logic for groups |
if options.type=="group" then |
for k,v in pairs(options.args) do |
validateKey(k,errlvl,"args",...) |
validate(v, errlvl,k,"args",...) |
end |
if options.plugins then |
for plugname,plugin in pairs(options.plugins) do |
if type(plugin)~="table" then |
err(": expected a table, got '"..tostring(plugin).."'", errlvl,tostring(plugname),"plugins",...) |
end |
for k,v in pairs(plugin) do |
validateKey(k,errlvl,tostring(plugname),"plugins",...) |
validate(v, errlvl,k,tostring(plugname),"plugins",...) |
end |
end |
end |
end |
end |
--------------------------------------------------------------------- |
-- :ValidateOptionsTable(options,name,errlvl) |
-- - options - the table |
-- - name - (string) name of table, used in error reports |
-- - errlvl - (optional number) error level offset, default 0 |
-- |
-- Validates basic structure and integrity of an options table |
-- Does NOT verify that get/set etc actually exist, since they can be defined at any depth |
function lib:ValidateOptionsTable(options,name,errlvl) |
errlvl=(errlvl or 0)+1 |
name = name or "Optionstable" |
if not options.name then |
options.name=name -- bit of a hack, the root level doesn't really need a .name :-/ |
end |
validate(options,errlvl,name) |
end |
------------------------------ |
-- :NotifyChange(appName) |
-- - appName - string identifying the addon |
-- |
-- Fires a ConfigTableChange callback for those listening in on it, allowing config GUIs to refresh |
------------------------------ |
function lib:NotifyChange(appName) |
if not lib.tables[appName] then return end |
lib.callbacks:Fire("ConfigTableChange", appName) |
end |
--------------------------------------------------------------------- |
-- Registering and retreiving options tables: |
-- validateGetterArgs: helper function for :GetOptionsTable (or, rather, the getter functions returned by it) |
local function validateGetterArgs(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+2 |
if uiType~="cmd" and uiType~="dropdown" and uiType~="dialog" then |
error(MAJOR..": Requesting options table: 'uiType' - invalid configuration UI type, expected 'cmd', 'dropdown' or 'dialog'", errlvl) |
end |
if not strmatch(uiName, "[A-Za-z]%-[0-9]") then -- Expecting e.g. "MyLib-1.2" |
error(MAJOR..": Requesting options table: 'uiName' - badly formatted or missing version number. Expected e.g. 'MyLib-1.2'", errlvl) |
end |
end |
--------------------------------------------------------------------- |
-- :RegisterOptionsTable(appName, options) |
-- - appName - string identifying the addon |
-- - options - table or function reference |
function lib:RegisterOptionsTable(appName, options) |
if type(options)=="table" then |
if options.type~="group" then -- quick sanity checker |
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - missing type='group' member in root group", 2) |
end |
lib.tables[appName] = function(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+1 |
validateGetterArgs(uiType, uiName, errlvl) |
if not lib.validated[uiType][appName] then |
lib:ValidateOptionsTable(options, appName, errlvl) -- upgradable |
lib.validated[uiType][appName] = true |
end |
return options |
end |
elseif type(options)=="function" then |
lib.tables[appName] = function(uiType, uiName, errlvl) |
errlvl=(errlvl or 0)+1 |
validateGetterArgs(uiType, uiName, errlvl) |
local tab = assert(options(uiType, uiName)) |
if not lib.validated[uiType][appName] then |
lib:ValidateOptionsTable(tab, appName, errlvl) -- upgradable |
lib.validated[uiType][appName] = true |
end |
return tab |
end |
else |
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - expected table or function reference", 2) |
end |
end |
--------------------------------------------------------------------- |
-- :IterateOptionsTables() |
-- |
-- Returns an iterator of ["appName"]=funcref pairs |
function lib:IterateOptionsTables() |
return pairs(lib.tables) |
end |
--------------------------------------------------------------------- |
-- :GetOptionsTable(appName) |
-- - appName - which addon to retreive the options table of |
-- Optional: |
-- - uiType - "cmd", "dropdown", "dialog" |
-- - uiName - e.g. "MyLib-1.0" |
-- |
-- If only appName is given, a function is returned which you |
-- can call with (uiType,uiName) to get the table. |
-- If uiType&uiName are given, the table is returned. |
function lib:GetOptionsTable(appName, uiType, uiName) |
local f = lib.tables[appName] |
if not f then |
return nil |
end |
if uiType then |
return f(uiType,uiName,1) -- get the table for us |
else |
return f -- return the function |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Include file="AceConfigRegistry-3.0\AceConfigRegistry-3.0.xml"/> |
<Include file="AceConfigCmd-3.0\AceConfigCmd-3.0.xml"/> |
<Include file="AceConfigDialog-3.0\AceConfigDialog-3.0.xml"/> |
<!--<Include file="AceConfigDropdown-3.0\AceConfigDropdown-3.0.xml"/>--> |
<Script file="AceConfig-3.0.lua"/> |
</Ui> |
--[[ $Id: AceConfig-3.0.lua 494 2008-02-03 13:03:56Z nevcairiel $ ]] |
--[[ |
AceConfig-3.0 |
Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole. |
Also automatically adds "config", "enable" and "disable" commands to options table as appropriate. |
]] |
local MAJOR, MINOR = "AceConfig-3.0", 2 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
local cfgreg = LibStub("AceConfigRegistry-3.0") |
local cfgcmd = LibStub("AceConfigCmd-3.0") |
local cfgdlg = LibStub("AceConfigDialog-3.0") |
--TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0") |
--------------------------------------------------------------------- |
-- :RegisterOptionsTable(appName, options, slashcmd, persist) |
-- |
-- - appName - (string) application name |
-- - options - table or function ref, see AceConfigRegistry |
-- - slashcmd - slash command (string) or table with commands, or nil to NOT create a slash command |
function lib:RegisterOptionsTable(appName, options, slashcmd) |
local ok,msg = pcall(cfgreg.RegisterOptionsTable, self, appName, options) |
if not ok then error(msg, 2) end |
if slashcmd then |
if type(slashcmd) == "table" then |
for _,cmd in pairs(slashcmd) do |
cfgcmd:CreateChatCommand(cmd, appName) |
end |
else |
cfgcmd:CreateChatCommand(slashcmd, appName) |
end |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConfigDialog-3.0.lua"/> |
</Ui> |
--[[ |
AceConfigDialog-3.0 |
]] |
local LibStub = LibStub |
local MAJOR, MINOR = "AceConfigDialog-3.0", 25 |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
lib.OpenFrames = lib.OpenFrames or {} |
lib.Status = lib.Status or {} |
lib.frame = lib.frame or CreateFrame("Frame") |
lib.frame.apps = lib.frame.apps or {} |
lib.frame.closing = lib.frame.closing or {} |
local gui = LibStub("AceGUI-3.0") |
local reg = LibStub("AceConfigRegistry-3.0") |
local select = select |
local pairs = pairs |
local type = type |
local assert = assert |
local tinsert = tinsert |
local tremove = tremove |
local error = error |
local table = table |
local unpack = unpack |
local string = string |
local next = next |
local math = math |
local _ |
--[[ |
xpcall safecall implementation |
]] |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local xpcall, eh = ... |
local method, ARGS |
local function call() return method(ARGS) end |
local function dispatch(func, ...) |
method = func |
if not method then return end |
ARGS = ... |
return xpcall(call, eh) |
end |
return dispatch |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", table.concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
Dispatchers[0] = function(func) |
return xpcall(func, errorhandler) |
end |
local function safecall(func, ...) |
return Dispatchers[select('#', ...)](func, ...) |
end |
local width_multiplier = 170 |
--[[ |
Group Types |
Tree - All Descendant Groups will all become nodes on the tree, direct child options will appear above the tree |
- Descendant Groups with inline=true and thier children will not become nodes |
Tab - Direct Child Groups will become tabs, direct child options will appear above the tab control |
- Grandchild groups will default to inline unless specified otherwise |
Select- Same as Tab but with entries in a dropdown rather than tabs |
Inline Groups |
- Will not become nodes of a select group, they will be effectivly part of thier parent group seperated by a border |
- If declared on a direct child of a root node of a select group, they will appear above the group container control |
- When a group is displayed inline, all descendants will also be inline members of the group |
]] |
-- Recycling functions |
local new, del, copy |
--newcount, delcount,createdcount,cached = 0,0,0 |
do |
local pool = setmetatable({},{__mode='k'}) |
function new() |
--newcount = newcount + 1 |
local t = next(pool) |
if t then |
pool[t] = nil |
return t |
else |
--createdcount = createdcount + 1 |
return {} |
end |
end |
function copy(t) |
local c = new() |
for k, v in pairs(t) do |
c[k] = v |
end |
return c |
end |
function del(t) |
--delcount = delcount + 1 |
for k in pairs(t) do |
t[k] = nil |
end |
pool[t] = true |
end |
-- function cached() |
-- local n = 0 |
-- for k in pairs(pool) do |
-- n = n + 1 |
-- end |
-- return n |
-- end |
end |
-- picks the first non-nil value and returns it |
local function pickfirstset(...) |
for i=1,select("#",...) do |
if select(i,...)~=nil then |
return select(i,...) |
end |
end |
end |
--gets an option from a given group, checking plugins |
local function GetSubOption(group, key) |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
if t[key] then |
return t[key] |
end |
end |
end |
return group.args[key] |
end |
--Option member type definitions, used to decide how to access it |
--Is the member Inherited from parent options |
local isInherited = { |
set = true, |
get = true, |
func = true, |
confirm = true, |
validate = true, |
disabled = true, |
hidden = true |
} |
--Does a string type mean a literal value, instead of the default of a method of the handler |
local stringIsLiteral = { |
name = true, |
desc = true, |
icon = true, |
usage = true, |
width = true, |
image = true, |
} |
--Is Never a function or method |
local allIsLiteral = { |
type = true, |
imageWidth = true, |
imageHeight = true, |
} |
--gets the value for a member that could be a function |
--function refs are called with an info arg |
--every other type is returned |
local function GetOptionsMemberValue(membername, option, options, path, appName, ...) |
--get definition for the member |
local inherits = isInherited[membername] |
--get the member of the option, traversing the tree if it can be inherited |
local member |
if inherits then |
local group = options |
if group[membername] ~= nil then |
member = group[membername] |
end |
for i = 1, #path do |
group = GetSubOption(group, path[i]) |
if group[membername] ~= nil then |
member = group[membername] |
end |
end |
else |
member = option[membername] |
end |
--check if we need to call a functon, or if we have a literal value |
if ( not allIsLiteral[membername] ) and ( type(member) == "function" or ((not stringIsLiteral[membername]) and type(member) == "string") ) then |
--We have a function to call |
local info = new() |
--traverse the options table, picking up the handler and filling the info with the path |
local handler |
local group = options |
handler = group.handler or handler |
for i = 1, #path do |
group = GetSubOption(group, path[i]) |
info[i] = path[i] |
handler = group.handler or handler |
end |
info.options = options |
info.appName = appName |
info[0] = appName |
info.arg = option.arg |
info.handler = handler |
info.option = option |
info.type = option.type |
info.uiType = 'dialog' |
info.uiName = MAJOR |
local a, b, c ,d |
--using 4 returns for the get of a color type, increase if a type needs more |
if type(member) == "function" then |
--Call the function |
a,b,c,d = member(info, ...) |
else |
--Call the method |
if handler and handler[member] then |
a,b,c,d = handler[member](handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type %s", member, membername)) |
end |
end |
del(info) |
return a,b,c,d |
else |
--The value isnt a function to call, return it |
return member |
end |
end |
--[[calls an options function that could be inherited, method name or function ref |
local function CallOptionsFunction(funcname ,option, options, path, appName, ...) |
local info = new() |
local func |
local group = options |
local handler |
--build the info table containing the path |
-- pick up functions while traversing the tree |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
for i, v in ipairs(path) do |
group = GetSubOption(group, v) |
info[i] = v |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
end |
info.options = options |
info[0] = appName |
info.arg = option.arg |
local a, b, c ,d |
if type(func) == "string" then |
if handler and handler[func] then |
a,b,c,d = handler[func](handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
a,b,c,d = func(info, ...) |
end |
del(info) |
return a,b,c,d |
end |
--]] |
--tables to hold orders and names for options being sorted, will be created with new() |
--prevents needing to call functions repeatedly while sorting |
local tempOrders |
local tempNames |
local function compareOptions(a,b) |
if not a then |
return true |
end |
if not b then |
return false |
end |
local OrderA, OrderB = tempOrders[a] or 100, tempOrders[b] or 100 |
if OrderA == OrderB then |
local NameA = (type(tempNames[a] == "string") and tempNames[a]) or "" |
local NameB = (type(tempNames[b] == "string") and tempNames[b]) or "" |
return NameA:upper() < NameB:upper() |
end |
if OrderA < 0 then |
if OrderB > 0 then |
return false |
end |
else |
if OrderB < 0 then |
return true |
end |
end |
return OrderA < OrderB |
end |
--builds 2 tables out of an options group |
-- keySort, sorted keys |
-- opts, combined options from .plugins and args |
local function BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
tempOrders = new() |
tempNames = new() |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
for k, v in pairs(t) do |
if not opts[k] then |
tinsert(keySort, k) |
opts[k] = v |
path[#path+1] = k |
tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) |
tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) |
path[#path] = nil |
end |
end |
end |
end |
for k, v in pairs(group.args) do |
if not opts[k] then |
tinsert(keySort, k) |
opts[k] = v |
path[#path+1] = k |
tempOrders[k] = GetOptionsMemberValue("order", v, options, path, appName) |
tempNames[k] = GetOptionsMemberValue("name", v, options, path, appName) |
path[#path] = nil |
end |
end |
table.sort(keySort, compareOptions) |
del(tempOrders) |
del(tempNames) |
end |
local function DelTree(tree) |
if tree.children then |
local childs = tree.children |
for i = 1, #childs do |
DelTree(childs[i]) |
del(childs[i]) |
end |
del(childs) |
end |
end |
local function CleanUserData(widget, event) |
local user = widget:GetUserDataTable() |
if user.path then |
del(user.path) |
end |
if widget.type == "TreeGroup" then |
local tree = user.tree |
widget:SetTree(nil) |
if tree then |
for i = 1, #tree do |
DelTree(tree[i]) |
del(tree[i]) |
end |
del(tree) |
end |
end |
if widget.type == "TabGroup" then |
widget:SetTabs(nil) |
if user.tablist then |
del(user.tablist) |
end |
end |
if widget.type == "DropdownGroup" then |
widget:SetGroupList(nil) |
if user.grouplist then |
del(user.grouplist) |
end |
end |
end |
--[[ |
Gets a status table for the given appname and options path |
]] |
function lib:GetStatusTable(appName, path) |
local status = self.Status |
if not status[appName] then |
status[appName] = {} |
status[appName].status = {} |
status[appName].children = {} |
end |
status = status[appName] |
if path then |
for i = 1, #path do |
local v = path[i] |
if not status.children[v] then |
status.children[v] = {} |
status.children[v].status = {} |
status.children[v].children = {} |
end |
status = status.children[v] |
end |
end |
return status.status |
end |
--[[ |
Sets the given path to be selected |
]] |
function lib:SelectGroup(appName, ...) |
local path = new() |
local app = reg:GetOptionsTable(appName) |
if not app then |
error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) |
end |
local options = app("dialog", MAJOR) |
local group = options |
local status = self:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
status = status.groups |
local treevalue |
local treestatus |
for n = 1, select('#',...) do |
local key = select(n, ...) |
if group.childGroups == "tab" or group.childGroups == "select" then |
--if this is a tab or select group, select the group |
status.selected = key |
--children of this group are no longer extra levels of a tree |
treevalue = nil |
else |
--tree group by default |
if treevalue then |
--this is an extra level of a tree group, build a uniquevalue for it |
treevalue = treevalue.."\001"..key |
else |
--this is the top level of a tree group, the uniquevalue is the same as the key |
treevalue = key |
if not status.groups then |
status.groups = {} |
end |
--save this trees status table for any extra levels or groups |
treestatus = status |
end |
--make sure that the tree entry is open, and select it. |
--the selected group will be overwritten if a child is the final target but still needs to be open |
treestatus.selected = treevalue |
treestatus.groups[treevalue] = true |
end |
--move to the next group in the path |
group = GetSubOption(group, key) |
if not group then |
break |
end |
tinsert(path, key) |
status = self:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
status = status.groups |
end |
del(path) |
reg:NotifyChange(appName) |
end |
local function OptionOnMouseOver(widget, event) |
--show a tooltip/set the status bar to the desc text |
local user = widget:GetUserDataTable() |
local opt = user.option |
local options = user.options |
local path = user.path |
local appName = user.appName |
GameTooltip:SetOwner(widget.frame, "ANCHOR_TOPRIGHT") |
local name = GetOptionsMemberValue("name", opt, options, path, appName) |
local desc = GetOptionsMemberValue("desc", opt, options, path, appName) |
local usage = GetOptionsMemberValue("usage", opt, options, path, appName) |
GameTooltip:SetText(name, 1, .82, 0, 1) |
if opt.type == 'multiselect' then |
GameTooltip:AddLine(user.text,0.5, 0.5, 0.8, 1) |
end |
if type(desc) == "string" then |
GameTooltip:AddLine(desc, 1, 1, 1, 1) |
end |
if type(usage) == "string" then |
GameTooltip:AddLine("Usage: "..usage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) |
end |
GameTooltip:Show() |
end |
local function OptionOnMouseLeave(widget, event) |
GameTooltip:Hide() |
end |
local function GetFuncName(option) |
local type = option.type |
if type == 'execute' then |
return 'func' |
else |
return 'set' |
end |
end |
local function confirmPopup(appName, rootframe, basepath, info, message, func, ...) |
if not StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] then |
StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] = {} |
end |
local t = StaticPopupDialogs["ACECONFIGDIALOG30_CONFIRM_DIALOG"] |
for k in pairs(t) do |
t[k] = nil |
end |
t.text = message |
t.button1 = ACCEPT |
t.button2 = CANCEL |
local dialog, oldstrata |
t.OnAccept = function() |
safecall(func, unpack(t)) |
if dialog and oldstrata then |
dialog:SetFrameStrata(oldstrata) |
end |
lib:Open(appName, rootframe, basepath and unpack(basepath)) |
del(info) |
end |
t.OnCancel = function() |
if dialog and oldstrata then |
dialog:SetFrameStrata(oldstrata) |
end |
del(info) |
end |
for i = 1, select('#', ...) do |
t[i] = select(i, ...) or false |
end |
t.timeout = 0 |
t.whileDead = 1 |
t.hideOnEscape = 1 |
dialog = StaticPopup_Show("ACECONFIGDIALOG30_CONFIRM_DIALOG") |
if dialog then |
oldstrata = dialog:GetFrameStrata() |
dialog:SetFrameStrata("TOOLTIP") |
end |
end |
local function ActivateControl(widget, event, ...) |
--This function will call the set / execute handler for the widget |
--widget:GetUserDataTable() contains the needed info |
local user = widget:GetUserDataTable() |
local option = user.option |
local options = user.options |
local path = user.path |
local info = new() |
local func |
local group = options |
local funcname = GetFuncName(option) |
local handler |
local confirm |
local validate |
--build the info table containing the path |
-- pick up functions while traversing the tree |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
confirm = group.confirm |
validate = group.validate |
for i = 1, #path do |
local v = path[i] |
group = GetSubOption(group, v) |
info[i] = v |
if group[funcname] ~= nil then |
func = group[funcname] |
end |
handler = group.handler or handler |
if group.confirm ~= nil then |
confirm = group.confirm |
end |
if group.validate ~= nil then |
validate = group.validate |
end |
end |
info.options = options |
info.appName = user.appName |
info.arg = option.arg |
info.handler = handler |
info.option = option |
info.type = option.type |
info.uiType = 'dialog' |
info.uiName = MAJOR |
local name |
if type(option.name) == "function" then |
name = option.name(info) |
elseif type(option.name) == "string" then |
name = option.name |
else |
name = "" |
end |
local usage = option.usage |
local pattern = option.pattern |
local validated = true |
if option.type == "input" then |
if type(pattern)=="string" then |
if not strmatch(..., pattern) then |
validated = false |
end |
end |
end |
local success |
if validated and option.type ~= "execute" then |
if type(validate) == "string" then |
if handler and handler[validate] then |
success, validated = safecall(handler[validate], handler, info, ...) |
if not success then validated = false end |
else |
error(string.format("Method %s doesn't exist in handler for type execute", validate)) |
end |
elseif type(validate) == "function" then |
success, validated = safecall(validate, info, ...) |
if not success then validated = false end |
end |
end |
local rootframe = user.rootframe |
if type(validated) == "string" then |
--validate function returned a message to display |
if rootframe.SetStatusText then |
rootframe:SetStatusText(validated) |
end |
PlaySound("igPlayerInviteDecline") |
del(info) |
return true |
elseif not validated then |
--validate returned false |
if rootframe.SetStatusText then |
if usage then |
rootframe:SetStatusText(name..": "..usage) |
else |
if pattern then |
rootframe:SetStatusText(name..": Expected "..pattern) |
else |
rootframe:SetStatusText(name..": Invalid Value") |
end |
end |
end |
PlaySound("igPlayerInviteDecline") |
del(info) |
return true |
else |
local confirmText = option.confirmText |
--call confirm func/method |
if type(confirm) == "string" then |
if handler and handler[confirm] then |
success, confirm = safecall(handler[confirm], handler, info, ...) |
if success and type(confirm) == "string" then |
confirmText = confirm |
confirm = true |
elseif not success then |
confirm = false |
end |
else |
error(string.format("Method %s doesn't exist in handler for type confirm", confirm)) |
end |
elseif type(confirm) == "function" then |
success, confirm = safecall(confirm, info, ...) |
if success and type(confirm) == "string" then |
confirmText = confirm |
confirm = true |
elseif not success then |
confirm = false |
end |
end |
--confirm if needed |
if type(confirm) == "boolean" then |
if confirm then |
if not confirmText then |
local name, desc = option.name, option.desc |
if type(name) == "function" then |
name = name(info) |
end |
if type(desc) == "function" then |
desc = desc(info) |
end |
confirmText = name |
if desc then |
confirmText = confirmText.." - "..desc |
end |
end |
local iscustom = user.rootframe:GetUserData('iscustom') |
local rootframe |
if iscustom then |
rootframe = user.rootframe |
end |
local basepath = user.rootframe:GetUserData('basepath') |
if type(func) == "string" then |
if handler and handler[func] then |
confirmPopup(user.appName, rootframe, basepath, info, confirmText, handler[func], handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
confirmPopup(user.appName, rootframe, basepath, info, confirmText, func, info, ...) |
end |
--func will be called and info deleted when the confirm dialog is responded to |
return |
end |
end |
--call the function |
if type(func) == "string" then |
if handler and handler[func] then |
safecall(handler[func],handler, info, ...) |
else |
error(string.format("Method %s doesn't exist in handler for type func", func)) |
end |
elseif type(func) == "function" then |
safecall(func,info, ...) |
end |
local iscustom = user.rootframe:GetUserData('iscustom') |
local basepath = user.rootframe:GetUserData('basepath') |
--full refresh of the frame, some controls dont cause this on all events |
if option.type == "color" then |
if event == "OnValueConfirmed" then |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
end |
end |
elseif option.type == "range" then |
if event == "OnMouseUp" then |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
end |
end |
--multiselects don't cause a refresh on 'OnValueChanged' only 'OnClosed' |
elseif option.type == "multiselect" then |
user.valuechanged = true |
else |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
end |
end |
end |
del(info) |
end |
local function ActivateSlider(widget, event, value) |
local option = widget:GetUserData('option') |
local min, max, step = option.min or 0, option.max or 100, option.step |
if step then |
value = math.floor((value - min) / step + 0.5) * step + min |
else |
value = math.max(math.min(value,max),min) |
end |
ActivateControl(widget,event,value) |
end |
--called from a checkbox that is part of an internally created multiselect group |
--this type is safe to refresh on activation of one control |
local function ActivateMultiControl(widget, event, ...) |
ActivateControl(widget, event, widget:GetUserData('value'), ...) |
local user = widget:GetUserDataTable() |
local iscustom = user.rootframe:GetUserData('iscustom') |
local basepath = user.rootframe:GetUserData('basepath') |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
end |
end |
local function MultiControlOnClosed(widget, event, ...) |
local user = widget:GetUserDataTable() |
if user.valuechanged then |
local iscustom = user.rootframe:GetUserData('iscustom') |
local basepath = user.rootframe:GetUserData('basepath') |
if iscustom then |
lib:Open(user.appName, user.rootframe, basepath and unpack(basepath)) |
else |
lib:Open(user.appName, basepath and unpack(basepath)) |
end |
end |
end |
local function FrameOnClose(widget, event) |
local appName = widget:GetUserData('appName') |
lib.OpenFrames[appName] = nil |
gui:Release(widget) |
end |
local function CheckOptionHidden(option, options, path, appName) |
--check for a specific boolean option |
local hidden = pickfirstset(option.dialogHidden,option.guiHidden) |
if hidden ~= nil then |
return hidden |
end |
return GetOptionsMemberValue("hidden", option, options, path, appName) |
end |
local function CheckOptionDisabled(option, options, path, appName) |
--check for a specific boolean option |
local disabled = pickfirstset(option.dialogDisabled,option.guiDisabled) |
if disabled ~= nil then |
return disabled |
end |
return GetOptionsMemberValue("disabled", option, options, path, appName) |
end |
--[[ |
local function BuildTabs(group, options, path, appName) |
local tabs = new() |
local text = new() |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
tinsert(tabs, k) |
text[k] = GetOptionsMemberValue("name", v, options, path, appName) |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
return tabs, text |
end |
]] |
local function BuildSelect(group, options, path, appName) |
local groups = new() |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
groups[k] = GetOptionsMemberValue("name", v, options, path, appName) |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
return groups |
end |
local function BuildSubGroups(group, tree, options, path, appName) |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
local entry = new() |
entry.value = k |
entry.text = GetOptionsMemberValue("name", v, options, path, appName) |
entry.disabled = CheckOptionDisabled(v, options, path, appName) |
if not tree.children then tree.children = new() end |
tinsert(tree.children,entry) |
if (v.childGroups or "tree") == "tree" then |
BuildSubGroups(v,entry, options, path, appName) |
end |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
end |
local function BuildGroups(group, options, path, appName, recurse) |
local tree = new() |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
if v.type == "group" then |
path[#path+1] = k |
local inline = pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
local hidden = CheckOptionHidden(v, options, path, appName) |
if not inline and not hidden then |
local entry = new() |
entry.value = k |
entry.text = GetOptionsMemberValue("name", v, options, path, appName) |
entry.disabled = CheckOptionDisabled(v, options, path, appName) |
tinsert(tree,entry) |
if recurse and (v.childGroups or "tree") == "tree" then |
BuildSubGroups(v,entry, options, path, appName) |
end |
end |
path[#path] = nil |
end |
end |
del(keySort) |
del(opts) |
return tree |
end |
local function InjectInfo(control, options, option, path, rootframe, appName) |
local user = control:GetUserDataTable() |
for i = 1, #path do |
user[i] = path[i] |
end |
user.rootframe = rootframe |
user.option = option |
user.options = options |
user.path = copy(path) |
user.appName = appName |
control:SetCallback("OnRelease", CleanUserData) |
control:SetCallback("OnLeave", OptionOnMouseLeave) |
control:SetCallback("OnEnter", OptionOnMouseOver) |
end |
--[[ |
options - root of the options table being fed |
container - widget that controls will be placed in |
rootframe - Frame object the options are in |
path - table with the keys to get to the group being fed |
--]] |
local function FeedOptions(appName, options,container,rootframe,path,group,inline) |
local keySort = new() |
local opts = new() |
BuildSortedOptionsTable(group, keySort, opts, options, path, appName) |
for i = 1, #keySort do |
local k = keySort[i] |
local v = opts[k] |
tinsert(path, k) |
local hidden = CheckOptionHidden(v, options, path, appName) |
local name = GetOptionsMemberValue("name", v, options, path, appName) |
if not hidden then |
if v.type == "group" then |
if inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
--Inline group |
local GroupContainer |
if name and name ~= "" then |
GroupContainer = gui:Create("InlineGroup") |
GroupContainer:SetTitle(name or "") |
else |
GroupContainer = gui:Create("SimpleGroup") |
end |
GroupContainer.width = "fill" |
GroupContainer:SetLayout("flow") |
container:AddChild(GroupContainer) |
FeedOptions(appName,options,GroupContainer,rootframe,path,v,true) |
end |
else |
--Control to feed |
local control |
local name = GetOptionsMemberValue("name", v, options, path, appName) |
if v.type == "execute" then |
control = gui:Create("Button") |
control:SetText(name) |
control:SetCallback("OnClick",ActivateControl) |
elseif v.type == "input" then |
local controlType = v.dialogControl or v.control or (v.multiline and "MultiLineEditBox") or "EditBox" |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
end |
if v.multiline then |
local lines = 4 |
if type(v.multiline) == "number" then |
lines = v.multiline |
end |
control:SetHeight(60 + (14*lines)) |
end |
control:SetLabel(name) |
control:SetCallback("OnEnterPressed",ActivateControl) |
local text = GetOptionsMemberValue("get",v, options, path, appName) |
if type(text) ~= "string" then |
text = "" |
end |
control:SetText(text) |
elseif v.type == "toggle" then |
control = gui:Create("CheckBox") |
control:SetLabel(name) |
control:SetTriState(v.tristate) |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateControl) |
elseif v.type == "range" then |
control = gui:Create("Slider") |
control:SetLabel(name) |
control:SetSliderValues(v.min or 0,v.max or 100, v.bigStep or v.step or 0) |
control:SetIsPercent(v.isPercent) |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
if type(value) ~= "number" then |
value = 0 |
end |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateSlider) |
control:SetCallback("OnMouseUp",ActivateSlider) |
elseif v.type == "select" then |
local values = GetOptionsMemberValue("values", v, options, path, appName) |
local controlType = v.dialogControl or v.control or "Dropdown" |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
end |
control:SetLabel(name) |
control:SetList(values) |
local value = GetOptionsMemberValue("get",v, options, path, appName) |
if not values[value] then |
value = nil |
end |
control:SetValue(value) |
control:SetCallback("OnValueChanged",ActivateControl) |
elseif v.type == "multiselect" then |
local values = GetOptionsMemberValue("values", v, options, path, appName) |
local disabled = CheckOptionDisabled(v, options, path, appName) |
local controlType = v.dialogControl or v.control |
local valuesort = new() |
if values then |
for value, text in pairs(values) do |
tinsert(valuesort, value) |
end |
end |
table.sort(valuesort) |
if controlType then |
control = gui:Create(controlType) |
if not control then |
error(("Invalid Custom Control Type - %s"):format(tostring(controlType))) |
end |
control:SetMultiselect(true) |
control:SetLabel(name) |
control:SetList(values) |
control:SetDisabled(disabled) |
control:SetCallback("OnValueChanged",ActivateControl) |
control:SetCallback("OnClosed", MultiControlOnClosed) |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
if width == "double" then |
control:SetWidth(width_multiplier * 2) |
elseif width == "half" then |
control:SetWidth(width_multiplier / 2) |
elseif width == "full" then |
control.width = "fill" |
else |
control:SetWidth(width_multiplier) |
end |
--check:SetTriState(v.tristate) |
for i = 1, #valuesort do |
local key = valuesort[i] |
local value = GetOptionsMemberValue("get",v, options, path, appName, key) |
control:SetItemValue(key,value) |
end |
else |
control = gui:Create("InlineGroup") |
control:SetLayout("Flow") |
control:SetTitle(name) |
control.width = "fill" |
control:PauseLayout() |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
for i = 1, #valuesort do |
local value = valuesort[i] |
local text = values[value] |
local check = gui:Create("CheckBox") |
check:SetLabel(text) |
check:SetUserData('value', value) |
check:SetUserData('text', text) |
check:SetDisabled(disabled) |
check:SetTriState(v.tristate) |
check:SetValue(GetOptionsMemberValue("get",v, options, path, appName, value)) |
check:SetCallback("OnValueChanged",ActivateMultiControl) |
InjectInfo(check, options, v, path, rootframe, appName) |
control:AddChild(check) |
if width == "double" then |
check:SetWidth(width_multiplier * 2) |
elseif width == "half" then |
check:SetWidth(width_multiplier / 2) |
elseif width == "full" then |
check.width = "fill" |
else |
check:SetWidth(width_multiplier) |
end |
end |
control:ResumeLayout() |
control:DoLayout() |
end |
del(valuesort) |
elseif v.type == "color" then |
control = gui:Create("ColorPicker") |
control:SetLabel(name) |
control:SetHasAlpha(v.hasAlpha) |
control:SetColor(GetOptionsMemberValue("get",v, options, path, appName)) |
control:SetCallback("OnValueChanged",ActivateControl) |
control:SetCallback("OnValueConfirmed",ActivateControl) |
elseif v.type == "keybinding" then |
control = gui:Create("Keybinding") |
control:SetLabel(name) |
control:SetKey(GetOptionsMemberValue("get",v, options, path, appName)) |
control:SetCallback("OnKeyChanged",ActivateControl) |
elseif v.type == "header" then |
control = gui:Create("Heading") |
control:SetText(name) |
control.width = "fill" |
elseif v.type == "description" then |
control = gui:Create("Label") |
control:SetText(name) |
local imageCoords = GetOptionsMemberValue("imageCoords",v, options, path, appName) |
local image, width, height = GetOptionsMemberValue("image",v, options, path, appName) |
if type(image) == 'string' then |
if not width then |
width = GetOptionsMemberValue("imageWidth",v, options, path, appName) |
end |
if not height then |
height = GetOptionsMemberValue("imageHeight",v, options, path, appName) |
end |
if type(imageCoords) == 'table' then |
control:SetImage(image, unpack(imageCoords)) |
else |
control:SetImage(image) |
end |
if type(width) ~= "number" then |
width = 32 |
end |
if type(height) ~= "number" then |
height = 32 |
end |
control:SetImageSize(width, height) |
end |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
control.width = not width and "fill" |
end |
--Common Init |
if control then |
if control.width ~= "fill" then |
local width = GetOptionsMemberValue("width",v,options,path,appName) |
if width == "double" then |
control:SetWidth(width_multiplier * 2) |
elseif width == "half" then |
control:SetWidth(width_multiplier / 2) |
elseif width == "full" then |
control.width = "fill" |
else |
control:SetWidth(width_multiplier) |
end |
end |
if control.SetDisabled then |
local disabled = CheckOptionDisabled(v, options, path, appName) |
control:SetDisabled(disabled) |
end |
InjectInfo(control, options, v, path, rootframe, appName) |
container:AddChild(control) |
end |
end |
end |
tremove(path) |
end |
container:ResumeLayout() |
container:DoLayout() |
del(keySort) |
del(opts) |
end |
local function BuildPath(path, ...) |
for i = 1, select('#',...) do |
tinsert(path, (select(i,...))) |
end |
end |
local function TreeOnButtonEnter(widget, event, uniquevalue, button) |
local user = widget:GetUserDataTable() |
if not user then return end |
local options = user.options |
local option = user.option |
local path = user.path |
local appName = user.appName |
local feedpath = new() |
for i = 1, #path do |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
if not group then return end |
group = GetSubOption(group, feedpath[i]) |
end |
local name = GetOptionsMemberValue("name", group, options, feedpath, appName) |
local desc = GetOptionsMemberValue("desc", group, options, feedpath, appName) |
GameTooltip:SetOwner(button, "ANCHOR_NONE") |
if widget.type == "TabGroup" then |
GameTooltip:SetPoint("BOTTOM",button,"TOP") |
else |
GameTooltip:SetPoint("LEFT",button,"RIGHT") |
end |
GameTooltip:SetText(name, 1, .82, 0, 1) |
if type(desc) == "string" then |
GameTooltip:AddLine(desc, 1, 1, 1, 1) |
end |
GameTooltip:Show() |
end |
local function TreeOnButtonLeave(widget, event, value, button) |
GameTooltip:Hide() |
end |
local function GroupExists(appName, options, path, uniquevalue) |
if not uniquevalue then return false end |
local feedpath = new() |
local temppath = new() |
for i = 1, #path do |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
local v = feedpath[i] |
temppath[i] = v |
group = GetSubOption(group, v) |
if not group or group.type ~= "group" or CheckOptionHidden(group, options, temppath, appName) then |
del(feedpath) |
del(temppath) |
return false |
end |
end |
del(feedpath) |
del(temppath) |
return true |
end |
local function GroupSelected(widget, event, uniquevalue) |
local user = widget:GetUserDataTable() |
local options = user.options |
local option = user.option |
local path = user.path |
local rootframe = user.rootframe |
local feedpath = new() |
for i = 1, #path do |
feedpath[i] = path[i] |
end |
BuildPath(feedpath, string.split("\001", uniquevalue)) |
local group = options |
for i = 1, #feedpath do |
group = GetSubOption(group, feedpath[i]) |
end |
widget:ReleaseChildren() |
lib:FeedGroup(user.appName,options,widget,rootframe,feedpath) |
del(feedpath) |
end |
--[[ |
This function will feed one group, and any inline child groups into the given container |
Select Groups will only have the selection control (tree, tabs, dropdown) fed in |
and have a group selected, this event will trigger the feeding of child groups |
Rules: |
If the group is Inline, FeedOptions |
If the group has no child groups, FeedOptions |
If the group is a tab or select group, FeedOptions then add the Group Control |
If the group is a tree group FeedOptions then |
its parent isnt a tree group: then add the tree control containing this and all child tree groups |
if its parent is a tree group, its already a node on a tree |
--]] |
function lib:FeedGroup(appName,options,container,rootframe,path, isRoot) |
local group = options |
--follow the path to get to the curent group |
local inline |
local grouptype, parenttype = options.childGroups, "none" |
--temp path table to pass to callbacks as we traverse the tree |
local temppath = new() |
for i = 1, #path do |
local v = path[i] |
temppath[i] = v |
group = GetSubOption(group, v) |
inline = inline or pickfirstset(v.dialogInline,v.guiInline,v.inline, false) |
parenttype = grouptype |
grouptype = group.childGroups |
end |
del(temppath) |
if not parenttype then |
parenttype = "tree" |
end |
--check if the group has child groups |
local hasChildGroups |
for k, v in pairs(group.args) do |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
hasChildGroups = true |
end |
end |
if group.plugins then |
for plugin, t in pairs(group.plugins) do |
for k, v in pairs(t) do |
if v.type == "group" and not pickfirstset(v.dialogInline,v.guiInline,v.inline, false) then |
hasChildGroups = true |
end |
end |
end |
end |
container:SetLayout("flow") |
local scroll |
--Add a scrollframe if we are not going to add a group control, this is the inverse of the conditions for that later on |
if (not (hasChildGroups and not inline)) or (grouptype ~= "tab" and grouptype ~= "select" and (parenttype == "tree" and not isRoot)) then |
if container.type ~= "InlineGroup" then |
scroll = gui:Create("ScrollFrame") |
scroll:SetLayout("flow") |
scroll.width = "fill" |
scroll.height = "fill" |
container:SetLayout("fill") |
container:AddChild(scroll) |
container = scroll |
end |
end |
FeedOptions(appName,options,container,rootframe,path,group,nil) |
if scroll then |
container:PerformLayout() |
local status = self:GetStatusTable(appName, path) |
if not status.scroll then |
status.scroll = {} |
end |
scroll:SetStatusTable(status.scroll) |
end |
if hasChildGroups and not inline then |
if grouptype == "tab" then |
local tab = gui:Create("TabGroup") |
InjectInfo(tab, options, group, path, rootframe, appName) |
tab:SetCallback("OnGroupSelected", GroupSelected) |
tab:SetCallback("OnTabEnter", TreeOnButtonEnter) |
tab:SetCallback("OnTabLeave", TreeOnButtonLeave) |
local status = lib:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
tab:SetStatusTable(status.groups) |
tab.width = "fill" |
tab.height = "fill" |
local tabs = BuildGroups(group, options, path, appName) |
tab:SetTabs(tabs) |
tab:SetUserData("tablist", tabs) |
for i = 1, #tabs do |
local entry = tabs[i] |
if not entry.disabled then |
tab:SelectTab((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) |
break |
end |
end |
container:AddChild(tab) |
elseif grouptype == "select" then |
local select = gui:Create("DropdownGroup") |
InjectInfo(select, options, group, path, rootframe, appName) |
select:SetCallback("OnGroupSelected", GroupSelected) |
local status = lib:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
select:SetStatusTable(status.groups) |
local grouplist = BuildSelect(group, options, path, appName) |
select:SetGroupList(grouplist) |
select:SetUserData("grouplist", grouplist) |
local firstgroup |
for k, v in pairs(grouplist) do |
if not firstgroup or k < firstgroup then |
firstgroup = k |
end |
end |
if firstgroup then |
select:SetGroup( (GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or firstgroup) |
end |
select.width = "fill" |
select.height = "fill" |
container:AddChild(select) |
--assume tree group by default |
--if parenttype is tree then this group is already a node on that tree |
elseif (parenttype ~= "tree") or isRoot then |
local tree = gui:Create("TreeGroup") |
InjectInfo(tree, options, group, path, rootframe, appName) |
tree:EnableButtonTooltips(false) |
tree.width = "fill" |
tree.height = "fill" |
tree:SetCallback("OnGroupSelected", GroupSelected) |
tree:SetCallback("OnButtonEnter", TreeOnButtonEnter) |
tree:SetCallback("OnButtonLeave", TreeOnButtonLeave) |
local status = lib:GetStatusTable(appName, path) |
if not status.groups then |
status.groups = {} |
end |
local treedefinition = BuildGroups(group, options, path, appName, true) |
tree:SetStatusTable(status.groups) |
tree:SetTree(treedefinition) |
tree:SetUserData("tree",treedefinition) |
for i = 1, #treedefinition do |
local entry = treedefinition[i] |
if not entry.disabled then |
tree:SelectByValue((GroupExists(appName, options, path,status.groups.selected) and status.groups.selected) or entry.value) |
break |
end |
end |
container:AddChild(tree) |
end |
end |
end |
local old_CloseSpecialWindows |
local function RefreshOnUpdate(this) |
for appName in pairs(this.closing) do |
if lib.OpenFrames[appName] then |
lib.OpenFrames[appName]:Hide() |
end |
this.closing[appName] = nil |
end |
if this.closeAll then |
for k, v in pairs(lib.OpenFrames) do |
v:Hide() |
end |
this.closeAll = nil |
end |
for appName in pairs(this.apps) do |
if lib.OpenFrames[appName] then |
local user = lib.OpenFrames[appName]:GetUserDataTable() |
lib:Open(appName, user.basepath and unpack(user.basepath)) |
end |
if lib.BlizOptions and lib.BlizOptions[appName] then |
local widget = lib.BlizOptions[appName] |
local user = widget:GetUserDataTable() |
if widget:IsVisible() then |
lib:Open(widget:GetUserData('appName'), widget, user.basepath and unpack(user.basepath)) |
end |
end |
this.apps[appName] = nil |
end |
this:SetScript("OnUpdate", nil) |
end |
function lib:CloseAll() |
lib.frame.closeAll = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
if next(self.OpenFrames) then |
return true |
end |
end |
function lib:Close(appName) |
if self.OpenFrames[appName] then |
lib.frame.closing[appName] = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
return true |
end |
end |
function lib:ConfigTableChanged(event, appName) |
lib.frame.apps[appName] = true |
lib.frame:SetScript("OnUpdate", RefreshOnUpdate) |
end |
reg.RegisterCallback(lib, "ConfigTableChange", "ConfigTableChanged") |
function lib:SetDefaultSize(appName, width, height) |
local status = lib:GetStatusTable(appName) |
if type(width) == "number" and type(height) == "number" then |
status.width = width |
status.height = height |
end |
end |
-- :Open(appName, [container], [path ...]) |
function lib:Open(appName, container, ...) |
if not old_CloseSpecialWindows then |
old_CloseSpecialWindows = CloseSpecialWindows |
CloseSpecialWindows = function() |
local found = old_CloseSpecialWindows() |
return self:CloseAll() or found |
end |
end |
local app = reg:GetOptionsTable(appName) |
if not app then |
error(("%s isn't registed with AceConfigRegistry, unable to open config"):format(appName), 2) |
end |
local options = app("dialog", MAJOR) |
local f |
local path = new() |
local name = GetOptionsMemberValue("name", options, options, path, appName) |
--If an optional path is specified add it to the path table before feeding the options |
--as container is optional as well it may contain the first element of the path |
if type(container) == "string" then |
tinsert(path, container) |
container = nil |
end |
for n = 1, select('#',...) do |
tinsert(path, (select(n, ...))) |
end |
--if a container is given feed into that |
if container then |
f = container |
f:ReleaseChildren() |
f:SetUserData('appName', appName) |
f:SetUserData('iscustom', true) |
if #path > 0 then |
f:SetUserData('basepath', copy(path)) |
end |
local status = lib:GetStatusTable(appName) |
if not status.width then |
status.width = 700 |
end |
if not status.height then |
status.height = 500 |
end |
if f.SetStatusTable then |
f:SetStatusTable(status) |
end |
else |
if not self.OpenFrames[appName] then |
f = gui:Create("Frame") |
self.OpenFrames[appName] = f |
else |
f = self.OpenFrames[appName] |
end |
f:ReleaseChildren() |
f:SetCallback("OnClose", FrameOnClose) |
f:SetUserData('appName', appName) |
if #path > 0 then |
f:SetUserData('basepath', copy(path)) |
end |
f:SetTitle(name or "") |
local status = lib:GetStatusTable(appName) |
f:SetStatusTable(status) |
end |
self:FeedGroup(appName,options,f,f,path,true) |
if f.Show then |
f:Show() |
end |
del(path) |
end |
lib.BlizOptions = lib.BlizOptions or {} |
local function FeedToBlizPanel(widget, event) |
local path = widget:GetUserData('path') |
lib:Open(widget:GetUserData('appName'), widget, path and unpack(path)) |
end |
local function ClearBlizPanel(widget, event) |
widget:ReleaseChildren() |
end |
function lib:AddToBlizOptions(appName, name, parent, ...) |
local BlizOptions = lib.BlizOptions |
local key = appName |
for n = 1, select('#', ...) do |
key = key..'\001'..select(n, ...) |
end |
if not BlizOptions[key] then |
local group = gui:Create("BlizOptionsGroup") |
BlizOptions[key] = group |
group:SetName(name or appName, parent) |
group:SetTitle(name or appName) |
group:SetUserData('appName', appName) |
if select('#', ...) > 0 then |
local path = {} |
for n = 1, select('#',...) do |
tinsert(path, (select(n, ...))) |
end |
group:SetUserData('path', path) |
end |
group:SetCallback("OnShow", FeedToBlizPanel) |
group:SetCallback("OnHide", ClearBlizPanel) |
InterfaceOptions_AddCategory(group.frame) |
return group.frame |
else |
error(("%s has already been added to the Blizzard Options Window with the given path"):format(appName), 2) |
end |
end |
-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0 |
-- Widget created by Yssaril |
local AceGUI = LibStub("AceGUI-3.0") |
local Media = LibStub("LibSharedMedia-3.0") |
do |
local min, max, floor = math.min, math.max, math.floor |
local fixlevels = AceGUISharedMediaWidgets.fixlevels |
local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged |
do |
local widgetType = "LSM30_Font_Item_Select" |
local widgetVersion = 1 |
local function SetText(self, text) |
if text and text ~= '' then |
local _, size, outline= self.text:GetFont() |
self.text:SetFont(Media:Fetch('font',text),size,outline) |
end |
self.text:SetText(text or "") |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown-Item-Toggle") |
self.type = widgetType |
self.SetText = SetText |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
do |
local widgetType = "LSM30_Font" |
local widgetVersion = 2 |
local function SetText(self, text) |
if text and text ~= '' then |
local _, size, outline= self.text:GetFont() |
self.text:SetFont(Media:Fetch('font',text),size,outline) |
end |
self.text:SetText(text or "") |
end |
local function AddListItem(self, value, text) |
local item = AceGUI:Create("LSM30_Font_Item_Select") |
item:SetText(text) |
item.userdata.obj = self |
item.userdata.value = value |
item:SetCallback("OnValueChanged", OnItemValueChanged) |
self.pullout:AddItem(item) |
end |
local sortlist = {} |
local function SetList(self, list) |
self.list = list or Media:HashTable("font") |
self.pullout:Clear() |
for v in pairs(self.list) do |
sortlist[#sortlist + 1] = v |
end |
table.sort(sortlist) |
for i, value in pairs(sortlist) do |
AddListItem(self, value, value) |
sortlist[i] = nil |
end |
if self.multiselect then |
AddCloseButton() |
end |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown") |
self.type = widgetType |
self.SetText = SetText |
self.SetValue = AceGUISharedMediaWidgets.SetValue |
self.SetList = SetList |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
end |
-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0 |
-- Widget created by Yssaril |
local AceGUI = LibStub("AceGUI-3.0") |
local Media = LibStub("LibSharedMedia-3.0") |
do |
local min, max, floor = math.min, math.max, math.floor |
local fixlevels = AceGUISharedMediaWidgets.fixlevels |
local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged |
do |
local widgetType = "LSM30_Sound_Item_Select" |
local widgetVersion = 1 |
local function Frame_OnEnter(this) |
local self = this.obj |
if self.useHighlight then |
self.highlight:Show() |
end |
self:Fire("OnEnter") |
if self.specialOnEnter then |
self.specialOnEnter(self) |
end |
end |
local function Frame_OnLeave(this) |
local self = this.obj |
self.highlight:Hide() |
self:Fire("OnLeave") |
if self.specialOnLeave then |
self.specialOnLeave(self) |
end |
end |
local function OnAcquire(self) |
self.frame:SetToplevel(true) |
self.frame:SetFrameStrata("FULLSCREEN_DIALOG") |
end |
local function OnRelease(self) |
self.pullout = nil |
self.frame:SetParent(nil) |
self.frame:ClearAllPoints() |
self.frame:Hide() |
end |
local function SetPullout(self, pullout) |
self.pullout = pullout |
self.frame:SetParent(nil) |
self.frame:SetParent(pullout.itemFrame) |
self.parent = pullout.itemFrame |
fixlevels(pullout.itemFrame, pullout.itemFrame:GetChildren()) |
end |
local function SetText(self, text) |
self.sound = text or '' |
self.text:SetText(text or "") |
end |
local function GetText(self) |
return self.text:GetText() |
end |
local function SetPoint(self, ...) |
self.frame:SetPoint(...) |
end |
local function Show(self) |
self.frame:Show() |
end |
local function Hide(self) |
self.frame:Hide() |
end |
local function SetDisabled(self, disabled) |
self.disabled = disabled |
if disabled then |
self.useHighlight = false |
self.text:SetTextColor(.5, .5, .5) |
else |
self.useHighlight = true |
self.text:SetTextColor(1, 1, 1) |
end |
end |
local function SetOnLeave(self, func) |
self.specialOnLeave = func |
end |
local function SetOnEnter(self, func) |
self.specialOnEnter = func |
end |
local function UpdateToggle(self) |
if self.value then |
self.check:Show() |
else |
self.check:Hide() |
end |
end |
local function Frame_OnClick(this, button) |
local self = this.obj |
self.value = not self.value |
UpdateToggle(self) |
self:Fire("OnValueChanged", self.value) |
end |
local function Speaker_OnClick(this, button) |
local self = this.obj |
PlaySoundFile(Media:Fetch('sound',self.sound)) |
end |
local function SetValue(self, value) |
self.value = value |
UpdateToggle(self) |
end |
local function Constructor() |
local count = AceGUI:GetNextWidgetNum(type) |
local frame = CreateFrame("Frame", "LSM30_Sound_DropDownItem"..count) |
local self = {} |
self.frame = frame |
frame.obj = self |
self.type = type |
self.useHighlight = true |
frame:SetHeight(17) |
frame:SetFrameStrata("FULLSCREEN_DIALOG") |
local button = CreateFrame("Button", nil, frame) |
button:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-22,0) |
button:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) |
self.button = button |
button.obj = self |
local speakerbutton = CreateFrame("Button", nil, frame) |
speakerbutton:SetWidth(16) |
speakerbutton:SetHeight(16) |
speakerbutton:SetPoint("RIGHT",frame,"RIGHT",-6,0) |
self.speakerbutton = speakerbutton |
speakerbutton.obj = self |
local speaker = frame:CreateTexture(nil, "BACKGROUND") |
speaker:SetTexture("Interface\\Common\\VoiceChat-Speaker") |
speaker:SetAllPoints(speakerbutton) |
self.speaker = speaker |
local speakeron = speakerbutton:CreateTexture(nil, "HIGHLIGHT") |
speakeron:SetTexture("Interface\\Common\\VoiceChat-On") |
speakeron:SetAllPoints(speakerbutton) |
self.speakeron = speakeron |
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") |
text:SetTextColor(1,1,1) |
text:SetJustifyH("LEFT") |
text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0) |
text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-24,0) |
self.text = text |
local highlight = button:CreateTexture(nil, "OVERLAY") |
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") |
highlight:SetBlendMode("ADD") |
highlight:SetHeight(14) |
highlight:ClearAllPoints() |
highlight:SetPoint("RIGHT",frame,"RIGHT",-19,0) |
highlight:SetPoint("LEFT",frame,"LEFT",5,0) |
highlight:Hide() |
self.highlight = highlight |
local check = frame:CreateTexture("OVERLAY") |
check:SetWidth(16) |
check:SetHeight(16) |
check:SetPoint("LEFT",frame,"LEFT",3,-1) |
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") |
check:Hide() |
self.check = check |
local sub = frame:CreateTexture("OVERLAY") |
sub:SetWidth(16) |
sub:SetHeight(16) |
sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1) |
sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow") |
sub:Hide() |
self.sub = sub |
button:SetScript("OnEnter", Frame_OnEnter) |
button:SetScript("OnLeave", Frame_OnLeave) |
self.OnAcquire = OnAcquire |
self.OnRelease = OnRelease |
self.SetPullout = SetPullout |
self.GetText = GetText |
self.SetText = SetText |
self.SetDisabled = SetDisabled |
self.SetPoint = SetPoint |
self.Show = Show |
self.Hide = Hide |
self.SetOnLeave = SetOnLeave |
self.SetOnEnter = SetOnEnter |
self.button:SetScript("OnClick", Frame_OnClick) |
self.speakerbutton:SetScript("OnClick", Speaker_OnClick) |
self.SetValue = SetValue |
AceGUI:RegisterAsWidget(self) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
do |
local widgetType = "LSM30_Sound" |
local widgetVersion = 2 |
local function AddListItem(self, value, text) |
local item = AceGUI:Create("LSM30_Sound_Item_Select") |
item:SetText(text) |
item.userdata.obj = self |
item.userdata.value = value |
item:SetCallback("OnValueChanged", OnItemValueChanged) |
self.pullout:AddItem(item) |
end |
local sortlist = {} |
local function SetList(self, list) |
self.list = list or Media:HashTable("sound") |
self.pullout:Clear() |
for v in pairs(self.list) do |
sortlist[#sortlist + 1] = v |
end |
table.sort(sortlist) |
for i, value in pairs(sortlist) do |
AddListItem(self, value, value) |
sortlist[i] = nil |
end |
if self.multiselect then |
AddCloseButton() |
end |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown") |
self.type = widgetType |
self.SetList = SetList |
self.SetValue = AceGUISharedMediaWidgets.SetValue |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
end |
-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0 |
-- Widget created by Yssaril |
local AceGUI = LibStub("AceGUI-3.0") |
local Media = LibStub("LibSharedMedia-3.0") |
do |
local min, max, floor = math.min, math.max, math.floor |
local fixlevels = AceGUISharedMediaWidgets.fixlevels |
local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged |
do |
local widgetType = "LSM30_Statusbar_Item_Select" |
local widgetVersion = 1 |
local function SetText(self, text) |
if text and text ~= '' then |
self.texture:SetTexture(Media:Fetch('statusbar',text)) |
self.texture:SetVertexColor(.5,.5,.5) |
end |
self.text:SetText(text or "") |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown-Item-Toggle") |
self.type = widgetType |
self.SetText = SetText |
local texture = self.frame:CreateTexture(nil, "BACKGROUND") |
texture:SetTexture(0,0,0,0) |
texture:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT",-4,1) |
texture:SetPoint("TOPLEFT",self.frame,"TOPLEFT",6,-1) |
self.texture = texture |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
do |
local widgetType = "LSM30_Statusbar" |
local widgetVersion = 2 |
local function SetText(self, text) |
if text and text ~= '' then |
self.texture:SetTexture(Media:Fetch('statusbar',text)) |
self.texture:SetVertexColor(.5,.5,.5) |
end |
self.text:SetText(text or "") |
end |
local function AddListItem(self, value, text) |
local item = AceGUI:Create("LSM30_Statusbar_Item_Select") |
item:SetText(text) |
item.userdata.obj = self |
item.userdata.value = value |
item:SetCallback("OnValueChanged", OnItemValueChanged) |
self.pullout:AddItem(item) |
end |
local sortlist = {} |
local function SetList(self, list) |
self.list = list or Media:HashTable("statusbar") |
self.pullout:Clear() |
for v in pairs(self.list) do |
sortlist[#sortlist + 1] = v |
end |
table.sort(sortlist) |
for i, value in pairs(sortlist) do |
AddListItem(self, value, value) |
sortlist[i] = nil |
end |
if self.multiselect then |
AddCloseButton() |
end |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown") |
self.type = widgetType |
self.SetText = SetText |
self.SetList = SetList |
self.SetValue = AceGUISharedMediaWidgets.SetValue |
local left = _G[self.dropdown:GetName() .. "Left"] |
local middle = _G[self.dropdown:GetName() .. "Middle"] |
local right = _G[self.dropdown:GetName() .. "Right"] |
local texture = self.dropdown:CreateTexture(nil, "ARTWORK") |
texture:SetPoint("BOTTOMRIGHT", right, "BOTTOMRIGHT" ,-39, 26) |
texture:SetPoint("TOPLEFT", left, "TOPLEFT", 24, -24) |
self.texture = texture |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
end |
-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0 |
-- Widget created by Yssaril |
LoadAddOn("LibSharedMedia-3.0") |
local AceGUI = LibStub("AceGUI-3.0") |
local Media = LibStub("LibSharedMedia-3.0") |
AceGUISharedMediaWidgets = {} |
do |
AceGUIWidgetLSMlists = { |
['font'] = Media:HashTable("font"), |
['sound'] = Media:HashTable("sound"), |
['statusbar'] = Media:HashTable("statusbar"), |
['border'] = Media:HashTable("border"), |
['background'] = Media:HashTable("background"), |
} |
local min, max, floor = math.min, math.max, math.floor |
local function fixlevels(parent,...) |
local i = 1 |
local child = select(i, ...) |
while child do |
child:SetFrameLevel(parent:GetFrameLevel()+1) |
fixlevels(child, child:GetChildren()) |
i = i + 1 |
child = select(i, ...) |
end |
end |
local function OnItemValueChanged(this, event, checked) |
local self = this.userdata.obj |
if self.multiselect then |
self:Fire("OnValueChanged", this.userdata.value, checked) |
else |
if checked then |
self:SetValue(this.userdata.value) |
self:Fire("OnValueChanged", this.userdata.value) |
else |
this:SetValue(true) |
end |
self.pullout:Close() |
end |
end |
local function SetValue(self, value) |
if value then |
self:SetText(value or "") |
end |
self.value = value |
end |
AceGUISharedMediaWidgets.fixlevels = fixlevels |
AceGUISharedMediaWidgets.OnItemValueChanged = OnItemValueChanged |
AceGUISharedMediaWidgets.SetValue = SetValue |
end |
-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0 |
-- Widget created by Yssaril |
local AceGUI = LibStub("AceGUI-3.0") |
local Media = LibStub("LibSharedMedia-3.0") |
do |
local min, max, floor = math.min, math.max, math.floor |
local fixlevels = AceGUISharedMediaWidgets.fixlevels |
local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged |
do |
local widgetType = "LSM30_Border_Item_Select" |
local widgetVersion = 1 |
local function Frame_OnEnter(this) |
local self = this.obj |
if self.useHighlight then |
self.highlight:Show() |
self.border:Show() |
end |
self:Fire("OnEnter") |
if self.specialOnEnter then |
self.specialOnEnter(self) |
end |
end |
local function Frame_OnLeave(this) |
local self = this.obj |
self.border:Hide() |
self.highlight:Hide() |
self:Fire("OnLeave") |
if self.specialOnLeave then |
self.specialOnLeave(self) |
end |
end |
local function SetText(self, text) |
if text and text ~= '' then |
local backdropTable = self.border:GetBackdrop() |
backdropTable.edgeFile = Media:Fetch('border',text) |
self.border:SetBackdrop(backdropTable) |
end |
self.text:SetText(text or "") |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown-Item-Toggle") |
self.type = widgetType |
self.SetText = SetText |
local border = CreateFrame('Frame') |
border:SetFrameStrata("TOOLTIP") |
border:SetWidth(64) |
border:SetHeight(32) |
border:SetPoint("LEFT",self.frame,"RIGHT",5,0) |
border:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 4, right = 4, top = 4, bottom = 4 }}) |
self.border = border |
border:Hide() |
self.frame:SetScript("OnEnter", Frame_OnEnter) |
self.frame:SetScript("OnLeave", Frame_OnLeave) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
do |
local widgetType = "LSM30_Border" |
local widgetVersion = 2 |
local function Frame_OnEnter(this) |
local self = this.obj |
local text = self.text:GetText() |
if text ~= nil and text ~= '' then |
self.borderframe:Show() |
end |
end |
local function Frame_OnLeave(this) |
local self = this.obj |
self.borderframe:Hide() |
end |
local function AddListItem(self, value, text) |
local item = AceGUI:Create("LSM30_Border_Item_Select") |
item:SetText(text) |
item.userdata.obj = self |
item.userdata.value = value |
item:SetCallback("OnValueChanged", OnItemValueChanged) |
self.pullout:AddItem(item) |
end |
local sortlist = {} |
local function SetList(self, list) |
self.list = list or Media:HashTable("border") |
self.pullout:Clear() |
for v in pairs(self.list) do |
sortlist[#sortlist + 1] = v |
end |
table.sort(sortlist) |
for i, value in pairs(sortlist) do |
AddListItem(self, value, value) |
sortlist[i] = nil |
end |
if self.multiselect then |
AddCloseButton() |
end |
end |
local function SetText(self, text) |
if text and text ~= '' then |
local backdropTable = self.borderframe:GetBackdrop() |
backdropTable.edgeFile = Media:Fetch('border',text) |
self.borderframe:SetBackdrop(backdropTable) |
end |
self.text:SetText(text or "") |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown") |
self.type = widgetType |
self.SetList = SetList |
self.SetText = SetText |
self.SetValue = AceGUISharedMediaWidgets.SetValue |
local left = _G[self.dropdown:GetName() .. "Left"] |
local middle = _G[self.dropdown:GetName() .. "Middle"] |
local right = _G[self.dropdown:GetName() .. "Right"] |
local borderframe = CreateFrame('Frame') |
borderframe:SetFrameStrata("TOOLTIP") |
borderframe:SetWidth(64) |
borderframe:SetHeight(32) |
borderframe:SetPoint("LEFT",right,"RIGHT",-15,0) |
borderframe:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background", |
tile = true, tileSize = 16, edgeSize = 16, |
insets = { left = 4, right = 4, top = 4, bottom = 4 }}) |
self.borderframe = borderframe |
borderframe:Hide() |
self.dropdown:EnableMouse(true) |
self.dropdown:SetScript("OnEnter", Frame_OnEnter) |
self.dropdown:SetScript("OnLeave", Frame_OnLeave) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="SharedFunctions.lua" /> |
<Script file="FontWidget.lua" /> |
<Script file="SoundWidget.lua" /> |
<Script file="StatusbarWidget.lua" /> |
<Script file="BorderWidget.lua" /> |
<Script file="BackgroundWidget.lua" /> |
</Ui> |
-- Widget is based on the AceGUIWidget-DropDown.lua supplied with AceGUI-3.0 |
-- Widget created by Yssaril |
local AceGUI = LibStub("AceGUI-3.0") |
local Media = LibStub("LibSharedMedia-3.0") |
do |
local min, max, floor = math.min, math.max, math.floor |
local fixlevels = AceGUISharedMediaWidgets.fixlevels |
local OnItemValueChanged = AceGUISharedMediaWidgets.OnItemValueChanged |
do |
local widgetType = "LSM30_Background_Item_Select" |
local widgetVersion = 1 |
local function Frame_OnEnter(this) |
local self = this.obj |
if self.useHighlight then |
self.highlight:Show() |
self.texture:Show() |
end |
self:Fire("OnEnter") |
if self.specialOnEnter then |
self.specialOnEnter(self) |
end |
end |
local function Frame_OnLeave(this) |
local self = this.obj |
self.texture:Hide() |
self.highlight:Hide() |
self:Fire("OnLeave") |
if self.specialOnLeave then |
self.specialOnLeave(self) |
end |
end |
local function SetText(self, text) |
if text and text ~= '' then |
self.texture:SetTexture(Media:Fetch('background',text)) |
end |
self.text:SetText(text or "") |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown-Item-Toggle") |
self.type = widgetType |
self.SetText = SetText |
local textureframe = CreateFrame('Frame') |
textureframe:SetFrameStrata("TOOLTIP") |
textureframe:SetWidth(128) |
textureframe:SetHeight(128) |
textureframe:SetPoint("LEFT",self.frame,"RIGHT",5,0) |
self.textureframe = textureframe |
local texture = textureframe:CreateTexture(nil, "OVERLAY") |
texture:SetTexture(0,0,0,0) |
texture:SetAllPoints(textureframe) |
texture:Hide() |
self.texture = texture |
self.frame:SetScript("OnEnter", Frame_OnEnter) |
self.frame:SetScript("OnLeave", Frame_OnLeave) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
do |
local widgetType = "LSM30_Background" |
local widgetVersion = 2 |
local function Frame_OnEnter(this) |
local self = this.obj |
local text = self.text:GetText() |
if text ~= nil and text ~= '' then |
self.textureframe:Show() |
end |
end |
local function Frame_OnLeave(this) |
local self = this.obj |
self.textureframe:Hide() |
end |
local function SetText(self, text) |
if text and text ~= '' then |
self.texture:SetTexture(Media:Fetch('background',text)) |
end |
self.text:SetText(text or "") |
end |
local function AddListItem(self, value, text) |
local item = AceGUI:Create("LSM30_Background_Item_Select") |
item:SetText(text) |
item.userdata.obj = self |
item.userdata.value = value |
item:SetCallback("OnValueChanged", OnItemValueChanged) |
self.pullout:AddItem(item) |
end |
local sortlist = {} |
local function SetList(self, list) |
self.list = list or Media:HashTable("background") |
self.pullout:Clear() |
for v in pairs(self.list) do |
sortlist[#sortlist + 1] = v |
end |
table.sort(sortlist) |
for i, value in pairs(sortlist) do |
AddListItem(self, value, value) |
sortlist[i] = nil |
end |
if self.multiselect then |
AddCloseButton() |
end |
end |
local function Constructor() |
local self = AceGUI:Create("Dropdown") |
self.type = widgetType |
self.SetText = SetText |
self.SetList = SetList |
self.SetValue = AceGUISharedMediaWidgets.SetValue |
local left = _G[self.dropdown:GetName() .. "Left"] |
local middle = _G[self.dropdown:GetName() .. "Middle"] |
local right = _G[self.dropdown:GetName() .. "Right"] |
local textureframe = CreateFrame('Frame') |
textureframe:SetFrameStrata("TOOLTIP") |
textureframe:SetWidth(128) |
textureframe:SetHeight(128) |
textureframe:SetPoint("LEFT",right,"RIGHT",-15,0) |
self.textureframe = textureframe |
local texture = textureframe:CreateTexture(nil, "OVERLAY") |
texture:SetTexture(0,0,0,0) |
texture:SetAllPoints(textureframe) |
textureframe:Hide() |
self.texture = texture |
self.dropdown:EnableMouse(true) |
self.dropdown:SetScript("OnEnter", Frame_OnEnter) |
self.dropdown:SetScript("OnLeave", Frame_OnLeave) |
return self |
end |
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion) |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceEvent-3.0.lua"/> |
</Ui> |
--[[ $Id: AceEvent-3.0.lua 494 2008-02-03 13:03:56Z nevcairiel $ ]] |
local MAJOR, MINOR = "AceEvent-3.0", 3 |
local AceEvent = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceEvent then return end |
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") |
AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame |
AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib |
-- APIs and registry for blizzard events, using CallbackHandler lib |
if not AceEvent.events then |
AceEvent.events = CallbackHandler:New(AceEvent, |
"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents") |
end |
function AceEvent.events:OnUsed(target, eventname) |
AceEvent.frame:RegisterEvent(eventname) |
end |
function AceEvent.events:OnUnused(target, eventname) |
AceEvent.frame:UnregisterEvent(eventname) |
end |
-- APIs and registry for IPC messages, using CallbackHandler lib |
if not AceEvent.messages then |
AceEvent.messages = CallbackHandler:New(AceEvent, |
"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages" |
) |
AceEvent.SendMessage = AceEvent.messages.Fire |
end |
--- embedding and embed handling |
local mixins = { |
"RegisterEvent", "UnregisterEvent", |
"RegisterMessage", "UnregisterMessage", |
"SendMessage", |
"UnregisterAllEvents", "UnregisterAllMessages", |
} |
-- AceEvent:Embed( target ) |
-- target (object) - target object to embed AceEvent in |
-- |
-- Embeds AceEvent into the target object making the functions from the mixins list available on target:.. |
function AceEvent:Embed(target) |
for k, v in pairs(mixins) do |
target[v] = self[v] |
end |
self.embeds[target] = true |
return target |
end |
-- AceEvent:OnEmbedDisable( target ) |
-- target (object) - target object that is being disabled |
-- |
-- Unregister all events messages etc when the target disables. |
-- this method should be called by the target manually or by an addon framework |
function AceEvent:OnEmbedDisable(target) |
target:UnregisterAllEvents() |
target:UnregisterAllMessages() |
end |
-- Script to fire blizzard events into the event listeners |
local events = AceEvent.events |
AceEvent.frame:SetScript("OnEvent", function(this, event, ...) |
events:Fire(event, ...) |
end) |
--- Finally: upgrade our old embeds |
for target, v in pairs(AceEvent.embeds) do |
AceEvent:Embed(target) |
end |
local parent = debugstack():match[[\AddOns\(.-)\]] |
local global = GetAddOnMetadata(parent, 'X-oUF') |
local oUF = _G[global] or oUF |
assert(oUF, 'oUF not loaded') |
oUF.TagEvents["[status]"] = "UNIT_HEALTH PLAYER_UPDATE_RESTING PLAYER_FLAGS_CHANGED" |
oUF.TagEvents["[afk]"] = "UNIT_HEALTH PLAYER_UPDATE_RESTING PLAYER_FLAGS_CHANGED" |
oUF.TagEvents["[shortName]"] = "UNIT_NAME_UPDATE" |
oUF.TagsLogicStrings = { |
["[class]"] = [[function(u) return UnitClass(u) end]], |
["[creature]"] = [[function(u) return UnitCreatureFamily(u) or UnitCreatureType(u) end]], |
["[curhp]"] = [[UnitHealth]], |
["[curpp]"] = [[UnitPower]], |
["[dead]"] = [[function(u) |
return UnitIsDead(u) and "Dead" or UnitIsGhost(u) and "Ghost" |
end]], |
["[difficulty]"] = [[function(u) |
if UnitCanAttack("player", u) then |
local l = UnitLevel(u); |
return Hex(GetDifficultyColor((l > 0) and l or 99)) |
end |
end]], |
["[faction]"] = [[function(u) return UnitFactionGroup(u) end]], |
["[leader]"] = [[function(u) return UnitIsPartyLeader(u) and "(L)" end]], |
["[leaderlong]"] = [[function(u) return UnitIsPartyLeader(u) and "(Leader)" end]], |
["[level]"] = [[function(u) |
local l = UnitLevel(u); |
return (l > 0) and l or "??" |
end]], |
["[maxhp]"] = [[UnitHealthMax]], |
["[maxpp]"] = [[UnitPowerMax]], |
["[missinghp]"] = [[function(u) m=UnitHealthMax(u) - UnitHealth(u); return m>0 and m.. " | " or "" end]], |
["[missingpp]"] = [[function(u) m=UnitPowerMax(u) - UnitPower(u); return m>0 and m.. " | " or "" end]], |
["[name]"] = [[function(u, r) return UnitName(r or u) end]], |
["[shortname]"] = [[function(u) return string.sub(UnitName(u),1,4) or '' end]], |
["[offline]"] = [[function(u) return (not UnitIsConnected(u) and "Offline") end]], |
["[perhp]"] = [[function(u) local m = UnitHealthMax(u); return m == 0 and 0 or math.floor(UnitHealth(u)/m*100+0.5) end]], |
["[perpp]"] = [[function(u) local m = UnitPowerMax(u); return m == 0 and 0 or math.floor(UnitPower(u)/m*100+0.5) end]], |
["[plus]"] = [[function(u) return UnitIsPlusMob(u) and "+" end]], |
["[pvp]"] = [[function(u) return UnitIsPVP(u) and "PvP" end]], |
["[race]"] = [[function(u) return UnitRace(u) end]], |
["[raidcolor]"] = [[function(u) |
local _, x = UnitClass(u); |
return x and Hex(RAID_CLASS_COLORS[x]) |
end]], |
["[rare]"] = [[function(u) |
local c = UnitClassification(u); |
return (c == "rare" or c == "rareelite") and "Rare" |
end]], |
["[resting]"] = [[function(u) return u == "player" and IsResting() and "zzz" end]], |
["[afk]"] = [[function(u) return UnitIsAFK(u) and "AFK" end]], |
["[sex]"] = [[function(u) local s = UnitSex(u) return s == 2 and "Male" or s == 3 and "Female" end]], |
["[smartclass]"] = [[function(u) return UnitIsPlayer(u) and oUF.Tags["[class]"](u) or oUF.Tags["[creature]"](u) end]], |
["[status]"] = [[function(u) return UnitIsDead(u) and "Dead" or UnitIsGhost(u) and "Ghost" or not UnitIsConnected(u) and "Offline" or oUF.Tags["[resting]"](u) end]], |
["[threat]"] = [[function(u) local s = UnitThreatSituation(u); return s == 1 and "++" or s == 2 and "--" or s == 3 and "Aggro" end]], |
["[threatplus]"] = [[function(u) |
local unitTarget = u.."target" |
if(UnitExists(unitTarget))then |
local_,_, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(u, uTarget) |
return rawthreatpct |
else |
return '' |
end |
end]], |
["[threatcolor]"] = [[function(u) return Hex(GetThreatStatusColor(UnitThreatSituation(u))) end]], |
["[cpoints]"] = [[function(u) local cp = GetComboPoints(u, 'target') return (cp > 0) and cp end]], |
['[smartlevel]'] = [[function(u) |
local c = UnitClassification(u) |
if(c == "worldboss") then |
return "Boss" |
else |
local plus = oUF.Tags["[plus]"](u) |
local level = oUF.Tags["[level]"](u) |
if(plus) then |
return level .. plus |
else |
return level |
end |
end |
end]], |
["[classification]"] = [[function(u) |
local c = UnitClassification(u) |
return c == "rare" and "Rare" or c == "eliterare" and "Rare Elite" or c == "elite" and "Elite" or c == "worldboss" and "Boss" |
end]], |
["[shortclassification]"] = [[function(u) |
local c = UnitClassification(u) |
return c == "rare" and "R" or c == "eliterare" and "R+" or c == "elite" and "+" or c == "worldboss" and "B" |
end]], |
} |
function oUF:ReWriteTag(tag,events,logic) |
if logic then |
builder = assert(loadstring("return " .. logic)) |
self.Tags[tag] = builder() |
end |
if events then |
oUF.TagEvents[tag] = events |
end |
end |
function oUF:CompileTagStringLogic() |
for tag,logic in pairs(self.TagsLogicStrings)do |
oUF:ReWriteTag(tag,oUF.TagEvents[tag],logic) |
end |
end |
## Interface: 30100 |
## Title: oUF_|cff336633TagEditor|r |
## Author: Smee |
## X-eMail: airtonix@gmail.com |
## Version: 30100.001 |
## RequiredDeps: oUF |
## DefaultState: enabled |
core.lua |
--============================================================================== |
-- |
-- oUF_HealComm |
-- |
-- Uses data from LibHealComm-3.0 to add incoming heal estimate bars onto units |
-- health bars. |
-- |
-- * currently won't update the frame if max HP is unknown (ie, restricted to |
-- players/pets in your group that are in range), hides the bar for these |
-- * can define frame.ignoreHealComm in layout to not have the bars appear on |
-- that frame |
-- |
--============================================================================= |
if not oUF then return end |
local oUF_HealComm = {} |
local healcomm = LibStub("LibHealComm-3.0") |
local playerName = UnitName("player") |
local playerIsCasting = false |
local playerHeals = 0 |
local playerTarget = "" |
oUF.debug=false |
local layoutName = "HealComm" |
local layoutPath = "Interface\\Addons\\oUF_"..layoutName |
local mediaPath = layoutPath.."\\media\\" |
local font, fontSize = mediaPath.."font.ttf", 11 -- The font and fontSize |
function round(num, idp) |
if idp and idp>0 then |
local mult = 10^idp |
return math.floor(num * mult + 0.5) / mult |
end |
return math.floor(num + 0.5) |
end |
local numberize = function(val) |
if(val >= 1e3) then |
return ("%.1fk"):format(val / 1e3) |
elseif (val >= 1e6) then |
return ("%.1fm"):format(val / 1e6) |
else |
return round(val,1) |
end |
end |
local function Hex(r, g, b) |
if type(r) == "table" then |
if r.r then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end |
end |
return string.format("|cff%02x%02x%02x", r*255, g*255, b*255) |
end |
--set texture and color here |
local color = { |
r = 0, |
g = 1, |
b = 0, |
a = .25, |
} |
--update a specific bar |
local updateHealCommBar = function(frame, unit) |
if not unit or unit == nil then return end |
local curHP = UnitHealth(unit) |
local maxHP = UnitHealthMax(unit) |
local percHP = curHP / maxHP |
local incHeals = select(2, healcomm:UnitIncomingHealGet(unit, GetTime())) or 0 |
local healCommBar = frame.HealCommBar |
local _,parentBar,_,_,_ = healCommBar:GetPoint() |
--add player's own heals if casting on this unit |
if playerIsCasting then |
for i = 1, select("#", playerTarget) do |
local target = select(i, playerTarget) |
if target == unit then |
incHeals = incHeals + playerHeals |
end |
end |
end |
local percInc = incHeals / maxHP |
--hide if unknown max hp or no heals inc |
if maxHP == 100 or incHeals == 0 then |
frame.HealCommBar:Hide() |
return |
else |
frame.HealCommBar:Show() |
end |
h = parentBar:GetHeight() |
w = frame:GetWidth()-2 |
orient = parentBar:GetOrientation() |
healCommBar:ClearAllPoints() |
healCommBar:SetFrameStrata("DIALOG") |
if(orient=="VERTICAL")then |
healCommBar:SetHeight(percInc * h) |
healCommBar:SetWidth(w) |
healCommBar:SetPoint("BOTTOM", parentBar, "BOTTOM", 0, h * percHP) |
else |
healCommBar:SetWidth(percInc * w) |
healCommBar:SetHeight(h) |
healCommBar:SetPoint("LEFT", parentBar, "LEFT", w * percHP,0) |
end |
if(healCommBar.Amount)then |
healCommBar.Amount:SetText(numberize(incHeals)) |
end |
end |
--used by library callbacks, arguments should be list of units to update |
local updateHealCommBars = function(...) |
for i = 1, select("#", ...) do |
local unit = select(i, ...) |
--search current oUF frames for this unit |
for frame in pairs(oUF.units) do |
local name, server = UnitName(frame) |
if server then name = strjoin("-",name,server) end |
if name == unit and not oUF.units[frame].ignoreHealComm then |
updateHealCommBar(oUF.units[frame],unit) |
end |
end |
end |
end |
local function hook(frame) |
if frame.ignoreHealComm then return end |
local parentBar = frame.Health |
--create heal bar here and set initial values |
local hcb = CreateFrame"StatusBar" |
if(parentBar:GetOrientation() =="VERTICAL")then |
hcb:SetWidth(parentBar:GetWidth()) -- same height as health bar |
hcb:SetHeight(4) --no initial width |
hcb:SetStatusBarTexture(parentBar:GetStatusBarTexture():GetTexture()) |
hcb:SetStatusBarColor(color.r, color.g, color.b, color.a) |
hcb:SetParent(frame) |
hcb:SetPoint("BOTTOMLEFT", parentBar, "TOPLEFT",0,0) --attach to immediate right of health bar to start |
hcb:Hide() --hide it for now |
else |
hcb:SetHeight(parentBar:GetHeight()) -- same height as health bar |
hcb:SetWidth(4) --no initial width |
hcb:SetStatusBarTexture(parentBar:GetStatusBarTexture():GetTexture()) |
hcb:SetStatusBarColor(color.r, color.g, color.b, color.a) |
hcb:SetParent(frame) |
hcb:SetPoint("LEFT", parentBar, "RIGHT",0,0) --attach to immediate right of health bar to start |
hcb:Hide() --hide it for now |
end |
healthBarTextObj = parentBar.value or parentBar.text |
if(healthBarTextObj)then |
fontName, fontHeight, fontFlags = healthBarTextObj:GetFont() |
else |
fontName, fontHeight, fontFlags = font, fontSize, "outline" |
end |
hcb.Amount = hcb:CreateFontString(nil, "OVERLAY") |
hcb.Amount:SetFont(fontName, fontHeight, fontFlags) |
hcb.Amount:SetPoint('CENTER',hcb, 0, 0) |
hcb.Amount:SetTextColor(1,1,1,.5) |
hcb.Amount:SetJustifyH("CENTER") |
if(frame.FontObjects ~= nil) then |
frame.FontObjects["incomingHeals"] = { |
name = "Incoming Heals", |
object = hcb.Amount |
} |
end |
frame.HealCommBar = hcb |
local o = frame.PostUpdateHealth |
frame.PostUpdateHealth = function(...) |
if o then o(...) end |
local name, server = UnitName(frame.unit) |
if server then name = strjoin("-",name,server) end |
updateHealCommBar(frame, name) --update the bar when unit's health is updated |
end |
end |
--hook into all existing frames |
for i, frame in ipairs(oUF.objects) do hook(frame) end |
--hook into new frames as they're created |
oUF:RegisterInitCallback(hook) |
--set up LibHealComm callbacks |
function oUF_HealComm:HealComm_DirectHealStart(event, healerName, healSize, endTime, ...) |
if healerName == playerName then |
playerIsCasting = true |
playerTarget = ... |
playerHeals = healSize |
end |
updateHealCommBars(...) |
end |
function oUF_HealComm:HealComm_DirectHealUpdate(event, healerName, healSize, endTime, ...) |
updateHealCommBars(...) |
end |
function oUF_HealComm:HealComm_DirectHealStop(event, healerName, healSize, succeeded, ...) |
if healerName == playerName then |
playerIsCasting = false |
end |
updateHealCommBars(...) |
end |
function oUF_HealComm:HealComm_HealModifierUpdate(event, unit, targetName, healModifier) |
updateHealCommBars(unit) |
end |
healcomm.RegisterCallback(oUF_HealComm, "HealComm_DirectHealStart") |
healcomm.RegisterCallback(oUF_HealComm, "HealComm_DirectHealUpdate") |
healcomm.RegisterCallback(oUF_HealComm, "HealComm_DirectHealStop") |
healcomm.RegisterCallback(oUF_HealComm, "HealComm_HealModifierUpdate") |
## Interface: 30000 |
## Title: oUF HealComm |
## Notes: Adds incoming healing bars to oUF frames. |
## Author: krage |
## Version: 1.1 |
## X-Category: UnitFrame |
## X-eMail: audion@gmail.com |
## Dependencies: oUF |
## DefaultState: enabled |
## OptionalDeps: LibHealComm-3.0 |
## X-Embeds: LibHealComm-3.0 |
Libs\LibHealComm-3.0\LibStub\LibStub.lua |
Libs\LibHealComm-3.0\CallbackHandler-1.0\CallbackHandler-1.0.lua |
Libs\LibHealComm-3.0\LibHealComm-3.0.lua |
oUF_HealComm.lua |
--[[ $Id: CallbackHandler-1.0.lua 60548 2008-02-07 11:04:06Z nevcairiel $ ]] |
local MAJOR, MINOR = "CallbackHandler-1.0", 3 |
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR) |
if not CallbackHandler then return end -- No upgrade needed |
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end} |
local type = type |
local pcall = pcall |
local pairs = pairs |
local assert = assert |
local concat = table.concat |
local loadstring = loadstring |
local next = next |
local select = select |
local type = type |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local next, xpcall, eh = ... |
local method, ARGS |
local function call() method(ARGS) end |
local function dispatch(handlers, ...) |
local index |
index, method = next(handlers) |
if not method then return end |
local OLD_ARGS = ARGS |
ARGS = ... |
repeat |
xpcall(call, eh) |
index, method = next(handlers, index) |
until not method |
ARGS = OLD_ARGS |
end |
return dispatch |
]] |
local ARGS, OLD_ARGS = {}, {} |
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end |
code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
-------------------------------------------------------------------------- |
-- CallbackHandler:New |
-- |
-- target - target object to embed public APIs in |
-- RegisterName - name of the callback registration API, default "RegisterCallback" |
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback" |
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API. |
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused) |
-- TODO: Remove this after beta has gone out |
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused") |
RegisterName = RegisterName or "RegisterCallback" |
UnregisterName = UnregisterName or "UnregisterCallback" |
if UnregisterAllName==nil then -- false is used to indicate "don't want this method" |
UnregisterAllName = "UnregisterAllCallbacks" |
end |
-- we declare all objects and exported APIs inside this closure to quickly gain access |
-- to e.g. function names, the "target" parameter, etc |
-- Create the registry object |
local events = setmetatable({}, meta) |
local registry = { recurse=0, events=events } |
-- registry:Fire() - fires the given event/message into the registry |
function registry:Fire(eventname, ...) |
if not rawget(events, eventname) or not next(events[eventname]) then return end |
local oldrecurse = registry.recurse |
registry.recurse = oldrecurse + 1 |
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...) |
registry.recurse = oldrecurse |
if registry.insertQueue and oldrecurse==0 then |
-- Something in one of our callbacks wanted to register more callbacks; they got queued |
for eventname,callbacks in pairs(registry.insertQueue) do |
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. |
for self,func in pairs(callbacks) do |
events[eventname][self] = func |
-- fire OnUsed callback? |
if first and registry.OnUsed then |
registry.OnUsed(registry, target, eventname) |
first = nil |
end |
end |
end |
registry.insertQueue = nil |
end |
end |
-- Registration of a callback, handles: |
-- self["method"], leads to self["method"](self, ...) |
-- self with function ref, leads to functionref(...) |
-- "addonId" (instead of self) with function ref, leads to functionref(...) |
-- all with an optional arg, which, if present, gets passed as first argument (after self if present) |
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]]) |
if type(eventname) ~= "string" then |
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2) |
end |
method = method or eventname |
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. |
if type(method) ~= "string" and type(method) ~= "function" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2) |
end |
local regfunc |
if type(method) == "string" then |
-- self["method"] calling style |
if type(self) ~= "table" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2) |
elseif self==target then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2) |
elseif type(self[method]) ~= "function" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2) |
end |
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! |
local arg=select(1,...) |
regfunc = function(...) self[method](self,arg,...) end |
else |
regfunc = function(...) self[method](self,...) end |
end |
else |
-- function ref with self=object or self="addonId" |
if type(self)~="table" and type(self)~="string" then |
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2) |
end |
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! |
local arg=select(1,...) |
regfunc = function(...) method(arg,...) end |
else |
regfunc = method |
end |
end |
if events[eventname][self] or registry.recurse<1 then |
-- if registry.recurse<1 then |
-- we're overwriting an existing entry, or not currently recursing. just set it. |
events[eventname][self] = regfunc |
-- fire OnUsed callback? |
if registry.OnUsed and first then |
registry.OnUsed(registry, target, eventname) |
end |
else |
-- we're currently processing a callback in this registry, so delay the registration of this new entry! |
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency |
registry.insertQueue = registry.insertQueue or setmetatable({},meta) |
registry.insertQueue[eventname][self] = regfunc |
end |
end |
-- Unregister a callback |
target[UnregisterName] = function(self, eventname) |
if not self or self==target then |
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2) |
end |
if type(eventname) ~= "string" then |
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2) |
end |
if rawget(events, eventname) and events[eventname][self] then |
events[eventname][self] = nil |
-- Fire OnUnused callback? |
if registry.OnUnused and not next(events[eventname]) then |
registry.OnUnused(registry, target, eventname) |
end |
end |
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then |
registry.insertQueue[eventname][self] = nil |
end |
end |
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds |
if UnregisterAllName then |
target[UnregisterAllName] = function(...) |
if select("#",...)<1 then |
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2) |
end |
if select("#",...)==1 and ...==target then |
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2) |
end |
for i=1,select("#",...) do |
local self = select(i,...) |
if registry.insertQueue then |
for eventname, callbacks in pairs(registry.insertQueue) do |
if callbacks[self] then |
callbacks[self] = nil |
end |
end |
end |
for eventname, callbacks in pairs(events) do |
if callbacks[self] then |
callbacks[self] = nil |
-- Fire OnUnused callback? |
if registry.OnUnused and not next(callbacks) then |
registry.OnUnused(registry, target, eventname) |
end |
end |
end |
end |
end |
end |
return registry |
end |
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it |
-- try to upgrade old implicit embeds since the system is selfcontained and |
-- relies on closures to work. |
------------------------------------------------------------------------ |
r48 | xbeeps | 2009-03-07 21:17:57 +0000 (Sat, 07 Mar 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Add support for Tenacity Wintergrasp buff. |
------------------------------------------------------------------------ |
r46 | xbeeps | 2009-02-22 14:01:00 +0000 (Sun, 22 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Make the name/realm pattern unable to extract an invalid realm name (empty string). No functional difference, since no invalid realm names are ever passed, but makes it look more like the other pattern used in the extractRealm function. |
------------------------------------------------------------------------ |
r45 | xbeeps | 2009-02-14 23:42:07 +0000 (Sat, 14 Feb 2009) | 2 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
- Optimize the realm name conversion and only invoke it when receiving messages in a battleground. |
- Unregister the PLAYER_ALIVE event after receiving it once, to avoid redundant version exchanges upon death and at entry and exit of battlegrounds. |
------------------------------------------------------------------------ |
r44 | greltok | 2009-02-12 08:14:45 +0000 (Thu, 12 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Fixes for cross-server BGs - callbacks are now passed the fully qualified name(s) of the healing target(s). HealSize data is now stored by fully qualified name. |
------------------------------------------------------------------------ |
r43 | jlam | 2009-02-11 16:05:05 +0000 (Wed, 11 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Divine Plea reduces healing by 50% in WoW patch 3.0.9. |
------------------------------------------------------------------------ |
r42 | xbeeps | 2009-02-08 13:42:55 +0000 (Sun, 08 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
- Properly invalidate glyph cache when changing glyphs. |
------------------------------------------------------------------------ |
local MAJOR_VERSION = "LibHealComm-3.0"; |
local MINOR_VERSION = 90000 + tonumber(("$Revision: 48 $"):match("%d+")); |
local lib = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION); |
if not lib then return end |
local playerName = UnitName('player'); |
local playerRealm = GetRealmName(); |
local playerClass = select(2, UnitClass('player')); |
local isHealer = (playerClass == "PRIEST") or (playerClass == "SHAMAN") or (playerClass == "DRUID") or (playerClass == "PALADIN"); |
----------------- |
-- Event Frame -- |
----------------- |
lib.EventFrame = lib.EventFrame or CreateFrame("Frame"); |
lib.EventFrame:SetScript("OnEvent", function (this, event, ...) lib[event](lib, ...) end); |
lib.EventFrame:UnregisterAllEvents(); |
-- Register Events |
lib.EventFrame:RegisterEvent("PLAYER_ALIVE"); |
lib.EventFrame:RegisterEvent("LEARNED_SPELL_IN_TAB"); |
lib.EventFrame:RegisterEvent("CHAT_MSG_ADDON"); |
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_DELAYED"); |
lib.EventFrame:RegisterEvent("UNIT_AURA"); |
lib.EventFrame:RegisterEvent("UNIT_TARGET"); |
lib.EventFrame:RegisterEvent("PLAYER_TARGET_CHANGED"); |
lib.EventFrame:RegisterEvent("PLAYER_FOCUS_CHANGED"); |
lib.EventFrame:RegisterEvent("GLYPH_ADDED"); |
lib.EventFrame:RegisterEvent("GLYPH_REMOVED"); |
lib.EventFrame:RegisterEvent("GLYPH_UPDATED"); |
-- For keeping track of versions |
lib.EventFrame:RegisterEvent("PARTY_MEMBERS_CHANGED"); |
lib.EventFrame:RegisterEvent("RAID_ROSTER_UPDATE"); |
-- Prune data at zone change |
lib.EventFrame:RegisterEvent("PLAYER_ENTERING_WORLD"); |
-- Only listen to these events if player is healing class |
if (isHealer) then |
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_SENT"); |
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_START"); |
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED"); |
lib.EventFrame:RegisterEvent("UNIT_SPELLCAST_STOP"); |
end |
---------------------- |
-- Scanning Tooltip -- |
---------------------- |
if (not lib.Tooltip) then |
lib.Tooltip = CreateFrame("GameTooltip"); |
lib.Tooltip:SetOwner(UIParent, "ANCHOR_NONE"); |
for i = 1, 4 do |
lib["TooltipTextLeft" .. i] = lib.Tooltip:CreateFontString(); |
lib["TooltipTextRight" .. i] = lib.Tooltip:CreateFontString(); |
lib.Tooltip:AddFontStrings(lib["TooltipTextLeft" .. i], lib["TooltipTextRight" .. i]); |
end |
end |
------------------------------- |
-- Embed CallbackHandler-1.0 -- |
------------------------------- |
lib.Callbacks = LibStub("CallbackHandler-1.0"):New(lib); |
----------------- |
-- Static Data -- |
----------------- |
-- Cache of spells and heal sizes |
local SpellCache = {}; |
-- Cache of glyphs |
local GlyphCache = {}; |
-- Info about spells being cast by other players |
local HealTime = {}; |
local HealTarget = {}; |
local HealSize = {}; |
-- Healing Modifiers (by name) |
local HealModifier = {}; |
-- Last target name from UNIT_SPELLCAST_SENT |
local SentTargetName; |
-- Last spellCastIndex from UNIT_SPELLCAST_STOP |
local LastSpellCastIndex; |
-- Info about the spell being cast by the player |
local CastInfoIsCasting; |
local CastInfoHealingTargetUnitID; |
local CastInfoHealingTargetNames; |
local CastInfoHealingSize; |
local CastInfoEndTime; |
-- Latency Measurement |
local SentTime = 0; |
local Latency = 0; |
-- Version Information Table |
local Versions = {}; |
-- Battleground/Arena/Group Indicators |
local InBattlegroundOrArena; |
local InRaidOrParty; |
--------------------------------- |
-- Frequently Accessed Globals -- |
--------------------------------- |
local type = type; |
local tonumber = tonumber; |
local math = math; |
local string = string; |
local select = select; |
local pairs = pairs; |
local unpack = unpack; |
local UnitName = UnitName; |
local SendAddonMessage = SendAddonMessage; |
local IsInInstance = IsInInstance; |
local UnitBuff = UnitBuff; |
local UnitDebuff = UnitDebuff; |
local UnitLevel = UnitLevel; |
local GetInventoryItemLink = GetInventoryItemLink; |
local GetTime = GetTime; |
local UnitCastingInfo = UnitCastingInfo; |
local GetSpellBonusHealing = GetSpellBonusHealing; |
local GetTalentInfo = GetTalentInfo; |
local UnitExists = UnitExists; |
local tinsert = table.insert; |
local tconcat = table.concat; |
--------------- |
-- Utilities -- |
--------------- |
local function unitFullName(unit) |
local name, realm = UnitName(unit); |
if (realm and realm ~= "") then |
return name .. "-" .. realm; |
else |
return name; |
end |
end |
local function extractRealm(fullName) |
return fullName:match("^[^%-]+%-(.+)$"); |
end |
-- Convert a remotely generated fully qualified name to |
-- a local fully qualified name. |
local function convertRealm(fullName, remoteRealm) |
if (remoteRealm) then |
local name, realm = fullName:match("^([^%-]+)%-(.+)$"); |
if (not realm) then |
-- Apply remote realm if there is no realm on the target |
return fullName .. "-" .. remoteRealm; |
elseif (realm == playerRealm) then |
-- Strip realm if it is equal to the local realm |
return name; |
end |
end |
return fullName; |
end |
local function commSend(contents, distribution, target) |
SendAddonMessage("HealComm", contents, distribution or (InBattlegroundOrArena and "BATTLEGROUND" or "RAID"), target); |
end |
-- Spellbook Scanner -- |
local function getBaseHealSize(name) |
-- Check if info is already cached |
if (SpellCache[name]) then |
return SpellCache[name]; |
end |
SpellCache[name] = {}; |
-- Gather info (only done if not in cache) |
local i = 1; |
while true do |
local spellName, spellRank = GetSpellName(i, BOOKTYPE_SPELL); |
if (not spellName) then |
break |
end |
if (spellName == name) then |
-- This is the spell we're looking for, gather info |
-- Determine rank |
spellRank = tonumber(spellRank:match("(%d+)")); |
lib.Tooltip:SetSpell(i, BOOKTYPE_SPELL); |
-- Determine healing |
local HealMin, HealMax = select(3, string.find(lib.TooltipTextLeft4:GetText() or lib.TooltipTextLeft3:GetText() or "", "(%d+) ?[\195\160tobisaå°~\-]+ ?(%d+)")); |
HealMin, HealMax = tonumber(HealMin) or 0, tonumber(HealMax) or 0; |
local Heal = (HealMin + HealMax) / 2; |
SpellCache[spellName][spellRank] = Heal; |
end |
i = i + 1; |
end |
return SpellCache[name]; |
end |
local function detectGlyph(id) |
-- Check if info is already cached |
if (GlyphCache[id] ~= nil) then |
return GlyphCache[id]; |
end |
GlyphCache[id] = false; |
-- Gather info (only done if not in cache) |
for i = 1, GetNumGlyphSockets() do |
local enabled, _, glyphId = GetGlyphSocketInfo(i); |
if (enabled and glyphId) then |
GlyphCache[glyphId] = true; |
end |
end |
return GlyphCache[id]; |
end |
-- Detects if a buff is present on the unit and returns the application number. |
-- Optionally, if the third argument is provided and is true, then only return |
-- true if the buff was placed by the player. |
local function detectBuff(unit, buffName, mineOnly) |
local name, _, _, count, _, _, _, isMine = UnitBuff(unit, buffName); |
return name and (not mineOnly or isMine) and count or false; |
end |
--[[ |
[GetSpellInfo(604)] = -20, -- Dampen Magic (Rank 1) |
[GetSpellInfo(8450)] = -40, -- Dampen Magic (Rank 2) |
[GetSpellInfo(8451)] = -80, -- Dampen Magic (Rank 3) |
[GetSpellInfo(10173)] = -120, -- Dampen Magic (Rank 4) |
[GetSpellInfo(10174)] = -180, -- Dampen Magic (Rank 5) |
[GetSpellInfo(33944)] = -240, -- Dampen Magic (Rank 6) |
[GetSpellInfo(1008)] = 30, -- Amplify Magic (Rank 1) |
[GetSpellInfo(8455)] = 60, -- Amplify Magic (Rank 2) |
[GetSpellInfo(10169)] = 100, -- Amplify Magic (Rank 3) |
[GetSpellInfo(10170)] = 150, -- Amplify Magic (Rank 4) |
[GetSpellInfo(27130)] = 180, -- Amplify Magic (Rank 5) |
[GetSpellInfo(33946)] = 240, -- Amplify Magic (Rank 6) |
[GetSpellInfo(32858)] = -345 -- Touch of the Forgotten (Auchenai Crypts) |
[GetSpellInfo(38377)] = -690 -- Touch of the Forgotten (Auchenai Crypts) |
]]-- |
local healingBuffs = |
{ |
[GetSpellInfo(706)] = 1.20, -- Demon Armor |
[GetSpellInfo(45234)] = function (count, rank) return (1.0 + (0.04 + 0.03 * (rank - 1)) * count) end, -- Focused Will |
[GetSpellInfo(34123)] = 1.06, -- Tree of Life |
[GetSpellInfo(58549)] = function (count, rank, texture) return ((texture == "Interface\\Icons\\Ability_Warrior_StrengthOfArms") and (1.18 ^ count) or 1.0) end, -- Tenacity (Wintergrasp) |
} |
local healingDebuffs = |
{ |
[GetSpellInfo(25646)] = function (count) return (1.0 - count * 0.10) end, -- Mortal Wound (Temporus - The Black Morass) |
[GetSpellInfo(45347)] = function (count) return (1.0 - count * 0.04) end, -- Dark Touched (Grand Warlock Alythess - Sunwell Plateau) |
[GetSpellInfo(30423)] = function (count) return (1.0 - count * 0.01) end, -- Nether Portal - Dominance (Netherspite - Karazhan) |
[GetSpellInfo(13218)] = function (count) return (1.0 - count * 0.10) end, -- Wound Poison |
[GetSpellInfo(19434)] = 0.50, -- Aimed Shot |
[GetSpellInfo(12294)] = 0.50, -- Mortal Strike |
[GetSpellInfo(40599)] = 0.50, -- Arcing Smash (Gurtogg Bloodboil) |
[GetSpellInfo(23169)] = 0.50, -- Brood Affliction: Green (Chromaggus) |
[GetSpellInfo(34073)] = 0.85, -- Curse of the Bleeding Hollow (Hellfire Peninsula) |
[GetSpellInfo(13583)] = 0.50, -- Curse of the Deadwood (Deadwood Furbolgs - Felwood) |
[GetSpellInfo(36023)] = 0.50, -- Deathblow |
[GetSpellInfo(34625)] = 0.25, -- Demolish (Negatron - Netherstorm) |
[GetSpellInfo(34366)] = 0.75, -- Ebon Poison (Black Morass) |
[GetSpellInfo(32378)] = 0.50, -- Filet (Spectral Chef - Karazhan) |
[GetSpellInfo(19716)] = 0.25, -- Gehennas' Curse (Gehennas - Molten Core) |
[GetSpellInfo(36917)] = 0.50, -- Magma-Thrower's Curse (Sulfuron Magma-Thrower - The Arcatraz) |
[GetSpellInfo(22859)] = 0.50, -- Mortal Cleave (High Priestess Thekal - Zul'Gurub) |
[GetSpellInfo(38572)] = 0.50, -- Mortal Cleave (High Priestess Thekal - Zul'Gurub) |
[GetSpellInfo(39595)] = 0.50, -- Mortal Cleave (High Priestess Thekal - Zul'Gurub) |
[GetSpellInfo(28776)] = 0.10, -- Necrotic Poison (Maexxna - Naxxramas) |
[GetSpellInfo(35189)] = 0.50, -- Solar Strike (The Mechanar) |
[GetSpellInfo(32315)] = 0.50, -- Soul Strike (Ethereal Crypt Raiders - Mana-Tombs) |
[GetSpellInfo(7068)] = 0.25, -- Veil of Shadow (Nefarian - Blackwing Lair) |
[GetSpellInfo(38387)] = 1.50, -- Bane of Infinity (CoT: Escape from Durholde) |
[GetSpellInfo(31977)] = 1.50, -- Curse of Infinity (CoT: Escape from Durholde) |
[GetSpellInfo(41292)] = 0.00, -- Aura of Suffering (Essence of Souls - Black Temple) |
[GetSpellInfo(41350)] = 2.00, -- Aura of Desire (Essence of Souls - Black Temple) |
[GetSpellInfo(30843)] = 0.00, -- Enfeeble (Prince Malchezaar - Karazhan) |
} |
local function calculateHealModifier(unit) |
local modifier = 1.0; |
for i = 1, 40 do |
local name, rank, texture, count = UnitDebuff(unit, i); |
if (not name) then |
break; |
end |
local mark = healingDebuffs[name]; |
if (mark) then |
if (type(mark) == "function") then |
mark = mark(count); |
end |
if (mark < modifier) then |
modifier = mark; |
end |
end |
end |
for i = 1, 40 do |
local name, rank, texture, count = UnitBuff(unit, i); |
if (not name) then |
break; |
end |
local mark = healingBuffs[name]; |
if (mark) then |
if (type(mark) == "function") then |
mark = mark(count, rank and tonumber(rank:match("(%d+)")), texture); |
end |
modifier = modifier * mark; |
end |
end |
return modifier; |
end |
local function getDownrankingFactor(spellLevel, playerLevel) |
local factor = 0.05 * ((spellLevel + 7) - playerLevel) + 1; |
if (factor > 1.0) then |
return 1; |
elseif (factor < 0.0) then |
return 0; |
else |
return factor; |
end |
end |
local relicSlotNumber = GetInventorySlotInfo("RangedSlot"); |
local function getEquippedRelicID() |
local itemLink = GetInventoryItemLink('player', relicSlotNumber); |
if (itemLink) then |
return tonumber(itemLink:match("(%d+):")); |
end |
end |
----------------------------- |
-- Healing Data Management -- |
----------------------------- |
local function entryDelete(healerName) |
local targetNames = HealTarget[healerName]; |
HealTime[healerName] = nil; |
HealTarget[healerName] = nil; |
if (type(targetNames) == "table") then |
for i, targetName in pairs(targetNames) do |
if HealSize[targetName] then |
HealSize[targetName][healerName] = nil; |
end |
end |
elseif (targetNames and HealSize[targetNames]) then |
HealSize[targetNames][healerName] = nil; |
end |
end |
local function entryUpdate(healerName, targetNames, healSize, healTime) |
entryDelete(healerName); |
HealTime[healerName] = healTime; |
HealTarget[healerName] = targetNames; |
if (type(targetNames) == "table") then |
for i, targetName in pairs(targetNames) do |
if (not HealSize[targetName]) then |
HealSize[targetName] = {}; |
end |
HealSize[targetName][healerName] = healSize; |
end |
elseif (targetNames) then |
if (not HealSize[targetNames]) then |
HealSize[targetNames] = {}; |
end |
HealSize[targetNames][healerName] = healSize; |
end |
end |
local function entryRetrieve(healerName) |
local healTime = HealTime[healerName]; |
if (healTime) then |
local targetNames = HealTarget[healerName]; |
if (type(targetNames) == "table") then |
return targetNames, HealSize[targetNames[1]][healerName], healTime; |
elseif (targetNames) then |
return targetNames, HealSize[targetNames][healerName], healTime; |
end |
end |
end |
---------------------- |
-- Public Functions -- |
---------------------- |
--[[ UnitIncomingHealGet(unit, time) |
Description: Retrieve info about the incoming heals to a specific |
target. The second argument specifies a boundary time, |
relative to the current time. Examples: |
UnitIncomingHealGet("Kaki", GetTime() + 3) |
UnitIncomingHealGet("Kaki-Emerald Dream", GetTime() + 3) |
UnitIncomingHealGet("player", GetTime() + 3) |
UnitIncomingHealGet("raid10", GetTime() + 3) |
UnitIncomingHealGet("target", GetTime() + 3) |
Retrieves info about the incoming heals on the specified |
target. incomingHealBefore will contain the sum of heals |
that will land within the next 3 seconds, and |
incomingHealAfter will contain the sum of heals that will |
land after 3 seconds. |
Input: |
unit - The exact name or UnitID of the unit to retrieve information about. |
time - the desired boundary time of the inquiry. |
Output: |
incomingHealBefore - The total size of the incoming heals before the boundary time. |
incomingHealAfter - The total size of the incoming heals after the boundary time. |
nextTime - the time left until the next incoming heal will land. |
nextSize - the size of the next incoming heal. |
nextName - the name of the healer casting the next incoming heal. |
]]-- |
function lib:UnitIncomingHealGet(unit, time) |
if (type(unit) ~= "string") then return end |
if (type(time) ~= "number") then return end |
local targetName = unitFullName(unit); |
if (HealSize[targetName]) then |
local now = GetTime(); |
local incomingHealBefore, incomingHealAfter = 0, 0; |
local nextTime, nextSize, nextName; |
for healerName, size in pairs(HealSize[targetName]) do |
local healTime = HealTime[healerName]; |
if (size and healTime) then |
healTime = healTime + Latency; |
if (healTime > now) then |
if (healTime < time) then |
-- Due before boundary time |
incomingHealBefore = incomingHealBefore + size; |
else |
-- Due after boundary time |
incomingHealAfter = incomingHealAfter + size; |
end |
if ((not nextTime) or (healTime < nextTime)) then |
nextTime = healTime; |
nextSize = size; |
nextName = healerName; |
end |
end |
end |
end |
if ((incomingHealBefore > 0) or (incomingHealAfter > 0)) then |
return incomingHealBefore, incomingHealAfter, nextTime, nextSize, nextName; |
end |
end |
end |
--[[ UnitCastingHealGet(unit) |
Description: Retrieve info about the direct healing spell |
currently being cast by any unit. Examples: |
UnitCastingHealGet("Kaki"); |
UnitCastingHealGet("Kaki-Emerald Dream"); |
UnitCastingHealGet("player") |
UnitCastingHealGet("raid10") |
UnitCastingHealGet("target") |
Input: |
unit - The name or UnitID of the unit to retrieve information about. |
Output: |
healSize - Size of the healing being cast. |
endTime - The time when the healing completes. |
targetName - Name of the unit(s) being targeted for heal. |
]]-- |
function lib:UnitCastingHealGet(unit) |
if (type(unit) ~= "string") then return end |
local healerName = unitFullName(unit); |
if (healerName == playerName) then |
if (CastInfoIsCasting) then |
return CastInfoHealingSize, CastInfoEndTime, CastInfoHealingTargetNames; |
end |
else |
local targetNames, healSize, endTime = entryRetrieve(healerName); |
if (targetNames) then |
return healSize, endTime, targetNames; |
end |
end |
end |
--[[ UnitHealModifierGet(unit) |
Description: Returns the modifier to healing (as a factor) |
caused by buffs and debuffs. Examples: |
UnitHealModifierGet("Kaki"); |
UnitHealModifierGet("Kaki-Emerald Dream"); |
UnitHealModifierGet("player", 3) |
UnitHealModifierGet("raid10", 3) |
UnitHealModifierGet("target", 3) |
Input: |
unit - The name or UnitID of the unit to retrieve information about. |
Output: |
factor - Always a fractional number - will be 1.0 if no buffs/debuffs |
affect healing. |
]]-- |
function lib:UnitHealModifierGet(unit) |
if (type(unit) ~= "string") then return end |
local targetName = unitFullName(unit); |
return HealModifier[targetName] or calculateHealModifier(unit); |
end |
function lib:GetRaidOrPartyVersions() |
local tab = {}; |
if (GetNumRaidMembers() > 0) then |
for i = 1, GetNumRaidMembers() do |
local name = unitFullName('raid' .. i); |
if (not (name == playerName)) then |
tab[name] = Versions[name] or false; |
end |
end |
elseif (GetNumPartyMembers() > 0) then |
for i = 1, GetNumPartyMembers() do |
local name = unitFullName('party' .. i); |
tab[name] = Versions[name] or false; |
end |
end |
tab[playerName] = MINOR_VERSION; |
return tab; |
end |
function lib:GetGuildVersions() |
local tab = {}; |
if (IsInGuild()) then |
GuildRoster(); |
for i = 1, GetNumGuildMembers(false) do |
local name, rank, rankIndex, level, class, zone, note, officernote, online, status = GetGuildRosterInfo(i); |
if (online and not (name == playerName)) then |
tab[name] = Versions[name] or false; |
end |
end |
end |
tab[playerName] = MINOR_VERSION; |
return tab; |
end |
function lib:GetUnitVersion(unit) |
if (type(unit) ~= "string") then return end |
local targetName = unitFullName(unit); |
if (targetName == playerName) then return MINOR_VERSION end |
return Versions[targetName] or false; |
end |
-------------------- |
-- Class Specific -- |
-------------------- |
local HealingSpells; |
--local HotSpells; |
local GetHealSize; |
-- Druid -- |
-- TODO: |
-- Talent: Empowered Rejuvenation. Increase effect of all HOTs by 4%-20% |
-- Idol: Idol of Rejuvenation |
if (playerClass == "DRUID") then |
local tHealingTouch = GetSpellInfo(5185); |
local tRegrowth = GetSpellInfo(8936); |
local tNourish = GetSpellInfo(50464); |
local tRejuvenation = GetSpellInfo(774); |
local tLifebloom = GetSpellInfo(33763); |
local tWildGrowth = GetSpellInfo(48438); |
--[[HotSpells = |
{ |
[tRegrowth] = |
{ |
Level = {17, 23, 29, 35, 41, 47, 53, 59, 65, 70, 76, 80}, |
Duration = 21, |
Ticks = 7, |
Pattern = "(%d+)[^0-9]+%d+[^0-9]+$", |
Type = "HoT", |
}, |
[tRejuvenation] = |
{ |
Level = {9, 15, 21, 27, 33, 39, 45, 51, 57, 59, 62, 68, 74, 79, 80}, |
Duration = 12, |
Ticks = 4, |
Pattern = "(%d+)", |
Type = "HoT", |
}, |
[tLifebloom] = |
{ |
Level = {71, 79, 80}, |
Duration = 7, |
Ticks = 7, |
Pattern = "(%d+)" |
Type = "Lifebloom", |
}, |
}]]-- |
HealingSpells = |
{ |
[tHealingTouch] = |
{ |
Level = {1, 8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 62, 69, 74, 79}, |
Type = "Direct", |
}, |
[tRegrowth] = |
{ |
Level = {12, 18, 24, 30, 36, 42, 48, 54, 60, 65, 71, 77}, |
Type = "Direct", |
}, |
[tNourish] = |
{ |
Level = {80}; |
Type = "Direct", |
} |
} |
local idolsHealingTouch = |
{ |
[28568] = 136, -- Idol of the Avian Heart |
[22399] = 100, -- Idol of Health |
} |
GetHealSize = function(name, rank, target) |
local i, effectiveHeal; |
-- Get static spell info |
local baseHealSize = getBaseHealSize(name)[rank]; |
local nBonus = 0; |
local effectiveHealModifier = 1.0; |
if (not baseHealSize) then |
return nil; |
end |
-- Get +healing bonus |
local bonus = GetSpellBonusHealing(); |
local spellTab = HealingSpells[name]; |
-- Gift of Nature Talent - Increases effective healing by 2% per rank on all spells |
effectiveHealModifier = effectiveHealModifier * (2 * select(5, GetTalentInfo(3, 13)) / 100 + 1); |
-- Process individual spells |
if (name == tHealingTouch) then |
local idolBonus = idolsHealingTouch[getEquippedRelicID()] or 0; |
baseHealSize = baseHealSize + idolBonus; |
-- Glyph of Healing Touch (decreases amount healed by 50%) |
if (detectGlyph(54825)) then |
effectiveHealModifier = effectiveHealModifier * 0.5 |
end |
-- Empowered Touch Talent (increases bonus healing effects by 20% per rank) |
local talentEmpoweredTouch = 20 * select(5, GetTalentInfo(3, 15)) / 100; |
if (rank < 5) then |
nBonus = bonus * (1.88 * (1.0 + rank * 0.5) / 3.5 + talentEmpoweredTouch); |
else |
nBonus = bonus * (1.88 + talentEmpoweredTouch); |
end |
elseif (name == tRegrowth) then |
-- Glyph of Regrowth (increases effective healing by 20% if player's Regrowth is on target) |
if (detectGlyph(54743) and detectBuff(target, tRegrowth, true)) then |
effectiveHealModifier = effectiveHealModifier * 1.2; |
end |
nBonus = bonus * 1.88 * (2.0 / 3.5) * 0.5; |
elseif (name == tNourish) then |
-- Nourish heals for 20% more if player's HoT is on the target. |
if (detectBuff(target, tRejuvenation, true) or detectBuff(target, tRegrowth, true) or detectBuff(target, tLifebloom, true) or detectBuff(target, tWildGrowth, true)) then |
effectiveHealModifier = effectiveHealModifier * 1.2 |
end |
nBonus = bonus * 1.88 * (1.5 / 3.5); |
end |
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player'))); |
return effectiveHeal; |
end |
end |
-- Paladin -- |
-- TODO: Track Beacon of Light (GetSpellInfo(53563) for name). |
if (playerClass == "PALADIN") then |
local tHolyLight = GetSpellInfo(635); |
local tFlashOfLight = GetSpellInfo(19750); |
local tDivineFavor = GetSpellInfo(20216); |
local tSealOfLight = GetSpellInfo(20167); |
local tAvengingWrath = GetSpellInfo(31884); |
local tDivinePlea = GetSpellInfo(54428); |
HealingSpells = |
{ |
[tHolyLight] = |
{ |
Level = {1, 6, 14, 22, 30, 38, 46, 54, 60, 62, 70, 75, 80}, |
Type = "Direct", |
}, |
[tFlashOfLight] = |
{ |
Level = {20, 26, 34, 42, 50, 58, 66, 74, 79}, |
Type = "Direct", |
}, |
} |
local libramsFlashOfLight = |
{ |
[42614] = 267, -- Deadly Gladiator's Libram of Justice |
[42613] = 236, -- Hateful Gladiator's Libram of Justice |
[42612] = 204, -- Savage Gladiator's Libram of Justice |
[28592] = 89, -- Libram of Souls Redeemed (TODO: may be changed to affect Holy Light in 3.0.3) |
[25644] = 79, -- Blessed Book of Nagrand |
[23006] = 43, -- Libram of Light |
[23201] = 28, -- Libram of Divinity |
} |
local libramsHolyLight = |
{ |
[40268] = 141, -- Libram of Tolerance |
[28296] = 47, -- Libram of the Lightbringer |
} |
GetHealSize = function(name, rank, target) |
local i, effectiveHeal; |
-- Get static spell info |
local baseHealSize = getBaseHealSize(name)[rank]; |
local nBonus = 0; |
local effectiveHealModifier = 1.0; |
if (not baseHealSize) then |
return nil; |
end |
-- Get +healing bonus |
local bonus = GetSpellBonusHealing(); |
local spellTab = HealingSpells[name]; |
-- Divine Favor (100% crit chance on heal spell) |
if (detectBuff('player', tDivineFavor)) then |
effectiveHealModifier = effectiveHealModifier * 1.5; |
end |
-- Avenging Wrath (increase all healing by 20%) |
if (detectBuff('player', tAvengingWrath)) then |
effectiveHealModifier = effectiveHealModifier * 1.2; |
end |
-- Divine Plea (decrease all healing by 50%) |
if (detectBuff('player', tDivinePlea)) then |
effectiveHealModifier = effectiveHealModifier * 0.5; |
end |
-- Glyph of Seal of Light (increases healing by 5% if Seal of Light is active) |
if (detectGlyph(54943) and detectBuff('player', tSealOfLight)) then |
effectiveHealModifier = effectiveHealModifier * 1.05; |
end |
-- Healing Light - Increases healing by 4% per rank on all spells |
effectiveHealModifier = effectiveHealModifier * (4 * select(5, GetTalentInfo(1, 3)) / 100 + 1); |
-- Process individual spells |
if (name == tFlashOfLight) then |
local libramBonus = libramsFlashOfLight[getEquippedRelicID()] or 0; |
nBonus = (bonus + libramBonus) * 1.88 * (1.5 / 3.5) * 1.25; |
elseif (name == tHolyLight) then |
local libramBonus = libramsHolyLight[getEquippedRelicID()] or 0; |
nBonus = (bonus + libramBonus) * 1.88 * (2.5 / 3.5) * 1.25; |
end |
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player'))); |
return effectiveHeal; |
end |
end |
-- Priest -- |
-- TODO: Talent: Improved Renew: increases renew by 5%-10%-15% |
-- Healing_Done = (Renew_Base + (Healbonus * Downrankfactor) ) * Improved_Renew * Spiritual_Healing |
if (playerClass == "PRIEST") then |
local tLesserHeal = GetSpellInfo(2050); |
local tHeal = GetSpellInfo(2054); |
local tGreaterHeal = GetSpellInfo(2060); |
local tFlashHeal = GetSpellInfo(2061); |
local tBindingHeal = GetSpellInfo(32546); |
local tPrayerOfHealing = GetSpellInfo(596); |
local tPowerWordFortitude = GetSpellInfo(1243); |
--local tRenew = GetSpellInfo(139); |
local tGrace = GetSpellInfo(47930); |
--[[HotSpells = |
{ |
[tRenew] = |
{ |
Level = {8, 14, 20, 26, 32, 38, 44, 50, 56, 60, 65, 74, 79, 80}, |
Duration = 15, |
Ticks = 5, |
Pattern = "(%d+)", |
Type = "HoT", |
}, |
}]]-- |
HealingSpells = |
{ |
[tLesserHeal] = |
{ |
Level = {1, 4, 10}, |
Type = "Direct" |
}, |
[tHeal] = |
{ |
Level = {16, 22, 28, 34}, |
Type = "Direct" |
}, |
[tGreaterHeal] = |
{ |
Level = {40, 46, 52, 58, 60, 63, 68, 73, 78}, |
Type = "Direct", |
}, |
[tFlashHeal] = |
{ |
Level = {20, 26, 32, 38, 44, 50, 56, 61, 67, 73, 79}, |
Type = "Direct", |
}, |
[tBindingHeal] = |
{ |
Level = {64, 72, 78}, |
Type = "Binding" |
}, |
[tPrayerOfHealing] = |
{ |
Level = {30, 40, 50, 60, 60, 68, 76}, |
Type = "Party", |
InRange = function(unit) return IsSpellInRange(tPowerWordFortitude, unit) == 1 end |
}, |
} |
GetHealSize = function(name, rank, target) |
local i, effectiveHeal; |
-- Get static spell info |
local baseHealSize = getBaseHealSize(name)[rank]; |
local nBonus = 0; |
local effectiveHealModifier = 1.0; |
if (not baseHealSize) then |
return nil; |
end |
-- Get +healing bonus |
local bonus = GetSpellBonusHealing(); |
local spellTab = HealingSpells[name]; |
-- Focused Power - Increases healing by 2% per rank on all spells |
effectiveHealModifier = effectiveHealModifier * (2 * select(5, GetTalentInfo(1, 16)) / 100 + 1); |
-- Spiritual Healing - Increases healing by 2% per rank on all spells |
effectiveHealModifier = effectiveHealModifier * (2 * select(5, GetTalentInfo(2, 16)) / 100 + 1); |
-- Grace (increases healing by 2% per application on target if buff was placed by the player) |
if (target) then |
local grace = detectBuff(target, tGrace, true); |
if (grace) then |
effectiveHealModifier = effectiveHealModifier * (1.0 + 0.02 * grace); |
end |
end |
-- Process individual spells |
if (name == tLesserHeal) then |
nBonus = bonus * 1.88 * (1.0 + rank * 0.5) / 3.5; |
elseif (name == tHeal) then |
nBonus = bonus * 1.88 * (3.0 / 3.5); |
elseif (name == tGreaterHeal) then |
local empoweredHealing = 8 * select(5, GetTalentInfo(2, 20)) / 100; |
nBonus = bonus * (1.88 * (3.0 / 3.5) + empoweredHealing); |
elseif (name == tFlashHeal) then |
local empoweredHealing = 4 * select(5, GetTalentInfo(2, 20)) / 100; |
nBonus = bonus * (1.88 * (1.5 / 3.5) + empoweredHealing); |
elseif (name == tBindingHeal) then |
local empoweredHealing = 4 * select(5, GetTalentInfo(2, 20)) / 100; |
nBonus = bonus * (1.88 * (1.5 / 3.5) + empoweredHealing); |
elseif (name == tPrayerOfHealing) then |
nBonus = bonus * 1.88 * (3.0 / 3.5) * 0.5; |
end |
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player'))); |
return effectiveHeal; |
end |
end |
-- Shaman -- |
-- TODO: Nature's Blessing (GetTalentInfo(3, 21)) is probably not accounted for automatically anymore (or is it?) |
-- TODO: Riptide 51point resto spell (instant cast regrowth (direct + hot)) |
-- TODO: Glyph of Healing Wave (binding heal, but self-heal is percentage of actual target healed) |
if (playerClass == "SHAMAN") then |
local tLesserHealingWave = GetSpellInfo(8004); |
local tHealingWave = GetSpellInfo(331); |
local tChainHeal = GetSpellInfo(1064); |
local tHealingWay = GetSpellInfo(29206); |
local tTidalWaves = GetSpellInfo(51562); |
local tRiptide = GetSpellInfo(61295); |
local tEarthShield = GetSpellInfo(974); |
--[[HotSpells = |
{ |
[tRiptide] = |
{ |
Level = {60, 70, 75, 80}, |
Duration = 15, |
Ticks = 5, |
Pattern = "(%d+)", |
Type = "HoT", |
}, |
}]]-- |
HealingSpells = |
{ |
[tLesserHealingWave] = |
{ |
Level = {20, 28, 36, 44, 52, 60, 66, 72, 77}, |
Type = "Direct", |
}, |
[tHealingWave] = |
{ |
Level = {1, 6, 12, 18, 24, 32, 40, 48, 56, 60, 63, 70, 75, 80}, |
Type = "Direct", |
}, |
[tChainHeal] = |
{ |
Level = {40, 46, 54, 61, 68, 74, 80}, |
Type = "Direct", |
}, |
} |
local totemsLesserHealingWave = |
{ |
[42597] = 267, -- Deadly Gladiator's Totem of the Third Wind |
[42596] = 236, -- Hateful Gladiator's Totem of the Third Wind |
[42595] = 204, -- Savage Gladiator's Totem of the Third Wind |
[25645] = 79, -- Totem of The Plains |
[22396] = 80, -- Totem of Life |
[23200] = 53, -- Totem of Sustaining |
} |
local totemsHealingWave = |
{ |
[27544] = 88, -- Totem of Spontaneous Regrowth |
} |
local totemsChainHeal = |
{ |
[38368] = 102, -- Totem of the Bay |
[28523] = 87, -- Totem of Healing Rains |
} |
GetHealSize = function(name, rank, target) |
local i, effectiveHeal; |
-- Get static spell info |
local baseHealSize = getBaseHealSize(name)[rank]; |
local nBonus = 0; |
local effectiveHealModifier = 1.0; |
if (not baseHealSize) then |
return nil; |
end |
-- Get +healing bonus |
local bonus = GetSpellBonusHealing(); |
-- Purification Talent (increases healing by 2% per rank). |
-- This is factored into effectiveHealModifier in the individual spell section below due to interaction with Improved Chain Heal. |
local talentPurification = 2 * select(5, GetTalentInfo(3, 15)) / 100 + 1; |
local spellTab = HealingSpells[name]; |
-- Process individual spells |
if (name == tLesserHealingWave) then |
local totemBonus = totemsLesserHealingWave[getEquippedRelicID()] or 0; |
effectiveHealModifier = effectiveHealModifier * talentPurification; |
-- Glyph of Lesser Healing Wave (increases effective healing by 20% if player's Earth Shield is on target) |
if (detectGlyph(55438) and detectBuff(target, tEarthShield, true)) then |
effectiveHealModifier = effectiveHealModifier * 1.2; |
end |
-- Tidal Waves Talent (increases bonus healing effects by 2% per rank) |
local talentTidalWaves = 2 * select(5, GetTalentInfo(3, 25)) / 100; |
nBonus = (bonus + totemBonus) * (1.88 * (1.5 / 3.5) + talentTidalWaves); |
elseif (name == tHealingWave) then |
local totemBonus = totemsHealingWave[getEquippedRelicID()] or 0; |
effectiveHealModifier = effectiveHealModifier * talentPurification; |
-- Healing Way Buff (target buff that increases effective healing by 18%) |
if (detectBuff(target, tHealingWay)) then |
effectiveHealModifier = effectiveHealModifier * 1.18; |
end; |
-- Tidal Waves Talent (increases bonus healing effects by 4% per rank) |
local talentTidalWaves = 4 * select(5, GetTalentInfo(3, 25)) / 100; |
-- Determine normalisation |
if (rank < 4) then |
nBonus = (bonus + totemBonus) * (1.88 * (1.0 + rank * 0.5) / 3.5 + talentTidalWaves); |
else |
nBonus = (bonus + totemBonus) * (1.88 * (3.0 / 3.5) + talentTidalWaves); |
end |
elseif (name == tChainHeal) then |
local totemBonus = totemsChainHeal[getEquippedRelicID()] or 0; |
baseHealSize = baseHealSize + totemBonus; |
-- Improved Chain Heal Talent (increases healing by 10% per rank) |
local talentImprovedChainHeal = 10 * select(5, GetTalentInfo(3, 20)) / 100; |
effectiveHealModifier = effectiveHealModifier * (talentPurification + talentImprovedChainHeal); |
-- Riptide Buff (target buff that increases effective healing by 25%) |
if (detectBuff(target, tRiptide, true)) then |
effectiveHealModifier = effectiveHealModifier * 1.25; |
end |
nBonus = bonus * 1.88 * (2.5 / 3.5); |
end |
effectiveHeal = effectiveHealModifier * (baseHealSize + nBonus * getDownrankingFactor(spellTab.Level[rank], UnitLevel('player'))); |
return effectiveHeal; |
end |
end |
-------------------- |
-- Event Handlers -- |
-------------------- |
function lib:PLAYER_FOCUS_CHANGED() |
if (UnitExists('focus')) then |
self:UNIT_AURA('focus'); |
end |
if (UnitExists('focustarget')) then |
self:UNIT_AURA('focustarget'); |
end |
end |
function lib:PLAYER_TARGET_CHANGED() |
if (UnitExists('target')) then |
self:UNIT_AURA('target'); |
end |
if (UnitExists('targettarget')) then |
self:UNIT_AURA('targettarget'); |
end |
end |
function lib:UNIT_TARGET(unit) |
if ((unit == 'target') or (unit == 'focus')) then |
local unitTarget = unit .. "target"; |
if (UnitExists(unitTarget)) then |
self:UNIT_AURA(unitTarget); |
end |
end |
end |
function lib:UNIT_AURA(unit) |
local targetName = unitFullName(unit); |
local oldModifier = HealModifier[targetName]; |
local newModifier = calculateHealModifier(unit); |
if (oldModifier) then |
if (newModifier == oldModifier) then |
return |
end |
else |
if (newModifier == 1.0) then |
return |
end |
end |
HealModifier[targetName] = newModifier; |
self.Callbacks:Fire("HealComm_HealModifierUpdate", unit, targetName, newModifier); |
end |
function lib:LEARNED_SPELL_IN_TAB() |
-- Invalidate cached spell data when learning new spells |
SpellCache = {}; |
end |
function lib:GLYPH_ADDED() |
-- Invalidate cached glyph data when updating glyphs |
GlyphCache = {}; |
end |
function lib:GLYPH_REMOVED() |
-- Invalidate cached glyph data when updating glyphs |
GlyphCache = {}; |
end |
function lib:GLYPH_UPDATED() |
-- Invalidate cached glyph data when updating glyphs |
GlyphCache = {}; |
end |
function lib:UNIT_SPELLCAST_SENT(unit, spellName, spellRank, targetName) |
if (unit ~= 'player') then return end |
-- Latency measurement |
SentTime = GetTime(); |
SentTargetName = targetName; |
end |
function lib:UNIT_SPELLCAST_START(unit, spellName, spellRank) |
if (unit ~= 'player') then return end |
-- Latency measurement |
local currentLatency = GetTime() - SentTime; |
if (currentLatency > 1) then -- Limit to 1 sec |
currentLatency = 1; |
end |
Latency = 0.5 * Latency + 0.70 * currentLatency; |
local spellInfo = HealingSpells[spellName]; |
-- Only process healing spells |
if (spellInfo) then |
if (spellInfo.Type == "Direct") then |
CastInfoHealingTargetNames = SentTargetName; |
CastInfoHealingSize = GetHealSize(spellName, tonumber(spellRank:match("(%d+)")), SentTargetName) or 0; |
CastInfoIsCasting = true; |
CastInfoEndTime = (select(6, UnitCastingInfo('player')) or 0) / 1000; |
self.Callbacks:Fire("HealComm_DirectHealStart", playerName, CastInfoHealingSize, CastInfoEndTime, SentTargetName); |
commSend(string.format("000%05d%s", math.min(CastInfoHealingSize, 99999), SentTargetName)); |
elseif (spellInfo.Type == "Binding") then |
CastInfoHealingTargetNames = {playerName, SentTargetName}; |
CastInfoHealingSize = GetHealSize(spellName, tonumber(spellRank:match("(%d+)")), SentTargetName) or 0; |
CastInfoIsCasting = true; |
CastInfoEndTime = (select(6, UnitCastingInfo('player')) or 0) / 1000; |
self.Callbacks:Fire("HealComm_DirectHealStart", playerName, CastInfoHealingSize, CastInfoEndTime, unpack(CastInfoHealingTargetNames)); |
commSend(string.format("002%05d%s", math.min(CastInfoHealingSize, 99999), SentTargetName)); |
elseif (spellInfo.Type == "Party") then |
CastInfoHealingTargetNames = {}; |
if (spellInfo.InRange('party1')) then tinsert(CastInfoHealingTargetNames, unitFullName('party1')) end |
if (spellInfo.InRange('party2')) then tinsert(CastInfoHealingTargetNames, unitFullName('party2')) end |
if (spellInfo.InRange('party3')) then tinsert(CastInfoHealingTargetNames, unitFullName('party3')) end |
if (spellInfo.InRange('party4')) then tinsert(CastInfoHealingTargetNames, unitFullName('party4')) end |
CastInfoHealingSize = GetHealSize(spellName, tonumber(spellRank:match("(%d+)"))) or 0; |
CastInfoIsCasting = true; |
CastInfoEndTime = (select(6, UnitCastingInfo('player')) or 0) / 1000; |
commSend(string.format("002%05d%s", math.min(CastInfoHealingSize, 99999), tconcat(CastInfoHealingTargetNames, ":"))); |
tinsert(CastInfoHealingTargetNames, 1, playerName); |
self.Callbacks:Fire("HealComm_DirectHealStart", playerName, CastInfoHealingSize, CastInfoEndTime, unpack(CastInfoHealingTargetNames)); |
end |
end |
end |
function lib:CHAT_MSG_ADDON(prefix, msg, distribution, sender) |
if (prefix ~= "HealComm") then return end |
if (sender == playerName) then return end |
-- Workaround: Sometimes in battlegrounds the sender argument is not a |
-- fully qualified name (the realm is missing), even though the sender is |
-- from a different realm. |
if (distribution == "BATTLEGROUND") then |
sender = unitFullName(sender) or sender; |
end |
-- Get message type |
local msgtype = tonumber(msg:sub(1, 3)); |
if (not msgtype) then return end |
if (msgtype == 0) then -- DirectHealStart |
local healSize = tonumber(msg:sub(4, 8)); |
local targetName = msg:sub(9, -1); |
if (healSize and targetName) then |
local endTime = select(6, UnitCastingInfo(sender)); |
if (endTime) then |
if (distribution == "BATTLEGROUND") then |
targetName = convertRealm(targetName, extractRealm(sender)); |
end |
endTime = endTime / 1000; |
entryUpdate(sender, targetName, healSize, endTime); |
self.Callbacks:Fire("HealComm_DirectHealStart", sender, healSize, endTime, targetName); |
end |
end |
elseif (msgtype == 1) then -- HealStop |
local targetNames, healSize = entryRetrieve(sender); |
entryDelete(sender); |
if (type(targetNames) == "table") then |
self.Callbacks:Fire("HealComm_DirectHealStop", sender, healSize, msg:sub(4, 4) == "S", unpack(targetNames)); |
elseif (targetNames) then |
self.Callbacks:Fire("HealComm_DirectHealStop", sender, healSize, msg:sub(4, 4) == "S", targetNames); |
end |
elseif (msgtype == 2) then -- MultiTargetHealStart |
local healSize = tonumber(msg:sub(4, 8)); |
local targetNames = {strsplit(":", msg:sub(9, -1))}; |
if (healSize) then |
local endTime = select(6, UnitCastingInfo(sender)); |
if (endTime) then |
if (distribution == "BATTLEGROUND") then |
local senderRealm = extractRealm(sender); |
for k, targetName in pairs(targetNames) do |
targetNames[k] = convertRealm(targetName, senderRealm); |
end |
end |
endTime = endTime / 1000; |
tinsert(targetNames, 1, sender); |
entryUpdate(sender, targetNames, healSize, endTime); |
self.Callbacks:Fire("HealComm_DirectHealStart", sender, healSize, endTime, unpack(targetNames)); |
end |
end |
elseif (msgtype >= 998) then -- AnnounceVersion |
local version = tonumber(msg:sub(4, -1)); |
if (version) then |
Versions[sender] = version; |
if (msgtype == 999) then -- RequestVersion |
if (distribution ~= "BATTLEGROUND") then |
-- Reply in whisper if possible |
commSend("998" .. tostring(MINOR_VERSION), "WHISPER", sender); |
else |
-- Reply to inbound distribution channel |
commSend("998" .. tostring(MINOR_VERSION), distribution); |
end |
end |
end |
end |
end |
function lib:UNIT_SPELLCAST_DELAYED(unit) |
if (unit == 'player') then |
if (CastInfoIsCasting) then |
local endTime = select(6, UnitCastingInfo('player')); |
if (endTime) then |
CastInfoEndTime = endTime / 1000; |
if (type(CastInfoHealingTargetNames) == "table") then |
self.Callbacks:Fire("HealComm_DirectHealDelayed", playerName, CastInfoHealingSize, CastInfoEndTime, unpack(CastInfoHealingTargetNames)); |
elseif (CastInfoHealingTargetNames) then |
self.Callbacks:Fire("HealComm_DirectHealDelayed", playerName, CastInfoHealingSize, CastInfoEndTime, CastInfoHealingTargetNames); |
end |
end |
end |
elseif (unit ~= 'target' and unit ~= 'focus') then |
local healerName = unitFullName(unit); |
local targetNames, healSize = entryRetrieve(healerName) |
if (targetNames) then |
local endTime = select(6, UnitCastingInfo(healerName)); |
if (endTime) then |
endTime = endTime / 1000; |
HealTime[healerName] = endTime; |
if (type(targetNames) == "table") then |
self.Callbacks:Fire("HealComm_DirectHealDelayed", healerName, healSize, endTime, unpack(targetNames)); |
elseif (targetNames) then |
self.Callbacks:Fire("HealComm_DirectHealDelayed", healerName, healSize, endTime, targetNames); |
end |
end |
end |
end |
end |
function lib:UNIT_SPELLCAST_SUCCEEDED(unit, spellName, spellRank, spellCastIndex) |
if (unit ~= 'player') then return end |
if (CastInfoIsCasting) then |
CastInfoIsCasting = false; |
commSend("001S"); |
if (type(CastInfoHealingTargetNames) == "table") then |
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, true, unpack(CastInfoHealingTargetNames)); |
elseif (CastInfoHealingTargetNames) then |
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, true, CastInfoHealingTargetNames); |
end |
else |
if (LastSpellCastIndex ~= spellCastIndex) then |
-- Instant Cast Spells |
end |
end |
end |
function lib:UNIT_SPELLCAST_STOP(unit, spellName, spellRank, spellCastIndex) |
if (unit == 'player' and CastInfoIsCasting) then |
LastSpellCastIndex = spellCastIndex; |
CastInfoIsCasting = false; |
commSend("001F"); |
if (type(CastInfoHealingTargetNames) == "table") then |
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, false, unpack(CastInfoHealingTargetNames)); |
elseif (CastInfoHealingTargetNames) then |
self.Callbacks:Fire("HealComm_DirectHealStop", playerName, CastInfoHealingSize, false, CastInfoHealingTargetNames); |
end |
end |
end |
function lib:PLAYER_ALIVE() |
-- This event is only fired at initial login, not at reloadui or load-on-demand loading. |
-- The initialisation is triggered again, since none of the initialisation had any effect |
-- prior to this event firing (no messages sent and InBattlegroundOrArena and InRaidOrParty |
-- are probably not correctly initialised). |
lib:Initialise(); |
-- Only receive once |
self.EventFrame:UnregisterEvent("PLAYER_ALIVE"); |
end |
function lib:PLAYER_ENTERING_WORLD() |
HealTime = {}; |
HealTarget = {}; |
HealSize = {}; |
HealModifier = {}; |
end |
function lib:PARTY_MEMBERS_CHANGED() |
local wasInRaidOrParty = InRaidOrParty; |
InRaidOrParty = (GetNumRaidMembers() > 0) or (GetNumPartyMembers() > 0); |
-- Announce and request version when joining a group |
if (not wasInRaidOrParty and InRaidOrParty) then |
commSend("999" .. tostring(MINOR_VERSION)); |
end |
end |
function lib:RAID_ROSTER_UPDATE() |
self:PARTY_MEMBERS_CHANGED(); |
end |
function lib:Initialise() |
local it = select(2, IsInInstance()); |
InBattlegroundOrArena = (it == "pvp") or (it == "arena"); |
InRaidOrParty = (GetNumRaidMembers() > 0) or (GetNumPartyMembers() > 0); |
-- Announce and request version in group and in guild |
commSend("999" .. tostring(MINOR_VERSION)); |
if (IsInGuild()) then |
commSend("999" .. tostring(MINOR_VERSION), "GUILD"); |
end |
end |
lib:Initialise(); |
## Interface: 30100 |
## Title: Lib: HealComm-3.0 |
## Notes: Keeps track of healing spells cast by everyone in a group. |
## Author: xbeeps |
## X-Category: Library |
## X-Curse-Packaged-Version: r48 |
## X-Curse-Project-Name: LibHealComm-3.0 |
## X-Curse-Project-ID: libhealcomm-3-0 |
## X-Curse-Repository-ID: wow/libhealcomm-3-0/mainline |
LibStub\LibStub.lua |
CallbackHandler-1.0\CallbackHandler-1.0.lua |
lib.xml |
------------------------------------------------------------------------ |
r44 | greltok | 2009-02-12 08:14:45 +0000 (Thu, 12 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Fixes for cross-server BGs - callbacks are now passed the fully qualified name(s) of the healing target(s). HealSize data is now stored by fully qualified name. |
------------------------------------------------------------------------ |
r43 | jlam | 2009-02-11 16:05:05 +0000 (Wed, 11 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Divine Plea reduces healing by 50% in WoW patch 3.0.9. |
------------------------------------------------------------------------ |
r42 | xbeeps | 2009-02-08 13:42:55 +0000 (Sun, 08 Feb 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
- Properly invalidate glyph cache when changing glyphs. |
------------------------------------------------------------------------ |
r39 | jlam | 2009-01-21 23:20:00 +0000 (Wed, 21 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Add support for Glyph of Regrowth (druids). |
------------------------------------------------------------------------ |
r38 | jlam | 2009-01-21 17:24:46 +0000 (Wed, 21 Jan 2009) | 2 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Druid fixes for 3.0.8: |
- Nourish also gets 20% boost when player's Wild Growth is on target. |
------------------------------------------------------------------------ |
r37 | jlam | 2009-01-20 23:41:32 +0000 (Tue, 20 Jan 2009) | 3 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Changes for patch 3.0.8: |
- Glyph of Flash of Light no longer modifies the heal amount. |
- Healing Way is a flat 18% increase to Healing Wave if the buff is on the target. |
------------------------------------------------------------------------ |
r36 | jlam | 2009-01-19 18:48:15 +0000 (Mon, 19 Jan 2009) | 4 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Paladin updates from cremor: |
- Drop Blessing of Light and Greater Blessing of Light, which have been removed in WotLK. |
- Detect effects of the self-buffs Divine Plea and Avenging Wrath. |
- In WotLK, the bonus healing effects have been increased by 25% for Flash of Light and Holy Light. |
------------------------------------------------------------------------ |
r35 | jlam | 2009-01-18 15:12:54 +0000 (Sun, 18 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
GetHealSize may be called with "nil" as the target in the case of a "Party" heal. Check that it's non-nil before checking the buffs on target. This should fix an error in detecting the Grace buff when Prayer of Healing is cast. |
------------------------------------------------------------------------ |
r34 | jlam | 2009-01-18 00:27:40 +0000 (Sun, 18 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Table indices can't be nil. |
------------------------------------------------------------------------ |
r33 | jlam | 2009-01-16 18:09:04 +0000 (Fri, 16 Jan 2009) | 3 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Account for the Discipline Priest talents Focused Power and Grace. |
The Grace buff detection will be moved after the more general buff/debuff modifications have been made. |
------------------------------------------------------------------------ |
r32 | jlam | 2009-01-16 17:29:28 +0000 (Fri, 16 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Rename "effectiveHealBuff" to "effectiveHealModifier" to more accurately self-document its purpose. |
------------------------------------------------------------------------ |
r31 | jlam | 2009-01-16 17:24:00 +0000 (Fri, 16 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Detect Glyph of Flash of Light for Paladins. |
------------------------------------------------------------------------ |
r29 | jlam | 2009-01-16 08:00:35 +0000 (Fri, 16 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Cache the glyphs that are being used instead of iterating over them each time we call detectGlyph() to see if a particular glyph is in use. |
------------------------------------------------------------------------ |
r28 | pastamancer | 2009-01-15 05:28:07 +0000 (Thu, 15 Jan 2009) | 2 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Ticket #3: effectiveHealingBuff -> effectiveHealBuff |
------------------------------------------------------------------------ |
r27 | jlam | 2009-01-13 20:25:19 +0000 (Tue, 13 Jan 2009) | 11 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Reorganize code slightly for less copy-and-paste duplication. We now set three variables that are tuned within each spell-specific section: |
- baseHealSize is the base heal amount of the spell |
- nBonus is the bonus healing effect for the spell |
- effectiveHealBuff is the increase to total (effective) healing of the spell. |
Each of these variables may be modified by talents, buffs and equipped relics. These variables are used in the final global formula to determine the heal estimate: |
effectiveHeal = effectiveHealBuff * (baseHealSize + nBonus) |
This makes it easier to see how each talent, buff or relic affects the heal estimate. |
------------------------------------------------------------------------ |
r26 | jlam | 2009-01-13 19:58:03 +0000 (Tue, 13 Jan 2009) | 4 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Druid fixes: |
- Account for Idols that affect Healing Touch. |
- Nourish has a 20% to effective healing if your rejuv/regrowth/lifebloom is on the target. |
- Clean up bookkeeping for effective healing buff. |
------------------------------------------------------------------------ |
r25 | jlam | 2009-01-13 18:05:51 +0000 (Tue, 13 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Clean up bookkeeping in shaman spell modifiers. |
------------------------------------------------------------------------ |
r24 | jlam | 2009-01-13 17:12:05 +0000 (Tue, 13 Jan 2009) | 6 lines |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Paladin fixes: |
- Create spell-specific tables of librams. |
- Add WotLK librarms. |
- Glyphed Seal of Light increases effective healing by 5%. |
- Simplify the bookkeeping of factors that buff effective healing by wrapping it all up into a single factor "effectiveHealBuff". |
- The librams all increase the spellpower of their respective spells, so make the calculation of the healing bonus more transparent and fix the Holy Light bonus calculation. |
------------------------------------------------------------------------ |
r23 | jlam | 2009-01-13 16:26:23 +0000 (Tue, 13 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Add Tree of Life target buff that increases healing received by 6%. |
------------------------------------------------------------------------ |
r22 | jlam | 2009-01-09 02:23:46 +0000 (Fri, 09 Jan 2009) | 1 line |
Changed paths: |
M /trunk/LibHealComm-3.0.lua |
Move Tidal Waves bonus back to proper spot in the calculations. |
------------------------------------------------------------------------ |
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info |
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke |
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! |
local LibStub = _G[LIBSTUB_MAJOR] |
if not LibStub or LibStub.minor < LIBSTUB_MINOR then |
LibStub = LibStub or {libs = {}, minors = {} } |
_G[LIBSTUB_MAJOR] = LibStub |
LibStub.minor = LIBSTUB_MINOR |
function LibStub:NewLibrary(major, minor) |
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") |
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") |
local oldminor = self.minors[major] |
if oldminor and oldminor >= minor then return nil end |
self.minors[major], self.libs[major] = minor, self.libs[major] or {} |
return self.libs[major], oldminor |
end |
function LibStub:GetLibrary(major, silent) |
if not self.libs[major] and not silent then |
error(("Cannot find a library instance of %q."):format(tostring(major)), 2) |
end |
return self.libs[major], self.minors[major] |
end |
function LibStub:IterateLibraries() return pairs(self.libs) end |
setmetatable(LibStub, { __call = LibStub.GetLibrary }) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/"> |
<Script file="LibHealComm-3.0.lua"/> |
</Ui> |
--[[ |
Elements handled: |
.RuneBar [fontstring or table] |
FontString only: |
- space: The space between each "counter". (Default: " ") |
- symbol: The symbol used when cooldowns are over. (Default: "*") |
- interval: The time offset used for the update script. (Default: 0.5) |
--]] |
local localized, class = UnitClass('player') |
local dummy = CreateFrame('Frame') |
local colors = { |
[1] = {0.77, 0.12, 0.23}, |
[2] = {0.3, 0.8, 0.1}, |
[3] = {0, 0.4, 0.7}, |
[4] = {0.8, 0.8, 0.8}, |
} |
local OnUpdateBar, OnUpdateText |
do |
local total = 0 |
function OnUpdateText(self, elapsed) |
total = total + elapsed |
if(total >= (self.bars.RuneBar.interval or 0.5)) then |
self:UpdateElement('RuneBar') |
total = 0 |
end |
end |
function OnUpdateBar(self, rune) |
local start, duration, ready = GetRuneCooldown(rune) |
if(ready) then |
self:SetValue(1) |
self:SetScript('OnUpdate', nil) |
else |
self:SetValue((GetTime() - start) / duration) |
end |
end |
end |
local function UpdateStatusBar(self, event, rune, usable) |
if(rune and not usable and GetRuneType(rune)) then |
self.bars.RuneBar.bars[rune]:SetScript('OnUpdate', function(self) OnUpdateBar(self, rune) end) |
end |
end |
local function Update(self, event, rune) |
local runebar = self.bars.RuneBar |
if(#runebar.bars == 0) then |
local text = '' |
for i = 1, 6 do |
local start, duration, ready, temp = GetRuneCooldown(i) |
local r, g, b = unpack(runebar.colors[GetRuneType(i)]) |
local temp = ready and (runebar.symbol or '*') or (duration - math.floor(GetTime() - start)) |
text = string.format('%s|cff%02x%02x%02x%s%s|r', text, r * 255, g * 255, b * 255, temp, runebar.space or ' ') |
end |
runebar:SetText(text) |
else |
for i = 1, 6 do |
local runetype = GetRuneType(i) |
if(runetype) then |
runebar.bars[i]:SetStatusBarColor(unpack(runebar.colors[runetype])) |
end |
end |
end |
end |
local function Enable(self, unit) |
if not (unit == 'player' and class == 'DEATHKNIGHT') then return end |
if(self.bars and self.bars.RuneBar)then |
local runebar = self.bars.RuneBar |
if(not runebar.colors) then |
runebar.colors = self.colors.runes or colors |
end |
self:RegisterEvent('RUNE_TYPE_UPDATE', Update) |
if(#runebar.bars == 0) then |
self:RegisterEvent('RUNE_POWER_UPDATE', Update) |
dummy:SetScript('OnUpdate', function(s, e) OnUpdateText(self, e) end) |
else |
self:RegisterEvent('RUNE_POWER_UPDATE', UpdateStatusBar) |
end |
RuneFrame:Hide() |
return true |
end |
end |
local function Disable(self) |
local runebar = self.bars.RuneBar |
if(runebar) then |
self:RegisterEvent('RUNE_TYPE_UPDATE', Update) |
if(#runebar == 0) then |
self:UnregisterEvent('RUNE_POWER_UPDATE', Update) |
dummy:SetScript('OnUpdate', nil) |
else |
self:UnregisterEvent('RUNE_POWER_UPDATE', UpdateStatusBar) |
end |
RuneFrame:Show() |
end |
end |
oUF:AddElement('RuneBar', Update, Enable, Disable) |
## Interface: 30100 |
## Author: p3lim | modified by Smee |
## Version: 30100.2 |
## Title: oUF_SmeeRunes |
## Notes: Death Knight rune support for oUF layouts. |
## RequiredDeps: oUF |
oUF_SmeeRunes.lua |
local _G = getfenv(0) |
local _,playerClass = UnitClass("player") |
local tinsert = table.insert |
local layoutName = "oUF_Smee2" |
_G[layoutName] = LibStub("AceAddon-3.0"):NewAddon(layoutName,"AceConsole-3.0") |
local addon = _G[layoutName]; |
addon.LSM = LibStub("LibSharedMedia-3.0") |
addon.build = {} |
addon.build.version, addon.build.build, addon.build.date, addon.build.tocversion = GetBuildInfo() |
--------------- |
-- UI tweaks -- |
--------------- |
local LDB = LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject(layoutName, { |
label = "|cFF006699oUF|r_|cFFFF3300Smee|r", |
type = "launcher", |
icon = "Interface\\Icons\\Spell_Nature_StormReach", |
}) |
function LDB.OnClick(self, button) |
if button == "RightButton" then |
addon:OpenConfig() |
else |
if addon.db.profile.enabled then |
if IsAltKeyDown() then |
if IsControlKeyDown() then |
addon:ToggleDebug() |
end |
elseif IsShiftKeyDown() then |
-- addon:DoSomething() |
end |
else |
--addon:ToggleActive(true) |
end |
end |
end |
function addon:ToggleDebug() |
self.enabledDebugMessages=not self.enabledDebugMessages |
self:Print("Debug messages : "..(self.enabledDebugMessages and "ON" or "OFF")) |
end |
function LDB.OnTooltipShow(tt) |
tt:AddLine(layoutName) |
tt:AddLine("Debugging "..(addon.enabledDebugMessages and "en" or "dis").."abled.") |
tt:AddLine("--") |
tt:AddLine("Ctrl + Alt + Left Click : Toggle Debug Messages") |
tt:AddLine("Right Click : Open Config") |
end |
local function dummy(arg) end |
local function GetClassColor(unit) |
local _,unitClass = UnitClass(unit) |
return unpack(addon.db.profile.colors.class[unitClass]) |
end |
local function menu(self) |
local unit,cunit = self.unit:sub(1, -2), self.unit:gsub("(.)", string.upper, 1) |
if(unit == "party" or unit == "partypet") then |
ToggleDropDownMenu(1, nil, _G["PartyMemberFrame"..self.id.."DropDown"], "cursor", 0, 0) |
elseif(_G[cunit.."FrameDropDown"]) then |
ToggleDropDownMenu(1, nil, _G[cunit.."FrameDropDown"], "cursor", 0, 0) |
end |
end |
---------------------- |
-- helper functions -- |
---------------------- |
local function round(num, idp) |
if idp and idp>0 then return math.floor(num * mult + 0.5) / (10^idp) end |
return math.floor(num + 0.5) |
end |
local function numberize(val) |
if(val >= 1e3) then return ("%.1fk"):format(val / 1e3) |
elseif (val >= 1e6) then return ("%.1fm"):format(val / 1e6) |
else return val |
end |
end |
function Hex(r, g, b) |
if type(r) == "table" then |
if r.r then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end |
end |
return string.format("|cff%02x%02x%02x", r*255, g*255, b*255) |
end |
local function GetDifficultyColor(level) |
if level == '??' then return .69,.31,.31 |
else |
local levelDiff = UnitLevel('target') - UnitLevel('player') |
if levelDiff >= 5 then |
return .69,.31,.31 |
elseif levelDiff >= 3 then |
return .71,.43,.27 |
elseif levelDiff >= -2 then |
return .84,.75,.65 |
elseif -levelDiff <= GetQuestGreenRange() then |
return .33,.59,.33 |
else |
return .55,.57,.61 |
end |
end |
end |
local function GetFormattedTime(s) |
local auraDb = addon.db.profile.auras.timers |
local DAY,HOUR,MINUTE,SHORT = auraDb.values.DAY,auraDb.values.HOUR,auraDb.values.MINUTE,auraDb.values.SHORT |
if s >= DAY then |
return format('%dd', floor(s/DAY + 0.5)), s % DAY |
elseif s >= HOUR then |
return format('%dh', floor(s/HOUR + 0.5)), s % HOUR |
elseif s >= MINUTE then |
if s <= MINUTE*3 and auraDb.UsingMMSS then |
return format('%d:%02d', floor(s/60), s % MINUTE), s - floor(s) |
end |
return format('%dm', floor(s/MINUTE + 0.5)), s % MINUTE |
elseif s > 2 then |
return floor(s + 0.5), s - floor(s) |
end |
return format("%0.1f", s), 0.1 |
end |
local function GetFormattedFont(s) |
local auraDb = addon.db.profile.auras.timers |
local DAY,HOUR,MINUTE,SHORT = auraDb.values.DAY,auraDb.values.HOUR,auraDb.values.MINUTE,auraDb.values.SHORT |
if s > DAY then |
style = auraDb.cooldownTimerStyle.days |
elseif s > HOUR then |
style = auraDb.cooldownTimerStyle.hrs |
elseif s > MINUTE then |
style = auraDb.cooldownTimerStyle.mins |
elseif s > SHORT then |
style = auraDb.cooldownTimerStyle.secs |
else |
style = auraDb.cooldownTimerStyle.short |
end |
return style.s, style.r, style.g, style.b |
end |
------------------ |
-- UPDATE HOOKS -- |
------------------ |
local function updateBanzai(self, unit, aggro) |
if self.unit ~= unit then return end |
if aggro == 1 then self.BanzaiIndicator:Show() |
else self.BanzaiIndicator:Hide() end |
end |
local function UpdateThreat(self, event, unit) |
if((not unit) or (not event)) or (UnitIsGhost(unit) or UnitIsDead(unit) or (not UnitIsConnected(unit))) then return end |
if unit == "player" or unit:gmatch("raid")() or unit:gmatch("party")() or unit:gmatch("pet")() or unit:gmatch("focus")() or unit:gmatch("target")() then |
unitTarget = unit.."target" |
else |
unitTarget = unit.."-target" |
end |
local unitTarget = (unit =='player' and "target" or unit.."target") |
local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(unit, unitTarget); |
if not(rawthreatpct == nil) then |
self.Threat:SetText( Hex(GetThreatStatusColor(status)) .. string.format("%.0f", rawthreatpct ) .. "|r" ) |
else |
self.Threat:SetText('') |
end |
end |
local function PostUpdateHealth(self, event, unit, bar, min, max) |
bar:SetStatusBarColor(0.25, 0.25, 0.25) -- Default statusbar color |
if (UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) or not UnitIsConnected(unit)) then |
color = self.colors.tapped |
else |
bar:SetStatusBarColor(.25,.25,.35) -- Default statusbar color |
end |
end |
local function PostUpdatePower(self, event, unit, bar, min, max) |
if (UnitIsGhost(unit) or UnitIsDead(unit) or not UnitIsConnected(unit)) then |
bar:SetValue(0) |
end |
end |
--[[---------------------------------------- |
AURA UPDATE HOOKS |
----------------------------------------]]-- |
local function SetAuraPosition(self, icons, x) |
addon:Debug("Repositioning auras.") |
if(icons and x > 0) then |
local col,row = 0,0 |
local spacing = icons.spacing or 0 |
local size = (icons.size or 16) + spacing |
local anchor = icons.initialAnchor or "BOTTOMLEFT" |
local growthx = (icons["growth-x"] == "LEFT" and -1) or 1 |
local growthy = (icons["growth-y"] == "DOWN" and -1) or 1 |
local cols = icons.Colomns |
-- local rows = icons.Rows -- Right now only horizontally conditioned aura frames are supported |
for i = 1, x do |
local button = icons[i] |
if(button and button:IsShown()) then |
if(icons.gap and button.debuff) then |
if(col > 0) then col = col + 1 end |
icons.gap = false |
end |
if(col >= cols) then col,row = 0, row + 1 end |
local anchorFrom,anchorObj,anchorTo |
local vert = icons["growth-y"] == 'UP' and 'BOTTOM' or 'TOP' |
if growthx < 0 then -- <<<< |
anchorFrom,anchorObj,anchorTo,anchorX,anchorY = vert.."RIGHT",icons[i-1],vert.."LEFT",-spacing, 0 |
if i == 1 or col == 0 then anchorObj,anchorTo,anchorY = icons,vert.."RIGHT", (row * size * growthy) end |
else -- >>>> |
anchorFrom,anchorObj,anchorTo,anchorX,anchorY = vert.."LEFT",icons[i-1],vert.."RIGHT",spacing,0 |
if i == 1 or col == 0 then anchorObj,anchorTo,anchorY = icons,vert.."LEFT", (row * size * growthy) end |
end |
addon:Debug("placing aura[".. i.."]"..anchorFrom..":"..tostring(anchorObj)..":"..anchorTo..":"..anchorX..":"..anchorY..".") |
button:SetWidth(icons.size) |
button:SetHeight(icons.size) |
button:ClearAllPoints() |
button:SetPoint(anchorFrom, anchorObj, anchorTo, anchorX, anchorY) |
col = col + 1 |
end |
end |
end |
end |
local function PreUpdateAura(event, unit) |
end |
local function PostUpdateAuraIcon(self,icons, unit, icon, index, offset, filter, isDebuff) |
-- print(self.unit,icons, unit, icon, index, offset, filter, isDebuff) |
-- if(index > icons.num)then icons[index]:Hide() end |
end |
local function updateAuraIcon(self,event) |
local db = addon.db.profile |
local name,size,outline = (addon.LSM:Fetch('font',db.auras.font.name) or self.db.profile.fonts.default), db.auras.font.size, db.auras.font.outline |
self.count:SetFont(name,size,outline) |
if(self.duration~=nil and self.timeLeft~=nil) then |
if(db.auras.timers.enabled)then |
self.remaining:Show() |
self.overlay:Show() |
if(self.duration > 0 or self.timeLeft > 0) then |
self.overlay:SetParent(self.cd) |
local timeLeft = self.timeLeft - GetTime() |
local multiplier, r, g, b = GetFormattedFont(timeLeft) |
self.overlay:SetVertexColor(r,g,b,1) |
self.remaining:SetText(GetFormattedTime(timeLeft)) |
self.remaining:SetFont(name,(db.auras.timers.useEnlargedFonts and size * multiplier or size),outline) |
self.remaining:SetTextColor(r,g,b) |
end |
else |
self.remaining:Hide() |
self.overlay:Hide() |
end |
end |
if(addon.build.tocversion >= 30100)then |
self:SetScale((self.owner == 'player' ) and self:GetParent().playerSize or 1) |
local frameUnit = self.parent:GetParent().unit |
if frameUnit and self.owner then |
local frameOwner, buffOwner = UnitGUID(frameUnit),UnitGUID(self.owner) |
if frameUnit ~='player' and buffOwner == frameOwner then self.ownership:Show() else self.ownership:Hide() end |
end |
else |
self:SetScale(self.isPlayer and self:GetParent().playerSize or 1) |
end |
end |
local function PostCreateAuraIcon(self, button, icons, index, debuff) |
local db = addon.db.profile |
local fontPath = addon.LSM:Fetch('font',db.auras.font.name) or self.db.profile.fonts.default |
button.cd:SetReverse() |
button.overlay:SetTexture(self.textures.border) |
button.overlay:SetTexCoord(0, 1, 0, 1) |
button.overlay.Hide = function(self) self:SetVertexColor(0.25, 0.25, 0.25) end |
local ownership = button:CreateTexture(nil, "OVERLAY") |
ownership:SetTexture(self.textures.border) |
ownership:SetPoint("TOPLEFT", button,"TOPLEFT", -2,2) |
ownership:SetPoint("BOTTOMRIGHT", button,"BOTTOMRIGHT",2,-2) |
ownership:SetTexCoord(0, 1, 0, 1) |
ownership:Hide() |
button.ownership = ownership |
button.count:SetParent(button.cd) |
button.count:SetFont(fontPath, db.frames.font.size, db.frames.font.outline) |
button.count:SetPoint("CENTER",button,"BOTTOM",0,-2) |
if (not debuff and self.unit =="player") then |
button:SetScript('OnMouseUp', function(self, mouseButton) |
if mouseButton == 'RightButton' then |
CancelUnitBuff('player', index) |
end |
end) |
end |
local remaining = button.cd:CreateFontString(nil, "OVERLAY") |
remaining:SetFont(fontPath, db.auras.font.size, db.auras.font.outline) |
remaining:SetPoint("CENTER", button, 4, 4) |
remaining:SetJustifyH("CENTER") |
button.remaining = remaining |
button:SetScript('OnUpdate', updateAuraIcon) |
end |
------------ |
-- makers -- |
------------ |
function addon:makeAuraFrame(obj,auraTypes) |
for _,auraType in pairs(auraTypes)do |
local db = obj.db[auraType] |
local auraFrame = CreateFrame("Frame", nil, obj) |
auraFrame.size = db.size * 1.1 |
auraFrame.playerSize = db.playerSize |
auraFrame:SetHeight(auraFrame.size) |
auraFrame:SetWidth(auraFrame.size * db.Colomns) |
auraFrame:SetPoint(db.anchorFromPoint,obj.elements[db.anchorTo],db.anchorToPoint,db.anchorX,db.anchorY) |
auraFrame.spacing=db.spacing |
auraFrame.num = db.count |
auraFrame.Colomns = db.Colomns |
auraFrame.rows = db.rows |
auraFrame["growth-x"] = db["growth-x"] |
auraFrame["growth-y"] = db["growth-y"] |
auraFrame.filter = db.filter |
auraFrame.onlyShowPlayer = db.onlyShowPlayer or nil |
db.setup = false; |
obj[auraType] = auraFrame; |
end |
end |
function makeComboPoints(self,anchorFrom,anchorTo,aX,aY) |
local db = addon.db.profile.frames.font |
local fontPath = addon.LSM:Fetch('font',db.name) or self.db.profile.fonts.default |
local cpoints = self:CreateFontString(nil, "OVERLAY") |
cpoints:SetPoint(anchorFrom, self, anchorTo, aX,aY) |
cpoints:SetFont(fontPath, db.size, db.outline) |
cpoints:SetTextColor(1,1,1) |
cpoints:SetJustifyH("CENTER") |
self.CPoints = cpoints |
self.FontObjects["ComboPoints"] = { |
name = "Combo Points", |
object = self.CPoints |
} |
end |
function makeCombatFeedbackText(self) |
if not IsAddOnLoaded("oUF_CombatFeedback") then return end |
local db = addon.db.profile.fonts |
local healthBar = self.bars.Health |
self.CombatFeedbackText = healthBar:CreateFontString(nil, "OVERLAY") |
self.CombatFeedbackText:SetPoint("CENTER", healthBar, "CENTER", 0, 0) |
self.CombatFeedbackText:SetFont(db['default'].name, db['default'].size, db['default'].outline) |
self.CombatFeedbackText.maxAlpha = .8 |
self.FontObjects["CombatFeedbackText"] ={ |
name = "Combat Feedback Text", |
object = self.CombatFeedbackText |
} |
end |
function makeCastBar(self) |
local db = addon.db.profile |
local bar = CreateFrame("StatusBar", nil, self) |
bar:SetBackdrop(self.db.bars.Castbar.Backdrop) |
bar:SetBackdropColor(unpack(self.db.bars.Castbar.BackdropColor)) |
bar:SetStatusBarTexture(self.textures.statusbar) |
bar:SetStatusBarColor(unpack(self.db.bars.Castbar.StatusBarColor)) |
bar.reverse = self.db.bars.Castbar.reverse |
bar.bg = bar:CreateTexture(nil, "BORDER") |
bar.bg:SetAllPoints(bar) |
bar.bg:SetTexture(unpack(self.db.bars.Castbar.bgColor)) |
self.elements["Castbar"] = bar |
bar.elements={ |
['parent'] = self, |
['self'] = bar, |
['Castbar'] = bar, |
} |
self.db.bars.Castbar.Text.anchorTo = "Castbar" |
bar.Text = addon:makeFontObject(bar,"Cast Name",self.db.bars.Castbar.Text) |
self.FontObjects['CastName']= { |
name = "Cast Name", |
object = bar.Text |
} |
self.db.bars.Castbar.Time.anchorTo = "Castbar" |
bar.Time = addon:makeFontObject(bar,"Cast Time",self.db.bars.Castbar.Time) |
self.FontObjects["CastTime"] = { |
name = "Cast Time", |
object = bar.Time |
} |
bar.CustomTimeText = function(self, duration) |
if self.casting then |
self.Time:SetFormattedText("%.1f", self.max - duration) |
elseif self.channeling then |
self.Time:SetFormattedText("%.1f", duration) |
end |
end |
if self.unit == "player" and self.db.bars.Castbar.SafeZone.enabled == true then |
bar.SafeZone = bar:CreateTexture(nil,"ARTWORK") |
bar.SafeZone:SetTexture(self.textures.statusbar) |
bar.SafeZone:SetVertexColor(unpack(self.db.bars.Castbar.SafeZone.colour)) |
bar.SafeZone:SetPoint("TOPRIGHT") |
bar.SafeZone:SetPoint("BOTTOMRIGHT") |
bar.accurate=true |
end |
bar:ClearAllPoints() |
bar:SetPoint(self.db.bars.Castbar.anchorFromPoint,self,self.db.bars.Castbar.anchorToPoint,self.db.bars.Castbar.anchorX,self.db.bars.Castbar.anchorY) |
bar:SetHeight(self.db.bars.Castbar.height) |
bar:SetWidth(self.db.bars.Castbar.width) |
self.bars.Castbar = bar; |
self.Castbar = bar; |
end |
function RunePostUpdate(self,settings) |
local runeBar = self:GetParent(); |
self:SetWidth(runeBar:GetWidth() / 100 * settings.width) |
self:SetHeight(runeBar:GetHeight() / 100 * settings.height) |
end |
function addon:makeRuneBar(frame) |
if frame.unitClass ~= "DEATHKNIGHT" then return end |
if not IsAddOnLoaded("oUF_SmeeRunes") then |
self:Error("oUF_SmeeRunes not loaded") |
return |
end |
local db = addon.db.profile |
local settings = frame.db.bars.RuneBar |
local rune, runeBarHeight,runeBarWidth=_,(frame.db.height/100) * settings.height, (frame.db.width/100) * settings.width |
local runebar = CreateFrame('Frame', nil, frame) |
runebar:SetParent(frame) |
runebar:SetBackdrop(frame.textures.background) |
runebar:SetBackdropColor(unpack(db.colors.backdropColors)) |
runebar:SetHeight(runeBarHeight) |
runebar:SetWidth(runeBarWidth) |
runebar:SetPoint(settings.anchorFromPoint,frame,settings.anchorToPoint,settings.anchorX,settings.anchorY) |
runebar.bars = {} |
runebar.runeMapping = { |
[1] = "BLOOD", |
[2] = "UNHOLY", |
[3] = "FROST", |
[4] = "DEATH", |
} |
runebar:Show() |
runebar.colors=db.colors.runes |
runebar.OnSizeChange = function() |
for index,bar in pairs(runebar.bars) do |
bar:RunePostUpdate(settings.runes[index]) |
end |
end |
runebar.SetChildOrientation = function(value) |
for index,bar in pairs(runebar.bars) do |
bar:SetOrientation(value) |
end |
end |
for i = 1, 6 do |
rune = CreateFrame('StatusBar', nil, runebar) |
local runeSetting = settings.runes[i] |
local anchor = runebar.bars[runeSetting.anchorTo] or runebar |
rune:SetPoint(runeSetting.anchorFromPoint, anchor, runeSetting.anchorToPoint,runeSetting.x,runeSetting.y) |
rune.index=i |
rune:SetStatusBarTexture(frame.textures.statusbar) |
rune:SetStatusBarColor(unpack(db.colors.runes[GetRuneType(i)])) |
rune:SetOrientation(settings.orientation) |
rune:SetBackdrop(frame.textures.background) |
rune:SetBackdropColor(0, 0, 0,0.2) |
rune:SetMinMaxValues(0, 1) |
rune.RunePostUpdate = RunePostUpdate |
rune.bg = rune:CreateTexture(nil, 'BORDER') |
rune.bg:SetAllPoints(rune) |
rune.bg:SetTexture(0, 0, 0,0.3) |
runebar.bars[i] = rune |
rune:RunePostUpdate(runeSetting) |
end |
runebar.OnSizeChange() |
frame.bars.RuneBar = runebar |
end |
function UpdateTotemBar(self) |
local totembar = self.bars.TotemBar |
if self.unitClass ~= "SHAMAN" or not totembar then return end |
local settings = self.db.bars.TotemBar |
local db = addon.db.profile |
local fontDb = db.frames.font -- setting this to the global font option for now, till i work out a per-frame policy. |
local fontName, fontSize, fontOutline = ( addon.LSM:Fetch('font',fontDb.name) or addon.db.profile.fonts.default), fontDb.size, fontDb.outline |
totembar:ClearAllPoints() |
totembar:SetPoint(settings.anchorFromPoint, self.elements[settings.anchorTo], settings.anchorToPoint,settings.anchorX,settings.anchorY) |
totembar:SetParent(self) |
totembar:Show() |
totembar:SetBackdropColor(1,1,1,0) |
for index,totem in pairs(totembar.totems)do |
totem.duration:ClearAllPoints() |
totem.duration:SetFont(fontName, fontSize, fontOutline) |
totem.duration:SetPoint(settings.Timer.anchorFromPoint,totem.icon,settings.Timer.anchorToPoint,settings.Timer.anchorX,settings.Timer.anchorY) |
totem:SetScale(settings.scale) |
end |
end |
function addon:makeTotemBar(frame) |
if frame.unitClass ~= "SHAMAN" then return end |
self:Debug("<<CREATING TOTEM FRAME>>") |
local totembar = _G["TotemFrame"] |
totembar.totems={} |
for i=1,TotemFrame:GetNumChildren() do |
local totem = _G["TotemFrameTotem"..i] |
local bg = _G["TotemFrameTotem"..i.."Background"] |
local icon = _G["TotemFrameTotem"..i.."Icon"] |
local duration = _G["TotemFrameTotem"..i.."Duration"] |
local _,oldTotemOverlay = totem:GetChildren() |
bg:Hide() |
oldTotemOverlay:Hide() |
local totemOverlay = icon:CreateTexture(nil, "OVERLAY") |
totemOverlay:SetTexture(frame.textures.border) |
totemOverlay:SetAllPoints(icon) |
totemOverlay:SetTexCoord(0, 1, 0, 1) |
totemOverlay.Hide = function(self) self:SetVertexColor(0.25, 0.25, 0.25) end |
duration:SetParent(totem) |
totem.overlay = totemOverlay |
totem.icon = icon |
totem.bg = bg |
totem.duration = duration |
totembar.totems[i] = totem |
frame.FontObjects["totem".. i .."Duration"] = { |
name = "Totem ".. i .." Timer Duration", |
object = totem.duration |
} |
end |
frame.bars.TotemBar = totembar |
end |
function RuneButton_OnEnter(self) |
if ( self.tooltipText ) then |
GameTooltip:SetOwner(self, "ANCHOR_RIGHT"); |
GameTooltip:SetText(self.tooltipText); |
GameTooltip:Show(); |
end |
end |
function addon:StealBar(self,frame,settings) |
local db = addon.db.profile |
if frame:IsUserPlaced() then |
frame:SetUserPlaced(false) |
end |
frame:ClearAllPoints() |
frame:SetPoint(settings.anchorFromPoint, self.elements[settings.anchorTo], settings.anchorToPoint,settings.anchorX,settings.anchorY) |
frame:SetHeight(settings.height) |
frame:SetWidth(settings.width) |
end |
function UpDateFrameSize(self) |
-- update bars dependant on frame size. |
for index,bar in pairs(self.db.bars)do |
if(bar.dependantOnFrameSize)then |
self.bars[index]:SetHeight( ((self.db.height - 3) / 100 ) * self.db.bars[index].height ) |
end |
end |
oUF_PowerSpark_ReapplySettings(self) |
end |
function addon:makeFontObject(frame,name,data) |
local db = addon.db.profile |
local parent = frame.elements and frame.elements[data.anchorTo] or frame |
-- make our font object, parenting it to the supplied anchor point. |
local fontObject = parent:CreateFontString(nil, "OVERLAY") |
fontObject:SetJustifyH(data.justifyH) |
fontObject:SetJustifyV(data.justifyV) |
fontObject:SetPoint(data.anchorFromPoint, parent,data.anchorToPoint, data.anchorX, data.anchorY) |
local fontDb = db.frames.font -- setting this to the global font option for now, till i work out a per-frame policy. |
fontObject:SetFont((self.LSM:Fetch('font',fontDb.name) or self.db.profile.fonts.default), fontDb.size, fontDb.outline) |
-- if the parent frame is the unitframe and therefore has an UpdateTag function, use it. |
if(frame.Tag~=nil and data.tag~=nil)then |
fontObject.tag = data.tag |
frame:Tag(fontObject, data.tag) |
end |
-- store the fontobject in the parent frames fontobject table. |
if(frame.FontObjects)then |
frame.FontObjects[name] = { |
name = data.desc, |
object = fontObject |
} |
end |
return fontObject |
end |
--==========================-- |
-- -- |
-- NORMAL STYLE -- |
-- -- |
--==========================-- |
local layout = function(self, unit) |
local db = addon.db.profile |
self.FontObjects = {} |
self.Indicators = {} |
self.db = db.frames.units[unit] |
self.colors = colors |
self.menu = menu |
_,self.unitClass = UnitClass(unit) |
self:RegisterForClicks("AnyUp") |
self:SetAttribute("*type2", "menu") |
self:SetScript("OnEnter", UnitFrame_OnEnter) |
self:SetScript("OnLeave", UnitFrame_OnLeave) |
self.BarFade = self.db.barFading |
self.disallowVehicleSwap = self.db.disallowVehicleSwap |
self.textures = { |
background = db.textures.backgrounds[self.db.textures.background], |
statusbar =db.textures.statusbars[self.db.textures.statusbar], |
border = db.textures.borders[self.db.textures.border], |
} |
self:SetBackdrop(self.textures.background) |
self:SetBackdropColor(unpack(db.colors.backdropColors)) |
self.elements={ |
["parent"] = self, |
["UIParent"] = UIParent, |
} |
--======-- |
-- BARS -- |
--======-- |
self.bars = {} |
local health = CreateFrame("StatusBar", nil, self) |
health:SetPoint("TOPLEFT", self,"TOPLEFT", 1,-1) |
health:SetPoint("TOPRIGHT", self,"TOPRIGHT",-1,-1) |
health:SetStatusBarTexture(self.textures.statusbar) |
health:SetHeight(((self.db.height - 3) / 100 ) * self.db.bars.Health.height) |
health.colorTapping = self.db.bars.Health.colorTapping |
health.colorDisconnected = self.db.bars.Health.colorDisconnected |
health.colorClass = self.db.bars.Health.colorClass |
health.colorReaction = self.db.bars.Health.colorReaction |
health.reverse = self.db.bars.Health.reverse |
health.bg = health:CreateTexture(nil, "BORDER") |
health.bg:SetAllPoints(health) |
local hpbg = self.db.bars.Health.bgColor |
health.bg:SetTexture(hpbg[1],hpbg[2],hpbg[3],hpbg[4]) |
self.elements["Health"] = health |
self.bars.Health = health |
self.hcbParent = health |
self.Health = health |
local power = CreateFrame("StatusBar", nil, self) |
power:SetPoint("TOPRIGHT", health, "BOTTOMRIGHT", 0, -1) |
power:SetPoint("TOPLEFT", health, "BOTTOMLEFT", 0, -1) |
power:SetStatusBarTexture(self.textures.statusbar) |
power:SetHeight( ((self.db.height - 3) / 100 ) * self.db.bars.Power.height) |
power.colorTapping = self.db.bars.Power.colorTapping |
power.colorDisconnected = self.db.bars.Power.colorDisconnected |
power.colorClass = self.db.bars.Power.colorClass |
power.colorReaction = self.db.bars.Power.colorReaction |
power.reverse = self.db.bars.Power.reverse |
power.OnSizeChange=function(object) |
self.db.bars.Health.height = 100 - self.db.bars.Power.height |
self:OnSizeChange() |
end |
health.OnSizeChange=function(object) |
self.db.bars.Power.height = 100 - self.db.bars.Health.height |
self:OnSizeChange() |
end |
power.bg = power:CreateTexture(nil, "BORDER") |
power.bg:SetAllPoints(power) |
local ppbg = self.db.bars.Power.bgColor |
power.bg:SetTexture(ppbg[1],ppbg[2],ppbg[3],ppbg[4]) |
self.elements["Power"] = power |
self.bars.Power = power |
self.Power = power |
for index, data in pairs(self.db.FontObjects) do |
self[index] = addon:makeFontObject(self,index,data) |
end |
--[[ |
if IsAddOnLoaded("oUF_SmeeAFK") and (unit ~= "targettarget" or unit ~= "focustarget") then |
self.AFK = {} |
self.AFK.object = health.text |
self.AFK.Tagger=self |
self.AFK.fontFormat = "|cFFFF0000AFK |cFFFFFFFF%s:%s" |
self.DC = {} |
self.DC.object = health.text |
self.DC.Tagger=self |
self.DC.fontFormat = "|cFFFF0000D/C |cFFFFFFFF%s:%s" |
end |
--]] |
--==========-- |
-- ICONS -- |
--==========-- |
--Leader Icon |
self.Leader = health:CreateTexture(nil, "OVERLAY") |
self.Leader:SetPoint("TOPLEFT", self, 0, 4) |
self.Leader:SetHeight(10) |
self.Leader:SetWidth(10) |
-- Raid Icon |
self.RaidIcon = health:CreateTexture(nil, "OVERLAY") |
self.RaidIcon:SetPoint("TOP", self, 0, 4) |
self.RaidIcon:SetHeight(10) |
self.RaidIcon:SetWidth(10) |
--Master Loot Icon |
self.MasterLooter = health:CreateTexture(nil, "OVERLAY") |
self.MasterLooter:SetPoint("TOPLEFT", self, 8, 4) |
self.MasterLooter:SetHeight(10) |
self.MasterLooter:SetWidth(10) |
self:SetAttribute("initial-height", self.db.height) -- Size |
self:SetAttribute("initial-width", self.db.width) -- Size |
--==========-- |
-- PLAYER -- |
--==========-- |
if unit == "player" then |
self.Combat = health:CreateTexture(nil, "OVERLAY") |
self.Combat:SetHeight(24) |
self.Combat:SetWidth(24) |
self.Combat:SetPoint("CENTER", self, "BOTTOMRIGHT", -5, 5) |
self.Combat:SetTexture([[Interface\CharacterFrame\UI-StateIcon]]) |
self.Combat:SetTexCoord(1/2, 1, 0.01, 0.5) |
-- Resting Icon |
self.Resting = health:CreateTexture(nil, "OVERLAY") |
self.Resting:SetPoint("TOP", self, 0, 8) |
self.Resting:SetHeight(16) |
self.Resting:SetWidth(16) |
self.Resting:SetTexture('Interface\\CharacterFrame\\UI-StateIcon') |
self.Resting:SetTexCoord(0, 0.5, 0, 0.42) |
if IsAddOnLoaded("oUF_PowerSpark") then |
self.Spark = power:CreateTexture(nil, "OVERLAY") |
self.Spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark") |
self.Spark:SetVertexColor(1, 1, 1, 0.8) |
self.Spark:SetBlendMode("ADD") |
self.Spark:SetHeight(power:GetHeight()*4) |
self.Spark:SetWidth(power:GetHeight()) |
end |
addon:makeRuneBar(self) |
addon:makeTotemBar(self) |
self.UpdateTotemBar = UpdateTotemBar |
self:UpdateTotemBar() |
if IsAddOnLoaded("CoolLine") and self.db.bars.CoolLine then |
addon:StealBar(self,_G["CoolLine"],self.db.bars.CoolLine) |
end |
addon:makeAuraFrame(self,{"Buffs","Debuffs"}) |
end |
--=====================================-- |
-- PET |
--=====================================-- |
if unit == "pet" then |
power.colorPower = true |
power.colorHappiness = true |
addon:makeAuraFrame(self,{"Buffs"}) |
-- makeComboPoints(self,"RIGHT","LEFT",-9, 3,38,"RIGHT") |
end |
--=====================================-- |
-- PET TARGET |
--=====================================-- |
if unit == "pettarget" then |
power.colorPower = true |
addon:makeAuraFrame(self,{"Debuffs"}) |
-- makeComboPoints(self,"RIGHT","LEFT",-9, 3,38,"RIGHT") |
end |
--===========-- |
-- TARGET -- |
--===========-- |
if unit == "target" then |
addon:makeAuraFrame(self,{"Debuffs","Buffs"}) |
end |
--===========-- |
-- FOCUS -- |
--===========-- |
if unit == "focus" then |
addon:makeAuraFrame(self,{"Buffs","Debuffs"}) |
end |
--=================-- |
-- FOCUSTARGET -- |
--=================-- |
if unit == "focustarget" then |
end |
--=================-- |
-- TARGETTARGET -- |
--=================-- |
if unit == "targettarget" then |
end |
--==============-- |
-- CASTBARS -- |
--==============-- |
if unit and self.db.bars.Castbar and self.db.bars.Castbar.enabled then |
makeCastBar(self) |
end |
--===============-- |
-- RANGEFADING -- |
--===============-- |
self.outsideRangeAlpha = self.db.range.outside |
self.inRangeAlpha = self.db.range.inside |
self.SpellRange = self.db.range.enabled |
--===================-- |
-- AGRRO INDICATOR -- |
--===================-- |
if IsAddOnLoaded("oUF_Banzai") then |
self.Banzai = updateBanzai |
self.BanzaiIndicator = power:CreateTexture(nil, "OVERLAY") |
self.BanzaiIndicator:SetPoint("CENTER", self, 0, 0) |
self.BanzaiIndicator:SetHeight(4) |
self.BanzaiIndicator:SetWidth(4) |
self.BanzaiIndicator:SetTexture(1, .25, .25) |
self.BanzaiIndicator:Hide() |
end |
--==============-- |
-- SMOOTH BARS -- |
--==============-- |
if IsAddOnLoaded("oUF_Smooth") then |
health.Smooth = true |
power.Smooth = true |
end |
--=======================-- |
-- DEBUFF HIGHLIGHTING -- |
--=======================-- |
if IsAddOnLoaded("oUF_DebuffHighlight") then |
self.DebuffHighlightBackdrop = self.db.DebuffHighlightBackdrop |
end |
--=======================-- |
-- |
-- |
--===============-- |
-- EVENT HOOKS -- |
--===============-- |
self.OnSizeChange = UpDateFrameSize |
self.SetAuraPosition = SetAuraPosition |
self.PostCreateAuraIcon = PostCreateAuraIcon |
self.PostUpdateAuraIcon = PostUpdateAuraIcon |
self.PostUpdateHealth = PostUpdateHealth |
self.PostUpdatePower = PostUpdatePower |
self:SetAttribute("initial-scale", db.frames.scale) |
return self |
end |
function addon:Debug(msg) |
if not self.db.profile.enabledDebugMessages then return end |
self:Print("|cFFFFFF00Debug : |r"..msg) |
end |
function addon:Error(msg) |
self:Print("|cFFFF0000Error : |r"..msg) |
end |
function addon:HideBlizzard() |
local hide = self.db.profile.hideBlizzard |
if(hide.TemporaryEnchantFrame) then |
TemporaryEnchantFrame:Hide() |
TemporaryEnchantFrame:UnregisterAllEvents() |
end |
if(hide.BuffFrame) then |
BuffFrame:Hide() |
BuffFrame:UnregisterAllEvents() |
end |
end |
function addon:reloadui(input) |
ReloadUI() |
end |
function addon:reloadgfx(input) |
RestartGx() |
end |
function addon:UpdateFontObject() |
end |
function addon:UpdateFontObjects(obj,size,name,outline) |
local db = self.db.profile |
if obj~=nil and obj.FontObjects then |
print(tostring(db.frames.font.size),tostring(db.frames.font.name),tostring(db.frames.font.outline)) |
for index,font in pairs(obj.FontObjects)do |
if(font.object:GetObjectType() == "FontString")then |
font.object:SetFont(addon.LSM:Fetch(addon.LSM.MediaType.FONT, db.frames.font.name),db.frames.font.size,db.frames.font.outline) |
end |
end |
else |
if size~= nil then db.frames.font.size = size end |
if name~= nil then db.frames.font.name = name end |
if outline~= nil then db.frames.font.outline = outline end |
for index,frame in pairs(addon.units)do |
if frame.unit ~= nil then |
self:UpdateFontObjects(frame) |
end |
end |
end |
end |
function addon:ImportSharedMedia() |
for name,path in pairs(self.db.profile.textures.statusbars)do |
self.LSM:Register(self.LSM.MediaType.STATUSBAR, name, path) |
end |
for name,data in pairs(self.db.profile.fonts)do |
self.LSM:Register(self.LSM.MediaType.FONT, name, data.name) |
end |
end |
function addon:OpenConfig(input) |
if(not IsAddOnLoaded(layoutName..'_Config')) then |
LoadAddOn(layoutName..'_Config') |
end |
InterfaceOptionsFrame:Hide() |
LibStub("AceConfigDialog-3.0"):SetDefaultSize(layoutName.."_Config", 700, 650) |
LibStub("AceConfigDialog-3.0"):Open(layoutName.."_Config") |
end |
function addon:OnInitialize() |
self.db = LibStub("AceDB-3.0"):New(layoutName.."DB",oUF_Smee_Settings) |
self.enabledDebugMessages = addon.db.profile.enabledDebugMessages |
self.units = {} |
self.Layout = layout |
self:HideBlizzard() |
self:RegisterChatCommand("oufsmee", "OpenConfig") |
self:RegisterChatCommand("rl", "reloadui") |
self:RegisterChatCommand("rgfx", "reloadgfx") |
self:ImportSharedMedia() |
end |
function addon:OnEnable() |
-- Called when the addon is enabled |
local db = self.db.profile |
if not db.enabled then |
self:Debug("Disabling") |
self:Disable() |
return |
end |
oUF:CompileTagStringLogic() |
self.enabledDebugMessages = false |
oUF:RegisterStyle("normal", self.Layout) |
oUF:SetActiveStyle("normal") |
oUF_Player = oUF:Spawn("player","oUF_Player") |
oUF_Player:SetPoint( |
db.frames.units.player.anchorFromPoint, |
oUF.units[db.frames.units.player.anchorTo] or UIParent, |
db.frames.units.player.anchorToPoint, |
db.frames.units.player.anchorX, |
db.frames.units.player.anchorY) |
self.units.target = oUF:Spawn("target","oUF_Target") |
self.units.target:SetPoint( |
db.frames.units.target.anchorFromPoint, |
oUF.units[db.frames.units.target.anchorTo] or UIParent, |
db.frames.units.target.anchorToPoint, |
db.frames.units.target.anchorX, |
db.frames.units.target.anchorY) |
self.units.focus = oUF:Spawn("focus","oUF_Focus") |
self.units.focus:SetPoint( |
db.frames.units.focus.anchorFromPoint, |
oUF.units[db.frames.units.focus.anchorTo] or UIParent, |
db.frames.units.focus.anchorToPoint, |
db.frames.units.focus.anchorX, |
db.frames.units.focus.anchorY) |
self.units.targettarget = oUF:Spawn("targettarget","oUF_TargetTarget") |
self.units.targettarget:SetPoint( |
db.frames.units.targettarget.anchorFromPoint, |
oUF.units[db.frames.units.targettarget.anchorTo] or UIParent, |
db.frames.units.targettarget.anchorToPoint, |
db.frames.units.targettarget.anchorX, |
db.frames.units.targettarget.anchorY) |
self.units.focustarget = oUF:Spawn("focustarget","oUF_FocusTarget") |
self.units.focustarget:SetPoint( |
db.frames.units.focustarget.anchorFromPoint, |
oUF.units[db.frames.units.focustarget.anchorTo] or UIParent, |
db.frames.units.focustarget.anchorToPoint, |
db.frames.units.focustarget.anchorX, |
db.frames.units.focustarget.anchorY) |
self.units.pet = oUF:Spawn("pet","oUF_Pet") |
self.units.pet:SetPoint( |
db.frames.units.pet.anchorFromPoint, |
oUF.units[db.frames.units.pet.anchorTo] or UIParent, |
db.frames.units.pet.anchorToPoint, |
db.frames.units.pet.anchorX, |
db.frames.units.pet.anchorY) |
self.units.pettarget = oUF:Spawn("pettarget","oUF_PetTarget") |
self.units.pettarget:SetPoint( |
db.frames.units.pettarget.anchorFromPoint, |
oUF.units[db.frames.units.pettarget.anchorTo] or UIParent, |
db.frames.units.pettarget.anchorToPoint, |
db.frames.units.pettarget.anchorX, |
db.frames.units.pettarget.anchorY) |
self.units = { |
['player'] = oUF_Player, |
['target'] = oUF_Target, |
['targettarget'] = oUF_TargetTarget, |
['focus'] = oUF_Focus, |
['focustarget'] = oUF_FocusTarget, |
['pet'] = oUF_Pet, |
['pettarget'] = oUF_PetTarget, |
} |
end |
function addon:OnDisable() |
-- Called when the addon is disabled |
db = self.db.profile |
if db.enabled then |
db.enabled = false |
return |
end |
self:Debug("Disabled") |
end |
## Interface: 30100 |
## Title: ouF_|cff336633Smee2|r |
## Notes: layout for oUF. |
## Author: Smee |
## X-eMail: airtonix@gmail.com |
## Version: 30100.008 |
## SavedVariables: oUF_Smee2DB |
## OptionalDeps: Ace3, SharedMedia, oUF_PowerSpark, oUF_Banzai, oUF_Smooth, oUF_CombatFeedback, oUF_HealComm, oUF_DebuffHighlight, oUF_SpellRange, oUF_ReadyCheck, oUF_TagEditor |
## RequiredDeps: oUF, oUF_TagEditor |
## DefaultState: enabled |
embeds.xml |
settings.lua |
core.lua |
assert(LibStub, "LibDataBroker-1.1 requires LibStub") |
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0") |
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 2) |
if not lib then return end |
oldminor = oldminor or 0 |
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib) |
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {} |
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks |
lib.domt = lib.domt or { |
__metatable = "access denied", |
__newindex = function(self, key, value) |
if not attributestorage[self] then attributestorage[self] = {} end |
if attributestorage[self][key] == value then return end |
attributestorage[self][key] = value |
local name = namestorage[self] |
if not name then return end |
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value) |
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value) |
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value) |
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value) |
end, |
__index = function(self, key) |
return attributestorage[self] and attributestorage[self][key] |
end, |
} |
function lib:NewDataObject(name, dataobj) |
if self.proxystorage[name] then return end |
if dataobj then |
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table") |
self.attributestorage[dataobj] = {} |
for i,v in pairs(dataobj) do |
self.attributestorage[dataobj][i] = v |
dataobj[i] = nil |
end |
end |
dataobj = setmetatable(dataobj or {}, self.domt) |
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name |
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj) |
return dataobj |
end |
if oldminor < 1 then |
function lib:DataObjectIterator() |
return pairs(self.proxystorage) |
end |
function lib:GetDataObjectByName(dataobjectname) |
return self.proxystorage[dataobjectname] |
end |
function lib:GetNameByDataObject(dataobject) |
return self.namestorage[dataobject] |
end |
end |
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info |
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke |
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! |
local LibStub = _G[LIBSTUB_MAJOR] |
if not LibStub or LibStub.minor < LIBSTUB_MINOR then |
LibStub = LibStub or {libs = {}, minors = {} } |
_G[LIBSTUB_MAJOR] = LibStub |
LibStub.minor = LIBSTUB_MINOR |
function LibStub:NewLibrary(major, minor) |
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") |
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") |
local oldminor = self.minors[major] |
if oldminor and oldminor >= minor then return nil end |
self.minors[major], self.libs[major] = minor, self.libs[major] or {} |
return self.libs[major], oldminor |
end |
function LibStub:GetLibrary(major, silent) |
if not self.libs[major] and not silent then |
error(("Cannot find a library instance of %q."):format(tostring(major)), 2) |
end |
return self.libs[major], self.minors[major] |
end |
function LibStub:IterateLibraries() return pairs(self.libs) end |
setmetatable(LibStub, { __call = LibStub.GetLibrary }) |
end |
## Interface: 20400 |
## Title: Lib: LibStub |
## Notes: Universal Library Stub |
## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel |
## X-Website: http://jira.wowace.com/browse/LS |
## X-Category: Library |
## X-License: Public Domain |
LibStub.lua |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="CallbackHandler-1.0.lua"/> |
</Ui> |
--[[ $Id: CallbackHandler-1.0.lua 3 2008-09-29 16:54:20Z nevcairiel $ ]] |
local MAJOR, MINOR = "CallbackHandler-1.0", 3 |
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR) |
if not CallbackHandler then return end -- No upgrade needed |
local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end} |
local type = type |
local pcall = pcall |
local pairs = pairs |
local assert = assert |
local concat = table.concat |
local loadstring = loadstring |
local next = next |
local select = select |
local type = type |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local next, xpcall, eh = ... |
local method, ARGS |
local function call() method(ARGS) end |
local function dispatch(handlers, ...) |
local index |
index, method = next(handlers) |
if not method then return end |
local OLD_ARGS = ARGS |
ARGS = ... |
repeat |
xpcall(call, eh) |
index, method = next(handlers, index) |
until not method |
ARGS = OLD_ARGS |
end |
return dispatch |
]] |
local ARGS, OLD_ARGS = {}, {} |
for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end |
code = code:gsub("OLD_ARGS", concat(OLD_ARGS, ", ")):gsub("ARGS", concat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
-------------------------------------------------------------------------- |
-- CallbackHandler:New |
-- |
-- target - target object to embed public APIs in |
-- RegisterName - name of the callback registration API, default "RegisterCallback" |
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback" |
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API. |
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName, OnUsed, OnUnused) |
-- TODO: Remove this after beta has gone out |
assert(not OnUsed and not OnUnused, "ACE-80: OnUsed/OnUnused are deprecated. Callbacks are now done to registry.OnUsed and registry.OnUnused") |
RegisterName = RegisterName or "RegisterCallback" |
UnregisterName = UnregisterName or "UnregisterCallback" |
if UnregisterAllName==nil then -- false is used to indicate "don't want this method" |
UnregisterAllName = "UnregisterAllCallbacks" |
end |
-- we declare all objects and exported APIs inside this closure to quickly gain access |
-- to e.g. function names, the "target" parameter, etc |
-- Create the registry object |
local events = setmetatable({}, meta) |
local registry = { recurse=0, events=events } |
-- registry:Fire() - fires the given event/message into the registry |
function registry:Fire(eventname, ...) |
if not rawget(events, eventname) or not next(events[eventname]) then return end |
local oldrecurse = registry.recurse |
registry.recurse = oldrecurse + 1 |
Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...) |
registry.recurse = oldrecurse |
if registry.insertQueue and oldrecurse==0 then |
-- Something in one of our callbacks wanted to register more callbacks; they got queued |
for eventname,callbacks in pairs(registry.insertQueue) do |
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. |
for self,func in pairs(callbacks) do |
events[eventname][self] = func |
-- fire OnUsed callback? |
if first and registry.OnUsed then |
registry.OnUsed(registry, target, eventname) |
first = nil |
end |
end |
end |
registry.insertQueue = nil |
end |
end |
-- Registration of a callback, handles: |
-- self["method"], leads to self["method"](self, ...) |
-- self with function ref, leads to functionref(...) |
-- "addonId" (instead of self) with function ref, leads to functionref(...) |
-- all with an optional arg, which, if present, gets passed as first argument (after self if present) |
target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]]) |
if type(eventname) ~= "string" then |
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2) |
end |
method = method or eventname |
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. |
if type(method) ~= "string" and type(method) ~= "function" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2) |
end |
local regfunc |
if type(method) == "string" then |
-- self["method"] calling style |
if type(self) ~= "table" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2) |
elseif self==target then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2) |
elseif type(self[method]) ~= "function" then |
error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2) |
end |
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! |
local arg=select(1,...) |
regfunc = function(...) self[method](self,arg,...) end |
else |
regfunc = function(...) self[method](self,...) end |
end |
else |
-- function ref with self=object or self="addonId" |
if type(self)~="table" and type(self)~="string" then |
error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string expected.", 2) |
end |
if select("#",...)>=1 then -- this is not the same as testing for arg==nil! |
local arg=select(1,...) |
regfunc = function(...) method(arg,...) end |
else |
regfunc = method |
end |
end |
if events[eventname][self] or registry.recurse<1 then |
-- if registry.recurse<1 then |
-- we're overwriting an existing entry, or not currently recursing. just set it. |
events[eventname][self] = regfunc |
-- fire OnUsed callback? |
if registry.OnUsed and first then |
registry.OnUsed(registry, target, eventname) |
end |
else |
-- we're currently processing a callback in this registry, so delay the registration of this new entry! |
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency |
registry.insertQueue = registry.insertQueue or setmetatable({},meta) |
registry.insertQueue[eventname][self] = regfunc |
end |
end |
-- Unregister a callback |
target[UnregisterName] = function(self, eventname) |
if not self or self==target then |
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2) |
end |
if type(eventname) ~= "string" then |
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2) |
end |
if rawget(events, eventname) and events[eventname][self] then |
events[eventname][self] = nil |
-- Fire OnUnused callback? |
if registry.OnUnused and not next(events[eventname]) then |
registry.OnUnused(registry, target, eventname) |
end |
end |
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then |
registry.insertQueue[eventname][self] = nil |
end |
end |
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds |
if UnregisterAllName then |
target[UnregisterAllName] = function(...) |
if select("#",...)<1 then |
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2) |
end |
if select("#",...)==1 and ...==target then |
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2) |
end |
for i=1,select("#",...) do |
local self = select(i,...) |
if registry.insertQueue then |
for eventname, callbacks in pairs(registry.insertQueue) do |
if callbacks[self] then |
callbacks[self] = nil |
end |
end |
end |
for eventname, callbacks in pairs(events) do |
if callbacks[self] then |
callbacks[self] = nil |
-- Fire OnUnused callback? |
if registry.OnUnused and not next(callbacks) then |
registry.OnUnused(registry, target, eventname) |
end |
end |
end |
end |
end |
end |
return registry |
end |
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it |
-- try to upgrade old implicit embeds since the system is selfcontained and |
-- relies on closures to work. |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceDBOptions-3.0.lua"/> |
</Ui> |
--[[ $Id: AceDBOptions-3.0.lua 668 2008-07-16 10:08:17Z funkydude $ ]] |
local ACEDBO_MAJOR, ACEDBO_MINOR = "AceDBOptions-3.0", 7 |
local AceDBOptions, oldminor = LibStub:NewLibrary(ACEDBO_MAJOR, ACEDBO_MINOR) |
if not AceDBOptions then return end -- No upgrade needed |
AceDBOptions.optionTables = AceDBOptions.optionTables or {} |
AceDBOptions.handlers = AceDBOptions.handlers or {} |
--[[ |
Localization of AceDBOptions-3.0 |
]] |
local L = { |
default = "Default", |
intro = "You can change the active database profile, so you can have different settings for every character.", |
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 = "Reset Profile", |
reset_sub = "Reset the current profile to the default", |
choose_desc = "You can either create a new profile by entering a name in the editbox, or choose one of the already exisiting profiles.", |
new = "New", |
new_sub = "Create a new empty profile.", |
choose = "Existing Profiles", |
choose_sub = "Select one of your currently available profiles.", |
copy_desc = "Copy the settings from one existing profile into the currently active profile.", |
copy = "Copy From", |
delete_desc = "Delete existing and unused profiles from the database to save space, and cleanup the SavedVariables file.", |
delete = "Delete a Profile", |
delete_sub = "Deletes a profile from the database.", |
delete_confirm = "Are you sure you want to delete the selected profile?", |
profiles = "Profiles", |
profiles_sub = "Manage Profiles", |
} |
local LOCALE = GetLocale() |
if LOCALE == "deDE" then |
L["default"] = "Standard" |
L["intro"] = "Hier kannst du das aktive Datenbankprofile \195\164ndern, damit du verschiedene Einstellungen f\195\188r jeden Charakter erstellen kannst, wodurch eine sehr flexible Konfiguration m\195\182glich wird." |
L["reset_desc"] = "Setzt das momentane Profil auf Standardwerte zur\195\188ck, f\195\188r den Fall das mit der Konfiguration etwas schief lief oder weil du einfach neu starten willst." |
L["reset"] = "Profil zur\195\188cksetzen" |
L["reset_sub"] = "Das aktuelle Profil auf Standard zur\195\188cksetzen." |
L["choose_desc"] = "Du kannst ein neues Profil erstellen, indem du einen neuen Namen in der Eingabebox 'Neu' eingibst, oder w\195\164hle eines der vorhandenen Profile aus." |
L["new"] = "Neu" |
L["new_sub"] = "Ein neues Profil erstellen." |
L["choose"] = "Vorhandene Profile" |
L["choose_sub"] = "W\195\164hlt ein bereits vorhandenes Profil aus." |
L["copy_desc"] = "Kopiere die Einstellungen von einem vorhandenen Profil in das aktive Profil." |
L["copy"] = "Kopieren von..." |
L["delete_desc"] = "L\195\182sche vorhandene oder unbenutzte Profile aus der Datenbank um Platz zu sparen und um die SavedVariables Datei 'sauber' zu halten." |
L["delete"] = "Profil l\195\182schen" |
L["delete_sub"] = "L\195\182scht ein Profil aus der Datenbank." |
L["delete_confirm"] = "Willst du das ausgew\195\164hlte Profil wirklich l\195\182schen?" |
L["profiles"] = "Profile" |
L["profiles_sub"] = "Profile verwalten" |
elseif LOCALE == "frFR" then |
L["default"] = "D\195\169faut" |
L["intro"] = "Vous pouvez changer le profil actuel afin d'avoir des param\195\168tres diff\195\169rents pour chaque personnage, permettant ainsi d'avoir une configuration tr\195\168s flexible." |
L["reset_desc"] = "R\195\169initialise le profil actuel au cas o\195\185 votre configuration est corrompue ou si vous voulez tout simplement faire table rase." |
L["reset"] = "R\195\169initialiser le profil" |
L["reset_sub"] = "R\195\169initialise le profil actuel avec les param\195\168tres par d\195\169faut." |
L["choose_desc"] = "Vous pouvez cr\195\169er un nouveau profil en entrant un nouveau nom dans la bo\195\174te de saisie, ou en choississant un des profils d\195\169j\195\160 existants." |
L["new"] = "Nouveau" |
L["new_sub"] = "Cr\195\169\195\169e un nouveau profil vierge." |
L["choose"] = "Profils existants" |
L["choose_sub"] = "Permet de choisir un des profils d\195\169j\195\160 disponibles." |
L["copy_desc"] = "Copie les param\195\168tres d'un profil d\195\169j\195\160 existant dans le profil actuellement actif." |
L["copy"] = "Copier \195\160 partir de" |
L["delete_desc"] = "Supprime les profils existants inutilis\195\169s de la base de donn\195\169es afin de gagner de la place et de nettoyer le fichier SavedVariables." |
L["delete"] = "Supprimer un profil" |
L["delete_sub"] = "Supprime un profil de la base de donn\195\169es." |
L["delete_confirm"] = "Etes-vous s\195\187r de vouloir supprimer le profil s\195\169lectionn\195\169 ?" |
L["profiles"] = "Profils" |
L["profiles_sub"] = "Gestion des profils" |
elseif LOCALE == "koKR" then |
L["default"] = "기본ê°" |
L["intro"] = "모ë ìºë¦í°ì ë¤ìí ì¤ì ê³¼ ì¬ì©ì¤ì¸ ë°ì´í°ë² ì´ì¤ íë¡í, ì´ëê²ì´ëì§ ë§¤ì° ë¤ë£¨ê¸° ì½ê² ë°ê¿ì ììµëë¤." |
L["reset_desc"] = "ë¨ìí ë¤ì ìë¡ê² 구ì±ì ìíë ê²½ì°, íì¬ íë¡íì 기본ê°ì¼ë¡ ì´ê¸°í í©ëë¤." |
L["reset"] = "íë¡í ì´ê¸°í" |
L["reset_sub"] = "íì¬ì íë¡íì 기본ê°ì¼ë¡ ì´ê¸°í í©ëë¤" |
L["choose_desc"] = "ìë¡ì´ ì´ë¦ì ì ë ¥íê±°ë, ì´ë¯¸ ìë íë¡íì¤ íë를 ì ííì¬ ìë¡ì´ íë¡íì ë§ë¤ ì ììµëë¤." |
L["new"] = "ìë¡ì´ íë¡í" |
L["new_sub"] = "ìë¡ì´ íë¡íì ë§ëëë¤." |
L["choose"] = "íë¡í ì í" |
L["choose_sub"] = "ë¹ì ì´ íì¬ ì´ì©í ì ìë íë¡íì ì íí©ëë¤." |
L["copy_desc"] = "íì¬ ì¬ì©ì¤ì¸ íë¡íì, ì íí íë¡íì ì¤ì ì ë³µì¬í©ëë¤." |
L["copy"] = "ë³µì¬" |
L["delete_desc"] = "ë°ì´í°ë² ì´ì¤ì ì¬ì©ì¤ì´ê±°ë ì ì¥ë íë¡íì¼ ìì ë¡ SavedVariables íì¼ì ì 리ì ê³µê° ì ì½ì´ ë©ëë¤." |
L["delete"] = "íë¡í ìì " |
L["delete_sub"] = "ë°ì´í°ë² ì´ì¤ì íë¡íì ìì í©ëë¤." |
L["delete_confirm"] = "ì ë§ë¡ ì íí íë¡íì ìì 를 ìíìëê¹?" |
L["profiles"] = "íë¡í" |
L["profiles_sub"] = "íë¡í ì¤ì " |
elseif LOCALE == "esES" then |
elseif LOCALE == "zhTW" then |
L["default"] = "é è¨" |
L["intro"] = "ä½ å¯ä»¥é¸æä¸åæ´»åçè³æè¨å®æªï¼éæ¨£ä½ çæ¯åè§è²å°±å¯ä»¥ææä¸åçè¨å®å¼ï¼å¯ä»¥çµ¦ä½ çæ件è¨å®å¸¶ä¾æ¥µå¤§çéæ´»æ§ã" |
L["reset_desc"] = "å°ç¶åçè¨å®æªæ¢å¾©å°å®çé è¨å¼ï¼ç¨æ¼ä½ çè¨å®æªæå£ï¼æè ä½ åªæ¯æ³éä¾çæ æ³ã" |
L["reset"] = "éç½®è¨å®æª" |
L["reset_sub"] = "å°ç¶åçè¨å®æªæ¢å¾©çºé è¨å¼" |
L["choose_desc"] = "ä½ å¯ä»¥ééå¨ææ¬æ¡å §è¼¸å ¥ä¸ååååµç«ä¸åæ°çè¨å®æªï¼ä¹å¯ä»¥é¸æä¸åå·²ç¶åå¨çè¨å®æªã" |
L["new"] = "æ°å»º" |
L["new_sub"] = "æ°å»ºä¸å空çè¨å®æªã" |
L["choose"] = "ç¾æçè¨å®æª" |
L["choose_sub"] = "å¾ç¶åå¯ç¨çè¨å®æªè£é¢é¸æä¸åã" |
L["copy_desc"] = "å¾ç¶åæåå·²ä¿åçè¨å®æªè¤è£½å°ç¶åæ£ä½¿ç¨çè¨å®æªã" |
L["copy"] = "è¤è£½èª" |
L["delete_desc"] = "å¾è³æ庫è£åªé¤ä¸å使ç¨çè¨å®æªï¼ä»¥ç¯ç空éï¼ä¸¦ä¸æ¸ çSavedVariablesæªã" |
L["delete"] = "åªé¤ä¸åè¨å®æª" |
L["delete_sub"] = "å¾è³æ庫è£åªé¤ä¸åè¨å®æªã" |
L["delete_confirm"] = "ä½ ç¢ºå®è¦åªé¤æé¸æçè¨å®æªåï¼" |
L["profiles"] = "è¨å®æª" |
L["profiles_sub"] = "管çè¨å®æª" |
elseif LOCALE == "zhCN" then |
L["default"] = "é»è®¤" |
L["intro"] = "ä½ å¯ä»¥éæ©ä¸ä¸ªæ´»å¨çæ°æ®é ç½®æ件ï¼è¿æ ·ä½ çæ¯ä¸ªè§è²å°±å¯ä»¥æ¥æä¸åç设置å¼ï¼å¯ä»¥ç»ä½ çæ件é 置带æ¥æ大ççµæ´»æ§ã" |
L["reset_desc"] = "å°å½åçé ç½®æ件æ¢å¤å°å®çé»è®¤å¼ï¼ç¨äºä½ çé ç½®æ件æåï¼æè ä½ åªæ¯æ³éæ¥çæ åµã" |
L["reset"] = "éç½®é ç½®æ件" |
L["reset_sub"] = "å°å½åçé ç½®æ件æ¢å¤ä¸ºé»è®¤å¼" |
L["choose_desc"] = "ä½ å¯ä»¥éè¿å¨ææ¬æ¡å è¾å ¥ä¸ä¸ªåååç«ä¸ä¸ªæ°çé ç½®æ件ï¼ä¹å¯ä»¥éæ©ä¸ä¸ªå·²ç»åå¨çé ç½®æ件ã" |
L["new"] = "æ°å»º" |
L["new_sub"] = "æ°å»ºä¸ä¸ªç©ºçé ç½®æ件ã" |
L["choose"] = "ç°æçé ç½®æ件" |
L["choose_sub"] = "ä»å½åå¯ç¨çé ç½®æ件éé¢éæ©ä¸ä¸ªã" |
L["copy_desc"] = "ä»å½åæ个已ä¿åçé ç½®æ件å¤å¶å°å½åæ£ä½¿ç¨çé ç½®æ件ã" |
L["copy"] = "å¤å¶èª" |
L["delete_desc"] = "ä»æ°æ®åºéå é¤ä¸å使ç¨çé ç½®æ件ï¼ä»¥èç空é´ï¼å¹¶ä¸æ¸ çSavedVariablesæ件ã" |
L["delete"] = "å é¤ä¸ä¸ªé ç½®æ件" |
L["delete_sub"] = "ä»æ°æ®åºéå é¤ä¸ä¸ªé ç½®æ件ã" |
L["delete_confirm"] = "ä½ ç¡®å®è¦å é¤æéæ©çé ç½®æ件ä¹ï¼" |
L["profiles"] = "é ç½®æ件" |
L["profiles_sub"] = "管çé ç½®æ件" |
elseif LOCALE == "ruRU" then |
L["default"] = "Ðо ÑмолÑаниÑ" |
L["intro"] = "ÐÑ Ð¼Ð¾Ð¶ÐµÑе ÑмениÑÑ Ð°ÐºÑивнÑй пÑоÑÐ¸Ð»Ñ ÐÐ, ÑÑим Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе ÑÑÑанавливаÑÑ ÑазлиÑнÑе наÑÑÑойки Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ пеÑÑонажа." |
L["reset_desc"] = "СбÑÐ¾Ñ ÑекÑÑего пÑоÑÐ¸Ð»Ñ Ð½Ð° его ÑÑандаÑÑнÑе знаÑениÑ, в Ñом ÑлÑÑаи еÑли ваÑа конÑигÑÑаÑÐ¸Ñ Ð¸ÑпоÑÑена, или Ð²Ñ Ð¶ÐµÐ»Ð°ÐµÑе вÑÑ Ð¿ÐµÑенаÑÑÑоиÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾." |
L["reset"] = "СбÑÐ¾Ñ Ð¿ÑоÑилÑ" |
L["reset"] = "СбÑÐ¾Ñ ÑекÑÑего пÑоÑÐ¸Ð»Ñ Ð½Ð° ÑÑандаÑÑнÑй" |
L["choose_desc"] = "ÐÑ Ð¼Ð¾Ð¶ÐµÑе ÑоздаÑÑ Ð½Ð¾Ð²Ñй пÑоÑÐ¸Ð»Ñ Ð²Ð²ÐµÐ´Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ в поле ввода, или вÑбÑаÑÑ Ð¾Ð´Ð¸Ð½ из Ñже ÑÑÑеÑÑвÑÑÑÐ¸Ñ Ð¿ÑоÑилей." |
L["new"] = "ÐовÑй" |
L["new_sub"] = "СоздаÑÑ Ð½Ð¾Ð²Ñй ÑиÑÑÑй пÑоÑилÑ." |
L["choose"] = "ÐÑоÑилÑ" |
L["choose_sub"] = "ÐÑбеÑиÑе один из Ñже доÑÑÑпнÑÑ Ð¿ÑоÑилей." |
L["copy_desc"] = "СкопиÑоваÑÑ Ð½Ð°ÑÑÑойки пÑоÑÐ¸Ð»Ñ Ð² на даннÑй Ð¼Ð¾Ð¼ÐµÐ½Ñ Ð°ÐºÑивнÑй пÑоÑилÑ." |
L["copy"] = "СкопиÑоваÑÑ Ñ" |
L["delete_desc"] = "УдалиÑÑ ÑÑÑеÑÑвÑÑÑий и неиÑполÑзÑемÑй пÑоÑÐ¸Ð»Ñ Ð¸Ð· ÐÐ Ð´Ð»Ñ ÑÐ¾Ñ ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð¼ÐµÑÑа, и оÑиÑÑиÑÑ SavedVariables Ñайл." |
L["delete"] = "УдалиÑÑ Ð¿ÑоÑилÑ" |
L["delete_sub"] = "Ð£Ð´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¿ÑоÑÐ¸Ð»Ñ Ð¸Ð· ÐÐ." |
L["delete_confirm"] = "ÐÑ ÑвеÑÐµÐ½Ñ ÑÑо Ð²Ñ Ñ Ð¾ÑиÑе ÑдалиÑÑ Ð²ÑбÑаннÑй пÑоÑилÑ?" |
L["profiles"] = "ÐÑоÑилÑ" |
L["profiles_sub"] = "УпÑавление пÑоÑилÑми" |
end |
local defaultProfiles |
local tmpprofiles = {} |
--[[ |
getProfileList(db, common, nocurrent) |
db - the db object to retrieve the profiles from |
common (boolean) - if common is true, getProfileList will add the default profiles to the return list, even if they have not been created yet |
nocurrent (boolean) - if true then getProfileList will not display the current profile in the list |
]]-- |
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 |
--[[ 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", |
}, |
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", |
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", |
arg = "nocurrent", |
confirm = true, |
confirmText = L["delete_confirm"], |
}, |
} |
--[[ |
GetOptionsTable(db) |
db - the database object to create the options table for |
creates and returns a option table to be used in your addon |
]] |
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 |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceConsole-3.0.lua"/> |
</Ui> |
--[[ $Id: AceConsole-3.0.lua 708 2008-10-19 10:59:58Z arrowmaster $ ]] |
local MAJOR,MINOR = "AceConsole-3.0", 6 |
local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceConsole then return end -- No upgrade needed |
AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in. |
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered |
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable |
-- local upvalues |
local _G = _G |
local pairs = pairs |
local select = select |
local type = type |
local tostring = tostring |
local strfind = string.find |
local strsub = string.sub |
local max = math.max |
-- AceConsole:Print( [chatframe,] ... ) |
-- |
-- Print to DEFAULT_CHAT_FRAME or given chatframe (anything with an .AddMessage member) |
function AceConsole:Print(...) |
local text = "" |
if self ~= AceConsole then |
text = "|cff33ff99"..tostring( self ).."|r: " |
end |
local frame = select(1, ...) |
if not ( type(frame) == "table" and frame.AddMessage ) then -- Is first argument something with an .AddMessage member? |
frame=nil |
end |
for i=(frame and 2 or 1), select("#", ...) do |
text = text .. tostring( select( i, ...) ) .." " |
end |
(frame or DEFAULT_CHAT_FRAME):AddMessage( text ) |
end |
-- AceConsole:RegisterChatCommand(. command, func, persist ) |
-- |
-- command (string) - chat command to be registered WITHOUT leading "/" |
-- func (function|membername) - function to call, or self[membername](self, ...) call |
-- persist (boolean) - false: the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true) |
-- |
-- Register a simple chat command |
function AceConsole:RegisterChatCommand( command, func, persist ) |
if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end |
if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk |
local name = "ACECONSOLE_"..command:upper() |
if type( func ) == "string" then |
SlashCmdList[name] = function(input, editBox) |
self[func](self, input, editBox) |
end |
else |
SlashCmdList[name] = func |
end |
_G["SLASH_"..name.."1"] = "/"..command:lower() |
AceConsole.commands[command] = name |
-- non-persisting commands are registered for enabling disabling |
if not persist then |
if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end |
AceConsole.weakcommands[self][command] = func |
end |
return true |
end |
-- AceConsole:UnregisterChatCommand( command ) |
-- |
-- Unregister a chatcommand |
function AceConsole:UnregisterChatCommand( command ) |
local name = AceConsole.commands[command] |
if name then |
SlashCmdList[name] = nil |
_G["SLASH_" .. name .. "1"] = nil |
hash_SlashCmdList["/" .. command:upper()] = nil |
AceConsole.commands[command] = nil |
end |
end |
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end |
local function nils(n, ...) |
if n>1 then |
return nil, nils(n-1, ...) |
elseif n==1 then |
return nil, ... |
else |
return ... |
end |
end |
-- AceConsole:GetArgs(string, numargs, startpos) |
-- |
-- Retreive one or more space-separated arguments from a string. |
-- Treats quoted strings and itemlinks as non-spaced. |
-- |
-- string - The raw argument string |
-- numargs - How many arguments to get (default 1) |
-- startpos - Where in the string to start scanning (default 1) |
-- |
-- Returns arg1, arg2, ..., nextposition |
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string. |
function AceConsole:GetArgs(str, numargs, startpos) |
numargs = numargs or 1 |
startpos = max(startpos or 1, 1) |
local pos=startpos |
-- find start of new arg |
pos = strfind(str, "[^ ]", pos) |
if not pos then -- whoops, end of string |
return nils(numargs, 1e9) |
end |
if numargs<1 then |
return pos |
end |
-- quoted or space separated? find out which pattern to use |
local delim_or_pipe |
local ch = strsub(str, pos, pos) |
if ch=='"' then |
pos = pos + 1 |
delim_or_pipe='([|"])' |
elseif ch=="'" then |
pos = pos + 1 |
delim_or_pipe="([|'])" |
else |
delim_or_pipe="([| ])" |
end |
startpos = pos |
while true do |
-- find delimiter or hyperlink |
local ch,_ |
pos,_,ch = strfind(str, delim_or_pipe, pos) |
if not pos then break end |
if ch=="|" then |
-- some kind of escape |
if strsub(str,pos,pos+1)=="|H" then |
-- It's a |H....|hhyper link!|h |
pos=strfind(str, "|h", pos+2) -- first |h |
if not pos then break end |
pos=strfind(str, "|h", pos+2) -- second |h |
if not pos then break end |
elseif strsub(str,pos, pos+1) == "|T" then |
-- It's a |T....|t texture |
pos=strfind(str, "|t", pos+2) |
if not pos then break end |
end |
pos=pos+2 -- skip past this escape (last |h if it was a hyperlink) |
else |
-- found delimiter, done with this arg |
return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1) |
end |
end |
-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink) |
return strsub(str, startpos), nils(numargs-1, 1e9) |
end |
--- embedding and embed handling |
local mixins = { |
"Print", |
"RegisterChatCommand", |
"UnregisterChatCommand", |
"GetArgs", |
} |
-- AceConsole:Embed( target ) |
-- target (object) - target object to embed AceBucket in |
-- |
-- Embeds AceConsole into the target object making the functions from the mixins list available on target:.. |
function AceConsole:Embed( target ) |
for k, v in pairs( mixins ) do |
target[v] = self[v] |
end |
self.embeds[target] = true |
return target |
end |
function AceConsole:OnEmbedEnable( target ) |
if AceConsole.weakcommands[target] then |
for command, func in pairs( AceConsole.weakcommands[target] ) do |
target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry |
end |
end |
end |
function AceConsole:OnEmbedDisable( target ) |
if AceConsole.weakcommands[target] then |
for command, func in pairs( AceConsole.weakcommands[target] ) do |
target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care? |
end |
end |
end |
for addon in pairs(AceConsole.embeds) do |
AceConsole:Embed(addon) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="LibSharedMedia-3.0.lua" /> |
</Ui> |
--[[ |
Name: LibSharedMedia-3.0 |
Revision: $Revision: 49 $ |
Author: Elkano (elkano@gmx.de) |
Inspired By: SurfaceLib by Haste/Otravi (troeks@gmail.com) |
Website: http://www.wowace.com/projects/libsharedmedia-3-0/ |
Description: Shared handling of media data (fonts, sounds, textures, ...) between addons. |
Dependencies: LibStub, CallbackHandler-1.0 |
License: LGPL v2.1 |
]] |
local MAJOR, MINOR = "LibSharedMedia-3.0", 90000 + tonumber(("$Revision: 49 $"):match("(%d+)")) |
local lib = LibStub:NewLibrary(MAJOR, MINOR) |
if not lib then return end |
local _G = getfenv(0) |
local pairs = _G.pairs |
local type = _G.type |
local band = _G.bit.band |
local table_insert = _G.table.insert |
local table_sort = _G.table.sort |
local locale = GetLocale() |
local locale_is_western |
local LOCALE_MASK = 0 |
lib.LOCALE_BIT_koKR = 1 |
lib.LOCALE_BIT_ruRU = 2 |
lib.LOCALE_BIT_zhCN = 4 |
lib.LOCALE_BIT_zhTW = 8 |
lib.LOCALE_BIT_western = 128 |
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0") |
lib.callbacks = lib.callbacks or CallbackHandler:New(lib) |
lib.DefaultMedia = lib.DefaultMedia or {} |
lib.MediaList = lib.MediaList or {} |
lib.MediaTable = lib.MediaTable or {} |
lib.MediaType = lib.MediaType or {} |
lib.OverrideMedia = lib.OverrideMedia or {} |
local defaultMedia = lib.DefaultMedia |
local mediaList = lib.MediaList |
local mediaTable = lib.MediaTable |
local overrideMedia = lib.OverrideMedia |
-- create mediatype constants |
lib.MediaType.BACKGROUND = "background" -- background textures |
lib.MediaType.BORDER = "border" -- border textures |
lib.MediaType.FONT = "font" -- fonts |
lib.MediaType.STATUSBAR = "statusbar" -- statusbar textures |
lib.MediaType.SOUND = "sound" -- sound files |
-- populate lib with default Blizzard data |
-- BACKGROUND |
if not lib.MediaTable.background then lib.MediaTable.background = {} end |
lib.MediaTable.background["Blizzard Dialog Background"] = [[Interface\DialogFrame\UI-DialogBox-Background]] |
lib.MediaTable.background["Blizzard Low Health"] = [[Interface\FullScreenTextures\LowHealth]] |
lib.MediaTable.background["Blizzard Out of Control"] = [[Interface\FullScreenTextures\OutOfControl]] |
lib.MediaTable.background["Blizzard Parchment"] = [[Interface\AchievementFrame\UI-Achievement-Parchment-Horizontal]] |
lib.MediaTable.background["Blizzard Parchment 2"] = [[Interface\AchievementFrame\UI-Achievement-Parchment]] |
lib.MediaTable.background["Blizzard Tabard Background"] = [[Interface\TabardFrame\TabardFrameBackground]] |
lib.MediaTable.background["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Background]] |
lib.MediaTable.background["Solid"] = [[Interface\Buttons\WHITE8X8]] |
-- BORDER |
if not lib.MediaTable.border then lib.MediaTable.border = {} end |
lib.MediaTable.border["None"] = [[Interface\None]] |
lib.MediaTable.border["Blizzard Dialog"] = [[Interface\DialogFrame\UI-DialogBox-Border]] |
lib.MediaTable.border["Blizzard Dialog Gold"] = [[Interface\DialogFrame\UI-DialogBox-Gold-Border]] |
lib.MediaTable.border["Blizzard Tooltip"] = [[Interface\Tooltips\UI-Tooltip-Border]] |
-- FONT |
if not lib.MediaTable.font then lib.MediaTable.font = {} end |
local SML_MT_font = lib.MediaTable.font |
if locale == "koKR" then |
LOCALE_MASK = lib.LOCALE_BIT_koKR |
-- |
SML_MT_font["êµµì ê¸ê¼´"] = [[Fonts\2002B.TTF]] |
SML_MT_font["기본 ê¸ê¼´"] = [[Fonts\2002.TTF]] |
SML_MT_font["ë°ë¯¸ì§ ê¸ê¼´"] = [[Fonts\K_Damage.TTF]] |
SML_MT_font["íì¤í¸ ê¸ê¼´"] = [[Fonts\K_Pagetext.TTF]] |
-- |
lib.DefaultMedia["font"] = "기본 ê¸ê¼´" -- someone from koKR please adjust if needed |
-- |
elseif locale == "zhCN" then |
LOCALE_MASK = lib.LOCALE_BIT_zhCN |
-- |
SML_MT_font["伤害æ°å"] = [[Fonts\ZYKai_C.ttf]] |
SML_MT_font["é»è®¤"] = [[Fonts\ZYKai_T.ttf]] |
SML_MT_font["è天"] = [[Fonts\ZYHei.ttf]] |
-- |
lib.DefaultMedia["font"] = "é»è®¤" -- someone from zhCN please adjust if needed |
-- |
elseif locale == "zhTW" then |
LOCALE_MASK = lib.LOCALE_BIT_zhTW |
-- |
SML_MT_font["æ示è¨æ¯"] = [[Fonts\bHEI00M.ttf]] |
SML_MT_font["è天"] = [[Fonts\bHEI01B.ttf]] |
SML_MT_font["å·å®³æ¸å"] = [[Fonts\bKAI00M.ttf]] |
SML_MT_font["é è¨"] = [[Fonts\bLEI00D.ttf]] |
-- |
lib.DefaultMedia["font"] = "é è¨" -- someone from zhTW please adjust if needed |
elseif locale == "ruRU" then |
LOCALE_MASK = lib.LOCALE_BIT_ruRU |
-- |
SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]] |
SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]] |
SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]] |
SML_MT_font["Nimrod MT"] = [[Fonts\NIM_____.ttf]] |
SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]] |
-- |
lib.DefaultMedia.font = "Friz Quadrata TT" |
-- |
else |
LOCALE_MASK = lib.LOCALE_BIT_western |
locale_is_western = true |
-- |
SML_MT_font["Arial Narrow"] = [[Fonts\ARIALN.TTF]] |
SML_MT_font["Friz Quadrata TT"] = [[Fonts\FRIZQT__.TTF]] |
SML_MT_font["Morpheus"] = [[Fonts\MORPHEUS.TTF]] |
SML_MT_font["Skurri"] = [[Fonts\SKURRI.TTF]] |
-- |
lib.DefaultMedia.font = "Friz Quadrata TT" |
-- |
end |
-- STATUSBAR |
if not lib.MediaTable.statusbar then lib.MediaTable.statusbar = {} end |
lib.MediaTable.statusbar["Blizzard"] = [[Interface\TargetingFrame\UI-StatusBar]] |
lib.DefaultMedia.statusbar = "Blizzard" |
-- SOUND |
if not lib.MediaTable.sound then lib.MediaTable.sound = {} end |
lib.MediaTable.sound["None"] = [[Interface\Quiet.mp3]] -- Relies on the fact that PlaySound[File] doesn't error on non-existing input. |
lib.DefaultMedia.sound = "None" |
local function rebuildMediaList(mediatype) |
local mtable = mediaTable[mediatype] |
if not mtable then return end |
if not mediaList[mediatype] then mediaList[mediatype] = {} end |
local mlist = mediaList[mediatype] |
-- list can only get larger, so simply overwrite it |
local i = 0 |
for k in pairs(mtable) do |
i = i + 1 |
mlist[i] = k |
end |
table_sort(mlist) |
end |
function lib:Register(mediatype, key, data, langmask) |
if mediatype == lib.MediaType.FONT and ((langmask and band(langmask, LOCALE_MASK) == 0) or not (langmask or locale_is_western)) then return false end |
mediatype = mediatype:lower() |
if not mediaTable[mediatype] then mediaTable[mediatype] = {} end |
local mtable = mediaTable[mediatype] |
if mtable[key] then return false end |
mtable[key] = data |
rebuildMediaList(mediatype) |
self.callbacks:Fire("LibSharedMedia_Registered", mediatype, key) |
return true |
end |
function lib:Fetch(mediatype, key, noDefault) |
local mtt = mediaTable[mediatype] |
local overridekey = overrideMedia[mediatype] |
local result = mtt and ((overridekey and mtt[overridekey] or mtt[key]) or (not noDefault and defaultMedia[mediatype] and mtt[defaultMedia[mediatype]])) or nil |
return result |
end |
function lib:IsValid(mediatype, key) |
return mediaTable[mediatype] and (not key or mediaTable[mediatype][key]) and true or false |
end |
function lib:HashTable(mediatype) |
return mediaTable[mediatype] |
end |
function lib:List(mediatype) |
if not mediaTable[mediatype] then |
return nil |
end |
if not mediaList[mediatype] then |
rebuildMediaList(mediatype) |
end |
return mediaList[mediatype] |
end |
function lib:GetGlobal(mediatype) |
return overrideMedia[mediatype] |
end |
function lib:SetGlobal(mediatype, key) |
if not mediaTable[mediatype] then |
return false |
end |
overrideMedia[mediatype] = (key and mediaTable[mediatype][key]) and key or nil |
self.callbacks:Fire("LibSharedMedia_SetGlobal", mediatype, overrideMedia[mediatype]) |
return true |
end |
function lib:GetDefault(mediatype) |
return defaultMedia[mediatype] |
end |
function lib:SetDefault(mediatype, key) |
if mediaTable[mediatype] and mediaTable[mediatype][key] and not defaultMedia[mediatype] then |
defaultMedia[mediatype] = key |
return true |
else |
return false |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceAddon-3.0.lua"/> |
</Ui> |
--[[ $Id: AceAddon-3.0.lua 654 2008-05-21 13:43:29Z ammo $ ]] |
local MAJOR, MINOR = "AceAddon-3.0", 5 |
local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
if not AceAddon then return end -- No Upgrade needed. |
AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame |
AceAddon.addons = AceAddon.addons or {} -- addons in general |
AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon. |
AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized |
AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled |
AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon |
local tinsert, tconcat = table.insert, table.concat |
local fmt = string.format |
local pairs, next, type = pairs, next, type |
--[[ |
xpcall safecall implementation |
]] |
local xpcall = xpcall |
local function errorhandler(err) |
return geterrorhandler()(err) |
end |
local function CreateDispatcher(argCount) |
local code = [[ |
local xpcall, eh = ... |
local method, ARGS |
local function call() return method(ARGS) end |
local function dispatch(func, ...) |
method = func |
if not method then return end |
ARGS = ... |
return xpcall(call, eh) |
end |
return dispatch |
]] |
local ARGS = {} |
for i = 1, argCount do ARGS[i] = "arg"..i end |
code = code:gsub("ARGS", tconcat(ARGS, ", ")) |
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) |
end |
local Dispatchers = setmetatable({}, {__index=function(self, argCount) |
local dispatcher = CreateDispatcher(argCount) |
rawset(self, argCount, dispatcher) |
return dispatcher |
end}) |
Dispatchers[0] = function(func) |
return xpcall(func, errorhandler) |
end |
local function safecall(func, ...) |
-- we check to see if the func is passed is actually a function here and don't error when it isn't |
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not |
-- present execution should continue without hinderance |
if type(func) == "function" then |
return Dispatchers[select('#', ...)](func, ...) |
end |
end |
-- local functions that will be implemented further down |
local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype |
-- used in the addon metatable |
local function addontostring( self ) return self.name end |
-- AceAddon:NewAddon( [object, ]name, [lib, lib, lib, ...] ) |
-- [object] (table) - table to use as the base for the addon |
-- name (string) - unique addon object name |
-- [lib] (string) - optional libs to embed in the addon object |
-- |
-- returns the addon object when succesful |
function AceAddon:NewAddon(objectorname, ...) |
local object,name |
local i=1 |
if type(objectorname)=="table" then |
object=objectorname |
name=... |
i=2 |
else |
name=objectorname |
end |
if type(name)~="string" then |
error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) |
end |
if self.addons[name] then |
error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2) |
end |
object = object or {} |
object.name = name |
local addonmeta = {} |
local oldmeta = getmetatable(object) |
if oldmeta then |
for k, v in pairs(oldmeta) do addonmeta[k] = v end |
end |
addonmeta.__tostring = addontostring |
setmetatable( object, addonmeta ) |
self.addons[name] = object |
object.modules = {} |
object.defaultModuleLibraries = {} |
Embed( object ) -- embed NewModule, GetModule methods |
self:EmbedLibraries(object, select(i,...)) |
-- add to queue of addons to be initialized upon ADDON_LOADED |
tinsert(self.initializequeue, object) |
return object |
end |
-- AceAddon:GetAddon( name, [silent]) |
-- name (string) - unique addon object name |
-- silent (boolean) - if true, addon is optional, silently return nil if its not found |
-- |
-- throws an error if the addon object can not be found (except silent is set) |
-- returns the addon object if found |
function AceAddon:GetAddon(name, silent) |
if not silent and not self.addons[name] then |
error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2) |
end |
return self.addons[name] |
end |
-- AceAddon:EmbedLibraries( addon, [lib, lib, lib, ...] ) |
-- addon (object) - addon to embed the libs in |
-- [lib] (string) - optional libs to embed |
function AceAddon:EmbedLibraries(addon, ...) |
for i=1,select("#", ... ) do |
local libname = select(i, ...) |
self:EmbedLibrary(addon, libname, false, 4) |
end |
end |
-- AceAddon:EmbedLibrary( addon, libname, silent, offset ) |
-- addon (object) - addon to embed the libs in |
-- libname (string) - lib to embed |
-- [silent] (boolean) - optional, marks an embed to fail silently if the library doesn't exist. |
-- [offset] (number) - will push the error messages back to said offset defaults to 2 |
function AceAddon:EmbedLibrary(addon, libname, silent, offset) |
local lib = LibStub:GetLibrary(libname, true) |
if not lib and not silent then |
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2) |
elseif lib and type(lib.Embed) == "function" then |
lib:Embed(addon) |
tinsert(self.embeds[addon], libname) |
return true |
elseif lib then |
error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2) |
end |
end |
-- addon:GetModule( name, [silent]) |
-- name (string) - unique module object name |
-- silent (boolean) - if true, module is optional, silently return nil if its not found |
-- |
-- throws an error if the addon object can not be found (except silent is set) |
-- returns the module object if found |
function GetModule(self, name, silent) |
if not self.modules[name] and not silent then |
error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2) |
end |
return self.modules[name] |
end |
local function IsModuleTrue(self) return true end |
-- addon:NewModule( name, [prototype, [lib, lib, lib, ...] ) |
-- name (string) - unique module object name for this addon |
-- prototype (object) - object to derive this module from, methods and values from this table will be mixed into the module, if a string is passed a lib is assumed |
-- [lib] (string) - optional libs to embed in the addon object |
-- |
-- returns the addon object when succesful |
function NewModule(self, name, prototype, ...) |
if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end |
if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end |
if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end |
-- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well. |
-- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is. |
local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name)) |
module.IsModule = IsModuleTrue |
module:SetEnabledState(self.defaultModuleState) |
module.moduleName = name |
if type(prototype) == "string" then |
AceAddon:EmbedLibraries(module, prototype, ...) |
else |
AceAddon:EmbedLibraries(module, ...) |
end |
AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries)) |
if not prototype or type(prototype) == "string" then |
prototype = self.defaultModulePrototype or nil |
end |
if type(prototype) == "table" then |
local mt = getmetatable(module) |
mt.__index = prototype |
setmetatable(module, mt) -- More of a Base class type feel. |
end |
safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy. |
self.modules[name] = module |
return module |
end |
--addon:GetName() |
-- Returns the real name of the addon or module, without any prefix |
function GetName(self) |
return self.moduleName or self.name |
end |
--addon:Enable() |
-- Enables the Addon if possible, return true or false depending on success |
function Enable(self) |
self:SetEnabledState(true) |
return AceAddon:EnableAddon(self) |
end |
--addon:Disable() |
-- Disables the Addon if possible, return true or false depending on success |
function Disable(self) |
self:SetEnabledState(false) |
return AceAddon:DisableAddon(self) |
end |
-- addon:EnableModule( name ) |
-- name (string) - unique module object name |
-- |
-- Enables the Module if possible, return true or false depending on success |
function EnableModule(self, name) |
local module = self:GetModule( name ) |
return module:Enable() |
end |
-- addon:DisableModule( name ) |
-- name (string) - unique module object name |
-- |
-- Disables the Module if possible, return true or false depending on success |
function DisableModule(self, name) |
local module = self:GetModule( name ) |
return module:Disable() |
end |
-- addon:SetDefaultModuleLibraries( [lib, lib, lib, ...] ) |
-- [lib] (string) - libs to embed in every module |
function SetDefaultModuleLibraries(self, ...) |
if next(self.modules) then |
error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2) |
end |
self.defaultModuleLibraries = {...} |
end |
-- addon:SetDefaultModuleState( state ) |
-- state (boolean) - default state for new modules (enabled=true, disabled=false) |
function SetDefaultModuleState(self, state) |
if next(self.modules) then |
error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2) |
end |
self.defaultModuleState = state |
end |
-- addon:SetDefaultModulePrototype( prototype ) |
-- prototype (string or table) - the default prototype to use if none is specified on module creation |
function SetDefaultModulePrototype(self, prototype) |
if next(self.modules) then |
error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2) |
end |
if type(prototype) ~= "table" then |
error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2) |
end |
self.defaultModulePrototype = prototype |
end |
-- addon:SetEnabledState ( state ) |
-- state ( boolean ) - set the state of an addon or module (enabled=true, disabled=false) |
-- |
-- should only be called before any Enabling actually happend, aka in OnInitialize |
function SetEnabledState(self, state) |
self.enabledState = state |
end |
local function IterateModules(self) return pairs(self.modules) end |
local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end |
local function IsEnabled(self) return self.enabledState end |
local mixins = { |
NewModule = NewModule, |
GetModule = GetModule, |
Enable = Enable, |
Disable = Disable, |
EnableModule = EnableModule, |
DisableModule = DisableModule, |
IsEnabled = IsEnabled, |
SetDefaultModuleLibraries = SetDefaultModuleLibraries, |
SetDefaultModuleState = SetDefaultModuleState, |
SetDefaultModulePrototype = SetDefaultModulePrototype, |
SetEnabledState = SetEnabledState, |
IterateModules = IterateModules, |
IterateEmbeds = IterateEmbeds, |
GetName = GetName, |
} |
local function IsModule(self) return false end |
local pmixins = { |
defaultModuleState = true, |
enabledState = true, |
IsModule = IsModule, |
} |
-- Embed( target ) |
-- target (object) - target object to embed aceaddon in |
-- |
-- this is a local function specifically since it's meant to be only called internally |
function Embed(target) |
for k, v in pairs(mixins) do |
target[k] = v |
end |
for k, v in pairs(pmixins) do |
target[k] = target[k] or v |
end |
end |
-- AceAddon:IntializeAddon( addon ) |
-- addon (object) - addon to intialize |
-- |
-- calls OnInitialize on the addon object if available |
-- calls OnEmbedInitialize on embedded libs in the addon object if available |
function AceAddon:InitializeAddon(addon) |
safecall(addon.OnInitialize, addon) |
local embeds = self.embeds[addon] |
for i = 1, #embeds do |
local lib = LibStub:GetLibrary(embeds[i], true) |
if lib then safecall(lib.OnEmbedInitialize, lib, addon) end |
end |
-- we don't call InitializeAddon on modules specifically, this is handled |
-- from the event handler and only done _once_ |
end |
-- AceAddon:EnableAddon( addon ) |
-- addon (object) - addon to enable |
-- |
-- calls OnEnable on the addon object if available |
-- calls OnEmbedEnable on embedded libs in the addon object if available |
function AceAddon:EnableAddon(addon) |
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end |
if self.statuses[addon.name] or not addon.enabledState then return false end |
-- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable. |
self.statuses[addon.name] = true |
-- TODO: handle 'first'? Or let addons do it on their own? |
safecall(addon.OnEnable, addon) |
-- make sure we're still enabled before continueing |
if self.statuses[addon.name] then |
local embeds = self.embeds[addon] |
for i = 1, #embeds do |
local lib = LibStub:GetLibrary(embeds[i], true) |
if lib then safecall(lib.OnEmbedEnable, lib, addon) end |
end |
-- enable possible modules. |
for name, module in pairs(addon.modules) do |
self:EnableAddon(module) |
end |
end |
return self.statuses[addon.name] -- return true if we're disabled |
end |
-- AceAddon:DisableAddon( addon ) |
-- addon (object|string) - addon to disable |
-- |
-- calls OnDisable on the addon object if available |
-- calls OnEmbedDisable on embedded libs in the addon object if available |
function AceAddon:DisableAddon(addon) |
if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end |
if not self.statuses[addon.name] then return false end |
-- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable. |
self.statuses[addon.name] = false |
safecall( addon.OnDisable, addon ) |
-- make sure we're still disabling... |
if not self.statuses[addon.name] then |
local embeds = self.embeds[addon] |
for i = 1, #embeds do |
local lib = LibStub:GetLibrary(embeds[i], true) |
if lib then safecall(lib.OnEmbedDisable, lib, addon) end |
end |
-- disable possible modules. |
for name, module in pairs(addon.modules) do |
self:DisableAddon(module) |
end |
end |
return not self.statuses[addon.name] -- return true if we're disabled |
end |
--The next few funcs are just because no one should be reaching into the internal registries |
--Thoughts? |
function AceAddon:IterateAddons() return pairs(self.addons) end |
function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end |
function AceAddon:IterateAddonStatus() return pairs(self.statuses) end |
function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end |
-- Event Handling |
local function onEvent(this, event, arg1) |
if event == "ADDON_LOADED" or event == "PLAYER_LOGIN" then |
-- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration |
while(#AceAddon.initializequeue > 0) do |
local addon = tremove(AceAddon.initializequeue, 1) |
-- this might be an issue with recursion - TODO: validate |
if event == "ADDON_LOADED" then addon.baseName = arg1 end |
AceAddon:InitializeAddon(addon) |
tinsert(AceAddon.enablequeue, addon) |
end |
if IsLoggedIn() then |
while(#AceAddon.enablequeue > 0) do |
local addon = tremove(AceAddon.enablequeue, 1) |
AceAddon:EnableAddon(addon) |
end |
end |
end |
end |
AceAddon.frame:RegisterEvent("ADDON_LOADED") |
AceAddon.frame:RegisterEvent("PLAYER_LOGIN") |
AceAddon.frame:SetScript("OnEvent", onEvent) |
-- upgrade embeded |
for name, addon in pairs(AceAddon.addons) do |
Embed(addon) |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ |
..\FrameXML\UI.xsd"> |
<Script file="AceDB-3.0.lua"/> |
</Ui> |
--[[ $Id: AceDB-3.0.lua 667 2008-06-11 14:34:58Z nevcairiel $ ]] |
local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 8 |
local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR) |
if not AceDB then return end -- No upgrade needed |
local type = type |
local pairs, next = pairs, next |
local rawget, rawset = rawget, rawset |
local setmetatable = setmetatable |
AceDB.db_registry = AceDB.db_registry or {} |
AceDB.frame = AceDB.frame or CreateFrame("Frame") |
local CallbackHandler |
local CallbackDummy = { Fire = function() end } |
local DBObjectLib = {} |
--[[------------------------------------------------------------------------- |
AceDB Utility Functions |
---------------------------------------------------------------------------]] |
-- Simple shallow copy for copying defaults |
local function copyTable(src, dest) |
if type(dest) ~= "table" then dest = {} end |
if type(src) == "table" then |
for k,v in pairs(src) do |
if type(v) == "table" then |
-- try to index the key first so that the metatable creates the defaults, if set, and use that table |
v = copyTable(v, dest[k]) |
end |
dest[k] = v |
end |
end |
return dest |
end |
-- Called to add defaults to a section of the database |
-- |
-- When a ["*"] default section is indexed with a new key, a table is returned |
-- and set in the host table. These tables must be cleaned up by removeDefaults |
-- in order to ensure we don't write empty default tables. |
local function copyDefaults(dest, src) |
-- this happens if some value in the SV overwrites our default value with a non-table |
--if type(dest) ~= "table" then return end |
for k, v in pairs(src) do |
if k == "*" or k == "**" then |
if type(v) == "table" then |
-- This is a metatable used for table defaults |
local mt = { |
-- This handles the lookup and creation of new subtables |
__index = function(t,k) |
if k == nil then return nil end |
local tbl = {} |
copyDefaults(tbl, v) |
rawset(t, k, tbl) |
return tbl |
end, |
} |
setmetatable(dest, mt) |
-- handle already existing tables in the SV |
for dk, dv in pairs(dest) do |
if not rawget(src, dk) and type(dv) == "table" then |
copyDefaults(dv, v) |
end |
end |
else |
-- Values are not tables, so this is just a simple return |
local mt = {__index = function(t,k) return k~=nil and v or nil end} |
setmetatable(dest, mt) |
end |
elseif type(v) == "table" then |
if not rawget(dest, k) then rawset(dest, k, {}) end |
if type(dest[k]) == "table" then |
copyDefaults(dest[k], v) |
if src['**'] then |
copyDefaults(dest[k], src['**']) |
end |
end |
else |
if rawget(dest, k) == nil then |
rawset(dest, k, v) |
end |
end |
end |
end |
-- Called to remove all defaults in the default table from the database |
local function removeDefaults(db, defaults, blocker) |
for k,v in pairs(defaults) do |
if k == "*" or k == "**" then |
if type(v) == "table" then |
-- Loop through all the actual k,v pairs and remove |
for key, value in pairs(db) do |
if type(value) == "table" then |
-- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables |
if defaults[key] == nil and (not blocker or blocker[key] == nil) then |
removeDefaults(value, v) |
-- if the table is empty afterwards, remove it |
if not next(value) then |
db[key] = nil |
end |
-- if it was specified, only strip ** content, but block values which were set in the key table |
elseif k == "**" then |
removeDefaults(value, v, defaults[key]) |
end |
end |
end |
elseif k == "*" then |
-- check for non-table default |
for key, value in pairs(db) do |
if defaults[key] == nil and v == value then |
db[key] = nil |
end |
end |
end |
elseif type(v) == "table" and type(db[k]) == "table" then |
-- if a blocker was set, dive into it, to allow multi-level defaults |
removeDefaults(db[k], v, blocker and blocker[k]) |
if not next(db[k]) then |
db[k] = nil |
end |
else |
-- check if the current value matches the default, and that its not blocked by another defaults table |
if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then |
db[k] = nil |
end |
end |
end |
-- remove all metatables from the db |
setmetatable(db, nil) |
end |
-- This is called when a table section is first accessed, to set up the defaults |
local function initSection(db, section, svstore, key, defaults) |
local sv = rawget(db, "sv") |
local tableCreated |
if not sv[svstore] then sv[svstore] = {} end |
if not sv[svstore][key] then |
sv[svstore][key] = {} |
tableCreated = true |
end |
local tbl = sv[svstore][key] |
if defaults then |
copyDefaults(tbl, defaults) |
end |
rawset(db, section, tbl) |
return tableCreated, tbl |
end |
-- Metatable to handle the dynamic creation of sections and copying of sections. |
local dbmt = { |
__index = function(t, section) |
local keys = rawget(t, "keys") |
local key = keys[section] |
if key then |
local defaultTbl = rawget(t, "defaults") |
local defaults = defaultTbl and defaultTbl[section] |
if section == "profile" then |
local new = initSection(t, section, "profiles", key, defaults) |
if new then |
-- Callback: OnNewProfile, database, newProfileKey |
t.callbacks:Fire("OnNewProfile", t, key) |
end |
elseif section == "profiles" then |
local sv = rawget(t, "sv") |
if not sv.profiles then sv.profiles = {} end |
rawset(t, "profiles", sv.profiles) |
elseif section == "global" then |
local sv = rawget(t, "sv") |
if not sv.global then sv.global = {} end |
if defaults then |
copyDefaults(sv.global, defaults) |
end |
rawset(t, section, sv.global) |
else |
initSection(t, section, section, key, defaults) |
end |
end |
return rawget(t, section) |
end |
} |
local function validateDefaults(defaults, keyTbl, offset) |
if not defaults then return end |
offset = offset or 0 |
for k in pairs(defaults) do |
if not keyTbl[k] or k == "profiles" then |
error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset) |
end |
end |
end |
local preserve_keys = { |
["callbacks"] = true, |
["RegisterCallback"] = true, |
["UnregisterCallback"] = true, |
["UnregisterAllCallbacks"] = true, |
["children"] = true, |
} |
local realmKey = GetRealmName() |
local charKey = UnitName("player") .. " - " .. realmKey |
local _, classKey = UnitClass("player") |
local _, raceKey = UnitRace("player") |
local factionKey = UnitFactionGroup("player") |
local factionrealmKey = factionKey .. " - " .. realmKey |
-- Actual database initialization function |
local function initdb(sv, defaults, defaultProfile, olddb, parent) |
-- Generate the database keys for each section |
-- Make a container for profile keys |
if not sv.profileKeys then sv.profileKeys = {} end |
-- Try to get the profile selected from the char db |
local profileKey = sv.profileKeys[charKey] or defaultProfile or charKey |
sv.profileKeys[charKey] = profileKey |
-- This table contains keys that enable the dynamic creation |
-- of each section of the table. The 'global' and 'profiles' |
-- have a key of true, since they are handled in a special case |
local keyTbl= { |
["char"] = charKey, |
["realm"] = realmKey, |
["class"] = classKey, |
["race"] = raceKey, |
["faction"] = factionKey, |
["factionrealm"] = factionrealmKey, |
["profile"] = profileKey, |
["global"] = true, |
["profiles"] = true, |
} |
validateDefaults(defaults, keyTbl, 1) |
-- This allows us to use this function to reset an entire database |
-- Clear out the old database |
if olddb then |
for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end |
end |
-- Give this database the metatable so it initializes dynamically |
local db = setmetatable(olddb or {}, dbmt) |
if not rawget(db, "callbacks") then |
-- try to load CallbackHandler-1.0 if it loaded after our library |
if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end |
db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy |
end |
-- Copy methods locally into the database object, to avoid hitting |
-- the metatable when calling methods |
if not parent then |
for name, func in pairs(DBObjectLib) do |
db[name] = func |
end |
else |
-- hack this one in |
db.RegisterDefaults = DBObjectLib.RegisterDefaults |
db.ResetProfile = DBObjectLib.ResetProfile |
end |
-- Set some properties in the database object |
db.profiles = sv.profiles |
db.keys = keyTbl |
db.sv = sv |
--db.sv_name = name |
db.defaults = defaults |
db.parent = parent |
-- store the DB in the registry |
AceDB.db_registry[db] = true |
return db |
end |
-- handle PLAYER_LOGOUT |
-- strip all defaults from all databases |
local function logoutHandler(frame, event) |
if event == "PLAYER_LOGOUT" then |
for db in pairs(AceDB.db_registry) do |
db.callbacks:Fire("OnDatabaseShutdown", db) |
for section, key in pairs(db.keys) do |
if db.defaults and db.defaults[section] and rawget(db, section) then |
removeDefaults(db[section], db.defaults[section]) |
end |
end |
end |
end |
end |
AceDB.frame:RegisterEvent("PLAYER_LOGOUT") |
AceDB.frame:SetScript("OnEvent", logoutHandler) |
--[[------------------------------------------------------------------------- |
AceDB Object Method Definitions |
---------------------------------------------------------------------------]] |
-- DBObject:RegisterDefaults(defaults) |
-- defaults (table) - A table of defaults for this database |
-- |
-- Sets the defaults table for the given database object by clearing any |
-- that are currently set, and then setting the new defaults. |
function DBObjectLib:RegisterDefaults(defaults) |
if defaults and type(defaults) ~= "table" then |
error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2) |
end |
validateDefaults(defaults, self.keys) |
-- Remove any currently set defaults |
if self.defaults then |
for section,key in pairs(self.keys) do |
if self.defaults[section] and rawget(self, section) then |
removeDefaults(self[section], self.defaults[section]) |
end |
end |
end |
-- Set the DBObject.defaults table |
self.defaults = defaults |
-- Copy in any defaults, only touching those sections already created |
if defaults then |
for section,key in pairs(self.keys) do |
if defaults[section] and rawget(self, section) then |
copyDefaults(self[section], defaults[section]) |
end |
end |
end |
end |
-- DBObject:SetProfile(name) |
-- name (string) - The name of the profile to set as the current profile |
-- |
-- Changes the profile of the database and all of it's namespaces to the |
-- supplied named profile |
function DBObjectLib:SetProfile(name) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2) |
end |
-- changing to the same profile, dont do anything |
if name == self.keys.profile then return end |
local oldProfile = self.profile |
local defaults = self.defaults and self.defaults.profile |
if oldProfile and defaults then |
-- Remove the defaults from the old profile |
removeDefaults(oldProfile, defaults) |
end |
self.profile = nil |
self.keys["profile"] = name |
self.sv.profileKeys[charKey] = name |
-- populate to child namespaces |
if self.children then |
for _, db in pairs(self.children) do |
DBObjectLib.SetProfile(db, name) |
end |
end |
-- Callback: OnProfileChanged, database, newProfileKey |
self.callbacks:Fire("OnProfileChanged", self, name) |
end |
-- DBObject:GetProfiles(tbl) |
-- tbl (table) - A table to store the profile names in (optional) |
-- |
-- Returns a table with the names of the existing profiles in the database. |
-- You can optionally supply a table to re-use for this purpose. |
function DBObjectLib:GetProfiles(tbl) |
if tbl and type(tbl) ~= "table" then |
error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2) |
end |
-- Clear the container table |
if tbl then |
for k,v in pairs(tbl) do tbl[k] = nil end |
else |
tbl = {} |
end |
local curProfile = self.keys.profile |
local i = 0 |
for profileKey in pairs(self.profiles) do |
i = i + 1 |
tbl[i] = profileKey |
if curProfile and profileKey == curProfile then curProfile = nil end |
end |
-- Add the current profile, if it hasn't been created yet |
if curProfile then |
i = i + 1 |
tbl[i] = curProfile |
end |
return tbl, i |
end |
-- DBObject:GetCurrentProfile() |
-- |
-- Returns the current profile name used by the database |
function DBObjectLib:GetCurrentProfile() |
return self.keys.profile |
end |
-- DBObject:DeleteProfile(name) |
-- name (string) - The name of the profile to be deleted |
-- |
-- Deletes a named profile. This profile must not be the active profile. |
function DBObjectLib:DeleteProfile(name, silent) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2) |
end |
if self.keys.profile == name then |
error("Cannot delete the active profile in an AceDBObject.", 2) |
end |
if not rawget(self.sv.profiles, name) and not silent then |
error("Cannot delete profile '" .. name .. "'. It does not exist.", 2) |
end |
self.sv.profiles[name] = nil |
-- populate to child namespaces |
if self.children then |
for _, db in pairs(self.children) do |
DBObjectLib.DeleteProfile(db, name, true) |
end |
end |
-- Callback: OnProfileDeleted, database, profileKey |
self.callbacks:Fire("OnProfileDeleted", self, name) |
end |
-- DBObject:CopyProfile(name) |
-- name (string) - The name of the profile to be copied into the current profile |
-- |
-- Copies a named profile into the current profile, overwriting any conflicting |
-- settings. |
function DBObjectLib:CopyProfile(name, silent) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2) |
end |
if name == self.keys.profile then |
error("Cannot have the same source and destination profiles.", 2) |
end |
if not rawget(self.sv.profiles, name) and not silent then |
error("Cannot copy profile '" .. name .. "'. It does not exist.", 2) |
end |
-- Reset the profile before copying |
DBObjectLib.ResetProfile(self) |
local profile = self.profile |
local source = self.sv.profiles[name] |
copyTable(source, profile) |
-- populate to child namespaces |
if self.children then |
for _, db in pairs(self.children) do |
DBObjectLib.CopyProfile(db, name, true) |
end |
end |
-- Callback: OnProfileCopied, database, sourceProfileKey |
self.callbacks:Fire("OnProfileCopied", self, name) |
end |
-- DBObject:ResetProfile() |
-- noChildren (boolean) - if set to true, the reset will not be populated to the child namespaces of this DB object |
-- |
-- Resets the current profile |
function DBObjectLib:ResetProfile(noChildren) |
local profile = self.profile |
for k,v in pairs(profile) do |
profile[k] = nil |
end |
local defaults = self.defaults and self.defaults.profile |
if defaults then |
copyDefaults(profile, defaults) |
end |
-- populate to child namespaces |
if self.children and not noChildren then |
for _, db in pairs(self.children) do |
DBObjectLib.ResetProfile(db) |
end |
end |
-- Callback: OnProfileReset, database |
self.callbacks:Fire("OnProfileReset", self) |
end |
-- DBObject:ResetDB(defaultProfile) |
-- defaultProfile (string) - The profile name to use as the default |
-- |
-- Resets the entire database, using the string defaultProfile as the default |
-- profile. |
function DBObjectLib:ResetDB(defaultProfile) |
if defaultProfile and type(defaultProfile) ~= "string" then |
error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2) |
end |
local sv = self.sv |
for k,v in pairs(sv) do |
sv[k] = nil |
end |
local parent = self.parent |
initdb(sv, self.defaults, defaultProfile, self) |
-- fix the child namespaces |
if self.children then |
if not sv.namespaces then sv.namespaces = {} end |
for name, db in pairs(self.children) do |
if not sv.namespaces[name] then sv.namespaces[name] = {} end |
initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self) |
end |
end |
-- Callback: OnDatabaseReset, database |
self.callbacks:Fire("OnDatabaseReset", self) |
-- Callback: OnProfileChanged, database, profileKey |
self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"]) |
return self |
end |
-- DBObject:RegisterNamespace(name [, defaults]) |
-- name (string) - The name of the new namespace |
-- defaults (table) - A table of values to use as defaults |
-- |
-- Creates a new database namespace, directly tied to the database. This |
-- is a full scale database in it's own rights other than the fact that |
-- it cannot control its profile individually |
function DBObjectLib:RegisterNamespace(name, defaults) |
if type(name) ~= "string" then |
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2) |
end |
if defaults and type(defaults) ~= "table" then |
error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2) |
end |
if self.children and self.children[name] then |
error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2) |
end |
local sv = self.sv |
if not sv.namespaces then sv.namespaces = {} end |
if not sv.namespaces[name] then |
sv.namespaces[name] = {} |
end |
local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self) |
if not self.children then self.children = {} end |
self.children[name] = newDB |
return newDB |
end |
--[[------------------------------------------------------------------------- |
AceDB Exposed Methods |
---------------------------------------------------------------------------]] |
-- AceDB:New(name, defaults, defaultProfile) |
-- name (table or string) - The name of variable, or table to use for the database |
-- defaults (table) - A table of database defaults |
-- defaultProfile (string) - The name of the default profile |
-- |
-- Creates a new database object that can be used to handle database settings |
-- and profiles. |
function AceDB:New(tbl, defaults, defaultProfile) |
if type(tbl) == "string" then |
local name = tbl |
tbl = getglobal(name) |
if not tbl then |
tbl = {} |
setglobal(name, tbl) |
end |
end |
if type(tbl) ~= "table" then |
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2) |
end |
if defaults and type(defaults) ~= "table" then |
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2) |
end |
if defaultProfile and type(defaultProfile) ~= "string" then |
error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string expected.", 2) |
end |
return initdb(tbl, defaults, defaultProfile) |
end |
-- upgrade existing databases |
for db in pairs(AceDB.db_registry) do |
if not db.parent then |
for name,func in pairs(DBObjectLib) do |
db[name] = func |
end |
else |
db.RegisterDefaults = DBObjectLib.RegisterDefaults |
end |
end |
<Ui xmlns="http://www.blizzard.com/wow/ui/"> |
<Script file="Libs\LibStub\LibStub.lua"/> |
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/> |
<Script file="Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua"/> |
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/> |
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/> |
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/> |
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/> |
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/> |
<Include file="Libs\LibSharedMedia-3.0\lib.xml"/> |
</Ui> |
oUF_Smee_Settings = { |
profile = { |
enabledDebugMessages = false, |
enabled = true, |
hideBlizzard = { |
TemporaryEnchantFrame = true, |
BuffFrame = true, |
}, |
colors = { |
class = { |
["DEATHKNIGHT"] = { 0.77, 0.12, 0.23 }, |
["DRUID"] = { 1.0 , 0.49, 0.04 }, |
["HUNTER"] = { 0.67, 0.83, 0.45 }, |
["MAGE"] = { 0.41, 0.8 , 0.94 }, |
["PALADIN"] = { 0.96, 0.55, 0.73 }, |
["PRIEST"] = { 1.0 , 1.0 , 1.0 }, |
["ROGUE"] = { 1.0 , 0.96, 0.41 }, |
["SHAMAN"] = { 0,0.86,0.73 }, |
["WARLOCK"] = { 0.58, 0.51, 0.7 }, |
["WARRIOR"] = { 0.78, 0.61, 0.43 }, |
}, |
backdropColors = {0,0,0}, |
castbars = { |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, |
insets = {left = -1.5, right = -1.5, top = -1.5, bottom = -1.5}, |
}, |
safezoneColor = {1, .2, .2, .5} |
}, |
runes={ |
[1] = {0.77, 0.12, 0.23}, |
[2] = {0.3, 0.8, 0.1}, |
[3] = {0, 0.4, 0.7}, |
[4] = {0.2, 0.2, 0.2}, |
}, |
}, |
classification = { |
worldboss = '%s |cffD7BEA5Boss|r', |
rareelite = '%s |cff%02x%02x%02x%s|r|cffD7BEA5+ R|r', |
elite = '%s |cff%02x%02x%02x%s|r|cffD7BEA5+|r', |
rare = '%s |cff%02x%02x%02x%s|r |cffD7BEA5R|r', |
normal = '%s |cff%02x%02x%02x%s|r', |
trivial = '%s |cff%02x%02x%02x%s|r', |
}, |
textures = { |
backgrounds = { |
default = { |
bgFile = "Interface\\BUTTONS\\WHITE8x8", tile = true, tileSize = 16, |
insets = {left = -1.5, right = -1.5, top = -1.5, bottom = -1.5}, |
}, |
}, |
statusbars = { |
default = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\default", |
gloss = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\gloss", |
slimDa = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dA", |
slimDm = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dM", |
slimDo = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dO", |
slimDp = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dP", |
slimDq = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dQ", |
slimDr = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dR", |
slimDu = "Interface\\Addons\\oUF_Smee2\\media\\statusbars\\slim-dU", |
}, |
borders = { |
default = "Interface\\Addons\\oUF_Smee2\\media\\borders\\default", |
slimbuff = "Interface\\Addons\\oUF_Smee2\\media\\borders\\slim-buff", |
slimunit = "Interface\\Addons\\oUF_Smee2\\media\\borders\\slim-unit", |
}, |
}, |
fonts = { |
default ="Interface\\Addons\\oUF_Smee2\\media\\fonts\\visitor.ttf" |
}, |
auras = { |
playerScale = 1.4, |
otherScale = 1, |
timers = { |
enabled = true, |
UsingMMSS = false, |
useEnlargedFonts = true, |
values = { |
DAY = 86400, |
HOUR = 3600, |
MINUTE = 60, |
SHORT = 5 |
}, |
cooldownTimerStyle = { |
short = {r = 1, g = 0, b = 0, s = .98}, -- <= 5 seconds |
secs = {r = 1, g = 1, b = 0.4, s = 1}, -- < 1 minute |
mins = {r = 0.8, g = 0.8, b = 0.9, s = 1}, -- >= 1 minute |
hrs = {r = 0.8, g = 0.8, b = 0.9, s = 1}, -- >= 1 hr |
days = {r = 0.8, g = 0.8, b = 0.9, s = 1}, -- >= 1 day |
}, |
}, |
font = { |
size = 8.5, |
name = 'default', |
outline = 'outline', |
} |
}, |
frames = { |
locked = true, |
scale = 1, |
font = { |
size = 8.5, |
name = 'default', |
outline = 'outline', |
}, |
units = { |
player = { |
anchorFromPoint = "BOTTOM", |
anchorTo = "UIParent", |
anchorToPoint = "BOTTOM", |
anchorX = 0, |
anchorY = 200, |
width = 230, |
height = 22, |
scale = 1, |
barFading = false, |
range = { |
enabled = false, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = false, |
modelScale = 4.5, |
}, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "BOTTOM", |
anchorTo = "Health", |
anchorFromPoint = "TOP", |
anchorX = 4, |
anchorY = -4, |
justifyH = "CENTER", |
justifyV = "TOP", |
tag = "[status] [name]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -4, |
anchorY = 0, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[curhp]", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = 0, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[curpp]", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Levels", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
bars = { |
Health = { |
enabled = true, |
dependantOnFrameSize = true, |
classFilter = false, |
reverse = false, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
dependantOnFrameSize = true, |
reverse = false, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
RuneBar = { |
enabled = true, |
classFilter = "DEATHKNIGHT", |
reverse = false, |
dependantOnFrameSize = false, |
width = 100, |
height = 50, |
anchorFromPoint = "TOP", |
anchorTo = 'parent', |
anchorToPoint = "BOTTOM", |
orientation = "VERTICAL", |
anchorX = 0, |
anchorY = -5, |
padding=2, |
runes = { |
[1] = { |
anchorToPoint="LEFT", |
anchorFromPoint="LEFT", |
anchorTo="runeBar", |
x = 1, |
y = 0, |
width=16.5, |
height=100, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
[2] = { |
anchorToPoint="RIGHT", |
anchorFromPoint="LEFT", |
anchorTo=1, |
x = 1, |
y = 0, |
width=16.5, |
height=100, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
[3] = { |
anchorToPoint="RIGHT", |
anchorFromPoint="LEFT", |
anchorTo=2, |
x = 1, |
y = 0, |
width=16.5, |
height=100, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
[4] = { |
anchorToPoint="RIGHT", |
anchorFromPoint="LEFT", |
anchorTo=3, |
x = 1, |
y = 0, |
width=16.5, |
height=100, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
[5] = { |
anchorToPoint="RIGHT", |
anchorFromPoint="LEFT", |
anchorTo=4, |
x = 1, |
y = 0, |
width=16.5, |
height=100, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
[6] = { |
anchorToPoint="RIGHT", |
anchorFromPoint="LEFT", |
anchorTo=5, |
width=16.5, |
height=100, |
x = 1, |
y = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
TotemBar = { |
enabled = true, |
classFilter = "SHAMAN", |
reverse = false, |
dependantOnFrameSize = false, |
anchorFromPoint = "TOP", |
anchorTo = 'parent', |
anchorToPoint = "BOTTOM", |
orientation = "HORIZONTAL", |
anchorX = 0, |
anchorY =10, |
height = 30, |
width = 230, |
scale = 1, |
Timer = { |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
anchorFromPoint = "BOTTOM", |
anchorToPoint = "TOP", |
anchorTo = "totem.icon", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
classFilter = false, |
reverse = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = -36, |
height = 14, |
width = 230, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, 1}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorTo = "Castbar", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorTo = "Castbar", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
CoolLine = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
orientation = "HORIZONTAL", |
anchorX = 0, |
anchorY = 5, |
height = 10, |
width = 230, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
} |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX = -4, |
anchorY = -2, |
["growth-x"] = "LEFT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 2, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 4, |
anchorY = -2, |
["growth-x"] = "RIGHT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'gloss', |
border = 'default', |
}, |
}, |
target = { |
anchorFromPoint = "BOTTOMLEFT", |
anchorTo = "player", |
anchorToPoint = "TOP", |
anchorX = 140, |
anchorY = 45, |
width = 230, |
height = 20, |
scale = 1, |
range = { |
enabled = true, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = false, |
modelScale = 4.5, |
}, |
barFading = false, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Power", |
anchorFromPoint = "RIGHT", |
anchorX = -2, |
anchorY = -1, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[difficulty][shortclassification][level] [name] [dead]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 2, |
anchorY = 1, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[perhp]%", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -4, |
anchorY = 1, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[curpp]", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Level", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
bars = { |
Health = { |
enabled = true, |
classFilter = false, |
dependantOnFrameSize = true, |
reverse = false, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
dependantOnFrameSize = true, |
reverse = false, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = true, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
classFilter = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = -36, |
height = 14, |
width = 230, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, .5}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 4, |
anchorY = -2, |
["growth-x"] = "RIGHT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 4, |
anchorY = -36, |
["growth-x"] = "RIGHT", |
["growth-y"] = "DOWN", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'default', |
border = 'default', |
}, |
}, |
targettarget = { |
anchorFromPoint = "BOTTOMLEFT", |
anchorTo = "target", |
anchorToPoint = "TOPLEFT", |
anchorX = 0, |
anchorY = 15, |
width =165, |
height = 15, |
scale = 1, |
range = { |
enabled = true, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = false, |
modelScale = 4.5, |
}, |
barFading = false, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Power", |
anchorFromPoint = "RIGHT", |
anchorX = -2, |
anchorY = -4, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[difficulty][shortclassification][level] [name] [dead]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[perhp]%", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText = { |
enabled = false, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -4, |
anchorY = 1, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[curpp]", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Level", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
bars = { |
Health = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = true, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
reverse = false, |
classFilter = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = 4, |
height = 14, |
width = 165, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, .5}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "LEFT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "RIGHT", |
["growth-y"] = "DOWN", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'default', |
border = 'default', |
}, |
}, |
focus = { |
anchorFromPoint = "BOTTOMRIGHT", |
anchorTo = "player", |
anchorToPoint = "TOP", |
anchorX = -140, |
anchorY = 45, |
width = 230, |
height = 20, |
scale = 1, |
barFading = false, |
range = { |
enabled = true, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = false, |
modelScale = 4.5, |
}, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Power", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = -1, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[difficulty][shortclassification][level] [name] [dead]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -2, |
anchorY = 1, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[missinghp][perhp]%", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = 1, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[perpp]%", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Level", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
bars = { |
Health = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = true, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
classFilter = false, |
reverse = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = -36, |
height = 14, |
width = 230, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, .5}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX = -4, |
anchorY = -2, |
["growth-x"] = "LEFT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX =-4, |
anchorY = -36, |
["growth-x"] = "LEFT", |
["growth-y"] = "DOWN", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'default', |
border = 'default', |
}, |
}, |
focustarget = { |
anchorFromPoint = "BOTTOMRIGHT", |
anchorTo = "focus", |
anchorToPoint = "TOPRIGHT", |
anchorX = 0, |
anchorY = 15, |
width = 165, |
height = 15, |
scale = 1, |
range = { |
enabled = true, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = false, |
modelScale = 4.5, |
}, |
barFading = false, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Power", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = -4, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[difficulty][shortclassification][level] [name] [dead]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -4, |
anchorY = 0, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[perhp]%", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = 1, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[curpp]", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Level", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
bars = { |
Health = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = true, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
classFilter = false, |
reverse = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = 4, |
height = 14, |
width = 165, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, .5}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "LEFT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "RIGHT", |
["growth-y"] = "DOWN", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'default', |
border = 'default', |
}, |
}, |
pet = { |
anchorFromPoint = "TOPRIGHT", |
anchorTo = "player", |
anchorToPoint = "BOTTOMLEFT", |
anchorX = 0, |
anchorY = -14, |
width = 145, |
height = 18, |
scale = 1, |
range = { |
enabled = true, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = true, |
modelScale = 4.5, |
}, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "BOTTOMRIGHT", |
anchorTo = "Health", |
anchorFromPoint = "TOPRIGHT", |
anchorX = 0, |
anchorY = -5, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[level] [name] [dead]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -4, |
anchorY = 1, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[perhp]%", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = 1, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[perpp]%", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Level", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
barFading = false, |
bars = { |
Health = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = true, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
classFilter = false, |
reverse = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = -36, |
height = 8, |
width = 115, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, .5}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX = -4, |
anchorY = 0, |
["growth-x"] = "LEFT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 16, |
playerSize = 1.2, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "RIGHT", |
["growth-y"] = "DOWN", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'default', |
border = 'default', |
}, |
}, |
pettarget = { |
anchorFromPoint = "TOPLEFT", |
anchorTo = "player", |
anchorToPoint = "BOTTOMRIGHT", |
anchorX = 0, |
anchorY = -14, |
width = 145, |
height = 18, |
scale = 1, |
range = { |
enabled = true, |
inside = 1, |
outside = .5, |
}, |
portraitFrame = { |
enabled = false, |
modelScale = 4.5, |
}, |
FontObjects={ |
NameText ={ |
enabled = true, |
anchorToPoint = "BOTTOMLEFT", |
anchorTo = "Health", |
anchorFromPoint = "TOPLEFT", |
anchorX = 0, |
anchorY = -5, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[difficulty][shortclassification][level] [name] [dead]", |
desc = "Name", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
HealthText ={ |
enabled = true, |
anchorToPoint = "RIGHT", |
anchorTo = "Health", |
anchorFromPoint = "RIGHT", |
anchorX = -4, |
anchorY = 1, |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
tag = "[perhp]%", |
desc = "Health", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
PowerText ={ |
enabled = true, |
anchorToPoint = "LEFT", |
anchorTo = "Health", |
anchorFromPoint = "LEFT", |
anchorX = 4, |
anchorY = 1, |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
tag = "[perpp]%", |
desc = "Power", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
ThreatText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "[threat]", |
desc = "Threat Level", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
CombatFeedbackText = { |
enabled = true, |
anchorToPoint = "CENTER", |
anchorTo = "Health", |
anchorFromPoint = "CENTER", |
anchorX = 4, |
anchorY = 1, |
justifyH = "CENTER", |
justifyV = "MIDDLE", |
tag = "", |
desc = "Combat Feedback Text", |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
barFading = false, |
bars = { |
Health = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 80, |
colorDisconnected = true, |
colorClass = false, |
colorTapping = true, |
colorReaction = false, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Power = { |
enabled = true, |
classFilter = false, |
reverse = false, |
dependantOnFrameSize = true, |
width = 100, |
height = 20, |
colorDisconnected = false, |
colorClass = true, |
colorTapping = false, |
colorReaction = true, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0.25,0.25,0.25,1}, |
BackdropColor = {0,0,0,1}, |
Backdrop = "default", |
}, |
Castbar = { |
enabled = true, |
classFilter = false, |
reverse = false, |
anchorFromPoint = "BOTTOM", |
anchorTo = 'parent', |
anchorToPoint = "TOP", |
anchorX = 0, |
anchorY = -36, |
height = 14, |
width =115, |
bgColor = {1,1,1,.3}, |
StatusBarColor = {0,0,0,0.6}, |
BackdropColor = {0,0,0,1}, |
Backdrop = { |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", |
tile = true, |
tileSize = 16, |
insets = { |
left = -1.5, |
right = -1.5, |
top = -1.5, |
bottom = -1.5 |
}, |
}, |
SafeZone={ |
enabled = true, |
colour = {1, .2, .2, .5}, |
}, |
Text = { |
justifyH = "LEFT", |
justifyV = "MIDDLE", |
anchorFromPoint = "LEFT", |
anchorToPoint = "LEFT", |
anchorX = 2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
Time = { |
justifyH = "RIGHT", |
justifyV = "MIDDLE", |
anchorFromPoint = "RIGHT", |
anchorToPoint = "RIGHT", |
anchorX = -2, |
anchorY = 0, |
font = { |
size = 8.5, |
name = 'default', |
outline = '', |
} |
}, |
}, |
}, |
DebuffHighlightBackdrop = true, |
disallowVehicleSwap = true, |
Buffs = { |
setup = false, |
anchorFromPoint = "RIGHT", |
anchorTo = 'parent', |
anchorToPoint = "LEFT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "LEFT", |
["growth-y"] = "UP", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
Debuffs = { |
setup = false, |
anchorFromPoint = "LEFT", |
anchorTo = 'parent', |
anchorToPoint = "RIGHT", |
anchorX = 0, |
anchorY = -2, |
["growth-x"] = "RIGHT", |
["growth-y"] = "DOWN", |
Colomns = 16, |
rows = 3, |
count = 22, |
size = 20, |
playerSize = 1.5, |
spacing = 1, |
filter = nil, |
showOnlyPlayer = nil, |
}, |
textures = { |
background = 'default', |
statusbar = 'default', |
border = 'default', |
}, |
} |
} |
} |
} |
} |
setmetatable(oUF_Smee_Settings.profile.colors.class, { |
__index = function(self, key) |
return { 0.78, 0.61, 0.43 } |
end |
}) |