Repository navigation
Expand file tree
/
Copy pathChrome.lua
More file actions
1720 lines (1615 loc) · 74.8 KB
/
Copy pathChrome.lua
File metadata and controls
1720 lines (1615 loc) · 74.8 KB
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
-- WickCore
-- Chrome.lua — the Wick visual system, built once.
--
-- The locked palette, drawn in the look the player picks (Chrome.Styles).
-- Each look belongs to a family: "og" draws flat panels with a border line,
-- and Wick OG, its reference, adds the single 1px muted-purple border and
-- fel-green L-bracket corners with 10px arms 2px thick flush to the corners;
-- "modern" draws 9-sliced glass panels with a ring in place of the border,
-- and Wick Modern, its reference, rounds them and lifts them on a soft
-- shadow. Products call Chrome:NewPanel and the controls here and get the
-- look in use; they take colours by reference from Chrome.Colors and never
-- draw their own frame.
local ADDON = ...
local Core = LibStub("WickCore-1.0", true)
if not Core or Core._sourceAddon ~= ADDON then return end
local Chrome = {}
Core.Chrome = Chrome
-- ============================================================
-- Palette (locked, do not drift)
-- Fel #4FC778 · Void #0D0A14 · Shadow #171124 · Border #383058 · Text #D4C8A1
-- ============================================================
Chrome.Colors = {
fel = { 0.310, 0.780, 0.471, 1 },
void = { 0.051, 0.039, 0.078, 1 },
voidBG = { 0.051, 0.039, 0.078, 0.97 },
shadow = { 0.090, 0.067, 0.141, 1 },
border = { 0.220, 0.188, 0.345, 1 },
text = { 0.831, 0.784, 0.631, 1 },
muted = { 0.560, 0.530, 0.440, 1 },
white = { 1, 1, 1, 1 },
}
Chrome.Hex = {
fel = "4FC778",
void = "0D0A14",
shadow = "171124",
border = "383058",
text = "D4C8A1",
}
-- A palette colour as a text colour code, read the moment it is used, so
-- text written after a theme or look change takes that look's colour.
-- Any token of Chrome.Colors (fel, text, muted...).
function Chrome:Esc(token)
local c = self.Colors[token] or self.Colors.text
return ("|cff%02x%02x%02x"):format(math.floor(c[1] * 255 + 0.5), math.floor(c[2] * 255 + 0.5), math.floor(c[3] * 255 + 0.5))
end
Chrome.BRACKET = 10
Chrome.HEADER_H = 22
Chrome.FONT = "Fonts\\FRIZQT__.TTF"
local C = Chrome.Colors
-- ============================================================
-- Palette registry
-- ============================================================
-- Every region Chrome paints with a palette token is remembered (weakly)
-- with the token's name, so a theme switch can re-tint it in place. A
-- color table that is not one of the tokens is a one-off and is left alone.
local TOKEN_OF = {}
for name, tbl in pairs(Chrome.Colors) do TOKEN_OF[tbl] = name end
local tinted = setmetatable({}, { __mode = "k" })
-- Register a region a product painted itself. kind: "texture" or "text".
function Chrome:Register(region, token, kind, alpha)
if type(token) == "table" then
-- A derived table keeps its own alpha; a token's alpha is part
-- of the token.
if alpha == nil and TOKEN_OF[token] and token[4] ~= (C[TOKEN_OF[token]] or {})[4] then
alpha = token[4]
end
token = TOKEN_OF[token]
end
if not token or not C[token] then return region end
tinted[region] = { token = token, kind = kind or "texture", alpha = alpha }
return region
end
-- A colour derived from a token at another alpha: a hover wash, a
-- selection fill. Kept and refreshed on a theme change, and treated as a
-- token by Register, so a product paints with it exactly as it would
-- paint with Chrome.Colors.fel.
local derived = {}
function Chrome:Wash(token, alpha)
if type(token) == "table" then token = TOKEN_OF[token] end
local src = token and C[token]
if not src then return { 0, 0, 0, alpha or 1 } end
local t = { src[1], src[2], src[3], alpha or 1 }
derived[#derived + 1] = { color = t, token = token }
TOKEN_OF[t] = token
return t
end
function Chrome:Retint()
-- The derived tables first, so a region registered with one is
-- repainted from numbers that are already current.
for _, d in ipairs(derived) do
local src = C[d.token]
if src then
d.color[1], d.color[2], d.color[3] = src[1], src[2], src[3]
end
end
for region, info in pairs(tinted) do
local c = C[info.token]
if c then
local a = info.alpha or c[4] or 1
if info.kind == "text" then
if region.SetTextColor then region:SetTextColor(c[1], c[2], c[3], a) end
elseif info.kind == "vertex" then
if region.SetVertexColor then region:SetVertexColor(c[1], c[2], c[3], a) end
elseif region.SetColorTexture then
region:SetColorTexture(c[1], c[2], c[3], a)
end
end
end
end
-- ============================================================
-- Styles
-- ============================================================
-- The shape the chrome is drawn in, separate from its colours (Theme.lua).
-- A style is a description, not code: every drawing call below reads the
-- current one, so a new look is a new entry here and the whole suite
-- follows it. Each belongs to one of two families, which is what the
-- drawing code branches on (Chrome:Modern()):
--
-- modern textured panels: 9-sliced glass on a soft lift, no border
-- line, a ring that stands in for a coloured border
-- og flat panels: a solid fill, a 1px border, corner marks
--
-- and then says how it differs from its family's base:
--
-- media textures: panel (rounded), ring, the tile and tileRing
-- small frames draw in instead (64 px or less; the panel
-- shape where a look has no tile of its own), the lift (shadow), the
-- mask for rounded corners (roundmask, the minimap too) and
-- the one for icons (iconmask)
-- slice the 9-slice margin of panel and ring
-- glass how solid panels are, times the family's own alpha
-- lift the lift's colour token (nil for black) and alpha
-- ringRest a ring shown at rest in the border's place: token, alpha
-- corners og: "brackets" or "none"
-- game og: the game's own art in place of ours where the client
-- has some (Classic): its window frame, title bar and close
-- button, its tooltip border round panels, its buttons and
-- check boxes. Read from the client as they are drawn, so
-- each client looks like itself.
-- edge og: the black pixel outside the border
-- font the Chrome font; uiFont, when set, is also what Wick's
-- UI's "Wick" font draws in, uiBump added to its sizes
-- headingFont headings and titles, headingBump added to their size
-- bump added to font sizes (faces that run small)
-- textShadow a firm drop shadow on text
-- upper headings in capitals
-- plate headings on a plate in the accent
-- dash a short line in the accent before each heading
-- borderPx og: border thickness
-- hardShadow og: a solid offset shadow under panels { x, y, alpha }
-- stripe true: a stripe in the text colour down the left edge of
-- panels; or { token, share, sides }: a short bar in that
-- colour, share of the height, centred on the left edge
-- (and the right with sides)
-- rivets a rivet in each corner of big panels
-- palette the theme the look comes with, chosen with it ("auto":
-- the player's class, as Wick Modern and Wick OG have)
-- sheen a gradient laid over panels (not tiles): the accent (or
-- { token }) at { top } alpha along the top, black at
-- { bottom } alpha
-- along the bottom, so a window is lit from above
-- edgeInset how far in from a panel's edge it reads as solid (a wash
-- that fades out at its sides): things set against the
-- edge, like a window's side tabs, move in by this much
-- iconTab icons start this far below their tile's top, so the
-- tile's shaped top shows above them as a tab (Arena's
-- folders: its 6 px notch and 1 px under it)
-- health unit health bars in the look's colours instead of by
-- class: { friend = token or hex, enemy = token or hex }
-- statusbar the bar texture the look draws in, where the player has
-- left the bar texture on the default
--
-- The choice is each character's own (a new character starts on
-- Chrome.DEFAULT_STYLE: Wick OG on TBC, Wick Modern on Forever), read from
-- the saved variable directly, as the theme is, and changing it takes a
-- reload: panels are built once.
local MEDIA = "Interface\\AddOns\\WickCore\\Media\\"
local TEX = MEDIA .. "Textures\\"
local FONTS = MEDIA .. "Fonts\\"
local PT_SANS = FONTS .. "PT_Sans-Narrow-Web-Bold.ttf"
local FRIZ = "Fonts\\FRIZQT__.TTF"
local ARIALN = "Fonts\\ARIALN.TTF"
local MORPHEUS = "Fonts\\MORPHEUS.TTF"
local BASE_MEDIA = {
rounded = TEX .. "rounded.png",
ring = TEX .. "ring.png",
shadow = TEX .. "shadow.png",
roundmask = TEX .. "roundmask.png",
iconmask = TEX .. "roundmask.png",
rivet = TEX .. "rivet.png",
glow = TEX .. "glow.png",
font = PT_SANS,
slice = 8,
}
local function modernBump(size) return size + (size <= 11 and 2 or 1) end
-- The open-licensed faces the looks draw in (SIL OFL 1.1, each licence
-- beside its font in Media/Fonts).
local F = {
rajdhani = FONTS .. "Rajdhani-SemiBold.ttf",
rajdhaniBold = FONTS .. "Rajdhani-Bold.ttf",
anton = FONTS .. "Anton-Regular.ttf",
archivo = FONTS .. "ArchivoNarrow-Bold.ttf",
cormorant = FONTS .. "CormorantGaramond-SemiBold.ttf",
cormorantSC = FONTS .. "CormorantSC-SemiBold.ttf",
barlow = FONTS .. "BarlowCondensed-SemiBold.ttf",
barlowBold = FONTS .. "BarlowCondensed-Bold.ttf",
jost = FONTS .. "Jost-Medium.ttf",
jostBold = FONTS .. "Jost-SemiBold.ttf",
michroma = FONTS .. "Michroma-Regular.ttf",
saira = FONTS .. "SairaSemiCondensed-Medium.ttf",
sairaBold = FONTS .. "SairaSemiCondensed-SemiBold.ttf",
-- Bundled for what comes next; any Wick's UI font list offers them.
tektur = FONTS .. "Tektur-Medium.ttf",
tekturBold = FONTS .. "Tektur-SemiBold.ttf",
italiana = FONTS .. "Italiana-Regular.ttf",
exo = FONTS .. "Exo2-Medium.ttf",
exoBold = FONTS .. "Exo2-SemiBold.ttf",
}
Chrome.Fonts = F
local function up(n) return function(size) return size + n end end
Chrome.Styles = {
{ id = "modern", name = "Wick Modern", family = "modern", palette = "auto",
blurb = "Rounded glass on a soft shadow, the Wick font, no border lines.",
font = PT_SANS, bump = modernBump, textShadow = true },
{ id = "og", name = "Wick OG", family = "og", palette = "auto",
blurb = "The original: flat panels, a single-pixel border, fel corners.",
font = FRIZ, corners = "brackets", edge = true },
{ id = "hologram", name = "Hologram", family = "modern", palette = "hologram",
blurb = "A projected display: faint glass, cut corners, a cyan outline and glow, clean geometric type with capital headings.",
font = F.jost, headingFont = F.jostBold, uiFont = F.jost, bump = up(1), uiBump = 1,
textShadow = true, upper = true,
media = { rounded = TEX .. "panel-chamfer.png", ring = TEX .. "ring-chamfer.png",
roundmask = TEX .. "mask-chamfer.png", iconmask = TEX .. "mask-chamfer.png" },
glass = 0.55, lift = { token = "fel", alpha = 0.3 }, ringRest = { token = "fel", alpha = 0.6 },
health = { friend = "fel", enemy = "FF3F6C" } },
{ id = "rebel", name = "Rebel", family = "og", palette = "rebel",
blurb = "Loud and graphic: black slabs, thick grey outlines, hard shadows, headings on red tags.",
font = F.archivo, headingFont = F.anton, uiFont = F.archivo, bump = up(1), headingBump = 2, uiBump = 1,
-- Unit frame and nameplate text in the heading face, two sizes up.
unitFont = F.anton, unitBump = 3,
-- A deeper drop shadow on the big unit frames, and names on red tags
-- like the headings.
unitShadow = 6, unitNameTag = true,
-- Text that asks for the look's own outline gets one here; every
-- other look draws it with a soft shadow instead.
textOutline = "OUTLINE",
textShadow = true, upper = true, plate = true,
corners = "none", edge = false, borderPx = 2, hardShadow = { x = 3, y = -3, alpha = 1 },
health = { friend = "border", enemy = "fel" } },
{ id = "gilded", name = "Gilded", family = "modern", palette = "gilded",
blurb = "Almost no chrome: a dark wash between thin gold rules, serif small capitals, round action buttons.",
font = F.cormorant, headingFont = F.cormorantSC, uiFont = F.cormorant, bump = up(3), headingBump = 1, uiBump = 3,
textShadow = true,
media = { rounded = TEX .. "panel-wash.png", ring = TEX .. "ring-rules.png",
iconmask = TEX .. "mask-circle.png", slice = 16 },
glass = 0.9, lift = { alpha = 0 }, ringRest = { token = "fel", alpha = 0.5 }, edgeInset = 12,
health = { friend = "A6282B", enemy = "fel" } },
{ id = "arena", name = "Arena", family = "modern", palette = "arena",
blurb = "Esports flat: hard panels with one notched corner, an edge stripe, bold condensed capitals.",
font = F.barlow, headingFont = F.barlowBold, uiFont = F.barlow, bump = up(2), uiBump = 2,
textShadow = true, upper = true, stripe = true, iconTab = 7, health = { friend = "text", enemy = "fel" },
media = { rounded = TEX .. "panel-notch.png", ring = TEX .. "ring-notch.png",
roundmask = TEX .. "mask-square.png", iconmask = TEX .. "mask-square.png", slice = 8 },
glass = 1.25, lift = { alpha = 0.7 }, ringRest = { token = "text", alpha = 0.16 },
statusbar = TEX .. "bar-edge.png" },
{ id = "foundry", name = "Foundry", family = "modern", palette = "foundry",
blurb = "Industrial: chamfered steel frames, rivets, orange hazard marks, angular capitals.",
font = F.exo, headingFont = F.tekturBold, uiFont = F.exo, bump = up(1), uiBump = 1,
textShadow = true, upper = true,
media = { rounded = TEX .. "panel-chamfer6.png", ring = TEX .. "ring-steel.png",
tile = TEX .. "panel-square.png", tileRing = TEX .. "ring-hair.png",
roundmask = TEX .. "mask-chamfer.png", iconmask = TEX .. "mask-square.png", slice = 8 },
glass = 1.25, lift = { alpha = 0.6 }, ringRest = { token = "border", alpha = 1 },
rivets = true, stripe = { token = "fel", share = 0.3, sides = true },
sheen = { top = 0.1, bottom = 0.4, token = "text" },
health = { friend = "C8352B", enemy = "fel" }, statusbar = TEX .. "bar-steel.png" },
{ id = "frost", name = "Frost", family = "modern", palette = "frost",
blurb = "Cold and sparse: see-through panels, hairlines in the icy accent, stark wide capitals over a narrow face.",
font = F.saira, headingFont = F.michroma, uiFont = F.saira, bump = up(1), headingBump = -2, uiBump = 1,
textShadow = true, upper = true,
media = { rounded = TEX .. "panel-square.png", ring = TEX .. "ring-hair.png",
roundmask = TEX .. "mask-square.png", iconmask = TEX .. "mask-square.png", slice = 4 },
glass = 0.55, lift = { alpha = 0 }, ringRest = { token = "fel", alpha = 0.3 },
health = { friend = "fel", enemy = "FFB36B" }, statusbar = TEX .. "bar-glass.png", dash = true,
sheen = { top = 0.32, bottom = 0.35 } },
-- Clean cut: the flat family with nothing added. One black pixel round
-- every panel and nothing outside it, square corners, no shadows or
-- glows, panels a little see-through, text with a thin outline.
{ id = "crisp", name = "Crisp", family = "og", palette = "crisp",
blurb = "Clean cut: see-through dark grey, one black pixel round everything, square corners, your class colour as the accent.",
font = PT_SANS, bump = modernBump, textShadow = true, textOutline = "OUTLINE",
corners = "none", edge = false, borderPx = 1, glass = 0.85 },
-- The game's own: its window frames, borders, buttons and fonts, for
-- players who want what the suite does without a look of its own. The
-- art is the client's, so on TBC Anniversary it is that client's and on
-- Forever Forever's. The flat family underneath, for whatever the game
-- has nothing of its own for.
{ id = "classic", name = "Classic", family = "og", palette = "classic", game = true,
blurb = "The game's own look: its window frames, borders, buttons and fonts, so everything sits in with the rest of the interface.",
font = FRIZ, textShadow = true, corners = "none", edge = false, borderPx = 1,
statusbar = "Interface\\TargetingFrame\\UI-StatusBar" },
}
Chrome.StyleByID = {}
for _, st in ipairs(Chrome.Styles) do Chrome.StyleByID[st.id] = st end
-- This character's own corner of the saved variable, where its style and
-- theme live. Read live, as the theme always was: the client hands the
-- table over just before WickCore loads.
function Chrome:CharStore()
local sv = rawget(_G, "WickCoreDB")
if type(sv) ~= "table" then return nil end
sv.char = sv.char or {}
-- Before the client has the player's name, a loose table: saving the
-- style under "Unknown" would hand it to every character next time.
local P = Core.Profiles
if P and P.NameKnown and not P:NameKnown() then
Chrome._unnamed = Chrome._unnamed or {}
return Chrome._unnamed
end
local key = (P and P.CharKey and P:CharKey())
or ((UnitName and UnitName("player") or "Unknown") .. " - " .. (GetRealmName and GetRealmName() or "Realm"))
sv.char[key] = sv.char[key] or {}
return sv.char[key]
end
-- The look a character starts on. Forever starts on Wick Modern. TBC
-- Anniversary starts on Wick OG, the flat fel-green look every TBC addon
-- had before WickCore, so an update changes nothing a player did not ask
-- for; Modern is one click away in the options.
Chrome.DEFAULT_STYLE = (Core.Client and Core.Client.isTBC) and "og" or "modern"
-- The style in use, its whole description.
function Chrome:StyleDef()
-- A window drawn in one look whatever the character's (Wick's UI's
-- setup is always Wick OG) sets this for as long as it is building.
local force = self.forceStyle and self.StyleByID[self.forceStyle]
if force then return force end
local c = self:CharStore()
return self.StyleByID[c and c.style or self.DEFAULT_STYLE] or self.StyleByID[self.DEFAULT_STYLE] or self.StyleByID.modern
end
function Chrome:StyleID() return self:StyleDef().id end
-- The family: "modern" or "og". What the drawing code branches on.
function Chrome:Style() return self:StyleDef().family end
function Chrome:Modern() return self:Style() == "modern" end
-- The game's own art in place of ours (Classic).
function Chrome:Game() return self:StyleDef().game == true end
-- The pieces of one of the game's frame borders, by the names its
-- nine-slice layouts give them.
local PIECES = { "TopLeftCorner", "TopRightCorner", "BottomLeftCorner", "BottomRightCorner",
"TopEdge", "BottomEdge", "LeftEdge", "RightEdge" }
-- One of the game's own borders round f, from its layout of that name
-- (TooltipDefaultLayout unless told), so each client draws its own art.
-- The layout's middle is left out: the frame's own fill, in the palette,
-- is the middle. The layout's offsets stay: the game's art carries
-- clear space round its metal, and the offsets are what put the metal on
-- a window's edge (drawn flush, the metal sat a few pixels inside the
-- panel and the panel's own fill showed round it as a dark band). A
-- window's own fill is put away instead (AddBorder), so the frame is the
-- window's only edge. Drawn on a child frame, so nothing is written onto f.
-- Returns the child, or nil where the client has no such layout (the
-- offline stub), and the caller draws its flat border instead.
function Chrome:GameBorder(f, layoutName)
local NSU = rawget(_G, "NineSliceUtil")
local layout = NSU and NSU.GetLayout and NSU.GetLayout(layoutName or "TooltipDefaultLayout")
if type(layout) ~= "table" or not NSU.ApplyLayout then return nil end
local copy = {}
for k, v in pairs(layout) do if k ~= "Center" then copy[k] = v end end
local holder = CreateFrame("Frame", nil, f)
holder:SetAllPoints()
if not pcall(NSU.ApplyLayout, holder, copy) then
holder:Hide()
return nil
end
holder.pieces = {}
for _, name in ipairs(PIECES) do
local t = rawget(holder, name)
if t then holder.pieces[#holder.pieces + 1] = t end
end
return holder
end
-- A window of a product's own: one the player drags about the screen, at
-- least window-sized. In Classic it wears the game's window frame and
-- background (those of DefaultPanelTemplate, which the game makes its
-- plain windows from) rather than the tooltip border smaller panels get.
-- Asked when its border is drawn: a product sets a window movable and
-- sized before it borders it.
function Chrome:GameWindowish(f)
if not (f and f.IsMovable and f:IsMovable() == true) then return false end
if f.GetParent and f:GetParent() ~= UIParent then return false end
local w, h = f:GetSize()
return (w and h and w >= 150 and h >= 100) and true or false
end
-- The game window's background, under the frame: its rock, tiled, and the
-- streaks along the top, placed as DefaultPanelTemplate places them. Above
-- the product's own fill, below everything it puts on child frames.
function Chrome:GameWindowBackground(f)
local bg = f:CreateTexture(nil, "BACKGROUND", nil, 2)
bg:SetTexture("Interface\\FrameGeneral\\UI-Background-Rock", "REPEAT", "REPEAT")
if bg.SetHorizTile then bg:SetHorizTile(true); bg:SetVertTile(true) end
bg:SetPoint("TOPLEFT", f, "TOPLEFT", 6, -21)
bg:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -2, 2)
local streaks = f:CreateTexture(nil, "BACKGROUND", nil, 3)
if pcall(streaks.SetAtlas, streaks, "_UI-Frame-TopTileStreaks", true) then
streaks:SetPoint("TOPLEFT", f, "TOPLEFT", 6, -21)
streaks:SetPoint("TOPRIGHT", f, "TOPRIGHT", -2, -21)
else
streaks:Hide()
end
-- TBC Anniversary's window frame draws only the title bar's rim; its
-- band is a texture of its own (TitleBg), placed as DefaultPanelTemplate
-- places it. Forever's metal frame has the band in its own art.
if Core.Client and Core.Client.isTBC then
local band = f:CreateTexture(nil, "BACKGROUND", nil, 4)
band:SetTexture("Interface\\FrameGeneral\\_UI-Frame")
band:SetTexCoord(0, 1, 0.2890625, 0.421875)
band:SetPoint("TOPLEFT", f, "TOPLEFT", 7, -4)
band:SetPoint("TOPRIGHT", f, "TOPRIGHT", -24, -4)
band:SetHeight(17)
f.gameTitleBand = band
end
return bg
end
-- Inside one of the game's windows (ours, made in Classic): a panel there is
-- one of the game's insets, its marble and recessed edge, as the game's own
-- windows divide themselves, rather than a tooltip border.
function Chrome:InGameWindow(f)
local p = f and f.GetParent and f:GetParent()
while p and p ~= UIParent do
if rawget(p, "gameWindowBG") or rawget(p, "wickGame") then return true end
p = p.GetParent and p:GetParent()
end
return false
end
-- The inset's marble, tiled, behind the panel's own contents.
function Chrome:GameInsetBackground(f)
local bg = f:CreateTexture(nil, "BACKGROUND", nil, 2)
bg:SetTexture("Interface\\FrameGeneral\\UI-Background-Marble", "REPEAT", "REPEAT")
if bg.SetHorizTile then bg:SetHorizTile(true); bg:SetVertTile(true) end
bg:SetAllPoints(f)
return bg
end
-- The game's button templates draw their art for the template's own size
-- (an action button 45 on Forever and 36 on TBC Anniversary, an item
-- button 37), much of it at fixed sizes and offsets, so at any other size
-- the frame runs past the button. Each piece is scaled with the button
-- instead: its size and offsets, from what they were when the button was
-- made, so call this before the button is given its own size. Text and the
-- icon (which fills the button) are left alone.
function Chrome:FitGameArt(button)
local w0, h0 = button:GetSize()
if not (w0 and h0 and w0 > 0 and h0 > 0) or rawget(button, "wickGameArt") then return end
local pieces, seen = {}, {}
local icon = rawget(button, "icon") or rawget(button, "Icon")
local function take(r)
if not r or seen[r] or r == icon then return end
seen[r] = true
if r.GetObjectType and r:GetObjectType() == "FontString" then return end
local n = r.GetNumPoints and r:GetNumPoints() or 0
if n == 0 then return end
local pts = {}
for i = 1, n do pts[i] = { r:GetPoint(i) } end
local w, h = r:GetSize()
pieces[#pieces + 1] = { r = r, pts = pts, w = w, h = h }
end
for _, r in ipairs({ button:GetRegions() }) do take(r) end
take(rawget(button, "IconMask"))
for _, c in ipairs({ button:GetChildren() }) do take(c) end
local function fit()
local w, h = button:GetSize()
if not (w and h and w > 0 and h > 0) then return end
local kx, ky = w / w0, h / h0
for _, p in ipairs(pieces) do
local r = p.r
r:ClearAllPoints()
for _, pt in ipairs(p.pts) do r:SetPoint(pt[1], pt[2], pt[3], (pt[4] or 0) * kx, (pt[5] or 0) * ky) end
-- One point: the piece has a size of its own, scaled too. Two
-- or more stretch it, and the size follows.
if #p.pts == 1 and p.w and p.w > 0 then r:SetSize(p.w * kx, p.h * ky) end
end
end
button.wickGameArt = fit
button:HookScript("OnSizeChanged", fit)
return fit
end
-- ============================================================
-- Classic: the game's art for a product's own controls
-- ============================================================
-- For products that draw their own controls (a bag's options, a list's
-- fields): the game's art laid on them in Classic, the same on every
-- client under the same names.
-- An input box's ends and stretched middle (InputBoxTemplate's art). The
-- left end reaches inset past the frame, as the game's does.
local INPUT = "Interface\\Common\\Common-Input-Border"
function Chrome:GameInputArt(f, inset)
inset = inset or 0
local l = f:CreateTexture(nil, "BACKGROUND")
l:SetTexture(INPUT)
l:SetTexCoord(0, 0.0625, 0, 0.625)
l:SetPoint("TOPLEFT", -inset, 0)
l:SetPoint("BOTTOMLEFT", -inset, 0)
l:SetWidth(8)
local r = f:CreateTexture(nil, "BACKGROUND")
r:SetTexture(INPUT)
r:SetTexCoord(0.9375, 1, 0, 0.625)
r:SetPoint("TOPRIGHT", 0, 0)
r:SetPoint("BOTTOMRIGHT", 0, 0)
r:SetWidth(8)
local m = f:CreateTexture(nil, "BACKGROUND")
m:SetTexture(INPUT)
m:SetTexCoord(0.0625, 0.9375, 0, 0.625)
m:SetPoint("TOPLEFT", l, "TOPRIGHT")
m:SetPoint("BOTTOMRIGHT", r, "BOTTOMLEFT")
return l, m, r
end
-- A check box's box, tick and hover glow, filling box. Returns the tick
-- (shown when checked) and the glow (shown on hover).
function Chrome:GameCheckArt(box)
local up = box:CreateTexture(nil, "BACKGROUND")
up:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
up:SetAllPoints()
local tick = box:CreateTexture(nil, "ARTWORK")
tick:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
tick:SetAllPoints()
local glow = box:CreateTexture(nil, "OVERLAY")
glow:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
glow:SetBlendMode("ADD")
glow:SetAllPoints()
glow:Hide()
return tick, glow
end
-- A slider's groove (on a frame made with BackdropTemplate) and its knob.
Chrome.GAME_SLIDER_BACKDROP = {
bgFile = "Interface\\Buttons\\UI-SliderBar-Background",
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
tile = true, tileSize = 8, edgeSize = 8,
insets = { left = 3, right = 3, top = 6, bottom = 6 },
}
Chrome.GAME_SLIDER_KNOB = "Interface\\Buttons\\UI-SliderBar-Button-Horizontal"
-- The game's close button, in place of a product's own. It is laid over
-- the old one, which stays where it was (other controls are anchored to
-- it) but unseen, and a click on the new one is the old one's click.
function Chrome:GameClose(old, window)
if not (old and window) then return old end
local ok, made = pcall(CreateFrame, "Button", nil, window, "UIPanelCloseButtonDefaultAnchors")
if not (ok and made) then return old end
if made:GetNumPoints() == 0 then made:SetPoint("TOPRIGHT", window, "TOPRIGHT", 4, 4) end
made:SetFrameLevel(math.max(window:GetFrameLevel(), old:GetFrameLevel()) + 4)
made:SetScript("OnClick", function(_, ...)
local fn = old:GetScript("OnClick")
if fn then fn(old, ...) end
end)
old:SetAlpha(0)
old:EnableMouse(false)
return made
end
-- Too small for one of the game's borders, whose corners alone are
-- several pixels: a tile, or a strip. Not sized yet counts as a panel.
function Chrome:GameSmall(f)
if not (f and f.GetSize) then return false end
local w, h = f:GetSize()
if not (w and h and w > 0 and h > 0) then return false end
return (w <= 64 and h <= 64) or w < 18 or h < 18
end
-- A frame's own full-size fill in a palette colour (the background a
-- product painted with Chrome:Texture), made see-through.
function Chrome:HideFill(f)
local fw, fh = f:GetSize()
for _, r in ipairs({ f:GetRegions() }) do
local info = r.GetObjectType and r:GetObjectType() == "Texture" and tinted[r]
if info and r:GetDrawLayer() == "BACKGROUND" then
local w, h = r:GetSize()
local full = (r:GetNumPoints() >= 2) or (fw and w and fh and h and w >= fw - 2 and h >= fh - 2)
if full then r:SetAlpha(0) end
end
end
end
-- A game border recoloured: white is the art as the game draws it.
function Chrome:TintGameBorder(holder, r, g, b, a)
for _, t in ipairs(holder and holder.pieces or {}) do t:SetVertexColor(r, g, b, a or 1) end
end
-- Something products can recolour, as they recolour a border's edges,
-- that changes nothing: the game's own frames have no line to colour.
local NOOP = setmetatable({}, { __index = function() return function() end end })
local function noBorder() return { top = NOOP, bottom = NOOP, left = NOOP, right = NOOP } end
function Chrome:SetStyle(style)
local c = self:CharStore()
if not c then return end
c.style = self.StyleByID[style] and style or "modern"
-- A look comes with its own colours; they are chosen with it, and can
-- be changed afterwards like any theme. Wick Modern and Wick OG draw in
-- the Wick palette and keep whatever theme is in use, unless that is
-- another look's own palette: then the Wick theme from before that look
-- comes back, or the client's default (Fel on TBC, the class on Forever).
local st = self.StyleByID[style]
if st and st.palette and self.SetTheme and self.ThemeByID then
local current = self.ThemeByID[self:ResolveTheme(self:ThemeSetting())]
if st.palette == "auto" then
if current and current.look then self:SetTheme(c.wickTheme or self.DEFAULT_SETTING) end
elseif self.ThemeByID[st.palette] then
if not (current and current.look) then c.wickTheme = self:ThemeSetting() end
self:SetTheme(st.palette)
end
end
if Core.Store then Core.Store:Dirty() end
end
-- Media for the style in use: its own textures where it has them, the
-- base set where it does not. Read at draw time, never cached.
Chrome.Media = setmetatable({}, { __index = function(_, k)
local own = Chrome:StyleDef().media
if own and own[k] ~= nil then return own[k] end
return BASE_MEDIA[k]
end })
function Chrome:Glyph(name) return TEX .. "glyph-" .. name .. ".png" end
-- The panel and ring textures for a frame: small ones (slots, buttons,
-- tiles) take the look's tile shape where it has one, since a square icon
-- inside a shaped tile shows past the shape. A frame not sized yet counts
-- as a panel.
function Chrome:IsTile(f)
local w, h = f and f.GetSize and f:GetSize()
return w and h and w > 0 and h > 0 and w <= 64 and h <= 64
end
function Chrome:PanelTex(f)
return (self:IsTile(f) and self.Media.tile) or self.Media.rounded
end
function Chrome:RingTex(f)
return (self:IsTile(f) and self.Media.tileRing) or self.Media.ring
end
-- An icon of the suite's own in a look with an icon tab: its top edge is
-- moved down to the tab, its sides and bottom kept, so the whole picture is
-- drawn a little shorter below the tile's shaped top. Returns true when it
-- did, and the icon's mask then covers all of it.
function Chrome:TabIcon(icon, tile)
local tab = self:Modern() and self:StyleDef().iconTab
if not (tab and icon and tile and icon.GetNumPoints) then return false end
local l, r, b = 0, 0, 0
for i = 1, icon:GetNumPoints() do
local p, _, _, x, y = icon:GetPoint(i)
if p == "TOPLEFT" then l = x or 0
elseif p == "BOTTOMRIGHT" then r, b = x or 0, y or 0 end
end
icon:ClearAllPoints()
icon:SetPoint("TOPLEFT", tile, "TOPLEFT", l, -tab)
icon:SetPoint("BOTTOMRIGHT", tile, "BOTTOMRIGHT", r, b)
return true
end
-- Where an icon's mask sits: over the whole icon, or, in a look with an
-- icon tab, over all of it but the top few pixels. This is for icons that
-- are Blizzard's, which are not moved: the picture is cut, keeping its
-- width, and the tile's shaped top shows above it. The suite's own icons
-- are drawn shorter instead (TabIcon). inset: how far the icon already
-- sits inside its tile's top.
function Chrome:PlaceIconMask(m, icon, inset)
m:ClearAllPoints()
local tab = self:Modern() and self:StyleDef().iconTab
if tab then
m:SetPoint("TOPLEFT", icon, "TOPLEFT", 0, -math.max(0, tab - (inset or 0)))
m:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 0, 0)
else
m:SetAllPoints(icon)
end
end
Chrome.FONT_OG = FRIZ
-- The font every Chrome text uses. Chrome.FONT is kept current for
-- products that read it directly.
function Chrome:Font() return self:StyleDef().font or PT_SANS end
function Chrome:HeadingFont() local st = self:StyleDef(); return st.headingFont or st.font or PT_SANS end
-- How solid a panel of the family's alpha is in this style.
function Chrome:GlassAlpha(a) return math.min(1, (a or 1) * (self:StyleDef().glass or 1)) end
-- The corner marks an og-family panel wears.
function Chrome:Corners() return self:StyleDef().corners or "none" end
local function slice(tex, m)
if tex.SetTextureSliceMargins then
tex:SetTextureSliceMargins(m, m, m, m)
if tex.SetTextureSliceMode then pcall(tex.SetTextureSliceMode, tex, 0) end
end
end
-- A rounded glass texture in a palette colour, re-tinted with the theme.
function Chrome:Glass(parent, layer, color, alpha, sub)
local t = parent:CreateTexture(nil, layer or "BACKGROUND", nil, sub)
t:SetTexture(self:PanelTex(parent))
slice(t, self.Media.slice)
color = color or C.void
local a = self:GlassAlpha(alpha or color[4] or 1)
t:SetVertexColor(color[1], color[2], color[3], a)
Chrome:Register(t, color, "vertex", a)
return t
end
-- The soft lift under a modern panel, reaching past its edges.
function Chrome:Lift(f)
local s = f:CreateTexture(nil, "BACKGROUND", nil, -8)
s:SetTexture(self.Media.shadow)
slice(s, 28)
s:SetPoint("TOPLEFT", f, "TOPLEFT", -12, 10)
s:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 12, -14)
local lift = self:StyleDef().lift or {}
local c = lift.token and C[lift.token] or { 0, 0, 0 }
s:SetVertexColor(c[1], c[2], c[3], lift.alpha or 0.6)
if lift.token then Chrome:Register(s, c, "vertex", lift.alpha or 0.6) end
return s
end
-- An item button in the modern style, drawn like the action buttons: the
-- icon trimmed to the rounded shape on a glass tile, and the quality shown
-- as a rounded ring in its colour. Returns the function that sets that
-- ring (colour table in, the resting border colour puts it away), or nil
-- in the original style, when the product keeps its own square edges.
function Chrome:ModernSlot(b, icon)
if not self:Modern() or not b then return nil end
if b.ItemSlotBackground then b.ItemSlotBackground:SetAlpha(0) end
local bg = b:CreateTexture(nil, "BACKGROUND", nil, -7)
bg:SetTexture(self.Media.tile or self.Media.rounded)
slice(bg, self.Media.slice)
bg:SetAllPoints()
-- The shadow colour, a step off the panel, so an empty slot (the Free
-- tile) still reads as a tile.
bg:SetVertexColor(C.shadow[1], C.shadow[2], C.shadow[3], 0.95)
Chrome:Register(bg, C.shadow, "vertex", 0.95)
if icon and icon.AddMaskTexture and b.CreateMaskTexture then
local m = b:CreateMaskTexture()
m:SetTexture(self.Media.iconmask, "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
self:TabIcon(icon, b)
m:SetAllPoints(icon)
icon:AddMaskTexture(m)
end
local ring = b:CreateTexture(nil, "OVERLAY", nil, 1)
ring:SetTexture(self.Media.tileRing or self.Media.ring)
slice(ring, self.Media.slice)
ring:SetAllPoints()
ring:Hide()
local rest = C.border
local ringRest = self:StyleDef().ringRest
return function(c)
-- The resting colour (the theme's border, or the brand purple a
-- product passes for "no quality") puts the ring away.
local function is(x) return math.abs(c[1] - x[1]) < 0.03 and math.abs(c[2] - x[2]) < 0.03 and math.abs(c[3] - x[3]) < 0.03 end
if not c or is(rest) or is({ 0.20, 0.18, 0.34 }) then
if ringRest then
local rc = C[ringRest.token] or C.border
ring:SetVertexColor(rc[1], rc[2], rc[3], ringRest.alpha)
ring:Show()
else
ring:Hide()
end
else
ring:SetVertexColor(c[1], c[2], c[3], c[4] or 1)
ring:Show()
end
end
end
-- The rounded ring a modern control shows for hover or selection.
function Chrome:Ring(parent, color, layer)
local r = parent:CreateTexture(nil, layer or "BORDER", nil, 2)
r:SetTexture(self:RingTex(parent))
slice(r, self.Media.slice)
r:SetAllPoints()
color = color or C.fel
r:SetVertexColor(color[1], color[2], color[3], 1)
Chrome:Register(r, color, "vertex", 1)
r:Hide()
return r
end
-- ============================================================
-- Primitives
-- ============================================================
function Chrome:Texture(parent, layer, color)
local t = parent:CreateTexture(nil, layer or "BACKGROUND")
if color then
t:SetColorTexture(color[1], color[2], color[3], color[4] or 1)
-- Through Register, so the rule about a derived colour
-- keeping its own alpha is written once.
Chrome:Register(t, color, "texture")
end
return t
end
-- A font string in the style's font. PT Sans Narrow runs small next to
-- Friz, so the modern style sets it a size or two up, with a firm shadow,
-- to read as clearly as Wick's UI. Products that make their own font
-- strings call this too.
function Chrome:SetFont(fs, size, flags, heading)
size = size or 12
local st = self:StyleDef()
if st.bump then size = st.bump(size) end
if heading and st.headingBump then size = size + st.headingBump end
fs:SetFont(heading and self:HeadingFont() or self:Font(), size, flags or "")
if st.textShadow then
fs:SetShadowOffset(1, -1)
fs:SetShadowColor(0, 0, 0, 1)
end
end
function Chrome:Text(parent, size, color, flags, heading)
local fs = parent:CreateFontString(nil, "OVERLAY")
self:SetFont(fs, size or 12, flags, heading)
color = color or C.text
fs:SetTextColor(color[1], color[2], color[3], color[4] or 1)
Chrome:Register(fs, color, "text")
return fs
end
-- Four 1px muted-purple edges.
--
-- In the modern style a border is not drawn. The frame's flat background
-- (a full-size texture Chrome painted with a palette colour) becomes rounded
-- glass, a panel-sized frame gets the soft lift, and the four edges become
-- stand-ins for one rounded ring: a product that recolours its border for
-- hover or selection (for _, t in pairs(f.border) do t:SetColorTexture(...))
-- lights the ring in that colour, and setting it back to the border colour
-- puts the ring away. So every product built on these primitives takes the
-- modern look without changing a line.
local function near(a, b) return math.abs(a - b) < 0.02 end
local function roundify(f)
local fw, fh = f:GetSize()
for _, r in ipairs({ f:GetRegions() }) do
local info = r.GetObjectType and r:GetObjectType() == "Texture" and tinted[r]
if info and info.kind == "texture" and r:GetDrawLayer() == "BACKGROUND" then
local w, h = r:GetSize()
local full = (r:GetNumPoints() >= 2) or (fw and w and fh and h and w >= fw - 2 and h >= fh - 2)
if full then
local c = C[info.token]
local a = info.alpha or (c and c[4]) or 1
r:SetTexture(Chrome:PanelTex(f))
a = Chrome:GlassAlpha(a)
if r.SetTextureSliceMargins then
local m = Chrome.Media.slice
r:SetTextureSliceMargins(m, m, m, m)
if r.SetTextureSliceMode then pcall(r.SetTextureSliceMode, r, 0) end
end
if c then r:SetVertexColor(c[1], c[2], c[3], a) end
info.kind = "vertex"
info.alpha = a
end
end
end
end
local function ringProxy(ring, restColor)
local proxy = {}
local ringRest = Chrome:StyleDef().ringRest
local function paint(_, r, g, b, a)
if near(r, restColor[1]) and near(g, restColor[2]) and near(b, restColor[3]) then
if ringRest then
local rc = C[ringRest.token] or C.border
ring:SetVertexColor(rc[1], rc[2], rc[3], ringRest.alpha)
ring:Show()
else
ring:Hide()
end
else
ring:SetVertexColor(r, g, b, a or 1)
ring:Show()
end
end
proxy.SetColorTexture = paint
proxy.SetVertexColor = paint
return setmetatable(proxy, { __index = function() return function() end end })
end
function Chrome:AddBorder(f, color)
color = color or C.border
if self:Modern() then
roundify(f)
local w, h = f:GetSize()
if not f.lift and w and h and w >= 150 and h >= 100 then f.lift = self:Lift(f) end
local ring = f:CreateTexture(nil, "BORDER", nil, 2)
ring:SetTexture(self:RingTex(f))
local m = self.Media.slice
if ring.SetTextureSliceMargins then ring:SetTextureSliceMargins(m, m, m, m) end
ring:SetAllPoints()
ring:Hide()
f.ring = f.ring or ring
-- A border asked for in a colour other than the resting one is a
-- signal (a bank section, a highlighted row): the ring shows it.
local rest = C.border
local proxy = ringProxy(ring, rest)
-- Painted once now: a signal colour lights the ring, the resting
-- one shows the style's ring at rest (or nothing).
proxy:SetColorTexture(color[1], color[2], color[3], color[4] or 1)
f.border = { top = proxy, bottom = proxy, left = proxy, right = proxy }
return
end
-- Classic: the game's tooltip border round anything bigger than a tile,
-- in the art's own colour at rest. A product that colours its border
-- for hover or selection tints the art instead.
if self:Game() and not self:GameSmall(f) then
local window = self:GameWindowish(f)
local inset = not window and self:InGameWindow(f)
local holder = self:GameBorder(f, (window and "ButtonFrameTemplateNoPortrait") or (inset and "InsetFrameTemplate") or nil)
if holder and window then
-- Under what the product puts on its own child frames (its
-- header, its sections), over its fill.
holder:SetFrameLevel(f:GetFrameLevel())
f.gameWindowBG = self:GameWindowBackground(f)
-- The window's own fill goes: the game's frame and rock are
-- its edge and its ground, and a fill showing past them would
-- draw a second edge.
self:HideFill(f)
elseif holder and inset then
holder:SetFrameLevel(f:GetFrameLevel())
f.gameInsetBG = self:GameInsetBackground(f)
end
if holder then
local function paint(_, r, g, b, a)
local c = C.border
if near(r, c[1]) and near(g, c[2]) and near(b, c[3]) then r, g, b, a = 1, 1, 1, 1 end
Chrome:TintGameBorder(holder, r, g, b, a)
end
local proxy = setmetatable({ SetColorTexture = paint, SetVertexColor = paint },
{ __index = function() return function() end end })
proxy:SetColorTexture(color[1], color[2], color[3], color[4] or 1)
f.gameBorder = holder
f.border = { top = proxy, bottom = proxy, left = proxy, right = proxy }
return
end
end
local px = self:StyleDef().borderPx or 1
local top = self:Texture(f, "BORDER", color); top:SetPoint("TOPLEFT"); top:SetPoint("TOPRIGHT"); top:SetHeight(px)
local bot = self:Texture(f, "BORDER", color); bot:SetPoint("BOTTOMLEFT"); bot:SetPoint("BOTTOMRIGHT"); bot:SetHeight(px)
local left = self:Texture(f, "BORDER", color); left:SetPoint("TOPLEFT"); left:SetPoint("BOTTOMLEFT"); left:SetWidth(px)
local right = self:Texture(f, "BORDER", color); right:SetPoint("TOPRIGHT"); right:SetPoint("BOTTOMRIGHT"); right:SetWidth(px)
f.border = { top = top, bottom = bot, left = left, right = right }
end
-- Fel-green L-brackets. If a resizeButton is passed the BOTTOMRIGHT bracket
-- is parented to it so it doubles as the grip.
function Chrome:AddBrackets(parent, resizeButton, color)
color = color or C.fel
local B = self.BRACKET
parent.brackets = {}
-- No brackets in the modern style: the table is kept for products that
-- walk it, and stays empty.
if self:Modern() and not parent.wickPanel then return end
local corners = self:Modern() and "brackets" or self:Corners()
if corners == "none" then return end
for _, point in ipairs({ "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT" }) do
local host = (point == "BOTTOMRIGHT" and resizeButton) or parent
local h = self:Texture(host, "OVERLAY", color)
h:SetPoint(point, host, point, 0, 0)
h:SetSize(B, 2)
local v = self:Texture(host, "OVERLAY", color)
v:SetPoint(point, host, point, 0, 0)
v:SetSize(2, B)
parent.brackets[point] = { h, v }
end
end
-- Two-tone title: "Wick's" in text color, the noun in fel green.
function Chrome:TitleMarkup(title)