forked from Tojaso/Raven
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Nest.lua
2786 lines (2560 loc) · 136 KB
/
Nest.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- Nest is a graphics package that is optimized to display Raven's bar groups.
-- Bar groups share layout and appearance options (dimensions, fonts, textures, colors, configuration, special effects).
-- Each bar has a fixed set of graphical components (icon, iconText, foreground bar, background bar, labelText, timeText, spark).
local MOD = Raven
local SHIM = MOD.SHIM
local L = LibStub("AceLocale-3.0"):GetLocale("Raven")
MOD.Nest_SupportedConfigurations = { -- table of configurations can be used in dialogs to select appropriate options
[1] = { name = L["Right-to-left bars, label left, icon left"], iconOnly = false, bars = "r2l", label = "left", icon = "left" },
[2] = { name = L["Left-to-right bars, label left, icon left"], iconOnly = false, bars = "l2r", label = "left", icon = "left" },
[3] = { name = L["Right-to-left bars, label right, icon left"], iconOnly = false, bars = "r2l", label = "right", icon = "left" },
[4] = { name = L["Left-to-right bars, label right, icon left"], iconOnly = false, bars = "l2r", label = "right", icon = "left" },
[5] = { name = L["Right-to-left bars, label left, icon right"], iconOnly = false, bars = "r2l", label = "left", icon = "right" },
[6] = { name = L["Left-to-right bars, label left, icon right"], iconOnly = false, bars = "l2r", label = "left", icon = "right" },
[7] = { name = L["Right-to-left bars, label right, icon right"], iconOnly = false, bars = "r2l", label = "right", icon = "right" },
[8] = { name = L["Left-to-right bars, label right, icon right"], iconOnly = false, bars = "l2r", label = "right", icon = "right" },
[9] = { name = L["Icons in rows, with right-to-left mini-bars"], iconOnly = true, bars = "r2l", orientation = "horizontal" },
[10] = { name = L["Icons in rows, with left-to-right mini-bars"], iconOnly = true, bars = "l2r", orientation = "horizontal" },
[11] = { name = L["Icons in columns, right-to-left mini-bars"], iconOnly = true, bars = "r2l", orientation = "vertical" },
[12] = { name = L["Icons in columns, left-to-right mini-bars"], iconOnly = true, bars = "l2r", orientation = "vertical" },
[13] = { name = L["Icons on horizontal timeline, no mini-bars"], iconOnly = true, bars = "timeline", orientation = "horizontal" },
[14] = { name = L["Icons on vertical timeline, no mini-bars"], iconOnly = true, bars = "timeline", orientation = "vertical" },
[15] = { name = L["Icons with variable width on horizontal stripe"], iconOnly = true, bars = "stripe", orientation = "horizontal" },
}
MOD.Nest_MaxBarConfiguration = 8
local barGroups = {} -- current barGroups
local usedBarGroups = {} -- cache of recycled barGroups
local usedBars = {} -- cache of recycled bars
local update = false -- set whenever a global change has occured
local buttonName = 0 -- incremented for each button created
local callbacks = {} -- registered callback functions
local splashAnimationPool = {} -- pool of available bar animations
local splashAnimations = {} -- active bar animations
local shineEffectPool = {} -- pool of available shine animations
local sparkleEffectPool = {} -- pool of available sparkle animations
local pulseEffectPool = {} -- pool of available pulse animations
local glowEffectPool = {} -- pool of available glow animations
local displayWidth, displayHeight = UIParent:GetWidth(), UIParent:GetHeight()
local defaultBackdropColor = { r = 1, g = 1, b = 1, a = 1 }
local defaultGreen = { r = 0, g = 1, b = 0, a = 1 }
local defaultRed = { r = 1, g = 0, b = 0, a = 1 }
local defaultBlack = { r = 0, g = 0, b = 0, a = 1 }
local pixelScale = 1 -- adjusted by screen resolution and uiScale
local pixelPerfect -- global setting to enable pixel perfect size and position
local pixelWidth, pixelHeight = 0, 0 -- actual screen resolution
local rectIcons = false -- allow rectangular icons
local zoomIcons = false -- zoom rectangular icons
local inPetBattle = nil
local alignLeft = {} -- table of icons to be aligned left
local alignRight = {} -- table of icons to be aligned right
local alignCenter = {} -- table of icons to be aligned center
local customTimeFormatIndex = nil -- when defined, this is the index for custom time formats in the options list
local userDefinedTimeFormatFunction = nil -- this is the user-defined function for custom time formats
local MSQ = nil -- Masque support
local MSQ_ButtonData = nil
local CS = CreateFrame("ColorSelect")
local textures = {
["circle"] = [[Interface\Addons\Raven\Icons\Circle.tga]],
["diamond"] = [[Interface\Addons\Raven\Icons\Diamond.tga]],
["triangle"] = [[Interface\Addons\Raven\Icons\Triangle.tga]],
["trapezoid"] = [[Interface\Addons\Raven\Icons\Trapezoid.tga]],
}
local ANCHOR_TILESIZE, ANCHOR_EDGESIZE = 8, 6
local anchorDefaults = { -- backdrop initialization for bar group anchors
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = [[Interface\Addons\Raven\Borders\Rounded.tga]],
tile = true, tileSize = ANCHOR_TILESIZE, edgeSize = ANCHOR_EDGESIZE, insets = { left = 0, right = 0, top = 0, bottom = 0 }
}
local iconBackdrop = { -- backdrop initialization for icons when using optional border customization
bgFile = [[Interface\Addons\Raven\Statusbars\White.tga]],
edgeFile = [[Interface\BUTTONS\WHITE8X8.blp]], edgeSize = 1, insets = { left = 0, right = 0, top = 0, bottom = 0 }
}
local bgTemplate = { -- these fields are preserved when a bar group is deleted
attributes = 0, callbacks = 0, frame = 0, backdrop = 0, backdropTable = 0, borderTable = 0, anchor = 0, bars = 0,
position = 0, sorter = 0, sortFunction = 0, locked = 0, moving = 0, count = 0,
}
local barTemplate = { -- these fields are preserved when a bar is deleted
buttonName = 0, attributes = 0, callbacks = 0, frame = 0, container = 0, fgTexture = 0, bgTexture = 0, backdrop = 0, spark = 0, tick = 0,
textFrame = 0, tipFrame = 0, labelText = 0, timeText = 0, icon = 0, iconTexture = 0, cooldown = 0, iconTextFrame = 0, iconText = 0, iconBorder = 0,
tukbar = 0, tukcolor_r = 0, tukcolor_g = 0, tukcolor_b = 0, tukcolor_a = 0, buttonData = 0, segments = 0, segmentsAllocated = 0,
}
-- Check if using Tukui skin for icon and bar borders (which may require a reloadui)
local function UseTukui()
if Raven.frame then
return Raven.frame.CreateBackdrop and Raven.frame.SetOutside and Raven.db.global.TukuiSkin
end
return nil
end
local function GetTukuiFont(font) if Raven.db.global.TukuiFont and ChatFrame1 then return ChatFrame1:GetFont() else return font end end
local function PS(x) if pixelPerfect and type(x) == "number" then return pixelScale * math.floor(x / pixelScale + 0.5) else return x end end
local function PSetSize(frame, w, h)
if pixelPerfect then
if w then w = pixelScale * math.floor(w / pixelScale + 0.5) end
if h then h = pixelScale * math.floor(h / pixelScale + 0.5) end
end
frame:SetSize(w, h)
end
local function PSetWidth(region, w) if pixelPerfect and w then w = pixelScale * math.floor(w / pixelScale + 0.5) end region:SetWidth(w) end
local function PSetHeight(region, h) if pixelPerfect and h then h = pixelScale * math.floor(h / pixelScale + 0.5) end region:SetHeight(h) end
local function PCSetPoint(frame, point, relativeFrame, relativePoint, x, y)
frame:ClearAllPoints()
if pixelPerfect then
if x then x = pixelScale * math.floor(x / pixelScale + 0.5) end
if y then y = pixelScale * math.floor(y / pixelScale + 0.5) end
end
frame:SetPoint(point, relativeFrame, relativePoint, x or 0, y or 0)
end
local function PSetPoint(frame, point, relativeFrame, relativePoint, x, y)
if pixelPerfect then
if x then x = pixelScale * math.floor(x / pixelScale + 0.5) end
if y then y = pixelScale * math.floor(y / pixelScale + 0.5) end
end
frame:SetPoint(point, relativeFrame, relativePoint, x or 0, y or 0)
end
-- Trim and scale icon, including for optional rectangular dimensions
local function IconTextureTrim(tex, icon, trim, w, h)
local left, right, top, bottom = 0, 1, 0, 1 -- default without trim
if trim then left = 0.07; right = 0.93; top = 0.07; bottom = 0.93 end -- trim removes 7% of edges
if zoomIcons then -- only true if both rectangular and zoom icons enabled
if w > h then -- rectangular with width greater than height
local crop = (bottom - top) * (w - h)/ w / 2 -- aspect ratio to reduce height by
top = top + crop; bottom = bottom - crop
elseif h > w then -- rectangular with height greater than width
local crop = (right - left) * (h - w)/ h / 2 -- aspect ratio to reduce height by
left = left + crop; right = right - crop
end
end
tex:SetTexCoord(left, right, top, bottom) -- set the corner coordinates
PSetSize(tex, w, h)
PSetPoint(tex, "CENTER", icon, "CENTER") -- texture is always positioned in center of icon's frame
end
-- Calculate alpha for flashing bars, period is how long the total flash time should last
function MOD.Nest_FlashAlpha(maxAlpha, minAlpha, period)
local frac = GetTime() / period
frac = frac - math.floor(frac) -- get fractional part of current period
if frac >= 0.5 then frac = 1 - frac end -- now goes from 0 to 0.5 then back to 0
frac = frac * 2 -- adjust frac to range from 0 to 1
local alpha = minAlpha + (frac * (maxAlpha - minAlpha)) -- adjust alpha within range from minAlpha to maxAlpha
return alpha
end
-- Set and confirm frame level, working around potential bug when raising frame level above internal limits
local function SetFrameLevel(frame, level)
local i = 0
repeat
frame:SetFrameLevel(level); local a = frame:GetFrameLevel()
i = i + 1; if i > 10 then print("Raven: warning SetFrameLevel failed"); return end
until level == a
end
-- Validate that have a valid font reference
local function ValidFont(name)
local result = (name and (type(name) == "string") and (name ~= ""))
return result
end
-- Initialize and return a bar splash-style animation based on the icon texture
-- If anchor info is not passed in then splash will be centered over the bar's icon
local function SplashEffect(bar, anchor1, frame, anchor2, xoffset, yoffset)
local tex = bar.iconTexture:GetTexture(); if not tex then return end
local w, h = bar.icon:GetSize()
local b = next(splashAnimationPool)
if b then splashAnimationPool[b] = nil else
b = {} -- initialize a new animation
b.frame = CreateFrame("Frame", nil, UIParent)
b.frame:SetFrameLevel(bar.frame:GetFrameLevel() + 10)
b.texture = b.frame:CreateTexture(nil, "ARTWORK") -- texture for the texture to be animated
b.maskTexture = b.frame:CreateMaskTexture()
b.maskTexture:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
b.maskTexture:SetAllPoints(b.frame)
b.masked = false
b.anim = b.frame:CreateAnimationGroup()
b.anim:SetLooping("NONE")
local scale = b.anim:CreateAnimation("Scale")
scale:SetScale(3, 3); scale:SetOrigin("CENTER", 0, 0); scale:SetDuration(0.65); scale:SetOrder(1)
local alpha = b.anim:CreateAnimation("Alpha")
alpha:SetFromAlpha(1); alpha:SetToAlpha(0) -- LEGION change
alpha:SetDuration(0.65); alpha:SetSmoothing("IN"); alpha:SetEndDelay(5); alpha:SetOrder(1)
end
local w, h = bar.icon:GetSize()
PSetSize(b.frame, w, h)
if frame then
PCSetPoint(b.frame, anchor1 or "CENTER", frame, anchor2 or "CENTER", xoffset or 0, yoffset or 0)
else -- not provided a reference point so use position of bar's icon
PCSetPoint(b.frame, "BOTTOMLEFT", nil, "BOTTOMLEFT", bar.icon:GetLeft(), bar.icon:GetBottom())
end
b.frame:Show()
b.texture:SetTexture(tex)
IconTextureTrim(b.texture, bar.icon, true, w - 2, h - 2)
if MSQ and (bar.icon.__MSQ_Shape == "Circle") then
if not b.masked then -- check if need a mask texture and not already masked
b.texture:AddMaskTexture(b.maskTexture)
PSetSize(b.maskTexture, w, h)
b.masked = true
end
else
if b.masked then -- check if need to remove a previously added mask texture
b.texture:RemoveMaskTexture(b.maskTexture)
b.masked = false
end
end
b.texture:ClearAllPoints(); b.texture:SetAllPoints(b.frame); b.texture:Show()
b.anim:Stop(); b.anim:Play()
b.endTime = GetTime() + 1 -- stop after one second
table.insert(splashAnimations, b)
end
-- Update active bar animations, recycling when they are complete
local function UpdateSplashAnimations()
local now = GetTime()
for k, b in pairs(splashAnimations) do
if now > b.endTime then
b.anim:Pause(); splashAnimations[k] = nil; splashAnimationPool[b] = true
b.frame:ClearAllPoints(); b.texture:ClearAllPoints(); b.frame:Hide(); b.texture:Hide()
end
end
end
-- Show splash effect for a bar
function MOD.Nest_SplashEffect(bg, bar)
SplashEffect(bar)
end
-- Pulse animation on the bar icon
local function PulseEffect(bar)
local tex = bar.iconTexture:GetTexture(); if not tex then return end
local w, h = bar.icon:GetSize()
local a = bar.pulseEffect -- get an animation if one has already been allocated for this bar
if not a then -- allocate an animation if necessary
a = next(pulseEffectPool) -- get one from the recycling pool if available
if a then pulseEffectPool[a] = nil else
a = {} -- initialize a new animation for this pulse effect
a.frame = CreateFrame("Frame", nil, UIParent)
a.texture = a.frame:CreateTexture(nil, "ARTWORK") -- texture to be animated
a.maskTexture = a.frame:CreateMaskTexture()
a.maskTexture:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
a.maskTexture:SetAllPoints(a.frame)
a.masked = false
a.anim = a.frame:CreateAnimationGroup()
a.anim:SetLooping("NONE")
local alpha1 = a.anim:CreateAnimation("Alpha")
alpha1:SetFromAlpha(0); alpha1:SetToAlpha(1); alpha1:SetDuration(0.05); alpha1:SetOrder(1)
local grow = a.anim:CreateAnimation("Scale")
grow:SetScale(3, 3); grow:SetOrigin('CENTER', 0, 0); grow:SetDuration(0.25); grow:SetOrder(1)
local shrink = a.anim:CreateAnimation("Scale")
shrink:SetScale(-3, -3); shrink:SetOrigin('CENTER', 0, 0); shrink:SetDuration(0.25); shrink:SetOrder(2)
local alpha2 = a.anim:CreateAnimation("Alpha")
alpha2:SetFromAlpha(1); alpha2:SetToAlpha(0); alpha2:SetDuration(0.05); alpha2:SetOrder(3)
end
a.frame:ClearAllPoints()
a.frame:SetFrameStrata(bar.frame:GetFrameStrata())
a.frame:SetFrameLevel(bar.frame:GetFrameLevel() + 10)
local w, h = bar.icon:GetSize()
PSetSize(a.frame, w, h)
PCSetPoint(a.frame, "CENTER", bar.icon, "CENTER", 0, 0)
a.texture:SetAllPoints(a.frame)
a.frame:SetAlpha(0)
a.frame:Show(); a.texture:Show()
bar.pulseEffect = a
end
if not a.anim:IsPlaying() then
a.texture:SetTexture(tex)
IconTextureTrim(a.texture, bar.icon, true, w - 2, h - 2)
if MSQ and (bar.icon.__MSQ_Shape == "Circle") then
if not a.masked then -- check if need a mask texture and not already masked
a.texture:AddMaskTexture(a.maskTexture)
PSetSize(a.maskTexture, w, h)
a.masked = true
end
else
if a.masked then -- check if need to remove a previously added mask texture
a.texture:RemoveMaskTexture(a.maskTexture)
a.masked = false
end
end
a.anim:Stop(); a.anim:Play()
end
end
local function ReleasePulseEffect(bar)
local a = bar.pulseEffect -- get the pulse animation, if any, that is allocated for this bar
if a then
a.anim:Stop(); a.frame:ClearAllPoints(); a.texture:Hide(); a.frame:Hide()
pulseEffectPool[a] = true
bar.pulseEffect = nil
end
end
-- Fader to change from current to a new alpha
local function FaderEffect(bar, toAlpha, fade)
local anim = bar.frame.fader
if not anim then
anim = bar.frame:CreateAnimationGroup()
anim:SetLooping("NONE")
local alpha = anim:CreateAnimation("Alpha")
alpha:SetFromAlpha(1); alpha:SetToAlpha(1); alpha:SetDuration(0.1); alpha:SetOrder(1)
anim.alpha = alpha
anim:SetToFinalAlpha(true)
bar.frame.fader = anim
end
local isPlaying = anim:IsPlaying()
local alpha = anim.alpha
local current = bar.frame:GetAlpha() -- actual current alpha for the bar
local fromAlpha = isPlaying and alpha:GetToAlpha() or current -- for comparison, check target alpha if animation is playing otherwise use current
local delta = math.floor(math.abs(fromAlpha - toAlpha) * 100) -- zero if comparison is within 1% of same value
if not fade then -- just go straight to the target alpha if fade is disabled
if isPlaying then anim:Stop() end
bar.frame:SetAlpha(toAlpha)
else
if delta > 0 then -- use fader animation to get to target alpha
if isPlaying then anim:Stop() end -- need to restart the animation with new values if it is playing
alpha:SetFromAlpha(current); alpha:SetToAlpha(toAlpha)
anim:Play()
elseif not isPlaying then
bar.frame:SetAlpha(toAlpha) -- pretty close so just go straight there to finish up
end
end
end
local function ReleaseFaderEffect(bar)
local anim = bar.frame.fader
if anim then anim:Stop() end
end
-- Flash effect to change bar alpha in a noticeable way
local function FlashEffect(bar, maxAlpha, minAlpha, period)
local anim = bar.frame.flasher
if not anim then
anim = bar.frame:CreateAnimationGroup()
anim:SetLooping("REPEAT")
local a = anim:CreateAnimation("Animation") -- use animation to trigger associated OnUpdate script
a:SetDuration(1); a:SetOrder(1) -- this is done so that flashing bars can be synchronized
bar.frame.flasher = anim
end
if anim.maxAlpha ~= maxAlpha or anim.minAlpha ~= minAlpha or anim.flashPeriod ~= period then
local FlashAlpha = MOD.Nest_FlashAlpha -- function to get current alpha for all flashing bars
anim:SetScript("OnUpdate", function() bar.frame:SetAlpha(FlashAlpha(maxAlpha, minAlpha, period)) end)
anim.maxAlpha = maxAlpha; anim.minAlpha = minAlpha; anim.flashPeriod = period
end
if not anim:IsPlaying() then anim:Stop(); anim:Play() end
end
local function ReleaseFlashEffect(bar)
local anim = bar.frame.flasher
if anim then anim:Stop() end
end
-- Add a shine effect over a bar's icon
-- If color is set then apply it to the animation
local function ShineEffect(bar, color)
local a = bar.shineEffect -- get an animation if one has already been allocated for this bar
if not a then -- allocate an animation if necessary
a = next(shineEffectPool) -- get one from the recycling pool if available
if a then shineEffectPool[a] = nil else
a = {} -- initialize a new animation for this shine effect
a.frame = CreateFrame("Frame", nil, UIParent)
a.texture = a.frame:CreateTexture(nil, "ARTWORK") -- texture to be animated
a.texture:SetTexture("Interface\\Cooldown\\star4")
a.texture:SetBlendMode("ADD")
a.anim = a.frame:CreateAnimationGroup()
a.anim:SetLooping("NONE")
local alpha1 = a.anim:CreateAnimation("Alpha")
alpha1:SetFromAlpha(0); alpha1:SetToAlpha(1); alpha1:SetDuration(0.05); alpha1:SetOrder(1)
local scale1 = a.anim:CreateAnimation("Scale")
scale1:SetScale(2, 2); scale1:SetDuration(0.05); scale1:SetOrder(1)
local scale2 = a.anim:CreateAnimation("Scale")
scale2:SetScale(0.1, 0.1); scale2:SetDuration(0.5); scale2:SetOrder(2)
local rotation = a.anim:CreateAnimation("Rotation")
rotation:SetDegrees(135); rotation:SetDuration(0.5); rotation:SetOrder(2)
local alpha2 = a.anim:CreateAnimation("Alpha")
alpha2:SetFromAlpha(1); alpha2:SetToAlpha(0); alpha2:SetDuration(0.05); alpha2:SetOrder(3)
end
a.frame:ClearAllPoints()
a.frame:SetFrameStrata(bar.frame:GetFrameStrata())
a.frame:SetFrameLevel(bar.frame:GetFrameLevel() + 10)
local w, h = bar.icon:GetSize()
PSetSize(a.frame, w, h)
PCSetPoint(a.frame, "CENTER", bar.icon, "CENTER", 0, 0)
a.texture:SetAllPoints(a.frame)
a.frame:SetAlpha(0)
a.frame:Show(); a.texture:Show()
bar.shineEffect = a
end
if not a.anim:IsPlaying() then
local r, g, b = 1, 1, 1
if color then r = color.r; g = color.g; b = color.b end
a.texture:SetVertexColor(r, g, b, 1) -- add color to the texture
a.anim:Stop(); a.anim:Play()
end
end
-- When a bar is deleted then release allocated shine animation, if any
local function ReleaseShineEffect(bar)
local a = bar.shineEffect -- get the shine animation, if any, that is allocated for this bar
if a then
a.anim:Stop(); a.frame:ClearAllPoints(); a.texture:Hide(); a.frame:Hide()
shineEffectPool[a] = true
bar.shineEffect = nil
end
end
-- Configuration table for sparklers used in sparkle effect
local sparkleCount = 0
local sparkles = {
[1] = { x = 0.9, y = 0.9, scale = 1, delay = 0, duration = 0.5 },
[2] = { x = -0.9, y = -0.9, scale = 1, delay = 0, duration = 0.5 },
[3] = { x = 1, y = -1, scale = 0.5, delay = 0.1, duration = 0.5 },
[4] = { x = -1, y = 1, scale = 0.5, delay = 0.1, duration = 0.5 },
[5] = { x = 0, y = 1.5, scale = 0.5, delay = 0.2, duration = 0.4 },
[6] = { x = 0, y = -1.5, scale = 0.5, delay = 0.2, duration = 0.4 },
[7] = { x = -1, y = 0, scale = 1, delay = 0.2, duration = 0.4 },
[8] = { x = 1, y = 0, scale = 1, delay = 0.2, duration = 0.4 },
}
-- Add a sparkle effect over a bar's icon
-- If color is set then apply it to the animation
local function SparkleEffect(bar, color)
local a = bar.sparkleEffect -- get an animation if one has already been allocated for this bar
if not a then -- allocate an animation if necessary
a = next(sparkleEffectPool) -- get one from the recycling pool if available
if a then sparkleEffectPool[a] = nil else
sparkleCount = sparkleCount + 1
a = {} -- initialize a new animation for this sparkle effect
a.frame = CreateFrame("Frame", nil, UIParent)
a.texture = a.frame:CreateTexture(nil, "ARTWORK") -- texture to be animated
a.texture:SetTexture("Interface\\Cooldown\\starburst")
a.texture:SetBlendMode("ADD")
a.sparkleTextures = {}
a.sparkleTranslators = {}
a.anim = a.frame:CreateAnimationGroup()
a.anim:SetLooping("NONE")
a.anim:SetToFinalAlpha(true)
local x = a.anim:CreateAnimation("Alpha")
x:SetFromAlpha(0); x:SetToAlpha(1); x:SetDuration(0.15); x:SetOrder(1)
x = a.anim:CreateAnimation("Scale")
x:SetScale(1, 1); x:SetDuration(0.33); x:SetOrder(1)
x = a.anim:CreateAnimation("Alpha")
x:SetFromAlpha(1); x:SetToAlpha(0); x:SetStartDelay(0.45); x:SetDuration(0.15); x:SetOrder(1)
for i = 1, 8 do -- create sparklers
local name = "Raven_Animation" .. tostring(sparkleCount) .. "_Spark" .. tostring(i)
local animTarget = a.frame:CreateTexture(name, "ARTWORK") -- texture to be animated
animTarget:SetTexture("Interface\\Cooldown\\star4")
animTarget:SetBlendMode("ADD")
a.sparkleTextures[i] = animTarget
local s = sparkles[i]
x = a.anim:CreateAnimation("Alpha")
x:SetTarget(animTarget); x:SetFromAlpha(0); x:SetToAlpha(1); x:SetSmoothing("IN")
x:SetStartDelay(s.delay); x:SetDuration(0.15); x:SetOrder(1)
x = a.anim:CreateAnimation("Alpha")
x:SetTarget(animTarget); x:SetFromAlpha(1); x:SetToAlpha(0.25); x:SetSmoothing("OUT")
x:SetStartDelay(s.delay + s.duration - 0.15); x:SetDuration(0.15); x:SetOrder(1)
x = a.anim:CreateAnimation("Rotation")
x:SetTarget(animTarget); x:SetDegrees(60); x:SetStartDelay(s.delay); x:SetDuration(s.duration); x:SetOrder(1)
x = a.anim:CreateAnimation("Scale")
x:SetTarget(animTarget); x:SetScale(s.scale, s.scale); x:SetStartDelay(s.delay); x:SetDuration(0.25); x:SetOrder(1); x:SetSmoothing("IN")
x = a.anim:CreateAnimation("Scale")
x:SetTarget(animTarget); x:SetScale(0.1, 0.1); x:SetStartDelay(s.delay + s.duration - 0.25); x:SetDuration(0.25); x:SetOrder(1); x:SetSmoothing("OUT")
x = a.anim:CreateAnimation("Translation")
x:SetTarget(animTarget); x:SetOffset(s.x, s.y)
x:SetStartDelay(s.delay); x:SetDuration(0.5); x:SetOrder(1)
a.sparkleTranslators[i] = x -- save to set size
end
end
a.frame:ClearAllPoints()
a.frame:SetFrameStrata(bar.frame:GetFrameStrata())
a.frame:SetFrameLevel(bar.frame:GetFrameLevel() + 10)
local w, h = bar.icon:GetSize()
PSetSize(a.frame, w, h)
PCSetPoint(a.frame, "CENTER", bar.icon, "CENTER", 0, 0)
a.texture:SetAllPoints(a.frame)
a.frame:SetAlpha(0)
a.frame:Show(); a.texture:Show()
for i = 1, 8 do
local x = a.sparkleTranslators[i]
local s = sparkles[i]
x:SetOffset(w * s.x * 0.75, h * s.y * 0.75)
local tex = a.sparkleTextures[i]; tex:SetAllPoints(a.frame); tex:Show()
end
bar.sparkleEffect = a
end
if not a.anim:IsPlaying() then
local r, g, b = 1, 1, 1
if color then r = color.r; g = color.g; b = color.b end
a.texture:SetVertexColor(r, g, b, 1) -- add color to the starburst texture
for i = 1, 8 do
local tex = a.sparkleTextures[i]; tex:SetVertexColor(r, g, b, 1) -- add color to each of the sparkle textures
end
a.anim:Stop(); a.anim:Play()
end
end
-- When a bar is deleted then release allocated sparkle animation, if any
local function ReleaseSparkleEffect(bar)
local a = bar.sparkleEffect -- get the sparkle animation, if any, that is allocated for this bar
if a then
a.anim:Stop(); a.frame:ClearAllPoints(); a.texture:Hide(); a.frame:Hide()
for i = 1, 8 do a.sparkleTextures[i]:Hide() end
sparkleEffectPool[a] = true
bar.sparkleEffect = nil
end
end
-- Initialize and return a glow effect behind a bar's icon
-- If color is set then apply it to the animation
local function GlowEffect(bar, color)
local a = bar.glowEffect -- get a glow effect if one has already been allocated for this bar
if not a then -- allocate an animation if necessary
a = next(glowEffectPool) -- get one from the recycling pool if available
if a then glowEffectPool[a] = nil else
a = {} -- initialize a new table for this glow effect
a.frame = CreateFrame("Frame", nil, UIParent)
a.texture = a.frame:CreateTexture(nil, "BACKGROUND") -- texture to be animated
a.texture:SetTexture("Interface\\SpellActivationOverlay\\IconAlert")
a.texture:SetTexCoord(0.00781250, 0.50781250, 0.53515625, 0.78515625)
a.texture:SetBlendMode("ADD")
a.circle = a.frame:CreateTexture(nil, "BACKGROUND") -- alternative circle texture
a.circle:SetTexture("Interface\\Common\\GoldRing")
a.circle:SetBlendMode("ADD")
end
a.frame:ClearAllPoints()
a.frame:SetFrameStrata(bar.frame:GetFrameStrata())
a.frame:SetFrameLevel(bar.frame:GetFrameLevel())
a.frame:SetAlpha(0.5)
bar.glowEffect = a
end
local w, h = bar.icon:GetSize()
PSetSize(a.frame, w, h)
PCSetPoint(a.frame, "CENTER", bar.icon, "CENTER", 0, 0)
PSetSize(a.texture, (w - 2) * 2, (h - 2) * 2)
PCSetPoint(a.texture, "CENTER", a.frame, "CENTER", -1, 0)
PSetSize(a.circle, (w - 2) * 1.5, (h - 2) * 1.5)
PCSetPoint(a.circle, "CENTER", a.frame, "CENTER", -1, 0)
local r, g, b = 1, 1, 1
if color then r = color.r; g = color.g; b = color.b end
if MSQ and (bar.icon.__MSQ_Shape == "Circle") then
a.circle:SetVertexColor(r, g, b, 1) -- add color to the circle texture
a.texture:Hide(); a.circle:Show()
else
a.texture:SetVertexColor(r, g, b, 1) -- add color to the texture
a.texture:Show(); a.circle:Hide()
end
a.frame:Show()
end
-- When a bar is deleted then release allocated glow animation, if any
local function ReleaseGlowEffect(bar)
local a = bar.glowEffect -- get the glow animation, if any, that is allocated for this bar
if a then
a.texture:Hide(); a.circle:Hide(); a.frame:Hide()
glowEffectPool[a] = true
bar.glowEffect = nil
end
end
-- Show the timeline specific frames for a bar group
local function ShowTimeline(bg)
local back = bg.background
if back then
back:Show(); back.bar:ClearAllPoints(); back.bar:SetAllPoints(back); back.bar:Show()
if bg.tlTexture then back.bar:SetTexture(bg.tlTexture) end
local t = bg.tlColor; if t then back.bar:SetVertexColor(t.r, t.g, t.b, t.a) end
if bg.borderTexture then
PCSetPoint(back.backdrop, "CENTER", back, "CENTER", 0, 0)
back.backdrop:Show()
else
back.backdrop:Hide()
end
for _, v in pairs(back.labels) do if v.hidden then v:Hide() else v:Show() end end
end
end
-- Hide the timeline specific frames for a bar group (also works for horizontal stripe)
local function HideTimeline(bg)
local back = bg.background
if back then
back:Hide(); back.bar:Hide(); back.backdrop:Hide()
if back.labels then for _, v in pairs(back.labels) do v:Hide() end end
end
end
-- Calculate the offset for a time value on a timeline
local function Timeline_Offset(bg, t)
if t >= bg.tlDuration then return bg.tlWidth end
if t <= 0 then return 0 end
return bg.tlWidth * ((t / bg.tlDuration) ^ (1 / bg.tlScale))
end
-- Animate bars that are ending on a timeline
local function BarGroup_TimelineAnimation(bg, bar, config)
local dir = bg.growDirection and 1 or -1 -- plus or minus depending on direction
local isVertical = (config.orientation == "vertical")
local w, h, edge
if config.orientation == "horizontal" then
w = bg.tlWidth; h = bg.tlHeight; edge = bg.growDirection and "RIGHT" or "LEFT"
else
w = bg.tlHeight; h = bg.tlWidth; edge = bg.growDirection and "TOP" or "BOTTOM"
end
local delta = Timeline_Offset(bg, 0)
local x1 = isVertical and 0 or ((delta - w) * dir); local y1 = isVertical and ((delta - h) * dir) or 0
SplashEffect(bar, edge, bg.background, edge, x1 + (bg.tlSplashX or 0), y1 + (bg.tlSplashY or 0))
end
-- Bar sorting functions: alphabetic, time left, duration, bar's start time
-- Values are assumed equal if difference less than 0.05 seconds
local function sortValues(a, b, f, up)
if a.group ~= b.group then return a.group < b.group end
if a.gname ~= b.gname then return a.gname < b.gname end
if a.sortPlayer then if a.isMine ~= b.isMine then return a.isMine end end -- priority #1: optional isMine for cast by player detection
if math.abs(a[f] - b[f]) >= 0.05 then if up then return a[f] < b[f] else return a[f] > b[f] end end -- priority #2: selected sort function
if a.sortTime and (math.abs(a.timeLeft - b.timeLeft) >= 0.05) then return (a.timeLeft < b.timeLeft) end -- priority #3: optional increasing timeLeft
return a.name < b.name -- priority #4: ascending alphabetic order
end
local function SortTimeDown(a, b) return sortValues(a, b, "timeLeft", false) end
local function SortTimeUp(a, b) return sortValues(a, b, "timeLeft", true) end
local function SortDurationDown(a, b) return sortValues(a, b, "duration", false) end
local function SortDurationUp(a, b) return sortValues(a, b, "duration", true) end
local function SortStartDown(a, b) return sortValues(a, b, "start", false) end
local function SortStartUp(a, b) return sortValues(a, b, "start", true) end
local function SortClassDown(a, b)
if a.group ~= b.group then return a.group < b.group end
if a.gname ~= b.gname then return a.gname < b.gname end
if a.sortPlayer then if a.isMine ~= b.isMine then return a.isMine end end -- priority #1: optional isMine for cast by player detection
if a.class ~= b.class then return a.class > b.class end -- priority #2: selected sort function
if a.sortTime and (math.abs(a.timeLeft - b.timeLeft) >= 0.05) then return (a.timeLeft < b.timeLeft) end -- priority #3: optional increasing timeLeft
return a.name < b.name -- priority #4: ascending alphabetic order
end
local function SortClassUp(a, b)
if a.group ~= b.group then return a.group < b.group end
if a.gname ~= b.gname then return a.gname < b.gname end
if a.sortPlayer then if a.isMine ~= b.isMine then return a.isMine end end -- priority #1: optional isMine for cast by player detection
if a.class ~= b.class then return a.class < b.class end -- priority #2: selected sort function
if a.sortTime and (math.abs(a.timeLeft - b.timeLeft) >= 0.05) then return (a.timeLeft < b.timeLeft) end -- priority #3: optional increasing timeLeft
return a.name < b.name -- priority #4: ascending alphabetic order
end
local function SortAlphaDown(a, b)
if a.group ~= b.group then return a.group < b.group end
if a.gname ~= b.gname then return a.gname < b.gname end
if a.sortPlayer then if a.isMine ~= b.isMine then return a.isMine end end -- priority #1: optional isMine for cast by player detection
if a.name ~= b.name then return a.name > b.name end -- priority #2: selected sort function
if a.sortTime and (math.abs(a.timeLeft - b.timeLeft) >= 0.05) then return (a.timeLeft < b.timeLeft) end -- priority #3: optional increasing timeLeft
return false -- priority #4: ascending alphabetic order (for alphabetic must be equal at this point)
end
local function SortAlphaUp(a, b)
if a.group ~= b.group then return a.group < b.group end
if a.gname ~= b.gname then return a.gname < b.gname end
if a.sortPlayer then if a.isMine ~= b.isMine then return a.isMine end end -- priority #1: optional isMine for cast by player detection
if a.name ~= b.name then return a.name < b.name end -- priority #2: selected sort function
if a.sortTime and (math.abs(a.timeLeft - b.timeLeft) >= 0.05) then return (a.timeLeft < b.timeLeft) end -- priority #3: optional increasing timeLeft
return false -- priority #4: ascending alphabetic order (for alphabetic must be equal at this point)
end
-- Register callbacks that can be used by internal functions to communicate in special cases
function MOD.Nest_RegisterCallbacks(cbs) if cbs then for k, v in pairs(cbs) do callbacks[k] = v end end end
-- Event handling functions for bar group anchors, pass both anchor and bar group
local function BarGroup_OnEvent(anchor, callback)
local bg, bgName = nil, anchor.bgName
-- MOD.Debug("BarGroup_OnEvent", anchor, bgName, callback)
if bgName then bg = barGroups[bgName] end -- locate the bar group associated with the anchor
if bg then
local func = bg.callbacks[callback]
if func then func(anchor, bgName) end
end
end
local function BarGroup_OnEnter(anchor) BarGroup_OnEvent(anchor, "onEnter") end
local function BarGroup_OnLeave(anchor) BarGroup_OnEvent(anchor, "onLeave") end
-- OnClick does a callback (except for unmodified left click), passing bar group name and button
local function BarGroup_OnClick(anchor, button)
local bg, bgName = nil, anchor.bgName
if bgName then bg = barGroups[bgName] end -- locate the bar group associated with the anchor
if ((button ~= "LeftButton") or IsModifierKeyDown()) and bg and not bg.locked then
local func = bg.callbacks.onClick -- only pass left clicks if no modifier key is down
if func then func(anchor, bgName, button) end
end
end
-- OnMouseDown with no modifier key starts moving if frame unlocked and does callback, passing bar group name
local function BarGroup_OnMouseDown(anchor, button)
local bg, bgName = nil, anchor.bgName
if bgName then bg = barGroups[bgName] end -- locate the bar group associated with the anchor
if (button == "LeftButton") and not IsModifierKeyDown() and bg and not bg.locked then
bg.startX = PS(bg.frame:GetLeft()); bg.startY = PS(bg.frame:GetTop())
bg.moving = true
bg.frame:SetFrameStrata("HIGH")
bg.frame:StartMoving()
local func = bg.callbacks.onMove -- called to start movement as long as no modifier key is down
if func then func(anchor, bgName) end
end
end
-- OnMouseUp stops moving if frame is in motion and does a callback passing bar group name to indicate movement
local function BarGroup_OnMouseUp(anchor, button)
local bg, bgName = nil, anchor.bgName
if bgName then bg = barGroups[bgName] end -- locate the bar group associated with the anchor
if bg and bg.moving then
bg.frame:StopMovingOrSizing()
bg.frame:SetFrameStrata(bg.strata or "MEDIUM")
local func = bg.callbacks.onMove
if func then
local endX = PS(bg.frame:GetLeft()); local endY = PS(bg.frame:GetTop())
-- MOD.Debug("moved", bgName, bg.startX, endX, bg.startY, endY)
PCSetPoint(bg.frame, "TOPLEFT", UIParent, "BOTTOMLEFT", endX, endY)
if bg.startX ~= endX or bg.startY ~= endY then func(anchor, bgName) end -- only fires if actually moved
end
bg.moving = false
end
end
-- Initialize and return a new bar group containing either timer bars or enhanced icons
function MOD.Nest_CreateBarGroup(name)
if barGroups[name] then return nil end -- already have one with that name
local n, bg = next(usedBarGroups) -- get any available recycled bar group
if n then
usedBarGroups[n] = nil
else
bg = {}
local xname = string.gsub(name, " ", "_")
bg.frame = CreateFrame("Frame", "RavenBarGroup" .. xname, UIParent) -- add name for reference from other addons
bg.frame:SetFrameLevel(bg.frame:GetFrameLevel() + 20) -- higher than other addons
bg.frame:SetMovable(true); bg.frame:SetClampedToScreen(true)
PCSetPoint(bg.frame, "CENTER", UIParent, "CENTER")
bg.backdrop = CreateFrame("Frame", "RavenBarGroupBackdrop" .. xname, bg.frame, BackdropTemplateMixin and "BackdropTemplate")
bg.backdropTable = { tile = false, insets = { left = 0, right = 0, top = 0, bottom = 0 }}
bg.borderTable = { tile = false, insets = { left = 0, right = 0, top = 0, bottom = 0 }}
bg.anchor = CreateFrame("Button", nil, bg.frame, BackdropTemplateMixin and "BackdropTemplate")
anchorDefaults.tileSize = PS(ANCHOR_TILESIZE)
anchorDefaults.edgeSize = PS(ANCHOR_EDGESIZE)
bg.anchor:SetBackdrop(anchorDefaults)
bg.anchor:SetBackdropColor(0.3, 0.3, 0.3, 0.9)
bg.anchor:SetBackdropBorderColor(0, 0, 0, 0.9)
bg.anchor:SetNormalFontObject(ChatFontSmall)
bg.anchor:SetFrameLevel(bg.frame:GetFrameLevel() + 20) -- higher than the bar group frame
bg.bars = {}
bg.sorter = {}
bg.attributes = {}
bg.callbacks = {}
bg.position = {}
bg.sortFunction = SortAlphaUp
bg.locked = false; bg.moving = false
bg.count = 0
end
bg.anchor.bgName = name
bg.anchor:SetScript("OnMouseDown", BarGroup_OnMouseDown)
bg.anchor:SetScript("OnMouseUp", BarGroup_OnMouseUp)
bg.anchor:SetScript("OnClick", BarGroup_OnClick)
bg.anchor:SetScript("OnEnter", BarGroup_OnEnter)
bg.anchor:SetScript("OnLeave", BarGroup_OnLeave)
bg.anchor:RegisterForClicks("LeftButtonUp", "RightButtonUp")
bg.anchor:EnableMouse(true)
table.wipe(bg.position)
bg.name = name
if MSQ then bg.MSQ_Group = MSQ:Group("Raven", name) end
bg.update = true
barGroups[name] = bg
update = true
return bg
end
-- Return the bar group with the specified name
function MOD.Nest_GetBarGroup(name) return barGroups[name] end
-- Return the table of bar groups
function MOD.Nest_GetBarGroups() return barGroups end
-- Delete a bar group and move it to the recycled bar group table
function MOD.Nest_DeleteBarGroup(bg)
for _, bar in pairs(bg.bars) do MOD.Nest_DeleteBar(bg, bar) end -- empty out bars table
PCSetPoint(bg.frame, "CENTER", UIParent, "CENTER") -- return to neutral position
bg.anchor:SetScript("OnMouseDown", nil)
bg.anchor:SetScript("OnMouseUp", nil)
bg.anchor:SetScript("OnClick", nil)
bg.anchor:SetScript("OnEnter", nil)
bg.anchor:SetScript("OnLeave", nil)
bg.anchor:EnableMouse(false)
bg.anchor.bgName = nil
for n in pairs(bg.sorter) do bg.sorter[n] = nil end -- empty the sorting table
bg.sortFunction = SortAlphaUp; bg.sortTime = nil; bg.sortPlayer = nil
bg.count = 0
bg.locked = false; bg.moving = false
if bg.MSQ_Group then bg.MSQ_Group:Delete() end
bg.update = false
bg.anchor:Hide(); bg.backdrop:Hide(); HideTimeline(bg)
barGroups[bg.name] = nil
bg.name = nil
for n in pairs(bg.attributes) do bg.attributes[n] = nil end
for n in pairs(bg.callbacks) do bg.callbacks[n] = nil end
for n in pairs(bg) do if not bgTemplate[n] then bg[n] = nil end end -- remove all settings that don't belong
table.insert(usedBarGroups, bg)
update = true
end
-- Delete all bar groups (should trigger rebuild of existing bar groups in Bars.lua)
function MOD.Nest_DeleteAllBarGroups()
for _, bg in pairs(barGroups) do MOD.Nest_DeleteBarGroup(bg) end
update = true
end
-- Set layout options for a bar group
function MOD.Nest_SetBarGroupBarLayout(bg, barWidth, barHeight, iconSize, scale, spacingX, spacingY, iconOffsetX, iconOffsetY,
labelOffset, labelInset, labelWrap, labelAlign, labelCenter, labelAdjust, labelAuto, labelWidth,
timeOffset, timeInset, timeAlign, timeIcon, iconOffset, iconInset, iconHide, iconAlign,
configuration, growDirection, wrap, wrapDirection, snapCenter, fillBars, maxBars, strata)
bg.barWidth = PS(barWidth); bg.barHeight = PS(barHeight); bg.iconSize = PS(iconSize); bg.scale = scale or 1
bg.fillBars = fillBars; bg.maxBars = maxBars; bg.strata = strata
bg.spacingX = PS(spacingX or 0); bg.spacingY = PS(spacingY or 0); bg.iconOffsetX = (iconOffsetX or 0); bg.iconOffsetY = PS(iconOffsetY or 0)
bg.labelOffset = PS(labelOffset or 0); bg.labelInset = PS(labelInset or 0); bg.labelWrap = labelWrap;
bg.labelCenter = labelCenter; bg.labelAlign = labelAlign or "MIDDLE"; bg.labelAdjust = labelAdjust; bg.labelAuto = labelAuto; bg.labelWidth = labelWidth
bg.timeOffset = PS(timeOffset or 0); bg.timeInset = PS(timeInset or 0); bg.timeAlign = timeAlign or "normal"; bg.timeIcon = timeIcon
bg.iconOffset = PS(iconOffset or 0); bg.iconInset = PS(iconInset or 0); bg.iconHide = iconHide; bg.iconAlign = iconAlign or "CENTER"
bg.configuration = configuration or 1; bg.growDirection = growDirection; bg.wrap = wrap or 0; bg.wrapDirection = wrapDirection; bg.snapCenter = snapCenter
bg.update = true
end
function MOD.Nest_SetBarGroupSegments(bg, count, override, spacing, hideEmpty, fadeAll, shrinkW, shrinkH, gradient, gradientAll, startColor, endColor,
borderColor, advanced, curve, rotate, texture)
bg.segmentCount = count; bg.segmentOverride = override; bg.segmentSpacing = spacing; bg.segmentAdvanced = advanced
bg.segmentHideEmpty = hideEmpty; bg.segmentFadePartial = fadeAll; bg.segmentShrinkWidth = shrinkW; bg.segmentShrinkHeight = shrinkH
bg.segmentGradient = gradient; bg.segmentGradientAll = gradientAll; bg.segmentGradientStartColor = startColor; bg.segmentGradientEndColor = endColor
bg.segmentBorderColor = borderColor; bg.segmentCurve = curve; bg.segmentRotate = rotate; bg.segmentTexture = texture
bg.update = true
end
local function TextFlags(outline, thick, mono)
local t = nil
if not outline and not thick then mono = false end -- XXXX workaround for blizzard bugs caused by use of monochrome text flag by itself
if mono then
if outline then if thick then t = "MONOCHROME,OUTLINE,THICKOUTLINE" else t = "MONOCHROME,OUTLINE" end
else if thick then t = "MONOCHROME,THICKOUTLINE" else t = "MONOCHROME" end end
else
if outline then if thick then t = "OUTLINE,THICKOUTLINE" else t = "OUTLINE" end
else if thick then t = "THICKOUTLINE" end end
end
return t
end
-- Set label font options for a bar group
function MOD.Nest_SetBarGroupLabelFont(bg, font, fsize, alpha, color, outline, shadow, thick, mono, special)
if not color then color = { r = 1, g = 1, b = 1, a = 1 } end
if UseTukui() then font = GetTukuiFont(font) end
bg.labelFont = font; bg.labelFSize = fsize or 9; bg.labelAlpha = alpha or 1; bg.labelColor = color
bg.labelFlags = TextFlags(outline, thick, mono); bg.labelShadow = shadow; bg.labelSpecial = special
bg.update = true
end
-- Set time text font options for a bar group
function MOD.Nest_SetBarGroupTimeFont(bg, font, fsize, alpha, color, outline, shadow, thick, mono, special)
if not color then color = { r = 1, g = 1, b = 1, a = 1 } end
if UseTukui() then font = GetTukuiFont(font) end
bg.timeFont = font; bg.timeFSize = fsize or 9; bg.timeAlpha = alpha or 1; bg.timeColor = color
bg.timeFlags = TextFlags(outline, thick, mono); bg.timeShadow = shadow; bg.timeSpecial = special
bg.update = true
end
-- Set icon text font options for a bar group
function MOD.Nest_SetBarGroupIconFont(bg, font, fsize, alpha, color, outline, shadow, thick, mono, special)
if not color then color = defaultBackdropColor end
if UseTukui() then font = GetTukuiFont(font) end
bg.iconFont = font; bg.iconFSize = fsize or 9; bg.iconAlpha = alpha or 1; bg.iconColor = color
bg.iconFlags = TextFlags(outline, thick, mono); bg.iconShadow = shadow; bg.iconSpecial = special
bg.update = true
end
-- Set bar border options for a bar group
function MOD.Nest_SetBarGroupBorder(bg, texture, width, offset, color)
if not color then color = defaultBackdropColor end
bg.borderTexture = texture; bg.borderWidth = PS(width or 0); bg.borderOffset = PS(offset or 0); bg.borderColor = color
if bg.borderWidth == 0 then bg.borderWidth = PS(1) end -- don't let this be zero width
bg.update = true
end
-- Set backdrop options for a bar group
function MOD.Nest_SetBarGroupBackdrop(bg, panel, texture, width, inset, padding, color, fill, offsetX, offsetY, padW, padH)
if not color then color = { r = 1, g = 1, b = 1, a = 1 } end
if not fill then fill = { r = 1, g = 1, b = 1, a = 1 } end
bg.backdropPanel = panel; bg.backdropTexture = texture; bg.backdropWidth = PS(width or 0); bg.backdropInset = PS(inset or 0)
if bg.backdropWidth == 0 then bg.backdropWidth = PS(1) end -- don't let this be zero width
bg.backdropPadding = PS(padding or 0); bg.backdropColor = color; bg.backdropFill = fill
bg.backdropOffsetX = PS(offsetX or 0); bg.backdropOffsetY = PS(offsetY or 0); bg.backdropPadW = PS(padW or 0); bg.backdropPadH = PS(padH or 0)
bg.update = true
end
-- Set texture options for a bar group
function MOD.Nest_SetBarGroupTextures(bg, fgTexture, fgAlpha, bgTexture, bgAlpha, fgNotTimer, fgSaturation, fgBrightness, bgSaturation, bgBrightness)
bg.fgTexture = fgTexture; bg.fgAlpha = fgAlpha; bg.bgTexture = bgTexture; bg.bgAlpha = bgAlpha; bg.fgNotTimer = fgNotTimer
bg.fgSaturation = fgSaturation or 0; bg.fgBrightness = fgBrightness or 0; bg.bgSaturation = bgSaturation or 0; bg.bgBrightness = bgBrightness or 0
bg.update = true
end
-- Select visible components for a bar group
function MOD.Nest_SetBarGroupVisibles(bg, icon, cooldown, bar, spark, labelText, timeText)
bg.showIcon = icon; bg.showCooldown = cooldown; bg.showBar = bar; bg.showSpark = spark
bg.showLabelText = labelText; bg.showTimeText = timeText
bg.update = true
end
-- Set parameters related to timeline configurations
function MOD.Nest_SetBarGroupTimeline(bg, w, h, duration, scale, hide, alt, switch, percent, splash, x, y, offset, delta, tex, alpha, color, labels, btex, bw, bo, bc)
bg.tlWidth = PS(w); bg.tlHeight = PS(h); bg.tlDuration = duration; bg.tlScale = scale; bg.tlHide = hide; bg.tlAlternate = alt
bg.tlSwitch = switch; bg.tlPercent = percent; bg.tlSplash = splash; bg.tlSplashX = x; bg.tlSplashY = y; bg.tlOffset = offset; bg.tlDelta = delta
bg.tlTexture = tex; bg.tlAlpha = alpha; bg.tlColor = color; bg.tlLabels = labels
bg.tlBorderTexture = btex; bg.tlBorderWidth = PS(bw or 0); bg.tlBorderOffset = PS(bo or 0); bg.tlBorderColor = bc
if bg.tlBorderWidth == 0 then bg.tlBorderWidth = PS(1) end -- don't let this be zero width
bg.update = true
end
-- Set parameters related to horizontal stripe configurations
function MOD.Nest_SetBarGroupStripe(bg, fullWidth, w, h, inset, offset, barInset, barOffset, texture, color, btex, bw, bo, bc)
if fullWidth then bg.stWidth = GetScreenWidth() else bg.stWidth = PS(w) end
bg.stHeight = PS(h); bg.stInset = inset; bg.stOffset = offset; bg.stBarInset = barInset; bg.stBarOffset = barOffset; bg.stTexture = texture; bg.stColor = color
bg.stBorderTexture = btex; bg.stBorderWidth = PS(bw or 0); bg.stBorderOffset = PS(bo or 0); bg.stBorderColor = bc; bg.stFullWidth = fullWidth
if bg.stBorderWidth == 0 then bg.stBorderWidth = PS(1) end -- don't let this be zero width
bg.update = true
end
-- Sort the bars in a bar group using the designated sort method and direction (default is sort by name alphabetically)
function MOD.Nest_BarGroupSortFunction(bg, sortMethod, sortDirection, sortTime, sortPlayer)
if sortMethod == "time" then -- sort by time left on the bar
if sortDirection then bg.sortFunction = SortTimeDown else bg.sortFunction = SortTimeUp end
elseif sortMethod == "duration" then -- sort by bar duration
if sortDirection then bg.sortFunction = SortDurationDown else bg.sortFunction = SortDurationUp end
elseif sortMethod == "start" then -- sort by bar start time
if sortDirection then bg.sortFunction = SortStartDown else bg.sortFunction = SortStartUp end
elseif sortMethod == "class" then -- sort by bar class
if sortDirection then bg.sortFunction = SortClassDown else bg.sortFunction = SortClassUp end
else -- default is sort alphabetically by bar name
if sortDirection then bg.sortFunction = SortAlphaDown else bg.sortFunction = SortAlphaUp end
end
bg.sortTime = sortTime; bg.sortPlayer = sortPlayer
bg.update = true
end
-- Set the time format function for the bar group, if not set will use default
function MOD.Nest_SetBarGroupTimeFormat(bg, timeFormat, timeSpaces, timeCase)
bg.timeFormat = timeFormat; bg.timeSpaces = timeSpaces; bg.timeCase = timeCase
bg.update = true
end
-- If locked is true then lock the bar group anchor, otherwise unlock it
function MOD.Nest_SetBarGroupLock(bg, locked)
bg.locked = locked
bg.update = true
end
-- Return a bar group's display position as percentages of actual display size to edges of the anchor frame
-- Return values are descaled to match UIParent and include left, right, bottom and top plus descaled width and height
function MOD.Nest_GetAnchorPoint(bg)
local scale = bg.scale or 1
local dw, dh = displayWidth, displayHeight
local w, h = bg.frame:GetWidth() * scale, bg.frame:GetHeight() * scale
local left, bottom = bg.frame:GetLeft(), bg.frame:GetBottom() -- get scaled coordinates for frame's anchor
if left and bottom then left = (left * scale); bottom = (bottom * scale) else left = dw / 2; bottom = dh / 2 end -- default to center
local right, top = dw - (left + w), dh - (bottom + h)
local p = bg.position; p.left, p.right, p.bottom, p.top, p.width, p.height = left / dw, right / dw, bottom / dh, top / dh, w, h
return p.left, p.right, p.bottom, p.top, p.width, p.height
end
-- Set a bar group's scaled display position from left, right, bottom, top where left and bottom should always be valid
-- Use right, top, width and height only if valid and closer to that edge to fix position shift when UIParent dimensions change
function MOD.Nest_SetAnchorPoint(bg, left, right, bottom, top, scale, width, height)
if left and bottom and width and height then -- make sure valid settings
bg.scale = scale -- make sure save scale since may not have been initialized yet
local p = bg.position; p.left, p.right, p.bottom, p.top, p.width, p.height = left, right, bottom, top, width, height
local dw, dh = displayWidth, displayHeight
local xoffset = left * dw
local yoffset = bottom * dh