List of commits:
Subject Hash Author Date (UTC)
fix(choir): Attempt to fix performance issues 8d869b11c49aa636a6ea2f48da06148d58eed560 Vladyslav Bondarenko 2021-12-31 07:45:00
feat(choir): Render unit threat situation dee7167e3a7e453425248a1062560f9bfd8b3bb4 Vladyslav Bondarenko 2021-12-28 01:39:09
fix(choir): Render dungeon role correctly for Cata 590a4abfd82659624688c4c1658de87693d3cd30 Vladyslav Bondarenko 2021-12-28 00:31:52
fix(choir): Show raid frame at player login 21a86d3840fb810c4ff4c67490aae7d5251c1bae Vladyslav Bondarenko 2021-11-24 06:07:10
feat(choir)!: Add interface options for raid frame ce677d3d0e7cfe05d1da5b159f52a0808db9ce9a Vladyslav Bondarenko 2021-11-24 06:05:37
feat(choir): Add conf spell shortcut default button c8e2c3493896f5ae589c516f742bbafd528140db Vladyslav Bondarenko 2021-11-24 02:28:19
feat(choir)!: Add configuration menu 12319ce873aecea4bfc0addec7c5ac5c1f225237 Vladyslav Bondarenko 2021-11-21 09:26:05
feat(choir): Add unit game tooltip 3ec490e489bb105f5096d5bb3e56814873deec3f Vladyslav Bondarenko 2021-11-20 09:34:12
fix(choir): Raid group frame arrange correctly b592f3f018ee521a821447ccf837aded703ad447 Vladyslav Bondarenko 2021-11-18 22:02:52
feat(choir): Render group role indicator for units 5f3a7c24d3f79035b8faa156355a2d017688134e Vladyslav Bondarenko 2021-11-18 22:02:27
feat(choir): Adjust raid group frame position f99a54133a1761034baca93bfa8814c614191818 Vladyslav Bondarenko 2021-11-18 00:35:35
feat(choir): Add debuff buttons for raid fe66339420a9eb5e4823cc4fe45e0d97b2983073 Vladyslav Bondarenko 2021-11-17 00:55:54
feat(choir): Add texture to health bars ddaeb29d7ecfd95a98bcc85b3f4f7676c7ccd4d7 Vladyslav Bondarenko 2021-11-14 21:40:26
fix(choir)!: Obscure critical error 0f203b4d69f57240e97c66f1ab4510c6ac3e9276 Vladyslav Bondarenko 2021-11-11 12:52:02
feat(choir): Toggle button visibility given roster d193e7b5eb38cb3ac69d74eec1f96e00d90b6098 Vladyslav Bondarenko 2021-11-10 23:46:24
feat(choir)!: Add permanent raid frame 1839c35af4212c09038e972547d6b5893a9ce219 Vladyslav Bondarenko 2021-11-10 16:04:25
feat(choir)!: Employ Clearcasting subset feat 733c81538c3c965f07993fd7ddc482e724121b75 Vladyslav Bondarenko 2021-11-04 22:40:48
fix(choir): Improve shortcut binding keys eb636de6e3f7bada9f0064ffe4db1db3f6433f6c Vladyslav Bondarenko 2021-10-31 18:55:10
fix(choir): Improve choirBindingKey attribute handling 6c5c2214cc1809e5e5e59cd3672da3cca3f2701f Vladyslav Bondarenko 2021-10-31 13:23:24
feat(choir)!: Add spell shortcut prototype d34f22a6983ffc41122acb40d22e3cb29c208a3c Vladyslav Bondarenko 2021-10-31 12:39:39
Commit 8d869b11c49aa636a6ea2f48da06148d58eed560 - fix(choir): Attempt to fix performance issues
One of the most important performance bottlenecks is handling events
passed to the add-on by the client. There must be no redundant calls.
Attempt to solve this here.

Requires much more testing.
Author: Vladyslav Bondarenko
Author date (UTC): 2021-12-31 07:45
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2021-12-31 07:45
Parent(s): df423d71b6848ecce896ff4bfde0da0c39f998a4
Signer:
Signing key:
Signing status: N
Tree: 849811639c8499e5341c2e991f440100f088e1bd
File Lines added Lines deleted
.luacheckrc 1 0
choir.lua 398 101
choir.toc 1 1
File .luacheckrc changed (mode: 100644) (index eb53a2c..cc885a2)
... ... stds.choir = {
61 61 'ChoirSpoiler8', 'ChoirSpoiler8',
62 62 'ChoirSpoiler9', 'ChoirSpoiler9',
63 63 'ClearcastingFrame', 'ClearcastingFrame',
64 'UnifontRegular16',
64 65 } }
65 66 } }
66 67
File choir.lua changed (mode: 100644) (index 50eb78a..82576d2)
... ... local function createClearcastingSubset(unitButton)
24 24
25 25 local harmSubset = createSubset(unitButton, unitButton:GetName() .. 'HarmfulSubsetFrame', local harmSubset = createSubset(unitButton, unitButton:GetName() .. 'HarmfulSubsetFrame',
26 26 unitDesignation, 'HARMFUL', unitDesignation, 'HARMFUL',
27 5, 1)
27 8, 1)
28 28 harmSubset:SetPoint('BOTTOMLEFT', 0, 0) harmSubset:SetPoint('BOTTOMLEFT', 0, 0)
29 29
30 --[[ TODO Track tank defensives in addition to player (healer) buffs. ]]--
31 30 local helpSubset = createSubset(unitButton, unitButton:GetName() .. 'HelpfulSubsetFrame', local helpSubset = createSubset(unitButton, unitButton:GetName() .. 'HelpfulSubsetFrame',
32 31 unitDesignation, 'HELPFUL', unitDesignation, 'HELPFUL',
33 5, 1)
32 8, 1)
34 33 helpSubset:SetPoint('BOTTOMLEFT', 0, harmSubset:GetHeight()) helpSubset:SetPoint('BOTTOMLEFT', 0, harmSubset:GetHeight())
35 34
36 35 return harmSubset, helpSubset return harmSubset, helpSubset
 
... ... local function createBackground(ownerFrame)
142 141 return background return background
143 142 end end
144 143
144 local function getRoleTexCoord(isTank, isHealer, isDamager)
145 local size = 64
146 if isTank then
147 return 0 / size, 19 / size, 22 / size, 41 / size
148 elseif isHealer then
149 return 20 / size, 39 / size, 1 / size, 20 / size
150 elseif isDamager then
151 return 20 / size, 39 / size, 22 / size, 41 / size
152 else
153 error('invalid argument')
154 end
155 end
156
157 local function updateUnitButtonRoleWidget(roleWidget, unitDesignation)
158 assert (roleWidget ~= nil)
159 assert (unitDesignation ~= nil)
160
161 local isTank, isHealer, isDamager = UnitGroupRolesAssigned(unitDesignation)
162
163 --[[ Corner-case for Interface >= 40000 ]]--
164 if 'string' == type(isTank) then
165 local roleDesignation = isTank
166 isTank = 'TANK' == roleDesignation
167 isHealer = 'HEALER' == roleDesignation
168 isDamager = 'DAMAGER' == roleDesignation
169 end
170
171 if isTank or isHealer or isDamager then
172 roleWidget:Show()
173 roleWidget:SetTexCoord(getRoleTexCoord(isTank, isHealer, isDamager))
174 else
175 roleWidget:Hide()
176 end
177 end
178
145 179 local function createUnitButtonRoleWidget(unitButton) local function createUnitButtonRoleWidget(unitButton)
146 180 assert (unitButton ~= nil) assert (unitButton ~= nil)
147 181
148 local widgetSize = 12
149 local roleWidget = unitButton:CreateTexture(unitButton:GetName() .. 'RoleWidget', 'OVERLAY')
150 roleWidget:SetPoint('TOPRIGHT', unitButton, 'TOPRIGHT', 0, 0)
151 roleWidget:SetPoint('BOTTOMLEFT', unitButton, 'TOPRIGHT', -widgetSize, -widgetSize)
152 roleWidget:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp")
182 local n = unitButton:GetName() .. 'RoleWidgetFrame'
183
184 local widgetSize = 16
185 local widgetFrame = CreateFrame('FRAME', n, unitButton)
186 widgetFrame:SetPoint('TOPLEFT', 0, 0)
187 widgetFrame:SetPoint('TOPRIGHT', 0, 0)
188 widgetFrame:SetSize(unitButton:GetWidth(), widgetSize)
189
190 local artwork = widgetFrame:CreateTexture(widgetFrame:GetName() .. 'Artwork', 'ARTWORK')
191 artwork:SetPoint('TOPRIGHT', widgetFrame, 'TOPRIGHT', 0, 0)
192 artwork:SetPoint('BOTTOMLEFT', widgetFrame, 'TOPRIGHT', -widgetSize, -widgetSize)
193 artwork:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES.blp")
194
195 widgetFrame:RegisterEvent('LFG_ROLE_UPDATE')
196 widgetFrame:RegisterEvent('PLAYER_ENTERING_WORLD')
153 197
154 return roleWidget
198 widgetFrame:SetScript('OnEvent', function(self, ...)
199 local u = self:GetParent()
200 updateUnitButtonRoleWidget(self, u:GetAttribute('unit'))
201 end)
202
203 return widgetFrame
155 204 end end
156 205
157 206 local function getUnitHealthDeficit(unitDesignation) local function getUnitHealthDeficit(unitDesignation)
 
... ... local function updateUnitButtonText(self)
208 257 local unitDesignation = self:GetAttribute('unit') local unitDesignation = self:GetAttribute('unit')
209 258 assert (unitDesignation ~= nil) assert (unitDesignation ~= nil)
210 259
211 local n = UnitName(unitDesignation) or unitDesignation
260 local n
261 local unitName = UnitName(unitDesignation)
262 if not unitName or string.len(unitName) < 2 then
263 n = unitDesignation
264 else
265 n = unitName
266 end
267
268 local m = math.floor(self:GetWidth() / 16)
269 local sanitizedName = string.sub(n, 1, m)
270 if string.len(sanitizedName) < string.len(unitName) then
271 sanitizedName = sanitizedName .. '…'
272 end
212 273
213 274 local key = GetBindingKey('CLICK ' .. self:GetName() .. ':LeftButton') local key = GetBindingKey('CLICK ' .. self:GetName() .. ':LeftButton')
214 275 if not key then if not key then
215 276 key = self:GetAttribute('choirBindingKey') key = self:GetAttribute('choirBindingKey')
216 277 end end
278
279 local y
217 280 if key then if key then
218 n = n .. ' <' .. key .. '>'
281 y = ' <' .. key .. '>'
282 else
283 y = ''
219 284 end end
220 285
221 local m = math.floor(self:GetWidth() / 8)
286 sanitizedName = sanitizedName .. y
287 label:SetText(sanitizedName)
222 288
223 label:SetText(string.sub(n, 1, m))
224 289
225 local r = 1
226 local g = 1
227 local b = 1
228 local threatStatus = UnitThreatSituation(unitDesignation)
229 if threatStatus then
230 r, g, b = GetThreatStatusColor(threatStatus)
231 end
232 label:SetTextColor(r, g, b)
233 290 end end
234 291
235 292 local function getClassColor(classDesignation) local function getClassColor(classDesignation)
 
... ... local function createUnitButtonBar(unitButton)
291 348 local n = unitButton:GetName() or '' local n = unitButton:GetName() or ''
292 349 local padding = 4 local padding = 4
293 350 local marginTop = 24 local marginTop = 24
294 local barHeight = 12
351 local barHeight = 20
295 352 local bar = CreateFrame('FRAME', n .. 'Bar', unitButton) local bar = CreateFrame('FRAME', n .. 'Bar', unitButton)
296 353 bar:SetPoint('BOTTOMLEFT', unitButton, 'TOPLEFT', padding, -padding - marginTop - barHeight) bar:SetPoint('BOTTOMLEFT', unitButton, 'TOPLEFT', padding, -padding - marginTop - barHeight)
297 354 bar:SetPoint('TOPRIGHT', unitButton, 'TOPRIGHT', -padding, -padding - marginTop) bar:SetPoint('TOPRIGHT', unitButton, 'TOPRIGHT', -padding, -padding - marginTop)
 
... ... local function createUnitButtonBar(unitButton)
299 356 local b = bar:CreateTexture(bar:GetName() .. 'Overlay', 'OVERLAY') local b = bar:CreateTexture(bar:GetName() .. 'Overlay', 'OVERLAY')
300 357 b:SetAllPoints() b:SetAllPoints()
301 358 --b:SetTexture("Interface\\TARGETINGFRAME\\BarFill2") --b:SetTexture("Interface\\TARGETINGFRAME\\BarFill2")
302 b:SetTexture("Interface\\TARGETINGFRAME\\UI-StatusBar")
359 --b:SetTexture("Interface\\TARGETINGFRAME\\UI-StatusBar")
303 360 --b:SetTexture("Interface\\TARGETINGFRAME\\UI-TargetingFrame-BarFill") --b:SetTexture("Interface\\TARGETINGFRAME\\UI-TargetingFrame-BarFill")
361 b:SetTexture("Interface\\AddOns\\choir\\share\\Minimalist.tga")
304 362 b:SetVertexColor(getDefaultUnitButtonBarColor(), 1) b:SetVertexColor(getDefaultUnitButtonBarColor(), 1)
305 363 bar.overlay = b bar.overlay = b
306 364
307 local t = createLabel(bar)
365 local t = createLabel(bar, UnifontRegular16)
308 366 bar.text = t bar.text = t
309 367
310 368 bar.unitButton = unitButton bar.unitButton = unitButton
311 369
312 370 --[[ Update health indicator ]]-- --[[ Update health indicator ]]--
313 bar:SetScript('OnEvent', function(healthBarFrame)
371 bar:SetScript('OnEvent', function(healthBarFrame, eventCategory, targetUnit)
314 372 assert (healthBarFrame ~= nil) assert (healthBarFrame ~= nil)
373 assert (eventCategory ~= nil)
315 374 assert (unitButton ~= nil) assert (unitButton ~= nil)
316 375
317 376 local u = unitButton:GetAttribute('unit') local u = unitButton:GetAttribute('unit')
318 377 assert (u ~= nil) assert (u ~= nil)
378
379 if targetUnit and targetUnit ~= u then
380 return
381 end
382
319 383 updateUnitButtonBarOverlay(healthBarFrame, u) updateUnitButtonBarOverlay(healthBarFrame, u)
320 384 updateUnitButtonBarText(healthBarFrame, u) updateUnitButtonBarText(healthBarFrame, u)
321 385 end) end)
322 bar:RegisterEvent('PLAYER_FOCUS_CHANGED')
323 bar:RegisterEvent('PLAYER_TARGET_CHANGED')
324 386 bar:RegisterEvent('UNIT_HEALTH') bar:RegisterEvent('UNIT_HEALTH')
325 387 --[[ NOTE UNIT_SPELLCAST_* family of events are relied on to render range indicator correctly, --[[ NOTE UNIT_SPELLCAST_* family of events are relied on to render range indicator correctly,
326 388 -- instead of the more expensive update hook. ]]-- -- instead of the more expensive update hook. ]]--
 
... ... local function createUnitButtonBar(unitButton)
332 394 return bar return bar
333 395 end end
334 396
335 local function getRoleTexCoord(isTank, isHealer, isDamager)
336 local size = 64
337 if isTank then
338 return 0 / size, 19 / size, 22 / size, 41 / size
339 elseif isHealer then
340 return 20 / size, 39 / size, 1 / size, 20 / size
341 elseif isDamager then
342 return 20 / size, 39 / size, 22 / size, 41 / size
397 local function healthBarEventProcessor(healthBarFrame, eventCategory, targetUnitDesignation)
398 assert (healthBarFrame ~= nil)
399 assert (eventCategory == 'UNIT_HEALTH' or eventCategory == 'UNIT_COMBAT')
400
401 local unitButton = healthBarFrame:GetParent()
402 assert (unitButton ~= nil)
403
404 local unitDesignation = unitButton:GetAttribute('unit')
405 assert (unitDesignation ~= nil)
406 assert ('string' == type(unitDesignation))
407 unitDesignation = strtrim(unitDesignation)
408 assert (string.len(unitDesignation) >= 2)
409 assert (string.len(unitDesignation) <= 32)
410
411 --[[ Assume UNIT_HEALTH event is processed for the relevant unit. ]]--
412 if targetUnitDesignation ~= unitDesignation then
413 return
414 end
415
416 local overlay = healthBarFrame.overlay
417 assert (overlay ~= nil)
418
419 --[[ Apply health bar width changes. ]]--
420 local ratio = 1
421 if UnitExists(unitDesignation) then
422 ratio = getUnitHealthRatio(unitDesignation) or 1
423 end
424 ratio = math.min(math.max(0, ratio or 1), 1)
425 overlay:SetPoint('BOTTOMLEFT', healthBarFrame, 'BOTTOMLEFT', 0, 0)
426 overlay:SetPoint('TOPRIGHT', healthBarFrame, 'TOPRIGHT', (ratio - 1) * healthBarFrame:GetWidth(), 0)
427
428 --[[ Apply health bar color changes. ]]--
429 local a = 1
430 local rangeSpell = ChoirRangeSpellName
431 --[[ NOTE IsSpellInRange returns either 0, 1 or nil ]]--
432 if rangeSpell and 1 ~= IsSpellInRange(rangeSpell, unitDesignation) then
433 a = 0.5
434 end
435
436 if ratio <= 0.35 then
437 overlay:SetVertexColor(1, 0, 0, a)
438 elseif ratio <= 0.5 then
439 overlay:SetVertexColor(1, 1, 0, a)
343 440 else else
344 error('invalid argument')
441 overlay:SetVertexColor(0, 1, 0, a)
345 442 end end
443
444 --[[ Apply health bar text content. ]]--
445 local label = healthBarFrame.label
446 assert (label ~= nil)
447
448 local t
449 local d = getUnitHealthDeficit(unitDesignation) or 0
450 if UnitIsDead(unitDesignation) then
451 t = '(Dead)'
452 elseif UnitIsGhost(unitDesignation) then
453 t = '(Ghost)'
454 elseif d < 0 then
455 if math.abs(d) > 1000 then
456 t = math.floor(d / 1000) .. ' K'
457 elseif math.abs(d) > 1000000 then
458 t = math.floor(d / 1000000) .. ' M'
459 else
460 t = tostring(math.floor(d))
461 end
462 else
463 t = nil
464 end
465
466 label:SetText(t)
346 467 end end
347 468
348 local function updateUnitButtonRoleWidget(roleWidget, unitDesignation)
349 assert (roleWidget ~= nil)
469 local function createHealthBar(unitButton, width, height)
470 assert (unitButton ~= nil)
471
472 assert (width ~= nil)
473 assert ('number' == type(width))
474 assert (width >= 12)
475 assert (width <= 288)
476
477 assert (height ~= nil)
478 assert ('number' == type(height))
479 assert (height >= 12)
480 assert (height <= 288)
481
482 local n = (unitButton:GetName() or '') .. 'HealthBarFrame'
483 local healthBarFrame = CreateFrame('FRAME', n, unitButton)
484 healthBarFrame:SetSize(width, height)
485
486 local b = healthBarFrame:CreateTexture(n .. 'Overlay', 'OVERLAY')
487 b:SetAllPoints()
488 b:SetTexture("Interface\\AddOns\\choir\\share\\Minimalist.tga")
489 b:SetVertexColor(getDefaultUnitButtonBarColor(), 1)
490
491 local t = createLabel(healthBarFrame, UnifontRegular16)
492
493 healthBarFrame.label = t
494 healthBarFrame.overlay = b
495
496 healthBarFrame:RegisterEvent('UNIT_COMBAT')
497 healthBarFrame:RegisterEvent('UNIT_HEALTH')
498 healthBarFrame:SetScript('OnEvent', healthBarEventProcessor)
499
500 return healthBarFrame
501 end
502
503 local function headerEventProcessor(headerFrame)
504 assert (headerFrame ~= nil)
505
506 local label = headerFrame.label
507 assert (label ~= nil)
508
509 local unitButton = headerFrame:GetParent()
510 assert (unitButton ~= nil)
511
512 local unitDesignation = unitButton:GetAttribute('unit')
350 513 assert (unitDesignation ~= nil) assert (unitDesignation ~= nil)
351 514
352 local isTank, isHealer, isDamager = UnitGroupRolesAssigned(unitDesignation)
515 local n
516 local unitName = UnitName(unitDesignation)
517 if not unitName or string.len(unitName) < 2 then
518 n = unitDesignation
519 else
520 n = unitName
521 end
353 522
354 --[[ Corner-case for Interface >= 40000 ]]--
355 if 'string' == type(isTank) then
356 local roleDesignation = isTank
357 isTank = 'TANK' == roleDesignation
358 isHealer = 'HEALER' == roleDesignation
359 isDamager = 'DAMAGER' == roleDesignation
523 local m = math.floor(headerFrame:GetWidth() / 16)
524 local sanitizedName = string.sub(n, 1, m)
525 if string.len(sanitizedName) < string.len(unitName) then
526 sanitizedName = sanitizedName .. '…'
360 527 end end
361 528
362 if isTank or isHealer or isDamager then
363 roleWidget:Show()
364 roleWidget:SetTexCoord(getRoleTexCoord(isTank, isHealer, isDamager))
529 local key = GetBindingKey('CLICK ' .. unitButton:GetName() .. ':LeftButton')
530 if not key then
531 key = unitButton:GetAttribute('choirBindingKey')
532 end
533
534 local y
535 if key then
536 y = ' <' .. key .. '>'
365 537 else else
366 roleWidget:Hide()
538 y = ''
367 539 end end
540
541 sanitizedName = sanitizedName .. y
542 label:SetText(sanitizedName)
543
544 local _, classDesignation = UnitClass(unitDesignation)
545 local r = 1
546 local g = 1
547 local b = 1
548 if classDesignation then
549 r, g, b = getClassColor(classDesignation)
550 end
551 label:SetTextColor(r, g, b)
368 552 end end
369 553
370 local function unitButtonEventProcessor(unitButton)
554 local function createHeader(unitButton, width, height)
371 555 assert (unitButton ~= nil) assert (unitButton ~= nil)
372 556
373 updateUnitButtonText(unitButton)
557 assert (width ~= nil)
558 assert ('number' == type(width))
559 assert (width >= 12)
560 assert (width <= 288)
374 561
375 local bar = unitButton.bar
562 assert (height ~= nil)
563 assert ('number' == type(height))
564 assert (height >= 12)
565 assert (height <= 288)
566
567 local n = (unitButton:GetName() or '') .. 'HeaderFrame'
568 local headerFrame = CreateFrame('FRAME', n, unitButton)
569 headerFrame:SetSize(width, height)
570
571 local t = createLabel(headerFrame, UnifontRegular16)
572 assert (t ~= nil)
573
574 headerFrame.label = t
575
576 headerFrame:RegisterEvent('PARTY_CONVERTED_TO_RAID')
577 headerFrame:RegisterEvent('PARTY_MEMBERS_CHANGED')
578 headerFrame:RegisterEvent('PLAYER_ENTERING_WORLD')
579 headerFrame:RegisterEvent('RAID_ROSTER_UPDATE')
580 headerFrame:SetScript('OnEvent', headerEventProcessor)
581
582 assert (headerFrame ~= nil)
583 return headerFrame
584 end
585
586 local function unitButtonEventProcessor(unitButton, eventCategory, targetUnit, ...)
376 587 assert (unitButton ~= nil) assert (unitButton ~= nil)
588 assert (eventCategory ~= nil)
589
590 trace(unitButton:GetName(), eventCategory, targetUnit, ...)
377 591
378 592 local unitDesignation = unitButton:GetAttribute('unit') local unitDesignation = unitButton:GetAttribute('unit')
379 593 assert (unitDesignation ~= nil) assert (unitDesignation ~= nil)
 
... ... local function unitButtonEventProcessor(unitButton)
382 596 assert (string.len(unitDesignation) >= 2) assert (string.len(unitDesignation) >= 2)
383 597 assert (string.len(unitDesignation) <= 32) assert (string.len(unitDesignation) <= 32)
384 598
599 if targetUnit and targetUnit ~= unitDesignation then
600 return
601 end
602
603 updateUnitButtonText(unitButton)
604
605 local bar = unitButton.bar
606 assert (unitButton ~= nil)
607
385 608 updateUnitButtonBarOverlay(bar, unitDesignation) updateUnitButtonBarOverlay(bar, unitDesignation)
386 609 updateUnitButtonBarText(bar, unitDesignation) updateUnitButtonBarText(bar, unitDesignation)
387 updateUnitButtonRoleWidget(unitButton.roleWidget, unitDesignation)
388 610 end end
389 611
390 612 local function createInheritanceHandler(unitButton) local function createInheritanceHandler(unitButton)
 
... ... local function createUnitButtonTooltip(unitButton)
454 676 end) end)
455 677 end end
456 678
457 local function createUnitButton(parentFrame, frameName, unit, width, height)
679 local function createThreatWidget(unitButton, width, height)
680 local t = CreateFrame('FRAME', unitButton:GetName() .. 'ThreatFrame', unitButton)
681 t:SetSize(width, height)
682
683 local background = createBackground(t)
684
685 t:RegisterEvent('UNIT_THREAT_SITUATION_UPDATE')
686 t:RegisterEvent('PLAYER_ENTERING_WORLD')
687 t:SetScript('OnEvent', function(self, eventCategory, targetUnit)
688 assert (eventCategory ~= nil)
689
690 local u = unitButton:GetAttribute('unit')
691 if u ~= targetUnit then
692 return
693 end
694
695 local r = 0
696 local g = 0
697 local b = 0
698 local a = 0
699 local threatStatus = UnitThreatSituation(u)
700 if threatStatus then
701 r, g, b = GetThreatStatusColor(threatStatus)
702 a = 1
703 end
704 background:SetTexture(r, g, b, a)
705 end)
706
707 return t
708 end
709
710 local function createClearcastingIfNecessary(unitButton, targetFilter, optionalFilterDescriptor, width, height)
711 assert (unitButton ~= nil)
712
713 assert (targetFilter ~= nil)
714 assert ('string' == type(targetFilter))
715
716 if not optionalFilterDescriptor then
717 return
718 end
719 assert ('string' == type(optionalFilterDescriptor))
720
721 optionalFilterDescriptor = string.upper(optionalFilterDescriptor)
722 if string.match(optionalFilterDescriptor, targetFilter) then
723 local unitDesignation = unitButton:GetAttribute('unit')
724 assert (unitDesignation ~= nil)
725
726 targetFilter = string.upper(strtrim(targetFilter))
727 local n = unitButton:GetName() .. 'Clearcasting' .. targetFilter
728
729 width = width or unitButton:GetWidth()
730
731 local createSubset = ClearcastingFrame.createSubset
732 assert (createSubset ~= nil)
733
734 return createSubset(unitButton, n,
735 unitDesignation, targetFilter,
736 math.floor(width / 24), 1)
737 end
738 end
739
740 local function createUnitButton(parentFrame, frameName, unit, optionalFilterDescriptor, width, height)
458 741 assert (parentFrame ~= nil) assert (parentFrame ~= nil)
459 742 assert (frameName ~= nil) assert (frameName ~= nil)
460 743 assert (unit ~= nil) assert (unit ~= nil)
461 744
462 --[[ TODO Add children buttons that are secure spell buttons on the same target as the unit button.
463 -- Set override bindings to those buttons as ALT-A, CTRL-A, SHIFT-A, where A is the unit button key.
464 -- The spells assigned to the buttons let be something like Cleanse, Dispel Magic or Flash Heal.
465 -- That way, user may cast something without targeting or hiding the selection spoiler.
466 -- The problem is that it is class dependant.]]--
467 745 local u = CreateFrame('BUTTON', frameName, parentFrame, 'SecureUnitButtonTemplate') local u = CreateFrame('BUTTON', frameName, parentFrame, 'SecureUnitButtonTemplate')
468 746
469 747 createBindingKeyHandler(u) createBindingKeyHandler(u)
470 748 createInheritanceHandler(u) createInheritanceHandler(u)
471 749
750 u:SetAttribute('type', 'target')
751
752 local padding = 4
472 753 if not width then if not width then
473 width = 24 * 5 + 3 * 6
754 width = 16
474 755 end end
475 assert (width >= 12 and width <= 144)
756 assert (width >= 12 and width <= 288)
476 757
477 758 if not height then if not height then
478 height = 40 * 2 + 3 * 3 + 24 * 2
759 height = 12
479 760 end end
480 assert (height >= 12 and height <= 144)
761 assert (height >= 12 and height <= 288)
481 762
482 u:SetSize(width, height)
763 createUnitButtonExistanceHandler(u)
764 createUnitButtonTooltip(u)
483 765
484 local t = createLabel(u)
485 t:SetPoint('BOTTOMLEFT', u, 'TOPLEFT', 4, -24 - 4)
486 t:SetPoint('TOPRIGHT', u, 'TOPRIGHT', -4, -4)
487 u.text = t
766 u:SetAttribute('unit', unit)
488 767
489 local b = createBackground(u)
490 u.background = b
768 local roleWidget = createUnitButtonRoleWidget(u)
769 local headerFrame = createHeader(u, width, 24)
770 local healthBarFrame = createHealthBar(u, width, 24)
771 local threatWidget = createThreatWidget(u, width, 6)
772 local help = createClearcastingIfNecessary(u, 'HELPFUL', optionalFilterDescriptor, width, 24)
773 local harm = createClearcastingIfNecessary(u, 'HARMFUL', optionalFilterDescriptor, width, 24)
491 774
492 local bar = createUnitButtonBar(u)
493 u.bar = bar
775 local sectionTable = {roleWidget, headerFrame, healthBarFrame, threatWidget, help, harm}
494 776
495 local roleWidget = createUnitButtonRoleWidget(u)
496 u.roleWidget = roleWidget
777 local i = 0
778 local j = 0
779 local marginLeft = 0
780 local marginRight = 0
781 local y = 0
782 local sectionHeight = 0
783 while (i < #sectionTable) do
784 i = i + 1
497 785
498 createUnitButtonExistanceHandler(u)
499 createUnitButtonTooltip(u)
786 local section = sectionTable[i]
787 if section then
788 j = j + 1
500 789
501 u:SetAttribute('type', 'target')
502 u:SetAttribute('unit', unit)
790 width = math.max(width, section:GetWidth())
791 sectionHeight = sectionHeight + section:GetHeight()
792
793 section:SetPoint('TOPLEFT', marginLeft, y)
794 section:SetPoint('BOTTOMRIGHT', u, 'TOPRIGHT', -marginRight, y - section:GetHeight())
795 y = y - section:GetHeight() - padding
796 end
797 end
798 sectionHeight = sectionHeight + (j + 1) * padding
799 height = math.max(height, sectionHeight)
800 u:SetSize(width, height)
503 801
504 u:SetScript('OnEvent', unitButtonEventProcessor)
505 u:RegisterEvent('PARTY_CONVERTED_TO_RAID')
506 u:RegisterEvent('PARTY_MEMBERS_CHANGED')
507 u:RegisterEvent('PLAYER_ALIVE')
508 u:RegisterEvent('RAID_ROSTER_UPDATE')
509 u:RegisterEvent('UPDATE_BATTLEFIELD_SCORE')
510 u:RegisterEvent('ADDON_LOADED')
511 u:RegisterEvent('LFG_ROLE_UPDATE')
512 u:RegisterEvent('UNIT_COMBAT')
802 createBackground(u)
513 803
514 804 assert (u ~= nil) assert (u ~= nil)
515 805 return u return u
 
... ... local function createGroup(rootFrame, groupNumber, unitTable)
731 1021 local unitDesignation = u[i] local unitDesignation = u[i]
732 1022 assert (unitDesignation ~= nil) assert (unitDesignation ~= nil)
733 1023 local memberNumber = (groupNumber - 1) * groupSize + i local memberNumber = (groupNumber - 1) * groupSize + i
734 local b = createUnitButton(spoiler, 'ChoirUnitButton' .. tostring(memberNumber), unitDesignation)
1024 local b = createUnitButton(spoiler, 'ChoirUnitButton' .. tostring(memberNumber), unitDesignation, 'HELPFUL HARMFUL', 12 * 16, 12 * 12)
735 1025 b:SetPoint('BOTTOMLEFT', marginLeft, 0) b:SetPoint('BOTTOMLEFT', marginLeft, 0)
736 1026 marginLeft = marginLeft + b:GetWidth() + padding marginLeft = marginLeft + b:GetWidth() + padding
737 1027
 
... ... local function createGroup(rootFrame, groupNumber, unitTable)
752 1042 _G['BINDING_NAME_CLICK ' .. b:GetName() .. ':LeftButton'] = 'Unit ' .. tostring(i) _G['BINDING_NAME_CLICK ' .. b:GetName() .. ':LeftButton'] = 'Unit ' .. tostring(i)
753 1043
754 1044 createUnitButtonSpellShortcut(b, i) createUnitButtonSpellShortcut(b, i)
755 createClearcastingSubset(b)
756 1045 end end
757 spoiler:SetSize(marginLeft, 144 + 24)
1046 spoiler:SetSize(marginLeft, 12 * 20)
758 1047 spoiler:SetPoint('CENTER', 0, 12 * 6) spoiler:SetPoint('CENTER', 0, 12 * 6)
759 1048
760 local title = createLabel(spoiler)
1049 local title = createLabel(spoiler, UnifontRegular16)
761 1050 title:SetPoint('TOPRIGHT', 0, 0) title:SetPoint('TOPRIGHT', 0, 0)
762 1051 title:SetPoint('BOTTOMLEFT', spoiler, 'TOPLEFT', spoiler:GetWidth() / 2 - title:GetWidth() / 2, -24) title:SetPoint('BOTTOMLEFT', spoiler, 'TOPLEFT', spoiler:GetWidth() / 2 - title:GetWidth() / 2, -24)
763 1052 title:SetText('Group ' .. tostring(groupNumber)) title:SetText('Group ' .. tostring(groupNumber))
 
... ... end
773 1062 local function arrangeEveryRaidGroupFrame(raidFrame) local function arrangeEveryRaidGroupFrame(raidFrame)
774 1063 assert (raidFrame ~= nil) assert (raidFrame ~= nil)
775 1064
776 local i = 0
777 1065 local t = {raidFrame:GetChildren()} local t = {raidFrame:GetChildren()}
1066 local i = 0
1067 local j = 0
1068 local x = 0
778 1069 local y = 0 local y = 0
779 1070 local padding = 0 local padding = 0
1071 local row = 0
780 1072 while (i < #t) do while (i < #t) do
781 1073 i = i + 1 i = i + 1
782 1074 local raidGroupFrame = t[i] local raidGroupFrame = t[i]
783 1075 if raidGroupFrame and raidGroupFrame:IsShown() then if raidGroupFrame and raidGroupFrame:IsShown() then
784 raidGroupFrame:SetPoint('BOTTOMLEFT', raidFrame, 'BOTTOMLEFT', 0, y)
1076 raidGroupFrame:SetPoint('BOTTOMLEFT', raidFrame, 'BOTTOMLEFT', x, y)
1077 x = math.floor(j / 4) * (raidGroupFrame:GetWidth() + padding)
785 1078 y = y + raidGroupFrame:GetHeight() + padding y = y + raidGroupFrame:GetHeight() + padding
1079 row = row + 1
1080 if row > 4 then
1081 row = 0
1082 y = 0
1083 end
1084 j = j + 1
786 1085 end end
787 1086 end end
788 1087 end end
 
... ... local function createRaidGroupLabel(groupFrame, groupNumber)
839 1138 local labelWidth = 60 local labelWidth = 60
840 1139
841 1140 local groupLabel = groupFrame:CreateFontString(groupFrame:GetName() .. 'Label', 'OVERLAY') local groupLabel = groupFrame:CreateFontString(groupFrame:GetName() .. 'Label', 'OVERLAY')
842 local fontObject = NumberFont_OutlineThick_Mono_Small
1141 local fontObject = UnifontRegular16 or NumberFont_OutlineThick_Mono_Small
843 1142 assert (fontObject ~= nil) assert (fontObject ~= nil)
844 1143 groupLabel:SetFontObject(fontObject) groupLabel:SetFontObject(fontObject)
845 1144 groupLabel:SetPoint('BOTTOMLEFT', groupFrame, 'BOTTOMLEFT', 0, 0) groupLabel:SetPoint('BOTTOMLEFT', groupFrame, 'BOTTOMLEFT', 0, 0)
 
... ... local function createRaidGroupFrame(raidFrame, groupNumber, unitSetOverride)
874 1173 assert (#unitSetOverride == maxPartySize) assert (#unitSetOverride == maxPartySize)
875 1174 end end
876 1175
877 local buttonWidth = 48
878 local buttonHeight = 24 + 12 + 8 + 36
1176 local buttonWidth = 12 * 6
1177 local buttonHeight = 12 * 6
879 1178 local padding = 2 local padding = 2
880 1179 local labelWidth = 60 local labelWidth = 60
881 1180
 
... ... local function createRaidGroupFrame(raidFrame, groupNumber, unitSetOverride)
904 1203 assert (unitDesignation ~= nil) assert (unitDesignation ~= nil)
905 1204
906 1205 local n = groupFrame:GetName() .. 'RaidUnitButton' .. tostring(i) local n = groupFrame:GetName() .. 'RaidUnitButton' .. tostring(i)
907 local b = createUnitButton(groupFrame, n, unitDesignation, buttonWidth, buttonHeight)
1206 local b = createUnitButton(groupFrame, n, unitDesignation, 'HARMFUL', buttonWidth, buttonHeight)
908 1207 b:SetPoint('BOTTOMLEFT', labelWidth + (i - 1) * (padding + b:GetWidth()), 0) b:SetPoint('BOTTOMLEFT', labelWidth + (i - 1) * (padding + b:GetWidth()), 0)
909 createClearcastingSubsetRaid(b)
910 1208 end end
911 1209
912 1210 groupFrame:RegisterEvent('PARTY_CONVERTED_TO_RAID') groupFrame:RegisterEvent('PARTY_CONVERTED_TO_RAID')
 
... ... local function createRaidFrame(rootFrame, spoilerHolder)
937 1235 local maxPartySize = 5 local maxPartySize = 5
938 1236 local maxSubgroupQuantity = 8 local maxSubgroupQuantity = 8
939 1237
940 local buttonWidth = 48
941 local buttonHeight = 24 + 12 + 8 + 36
1238 local buttonWidth = 72
1239 local buttonHeight = 12 * 6
942 1240 local padding = 2 local padding = 2
943 1241
944 1242 local labelWidth = 60 local labelWidth = 60
 
... ... local function createRaidFrame(rootFrame, spoilerHolder)
946 1244 raidFrame:SetSize(labelWidth + (padding + buttonWidth) * maxPartySize, raidFrame:SetSize(labelWidth + (padding + buttonWidth) * maxPartySize,
947 1245 (padding + buttonHeight) * (maxSubgroupQuantity / 2)) (padding + buttonHeight) * (maxSubgroupQuantity / 2))
948 1246
949 --[[ TODO Add any debuff indicator ]]--
950 1247 local j = 0 local j = 0
951 1248 while (j < maxSubgroupQuantity) do while (j < maxSubgroupQuantity) do
952 1249 j = j + 1 j = j + 1
File choir.toc changed (mode: 100644) (index 3a926ca..58608bc)
1 ##Dependencies: Clearcasting
1 ##Dependencies: Clearcasting, Unifont
2 2 ##Interface: 30300 ##Interface: 30300
3 3 ##Notes: Raid targeting aid for healers ##Notes: Raid targeting aid for healers
4 4 ##SavedVariablesPerCharacter: ChoirRangeSpellName, ChoirShortcutSpellNameList, ChoirShortcutBindingKeyMap, ChoirConfRaidFlag, ChoirConfRaidX, ChoirConfRaidY ##SavedVariablesPerCharacter: ChoirRangeSpellName, ChoirShortcutSpellNameList, ChoirShortcutBindingKeyMap, ChoirConfRaidFlag, ChoirConfRaidX, ChoirConfRaidY
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/wowaddons

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

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

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