List of commits:
Subject Hash Author Date (UTC)
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
feat(choir)!: Add spell effects on unit d581df9fce342709267a3221dead4d00b9d14319 Vladyslav Bondarenko 2021-10-31 10:49:19
feat(choir)!: Close spoiler by pressing Esc 22d7370011ac45d25a410a3f569183d0cc9fb232 Vladyslav Bondarenko 2021-10-29 16:10:08
feat(choir)!: Range indicator 931a0510b562986ec76dc22f329f4af4ed723cdf Vladyslav Bondarenko 2021-10-29 10:40:46
fix(choir)!: Health bar in combat a6622578dd5a1b4e4babf699a4cf1e4eb6bb70d6 Vladyslav Bondarenko 2021-10-28 07:42:57
feat(choir)!: Decorate unit buttons 4dc5ed44a9519b275f4256cfe4281110b7a94c9a Vladyslav Bondarenko 2021-10-25 21:20:56
feat(choir)!: Copy action bar binding 70ce056ffda7f12d913ce9a42b128ea257bdd0dc Vladyslav Bondarenko 2021-10-23 23:34:56
feat!: Initial commit 45c4e781e5ff0a69f8b0bea3a869e2384c7ca454 Vladyslav Bondarenko 2021-10-23 06:03:14
Commit 1839c35af4212c09038e972547d6b5893a9ce219 - feat(choir)!: Add permanent raid frame
Render small unit buttnos for every member of the raid and party on the
screen permanently. The buttons largely re-use existing code.

When any spoiler is shown, the raid frame is temporarily hidden.

Next steps are the following:
- hide non-existant units;
- add debuff indicators for the minimized raid unit;
- decouple code logic where possible;
Author: Vladyslav Bondarenko
Author date (UTC): 2021-11-10 16:04
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2021-11-10 16:04
Parent(s): 46548af0816316da42bf5b5d4b33f3e0c69c5d10
Signer:
Signing key:
Signing status: N
Tree: 172927544a0710e0c7e2feefa62fe2bc374f8c09
File Lines added Lines deleted
choir.lua 168 19
File choir.lua changed (mode: 100644) (index 8aae38b..9507a87)
... ... local function createClearcastingSubset(unitButton)
29 29
30 30 --[[ TODO Track tank defensives in addition to player (healer) buffs. ]]-- --[[ TODO Track tank defensives in addition to player (healer) buffs. ]]--
31 31 local helpSubset = createSubset(unitButton, unitButton:GetName() .. 'HelpfulSubsetFrame', local helpSubset = createSubset(unitButton, unitButton:GetName() .. 'HelpfulSubsetFrame',
32 unitDesignation, 'PLAYER HELPFUL',
32 unitDesignation, 'HELPFUL',
33 33 5, 1) 5, 1)
34 34 helpSubset:SetPoint('BOTTOMLEFT', 0, harmSubset:GetHeight()) helpSubset:SetPoint('BOTTOMLEFT', 0, harmSubset:GetHeight())
35 35
 
... ... local function updateUnitButtonBarText(bar, unitDesignation)
138 138 elseif UnitIsGhost(unitDesignation) then elseif UnitIsGhost(unitDesignation) then
139 139 t = '(Ghost)' t = '(Ghost)'
140 140 elseif d < 0 then elseif d < 0 then
141 t = tostring(math.floor(d))
141 if math.abs(d) > 1000 then
142 t = math.floor(d / 1000) .. ' K'
143 elseif math.abs(d) > 1000000 then
144 t = math.floor(d / 1000000) .. ' M'
145 else
146 t = tostring(math.floor(d))
147 end
142 148 else else
143 149 t = nil t = nil
144 150 end end
 
... ... local function createUnitButtonBar(unitButton)
227 233 local n = unitButton:GetName() or '' local n = unitButton:GetName() or ''
228 234 local padding = 4 local padding = 4
229 235 local marginTop = 24 local marginTop = 24
230 local marginBottom = 40 * 2 + (3 * 3)
236 local barHeight = 12
231 237 local bar = CreateFrame('FRAME', n .. 'Bar', unitButton) local bar = CreateFrame('FRAME', n .. 'Bar', unitButton)
232 bar:SetPoint('BOTTOMLEFT', unitButton, 'BOTTOMLEFT', padding, padding + marginBottom)
238 bar:SetPoint('BOTTOMLEFT', unitButton, 'TOPLEFT', padding, -padding - marginTop - barHeight)
233 239 bar:SetPoint('TOPRIGHT', unitButton, 'TOPRIGHT', -padding, -padding - marginTop) bar:SetPoint('TOPRIGHT', unitButton, 'TOPRIGHT', -padding, -padding - marginTop)
234 240
235 241 local b = bar:CreateTexture(bar:GetName() .. 'Overlay', 'OVERLAY') local b = bar:CreateTexture(bar:GetName() .. 'Overlay', 'OVERLAY')
 
... ... local function createInheritanceHandler(unitButton)
314 320 return inheritor return inheritor
315 321 end end
316 322
317 local function createUnitButton(parentFrame, frameName, unit)
323 local function createUnitButton(parentFrame, frameName, unit, width, height)
318 324 assert (parentFrame ~= nil) assert (parentFrame ~= nil)
319 325 assert (frameName ~= nil) assert (frameName ~= nil)
320 326 assert (unit ~= nil) assert (unit ~= nil)
 
... ... local function createUnitButton(parentFrame, frameName, unit)
331 337 createBindingKeyHandler(u) createBindingKeyHandler(u)
332 338 createInheritanceHandler(u) createInheritanceHandler(u)
333 339
334 u:SetSize(24 * 5 + 3 * 6, 40 * 2 + 3 * 3 + 24 * 2)
340 if not width then
341 width = 24 * 5 + 3 * 6
342 end
343 assert (width >= 12 and width <= 144)
344
345 if not height then
346 height = 40 * 2 + 3 * 3 + 24 * 2
347 end
348 assert (height >= 12 and height <= 144)
349
350 u:SetSize(width, height)
335 351
336 352 local t = createLabel(u) local t = createLabel(u)
337 t:SetPoint('BOTTOMLEFT', u, 'BOTTOMLEFT', 4, u:GetHeight() - 24 - 4)
353 t:SetPoint('BOTTOMLEFT', u, 'TOPLEFT', 4, -24 - 4)
338 354 t:SetPoint('TOPRIGHT', u, 'TOPRIGHT', -4, -4) t:SetPoint('TOPRIGHT', u, 'TOPRIGHT', -4, -4)
339 355 u.text = t u.text = t
340 356
 
... ... local function createUnitButton(parentFrame, frameName, unit)
344 360 local bar = createUnitButtonBar(u) local bar = createUnitButtonBar(u)
345 361 u.bar = bar u.bar = bar
346 362
347 createClearcastingSubset(u)
348
349 363 u:SetScript('OnEvent', unitButtonEventProcessor) u:SetScript('OnEvent', unitButtonEventProcessor)
350 364 u:RegisterEvent('PARTY_CONVERTED_TO_RAID') u:RegisterEvent('PARTY_CONVERTED_TO_RAID')
351 365 u:RegisterEvent('PARTY_MEMBERS_CHANGED') u:RegisterEvent('PARTY_MEMBERS_CHANGED')
 
... ... local function createGroup(rootFrame, groupNumber, unitTable)
535 549 _G['BINDING_NAME_CLICK ' .. b:GetName() .. ':LeftButton'] = 'Unit ' .. tostring(i) _G['BINDING_NAME_CLICK ' .. b:GetName() .. ':LeftButton'] = 'Unit ' .. tostring(i)
536 550
537 551 createUnitButtonSpellShortcut(b, i) createUnitButtonSpellShortcut(b, i)
552 createClearcastingSubset(b)
538 553 end end
539 554 spoiler:SetSize(marginLeft, 144) spoiler:SetSize(marginLeft, 144)
540 555 spoiler:SetPoint('CENTER', 0, 12 * 6) spoiler:SetPoint('CENTER', 0, 12 * 6)
 
... ... local function createGroup(rootFrame, groupNumber, unitTable)
546 561 return spoiler return spoiler
547 562 end end
548 563
564 local function createRaidFrame(rootFrame, spoilerHolder)
565 assert (rootFrame ~= nil)
566 assert (spoilerHolder ~= nil)
567
568 local maxPartySize = 5
569 local maxSubgroupQuantity = 8
570 local maxRaidSize = maxPartySize * maxSubgroupQuantity
571
572 local buttonWidth = 48
573 local buttonHeight = 24 + 12 + 8
574 local padding = 2
575
576 local labelWidth = 60
577 local raidFrame = CreateFrame('FRAME', 'ChoirRaidFrame', rootFrame)
578 raidFrame:SetSize(labelWidth + (padding + buttonWidth) * maxPartySize,
579 (padding + buttonHeight) * (maxSubgroupQuantity + 1))
580 raidFrame:SetPoint('TOPLEFT', 0, -144)
581
582 --[[ TODO Add party frame ]]--
583 --[[ TODO Add any debuff indicator ]]--
584 --[[ TODO Render only buttons for existing units ]]--
585 local memberNumber = 0
586 local j = 0
587 --[[ NOTE The extra number is for the party frame.
588 -- Highly coupled with initSpoiler implementation. ]]--
589 while (j < maxSubgroupQuantity + 1) do
590 j = j + 1
591
592 local groupFrame = CreateFrame('FRAME', 'ChoirRaidGroupFrame' .. tostring(j), raidFrame)
593 groupFrame:SetSize(labelWidth + maxPartySize * (buttonWidth + padding), buttonHeight + padding)
594 groupFrame:SetPoint('BOTTOMLEFT', 0, (j - 1) * (buttonHeight + padding))
595
596 local groupLabel = groupFrame:CreateFontString(groupFrame:GetName() .. 'Label', 'OVERLAY')
597 local fontObject = NumberFont_OutlineThick_Mono_Small
598 assert (fontObject ~= nil)
599 groupLabel:SetFontObject(fontObject)
600 groupLabel:SetPoint('BOTTOMLEFT', groupFrame, 'BOTTOMLEFT', 0, 0)
601 groupLabel:SetPoint('TOPRIGHT', groupFrame, 'TOPLEFT', labelWidth, 0)
602
603 local labelContent
604 --[[ FIXME Update hotkey label at runtime without needing UI /reload. ]]--
605 local hotkey = GetBindingKey('CLICK ChoirSpoiler' .. tostring(j) .. ':LeftButton')
606 if hotkey then
607 labelContent = _G['BINDING_NAME_CLICK ChoirSpoiler' .. tostring(j) .. ':LeftButton']
608 labelContent = labelContent .. ' <' .. tostring(hotkey) .. '>'
609 else
610 labelContent = 'Group ' .. tostring(j)
611 end
612 groupLabel:SetText(labelContent)
613
614 local i = 0
615 while (i < maxPartySize) do
616 memberNumber = memberNumber + 1
617
618 local unitDesignation
619 local groupType
620 if memberNumber > maxRaidSize and memberNumber <= maxRaidSize + 5 then
621 groupType = 'party'
622 local partyMemberNumber = memberNumber - maxRaidSize
623 if partyMemberNumber > 1 then
624 unitDesignation = groupType .. tostring(memberNumber)
625 else
626 unitDesignation = 'player'
627 end
628 else
629 groupType = 'raid'
630 unitDesignation = groupType .. tostring(memberNumber)
631 end
632
633 local n = 'ChoirRaidUnitButton' .. tostring(memberNumber)
634 local debug = false
635 if debug then
636 unitDesignation = 'player'
637 end
638 local b = createUnitButton(groupFrame, n, unitDesignation)
639 b:SetSize(buttonWidth, buttonHeight)
640 b:SetPoint('BOTTOMLEFT', labelWidth + i * (padding + b:GetWidth()), 0)
641 i = i + 1
642 end
643 end
644
645 --[[ Given any spoiler is shown, then hide the raid frame. ]]--
646 --[[ Given all spoilers are hidden, show the raid frame. ]]--
647 local spoilerList = {spoilerHolder:GetChildren()}
648 local p = 0
649 while (p < #spoilerList) do
650 p = p + 1
651 local spoiler = spoilerList[p]
652
653 local handler = CreateFrame('FRAME', 'ChoirRaidFrameToggleHandler' .. tostring(p),
654 raidFrame, 'SecureHandlerShowHideTemplate')
655
656 spoiler:SetFrameRef('ChoirRaidFrame', raidFrame)
657 handler:WrapScript(spoiler, 'OnShow', [=[
658 local raidFrame = self:GetFrameRef('ChoirRaidFrame')
659 if raidFrame then
660 raidFrame:Hide()
661 end
662 ]=])
663
664 handler:WrapScript(spoiler, 'OnHide', [=[
665 local raidFrame = self:GetFrameRef('ChoirRaidFrame')
666 raidFrame:Show()
667
668 local spoilerHolder = self:GetParent()
669 local siblingList = spoilerHolder:GetChildList(newtable())
670 local i = 0
671 while (i < #siblingList) do
672 i = i + 1
673 local sibling = siblingList[i]
674 if sibling:IsShown() then
675 raidFrame:Hide()
676 break
677 end
678 end
679 ]=])
680 end
681
682 return raidFrame
683 end
684
549 685 local function getRangeSpellNameSuggestion() local function getRangeSpellNameSuggestion()
550 686 local _, classDesignation = UnitClass('player') local _, classDesignation = UnitClass('player')
551 687 assert (classDesignation ~= nil) assert (classDesignation ~= nil)
 
... ... end
611 747 local function initSpoiler(rootFrame) local function initSpoiler(rootFrame)
612 748 assert (rootFrame ~= nil) assert (rootFrame ~= nil)
613 749
614 createGroup(rootFrame, 1)
615 createGroup(rootFrame, 2)
616 createGroup(rootFrame, 3)
617 createGroup(rootFrame, 4)
618 createGroup(rootFrame, 5)
619 createGroup(rootFrame, 6)
620 createGroup(rootFrame, 7)
621 createGroup(rootFrame, 8)
750 --[[ WARNING All mutually exclusive frames must be placed under the same parent,
751 -- for the spoiler toggling to work correctly.
752 -- All siblings of spoiler frames will be toggled, which may not have been the intention. ]]--
753 local spoilerHolder = CreateFrame('FRAME', 'ChoirSpoilerRootFrame', rootFrame)
754 spoilerHolder:SetAllPoints()
755
756 createGroup(spoilerHolder, 1)
757 createGroup(spoilerHolder, 2)
758 createGroup(spoilerHolder, 3)
759 createGroup(spoilerHolder, 4)
760 createGroup(spoilerHolder, 5)
761 createGroup(spoilerHolder, 6)
762 createGroup(spoilerHolder, 7)
763 createGroup(spoilerHolder, 8)
622 764
623 765 --[[ TODO Add Esc key that closes the spoiler without clicking any of the children buttons ]]-- --[[ TODO Add Esc key that closes the spoiler without clicking any of the children buttons ]]--
624 766 --[[ TODO Generalize the interface to be used with any kind of child frames, --[[ TODO Generalize the interface to be used with any kind of child frames,
 
... ... local function initSpoiler(rootFrame)
628 770 -- local t = env['MySavedVariableTable'] -- local t = env['MySavedVariableTable']
629 771 -- ``` -- ```
630 772 -- See http://www.iriel.org/wow/docs/SecureHeadersGuide-4.0-r1.pdf ]]-- -- See http://www.iriel.org/wow/docs/SecureHeadersGuide-4.0-r1.pdf ]]--
631 local spoilerParty = createGroup(rootFrame, 9, {'player', 'party1', 'party2', 'party3', 'party4'})
773 local spoilerParty = createGroup(spoilerHolder, 9, {'player', 'party1', 'party2', 'party3', 'party4'})
632 774 _G['BINDING_NAME_CLICK ' .. spoilerParty:GetName() .. ':LeftButton'] = 'Player party' _G['BINDING_NAME_CLICK ' .. spoilerParty:GetName() .. ':LeftButton'] = 'Player party'
633 775
634 776 BINDING_HEADER_CHOIR = 'Choir' BINDING_HEADER_CHOIR = 'Choir'
777
778 return spoilerHolder
779 end
780
781 local function initRaidFrame(rootFrame, spoilerHolder)
782 return createRaidFrame(rootFrame, spoilerHolder)
635 783 end end
636 784
637 785 local function init(rootFrame) local function init(rootFrame)
 
... ... local function init(rootFrame)
646 794 rootFrame:SetPoint('CENTER', 0, 0) rootFrame:SetPoint('CENTER', 0, 0)
647 795
648 796 initRangeSpellName(rootFrame) initRangeSpellName(rootFrame)
649 initSpoiler(rootFrame)
797 local spoilerHolder = initSpoiler(rootFrame)
798 initRaidFrame(rootFrame, spoilerHolder)
650 799
651 800 trace('init') trace('init')
652 801 end end
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/choir

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

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

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