vrtc / chorus (public) (License: CC0) (since 2023-08-12) (hash sha1)
World of Warcraft add-on stub. The overall goal is to create a specialized raid frame.
List of commits:
Subject Hash Author Date (UTC)
fix!: Use native mechanism of inheriting values 6a2ebf1aa52e4a300b4ad959670878b292140c4f Vladyslav Bondarenko 2023-08-23 23:05:39
fix: Typo in unit button template anchor 98027a2779639493c97c4259b61cb53d25709255 Vladyslav Bondarenko 2023-08-23 22:06:16
feat!: Add unit button flavours 21e61a0dbd629d25f458ce1a8dd370da31c6f991 Vladyslav Bondarenko 2023-08-23 21:17:33
fix: Shrink range frame height b73521b13c6a9e5d34b2d425e5d7e9ddcb04f81c Vladyslav Bondarenko 2023-08-23 21:15:28
feat: Hide status bar text on demand 307d97f5afee88e55c6799e4c4390ca8d6e98974 Vladyslav Bondarenko 2023-08-23 21:11:35
feat: Hide auras out of frame bounds cb9ca55fb9dc361500e8b4f4c84320eb7d01b744 Vladyslav Bondarenko 2023-08-23 20:53:30
fix: Add text shadow to unit names 2b7d56c701ad81825a838c9faa818632bf38bfb6 Vladyslav Bondarenko 2023-08-23 17:30:23
fix: Further sanitize health bar 3318dee15eac9b8205e287997800c080b0aaf12d Vladyslav Bondarenko 2023-08-23 14:56:00
feat!: Correct range indcator for all characters 8ef1a987ac4431ce90131c354d1a1775b653f3e3 Vladyslav Bondarenko 2023-08-22 15:05:35
fix: Improve health bar robustness 2be053184653a6cd282437f8190e9196af495d81 Vladyslav Bondarenko 2023-08-22 13:24:12
fix: Upgrade aura sorting a2132995674f81e0775def67b0f5786945b0bba3 Vladyslav Bondarenko 2023-08-22 13:21:37
feat: Add threat percentage for hostiles 590bc95167dfc0f22a135fe173182104b6f5b923 Vladyslav Bondarenko 2023-08-22 13:17:57
feat: Add threat for hostiles 22562481a0c9dfb297ebbe47da4aa19046cf6eeb Vladyslav Bondarenko 2023-08-21 22:28:50
fix(build): Add more luacheck definitions aac4bb444d894b5d565af3abe417cbafd4f966f5 Vladyslav Bondarenko 2023-08-21 18:09:50
fix: Ensure TargetFrame is hidden 98bd355e604bf1c51791d5f5874ef33ab640123c Vladyslav Bondarenko 2023-08-21 14:04:40
fix: Show only meaningful numbers for health 37217b1dc78c0a2b4711dc45ada0c4873e904a1f Vladyslav Bondarenko 2023-08-21 13:32:19
fix: Remove testing range frame 4fcfd5c7bf4626137a1251cbcbe54157055ac875 Vladyslav Bondarenko 2023-08-21 13:10:16
fix: Place solo frames range frame correctly f926b1fb857a48b14a706926f14b5da730d210f1 Vladyslav Bondarenko 2023-08-21 13:08:42
feat!: Re-add player solo frames bf5346aa96a3daeadafeb3a723f7821012f1d5e5 Vladyslav Bondarenko 2023-08-21 12:22:48
fix: Render raid target icon on correct layer 890bb5340a3876b611f67ac1277cc009e5e4df5b Vladyslav Bondarenko 2023-08-20 18:12:31
Commit 6a2ebf1aa52e4a300b4ad959670878b292140c4f - fix!: Use native mechanism of inheriting values
Use SecureButton_GetAttribute function, defined by native
FrameXML/SecureTemplates, to inherit properties of frames from parent to
children. Previously a custom mechanism of duplicating values on demand
was used.

This is a major change that affects every module. There is a need for
different frames to access each others' mutable properties at runtime.
For example, this is required by the health bar implementation to know
which unit it belongs to.

Previously, the values were duplicated on demand using custom
implementation. When property of the parent frame changed, usually of
the "unit" attribute of secure unit button frame, every child of the
parent frame would be assigned the same value.

This approach worked, with a few minor issues.

There was ambiguity between frame fields (`self.unit`) and frame
properties (`self:GetAttribute('unit')`). Fields can only be accessed
from untrusted code. As of this commit, all code of the project consists
of untrusted code. Properties can be set and accessed by trusted code,
using secure templates and secure handlers.

This commit makes the project much more compatible with the secure
templates and handlers, that are required for making the especially
customized and fine tuned user interfaces.

Additionally, using the approach of secure templates allows for more
transparent implementation, since the secure templates API is
documented.

It has the additional benefit of explicitly declaring which attributes
are inheritable. In most cases it's the "unit" attribute. Thus every
frame that does rely on this inheritance, declares this by setting a
specific property like this.

```lua
self:SetAttribute('useparent-unit', true)
```

See FrameXML/SecureTemplates.lua for more details on the implementation.

It is uncertain which approach, 1) the custom duplication mechanism used
previously, or the new one that replaces it, 2) the secure template
functions, is more efficient at runtime. In the end, the author decided
that re-using existing code that is documented and is provided by the
framework is more preferable long-term, regardless of runtime
performance efficiency.
Author: Vladyslav Bondarenko
Author date (UTC): 2023-08-23 23:05
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2023-08-23 23:05
Parent(s): 98027a2779639493c97c4259b61cb53d25709255
Signer:
Signing key: EFF9624877D25D02
Signing status: E
Tree: 05f8236e3b3fafdea5ca74de577e4c7dd36d0c96
File Lines added Lines deleted
etc/luacheckrc.lua 3 0
src/ChorusAuraButtonTemplate.lua 20 20
src/ChorusAuraButtonTemplate.xml 6 8
src/ChorusAuraFrameTemplate.lua 2 16
src/ChorusAuraFrameTemplate.xml 3 0
src/ChorusHealthFrameTemplate.xml 3 0
src/ChorusPowerFrameTemplate.xml 3 0
src/ChorusProgressFrameTemplate.lua 2 17
src/ChorusRaidTargetIconFrameTemplate.lua 2 6
src/ChorusRaidTargetIconFrameTemplate.xml 3 0
src/ChorusRangeFrameTemplate.lua 1 12
src/ChorusRangeFrameTemplate.xml 3 0
src/ChorusThreatFrameTemplate.lua 2 5
src/ChorusThreatFrameTemplate.xml 3 0
src/ChorusUnitButtonTemplate.lua 7 78
src/ChorusUnitButtonTemplate.xml 8 6
src/ChorusUnitNameFrameTemplate.lua 1 3
src/ChorusUnitNameFrameTemplate.xml 3 0
File etc/luacheckrc.lua changed (mode: 100644) (index 5861842..819ac8e)
... ... stds.framexml = {
77 77 'FocusFrameManaBar', 'FocusFrameManaBar',
78 78 'FocusFrameSpellBar', 'FocusFrameSpellBar',
79 79 'GameFontWhite', 'GameFontWhite',
80 'GameTooltip',
80 81 'MAX_PARTY_MEMBERS', 'MAX_PARTY_MEMBERS',
81 82 'MAX_SPELLS', 'MAX_SPELLS',
82 83 'NumberFontNormalSmall', 'NumberFontNormalSmall',
 
... ... stds.framexml = {
97 98 'PowerBarColor', 'PowerBarColor',
98 99 'RAID_CLASS_COLORS', 'RAID_CLASS_COLORS',
99 100 'RuneFrame', 'RuneFrame',
101 'SecureButton_GetAttribute',
102 'SecureButton_GetUnit',
100 103 'SetRaidTargetIconTexture', 'SetRaidTargetIconTexture',
101 104 'TargetFrame', 'TargetFrame',
102 105 'TargetFrameDropDown', 'TargetFrameDropDown',
File src/ChorusAuraButtonTemplate.lua changed (mode: 100644) (index 10e7c58..01a6f6f)
1 1 local DebuffTypeColor = DebuffTypeColor local DebuffTypeColor = DebuffTypeColor
2 local GameTooltip = GameTooltip
2 3 local GetTime = GetTime local GetTime = GetTime
3 4 local UnitAura = UnitAura local UnitAura = UnitAura
4 5 local UnitExists = UnitExists local UnitExists = UnitExists
 
... ... local UnitIsConnected = UnitIsConnected
6 7
7 8 local Chorus = Chorus local Chorus = Chorus
8 9
9 local function getUnit(f)
10 local p = f:GetParent()
11 local u = f.unit or f:GetAttribute('unit')
12 if not u and p then
13 u = p.unit or p:GetAttribute('unit')
14 end
15 return u
16 end
17
18 10 local function auraButtonValidate(auraButton) local function auraButtonValidate(auraButton)
19 11 assert(auraButton ~= nil) assert(auraButton ~= nil)
20 12
 
... ... local function applyDuration(auraButton, now, totalDurationSec, expirationInstan
142 134 end end
143 135
144 136 local function auraButtonUpdateProcessor(self) local function auraButtonUpdateProcessor(self)
145 local unitDesignation = getUnit(self)
146 if not unitDesignation or not self.index then
137 local unitDesignation = SecureButton_GetUnit(self)
138 local index = self.index
139 local filter = SecureButton_GetAttribute(self, 'filter')
140 if not unitDesignation or not index then
147 141 return return
148 142 end end
149 local name, _, _, _, _, durationSec, expirationInstance = UnitAura(unitDesignation, self.index, self.filter)
143 local name, _, _, _, _, durationSec, expirationInstance = UnitAura(unitDesignation, index, filter)
150 144 if not name then if not name then
151 145 return return
152 146 end end
 
... ... local function auraButtonEventProcessor(self, eventCategory)
204 198 assert(string.len(eventCategory) >= 1) assert(string.len(eventCategory) >= 1)
205 199 assert(string.len(eventCategory) <= 256) assert(string.len(eventCategory) <= 256)
206 200
207 local p = self:GetParent()
208
209 local u = getUnit(self) or 'none'
201 local u = SecureButton_GetUnit(self) or 'none'
210 202 assert(u ~= nil) assert(u ~= nil)
211 203 assert('string' == type(u)) assert('string' == type(u))
212 204 u = string.lower(strtrim(u)) u = string.lower(strtrim(u))
 
... ... local function auraButtonEventProcessor(self, eventCategory)
225 217 assert('number' == type(i)) assert('number' == type(i))
226 218 i = math.min(math.max(0, math.abs(math.floor(i))), 8192) i = math.min(math.max(0, math.abs(math.floor(i))), 8192)
227 219
228 local filter = self.filter
229 if not filter and p then
230 filter = p.filter
231 end
232 filter = filter or 'HELPFUL'
220 local filter = SecureButton_GetAttribute(self, 'filter')
233 221 assert(filter ~= nil) assert(filter ~= nil)
234 222 assert('string' == type(filter)) assert('string' == type(filter))
235 223 filter = string.upper(strtrim(filter)) filter = string.upper(strtrim(filter))
 
... ... local function auraButtonEventProcessor(self, eventCategory)
239 227 apply(self, u, i, filter) apply(self, u, i, filter)
240 228 end end
241 229
230 function Chorus.auraButtonGameTooltipShow(self)
231 GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT");
232 GameTooltip:SetFrameLevel(self:GetFrameLevel() + 2);
233 local unitDesignation = SecureButton_GetUnit(self) or 'none'
234 local filter = SecureButton_GetAttribute(self, 'filter')
235 GameTooltip:SetUnitAura(unitDesignation, self.index, filter)
236 end
237
238 function Chorus.auraButtonGameTooltipHide()
239 GameTooltip:Hide();
240 end
241
242 242 function Chorus.auraButtonMain(self) function Chorus.auraButtonMain(self)
243 243 local n = self:GetName() local n = self:GetName()
244 244 if n then if n then
File src/ChorusAuraButtonTemplate.xml changed (mode: 100644) (index cfaaec2..37927ad)
52 52 </Layers> </Layers>
53 53 <Scripts> <Scripts>
54 54 <OnLoad>Chorus.auraButtonMain(self);</OnLoad> <OnLoad>Chorus.auraButtonMain(self);</OnLoad>
55 <OnEnter>
56 GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT");
57 GameTooltip:SetFrameLevel(self:GetFrameLevel() + 2);
58 GameTooltip:SetUnitAura(self.unit, self.index, self.filter);
59 </OnEnter>
60 <OnLeave>
61 GameTooltip:Hide();
62 </OnLeave>
55 <OnEnter>Chorus.auraButtonGameTooltipShow(self);</OnEnter>
56 <OnLeave>Chorus.auraButtonGameTooltipHide(self);</OnLeave>
63 57 </Scripts> </Scripts>
58 <Attributes>
59 <Attribute name="useparent-unit" type="boolean" value="true"/>
60 <Attribute name="useparent-filter" type="boolean" value="true"/>
61 </Attributes>
64 62 </Frame> </Frame>
65 63 </Ui> </Ui>
File src/ChorusAuraFrameTemplate.lua changed (mode: 100644) (index 69dc29f..859d2b0)
... ... local auraWeightMap = {
59 59
60 60 Chorus.auraWeightMap = auraWeightMap Chorus.auraWeightMap = auraWeightMap
61 61
62 local function getUnit(f)
63 local p = f:GetParent()
64 local u = f.unit or f:GetAttribute('unit')
65 if not u and p then
66 u = p.unit or p:GetAttribute('unit')
67 end
68 return u
69 end
70
71 62 local function getAuraWeight(unitDesignation, auraIndex, filter) local function getAuraWeight(unitDesignation, auraIndex, filter)
72 63 local name, _, _, _, category, durationSec, owner = UnitAura(unitDesignation, auraIndex, filter) local name, _, _, _, category, durationSec, owner = UnitAura(unitDesignation, auraIndex, filter)
73 64 if not name then if not name then
 
... ... end
131 122 local function auraFrameEventProcessor(self, eventCategory, ...) local function auraFrameEventProcessor(self, eventCategory, ...)
132 123 assert(self ~= nil) assert(self ~= nil)
133 124
134 local unitDesignation = getUnit(self) or 'none'
125 local unitDesignation = SecureButton_GetUnit(self) or 'none'
135 126 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
136 127 assert('string' == type(unitDesignation)) assert('string' == type(unitDesignation))
137 128 unitDesignation = string.lower(strtrim(unitDesignation)) unitDesignation = string.lower(strtrim(unitDesignation))
 
... ... local function auraFrameEventProcessor(self, eventCategory, ...)
152 143 end end
153 144 end end
154 145
155 local filter = self.filter or self:GetAttribute('filter')
146 local filter = SecureButton_GetAttribute(self, 'filter')
156 147 assert(filter ~= nil) assert(filter ~= nil)
157 148 assert('string' == type(filter)) assert('string' == type(filter))
158 149 filter = string.upper(strtrim(filter)) filter = string.upper(strtrim(filter))
 
... ... local function auraFrameEventProcessor(self, eventCategory, ...)
178 169 break break
179 170 end end
180 171
181 b.unit = unitDesignation
182 b.filter = filter
183
184 172 local k = q[i] local k = q[i]
185 173 assert(k ~= nil) assert(k ~= nil)
186 174 assert('number' == type(k)) assert('number' == type(k))
 
... ... local function auraFrameEventProcessor(self, eventCategory, ...)
198 186 i = i + 1 i = i + 1
199 187 local b = t[i] local b = t[i]
200 188 assert(b ~= nil) assert(b ~= nil)
201 b.unit = unitDesignation
202 b.filter = filter
203 189 b.index = 0 b.index = 0
204 190
205 191 local func = b:GetScript('OnEvent') local func = b:GetScript('OnEvent')
File src/ChorusAuraFrameTemplate.xml changed (mode: 100644) (index c0d8a79..38665e1)
79 79 <Scripts> <Scripts>
80 80 <OnLoad>Chorus.auraFrameMain(self);</OnLoad> <OnLoad>Chorus.auraFrameMain(self);</OnLoad>
81 81 </Scripts> </Scripts>
82 <Attributes>
83 <Attribute name="useparent-unit" type="boolean" value="true"/>
84 </Attributes>
82 85 </Frame> </Frame>
83 86 </Ui> </Ui>
File src/ChorusHealthFrameTemplate.xml changed (mode: 100644) (index 9e6475a..9a7f36d)
5 5 <Scripts> <Scripts>
6 6 <OnLoad>Chorus.healthFrameMain(self);</OnLoad> <OnLoad>Chorus.healthFrameMain(self);</OnLoad>
7 7 </Scripts> </Scripts>
8 <Attributes>
9 <Attribute name="useparent-unit" type="boolean" value="true"/>
10 </Attributes>
8 11 </StatusBar> </StatusBar>
9 12 </Ui> </Ui>
File src/ChorusPowerFrameTemplate.xml changed (mode: 100644) (index ba3025e..12b8b54)
5 5 <Scripts> <Scripts>
6 6 <OnLoad>Chorus.powerFrameMain(self);</OnLoad> <OnLoad>Chorus.powerFrameMain(self);</OnLoad>
7 7 </Scripts> </Scripts>
8 <Attributes>
9 <Attribute name="useparent-unit" type="boolean" value="true"/>
10 </Attributes>
8 11 </StatusBar> </StatusBar>
9 12 </Ui> </Ui>
File src/ChorusProgressFrameTemplate.lua changed (mode: 100644) (index 0cda9d2..e69a0c1)
... ... local UnitPowerType = UnitPowerType
14 14
15 15 local Chorus = Chorus local Chorus = Chorus
16 16
17 local function getUnit(f)
18 local p = f:GetParent()
19 local u = f.unit or f:GetAttribute('unit')
20 if not u and p then
21 u = p.unit or p:GetAttribute('unit')
22 end
23 return u
24 end
25
26 17 local function validateProgressFrame(self) local function validateProgressFrame(self)
27 18 assert(self ~= nil) assert(self ~= nil)
28 19
 
... ... local function validateProgressFrame(self)
50 41 strategy = strtrim(strategy) strategy = strtrim(strategy)
51 42 assert(string.len(strategy) >= 1) assert(string.len(strategy) >= 1)
52 43 assert(string.len(strategy) <= 8192) assert(string.len(strategy) <= 8192)
53
54 local unitDesignation = getUnit(self) or 'none'
55 assert('string' == type(unitDesignation))
56 unitDesignation = string.lower(strtrim(unitDesignation))
57 assert(string.len(unitDesignation) >= 1)
58 assert(string.len(unitDesignation) <= 256)
59 44 end end
60 45 end end
61 46
 
... ... end
345 330 local function healthFrameEventProcessor(self) local function healthFrameEventProcessor(self)
346 331 validateProgressFrame(self) validateProgressFrame(self)
347 332
348 local unitDesignation = getUnit(self) or 'none'
333 local unitDesignation = SecureButton_GetUnit(self) or 'none'
349 334
350 335 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
351 336 assert('string' == type(unitDesignation)) assert('string' == type(unitDesignation))
 
... ... end
418 403 local function powerFrameEventProcessor(self) local function powerFrameEventProcessor(self)
419 404 validateProgressFrame(self) validateProgressFrame(self)
420 405
421 local unitDesignation = getUnit(self) or 'none'
406 local unitDesignation = SecureButton_GetUnit(self) or 'none'
422 407
423 408 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
424 409 assert('string' == type(unitDesignation)) assert('string' == type(unitDesignation))
File src/ChorusRaidTargetIconFrameTemplate.lua changed (mode: 100644) (index 78e29ea..b34f9f8)
... ... local Chorus = Chorus
4 4 local SetRaidTargetIconTexture = SetRaidTargetIconTexture local SetRaidTargetIconTexture = SetRaidTargetIconTexture
5 5
6 6 local function raidTargetIconFrameEventProcessor(self) local function raidTargetIconFrameEventProcessor(self)
7 local p = self:GetParent()
7 assert(self ~= nil)
8 8
9 local u = self.unit or self:GetAttribute('unit')
10 if p and not u then
11 u = p.unit or p:GetAttribute('unit')
12 end
13 u = u or 'none'
9 local u = SecureButton_GetUnit(self) or 'none'
14 10
15 11 assert(u ~= nil) assert(u ~= nil)
16 12 assert('string' == type(u)) assert('string' == type(u))
File src/ChorusRaidTargetIconFrameTemplate.xml changed (mode: 100644) (index 04a7f3c..1da7eb8)
13 13 <Scripts> <Scripts>
14 14 <OnLoad>Chorus.raidTargetIconFrameMain(self);</OnLoad> <OnLoad>Chorus.raidTargetIconFrameMain(self);</OnLoad>
15 15 </Scripts> </Scripts>
16 <Attributes>
17 <Attribute name="useparent-unit" type="boolean" value="true"/>
18 </Attributes>
16 19 </Frame> </Frame>
17 20 </Ui> </Ui>
File src/ChorusRangeFrameTemplate.lua changed (mode: 100644) (index 5c5bd94..c0b358e)
... ... local function spellRangeMapUpdate()
74 74 spellRangeMap = buffer spellRangeMap = buffer
75 75 end end
76 76
77 --[[ Duplicate getUnit logic to make sure it cannot be overriden at runtime. ]]--
78
79 local function getUnit(f)
80 local p = f:GetParent()
81 local u = f.unit or f:GetAttribute('unit')
82 if not u and p then
83 u = p.unit or p:GetAttribute('unit')
84 end
85 return u
86 end
87
88 77 local function rangeFrameUpdate(self) local function rangeFrameUpdate(self)
89 78 assert(self ~= nil) assert(self ~= nil)
90 79
91 local unitDesignation = getUnit(self) or 'none'
80 local unitDesignation = SecureButton_GetUnit(self) or 'none'
92 81
93 82 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
94 83 assert('string' == type(unitDesignation)) assert('string' == type(unitDesignation))
File src/ChorusRangeFrameTemplate.xml changed (mode: 100644) (index b26e57d..bc3583e)
20 20 Chorus.rangeFrameMain(self); Chorus.rangeFrameMain(self);
21 21 </OnLoad> </OnLoad>
22 22 </Scripts> </Scripts>
23 <Attributes>
24 <Attribute name="useparent-unit" type="boolean" value="true"/>
25 </Attributes>
23 26 </Frame> </Frame>
24 27 </Ui> </Ui>
File src/ChorusThreatFrameTemplate.lua changed (mode: 100644) (index 75edb4b..bf604a4)
... ... local function threatFrameEventProcessor(self)
9 9 local unitButton = self:GetParent() local unitButton = self:GetParent()
10 10 assert(unitButton ~= nil) assert(unitButton ~= nil)
11 11
12 local u = self.unit or self:GetAttribute('unit')
13 if not u and unitButton then
14 u = unitButton.unit or unitButton:GetAttribute('unit')
15 end
16 u = u or 'none'
12 local u = SecureButton_GetUnit(self) or 'none'
13
17 14 assert(u ~= nil) assert(u ~= nil)
18 15
19 16 if UnitExists(u) and UnitIsConnected(u) then if UnitExists(u) and UnitIsConnected(u) then
File src/ChorusThreatFrameTemplate.xml changed (mode: 100644) (index f518ae7..6cd327c)
25 25 <Scripts> <Scripts>
26 26 <OnLoad>Chorus.threatFrameMain(self);</OnLoad> <OnLoad>Chorus.threatFrameMain(self);</OnLoad>
27 27 </Scripts> </Scripts>
28 <Attributes>
29 <Attribute name="useparent-unit" type="boolean" value="true"/>
30 </Attributes>
28 31 </Frame> </Frame>
29 32 </Ui> </Ui>
File src/ChorusUnitButtonTemplate.lua changed (mode: 100644) (index 2e9065f..68bac1e)
1 local tContains = tContains
2
3 1 local Chorus = Chorus local Chorus = Chorus
4 2
5 local function applySecureHandlerAttributeProcessor(secureHandlerFrame, unitButton)
6 assert(secureHandlerFrame ~= nil)
7 assert(unitButton ~= nil)
8
9 --[[ All frames that are children to the unit button inherit the
10 property of the 'unit' attribute of their parent. ]]--
11
12 secureHandlerFrame:WrapScript(unitButton, 'OnAttributeChanged', [=[
13 if not ('unit' == name) then
14 return
15 end
16
17 local unitButton = self
18
19 local t = unitButton:GetChildList(newtable())
20 local i = 0
21 while (i < #t) do
22 i = i + 1
23 local e = t[i]
24 if e then
25 e:SetAttribute(name, value)
26 end
27 end
28 ]=])
29 end
30
31 local function applyAttributeCascade(self, name, value)
32 assert(self ~= nil)
33
34 assert(name ~= nil)
35 assert('string' == type(name))
36 name = strtrim(name)
37 assert(string.len(name) >= 1)
38 assert(string.len(name) <= 256)
39
40 self[name] = value
41
42 local t = {self:GetChildren()}
43 local i = 0
44 while (i < #t) do
45 i = i + 1
46 local e = t[i]
47 if nil == e then
48 break
49 end
50 e[name] = value
3 function Chorus.unitButtonAttributeProcessor(self, name, value)
4 --[[ This is required for UnitFrame_OnEnter to work, which is in turn
5 used for unit game tooltips. The alternative is to wrap
6 GameTooltip:SetUnit ourselves.]]--
7 if 'unit' == name then
8 self[name] = value
51 9 end end
52 10 end end
53 11
54 function Chorus.unitButtonAttributeChangedProcessor(self, name, value)
55 assert(self ~= nil)
56
57 assert(name ~= nil)
58 assert('string' == type(name))
59 name = strtrim(name)
60 assert(string.len(name) >= 1)
61 assert(string.len(name) <= 256)
62
63 if not tContains({'unit'}, name) then
64 return
65 end
66
67 --[[ FIXME SecureGroupHandlerTemplate empties unit property for all children ]]--
68 if not value then
69 return
70 end
71
72 applyAttributeCascade(self, name, value)
73 end
74
75 12 function Chorus.unitButtonMain(self) function Chorus.unitButtonMain(self)
76 13 self:RegisterForClicks('AnyUp') self:RegisterForClicks('AnyUp')
77
78 self:SetAttribute('type1', 'target')
79
80 local secureHandlerFrame = _G[self:GetName() .. 'SecureHandlerAttributeFrame']
81 assert(secureHandlerFrame ~= nil)
82 applySecureHandlerAttributeProcessor(secureHandlerFrame, self)
83
84 applyAttributeCascade(self, 'unit', self:GetAttribute('unit'))
14 self.unit = self:GetAttribute('unit')
85 15
86 16 --[[ TODO Add menu popup for unit frames ]]-- --[[ TODO Add menu popup for unit frames ]]--
87 17 --[[ TODO Add role indicator ]]-- --[[ TODO Add role indicator ]]--
88 --[[ TODO Add raid mark indicator ]]--
89 18 end end
File src/ChorusUnitButtonTemplate.xml changed (mode: 100644) (index cd95b0f..7b81d4a)
6 6 <Frame name="$parentSecureHandlerAttributeFrame" inherits="SecureHandlerAttributeTemplate"/> <Frame name="$parentSecureHandlerAttributeFrame" inherits="SecureHandlerAttributeTemplate"/>
7 7 </Frames> </Frames>
8 8 <Scripts> <Scripts>
9 <OnAttributeChanged>
10 Chorus.unitButtonAttributeChangedProcessor(self, name, value);
11 </OnAttributeChanged>
12 <OnLoad>
13 Chorus.unitButtonMain(self);
14 </OnLoad>
9 <OnAttributeChanged>Chorus.unitButtonAttributeProcessor(self);</OnAttributeChanged>
15 10 <OnEnter function="UnitFrame_OnEnter"/> <OnEnter function="UnitFrame_OnEnter"/>
16 11 <OnLeave function="UnitFrame_OnLeave"/> <OnLeave function="UnitFrame_OnLeave"/>
12 <OnLoad>Chorus.unitButtonMain(self);</OnLoad>
17 13 </Scripts> </Scripts>
14 <Attributes>
15 <!-- When the button is clicked with left mouse button,
16 given no modifier keys are held,
17 target game unit that corresponds to the button. -->
18 <Attribute name="type1" type="string" value="target"/>
19 </Attributes>
18 20 </Button> </Button>
19 21 <Button name="ChorusTinyUnitButtonTemplate" inherits="ChorusUnitButtonTemplate" virtual="true"> <Button name="ChorusTinyUnitButtonTemplate" inherits="ChorusUnitButtonTemplate" virtual="true">
20 22 <Size> <Size>
File src/ChorusUnitNameFrameTemplate.lua changed (mode: 100644) (index a27b822..ff0d56d)
... ... local function unitNameEventProcessor(self)
11 11 local label = self.label local label = self.label
12 12 assert(label ~= nil) assert(label ~= nil)
13 13
14 local unitDesignation = self.unit or 'none'
14 local unitDesignation = SecureButton_GetUnit(self) or 'none'
15 15 assert(unitDesignation ~= nil) assert(unitDesignation ~= nil)
16 16 assert('string' == type(unitDesignation)) assert('string' == type(unitDesignation))
17 17 unitDesignation = string.lower(strtrim(unitDesignation)) unitDesignation = string.lower(strtrim(unitDesignation))
 
... ... function Chorus.unitNameFrameMain(self)
65 65 assert(label ~= nil) assert(label ~= nil)
66 66 self.label = label self.label = label
67 67
68 self.unit = 'none'
69
70 68 --[[ FIXME Sometimes some unit buttons in raid frames cannot be clicked. ]]-- --[[ FIXME Sometimes some unit buttons in raid frames cannot be clicked. ]]--
71 69 self:SetScript('OnEvent', unitNameEventProcessor) self:SetScript('OnEvent', unitNameEventProcessor)
72 70
File src/ChorusUnitNameFrameTemplate.xml changed (mode: 100644) (index 6a4a5ce..01e1dce)
13 13 <Scripts> <Scripts>
14 14 <OnLoad>Chorus.unitNameFrameMain(self);</OnLoad> <OnLoad>Chorus.unitNameFrameMain(self);</OnLoad>
15 15 </Scripts> </Scripts>
16 <Attributes>
17 <Attribute name="useparent-unit" type="boolean" value="true"/>
18 </Attributes>
16 19 </Frame> </Frame>
17 20 </Ui> </Ui>
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/chorus

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/chorus

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/chorus

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main