wuira 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -18
- package/cli/wuira.mjs +67 -0
- package/config/tsconfig.json +25 -0
- package/lib/Console.d.ts +5 -0
- package/lib/Console.lua +20 -0
- package/lib/console-polyfill.d.ts +9 -0
- package/lib/console-polyfill.lua +39 -0
- package/lib/datasources/DataSource.d.ts +17 -0
- package/lib/datasources/DataSource.lua +199 -0
- package/lib/datasources/NativeNumberSource.d.ts +7 -0
- package/lib/datasources/NativeNumberSource.lua +50 -0
- package/lib/datasources/ProgressSource.d.ts +23 -0
- package/lib/datasources/ProgressSource.lua +79 -0
- package/lib/datasources/Resources.d.ts +27 -0
- package/lib/datasources/Resources.lua +102 -0
- package/lib/datasources/index.d.ts +4 -0
- package/lib/datasources/index.lua +18 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.lua +11 -0
- package/{src → lib}/platform/forever.d.ts +17 -17
- package/{src/types/Anchor.ts → lib/types/Anchor.d.ts} +6 -7
- package/lib/types/Anchor.lua +8 -0
- package/lib/types/Color.lua +3 -0
- package/lib/types/Position.d.ts +21 -0
- package/lib/types/Position.lua +3 -0
- package/lib/types/Spacing.d.ts +14 -0
- package/lib/types/Spacing.lua +20 -0
- package/{src/types/Texture.ts → lib/types/Texture.d.ts} +0 -2
- package/lib/types/Texture.lua +3 -0
- package/{src/ui/Cell.tsx → lib/ui/Cell.d.ts} +3 -8
- package/lib/ui/Cell.lua +8 -0
- package/{src/ui/FlowLayout.tsx → lib/ui/FlowLayout.d.ts} +3 -8
- package/lib/ui/FlowLayout.lua +8 -0
- package/{src/ui/GridLayout.tsx → lib/ui/GridLayout.d.ts} +3 -8
- package/lib/ui/GridLayout.lua +8 -0
- package/{src/ui/Layout.ts → lib/ui/Layout.d.ts} +4 -3
- package/lib/ui/Layout.lua +3 -0
- package/{src/ui/ProgressBar.tsx → lib/ui/ProgressBar.d.ts} +4 -7
- package/lib/ui/ProgressBar.lua +8 -0
- package/lib/ui/decorations/Background.d.ts +10 -0
- package/lib/ui/decorations/Background.lua +76 -0
- package/lib/ui/decorations/Border.d.ts +18 -0
- package/lib/ui/decorations/Border.lua +188 -0
- package/lib/ui/decorations/Decoration.d.ts +13 -0
- package/lib/ui/decorations/Decoration.lua +81 -0
- package/{src/ui/decorations/Decorations.ts → lib/ui/decorations/Decorations.d.ts} +0 -1
- package/lib/ui/decorations/Decorations.lua +3 -0
- package/lib/ui/decorations/Glow.d.ts +14 -0
- package/lib/ui/decorations/Glow.lua +63 -0
- package/lib/ui/decorations/backgroundTextures.d.ts +25 -0
- package/lib/ui/decorations/backgroundTextures.lua +10 -0
- package/lib/ui/decorations/borderTextures.d.ts +41 -0
- package/lib/ui/decorations/borderTextures.lua +12 -0
- package/lib/ui/decorations/statusBarTextures.d.ts +12 -0
- package/lib/ui/decorations/statusBarTextures.lua +9 -0
- package/lib/ui/index.d.ts +11 -0
- package/lib/ui/index.lua +35 -0
- package/lib/ui/layout/calculateFlow.d.ts +12 -0
- package/lib/ui/layout/calculateFlow.lua +36 -0
- package/lib/ui/layout/calculateGrid.d.ts +9 -0
- package/lib/ui/layout/calculateGrid.lua +47 -0
- package/lib/ui/layout/measureLayout.d.ts +19 -0
- package/lib/ui/layout/measureLayout.lua +215 -0
- package/lib/ui/mount.d.ts +5 -0
- package/lib/ui/mount.lua +486 -0
- package/lib/ui/primitives/jsx.d.ts +38 -0
- package/lib/ui/primitives/jsx.lua +66 -0
- package/lib/wuira.d.ts +8 -0
- package/lib/wuira.lua +30 -0
- package/package.json +55 -5
- package/templates/addon/README.md +13 -0
- package/templates/addon/addon.toc.in +4 -0
- package/templates/addon/gitignore +3 -0
- package/templates/addon/package.json +11 -0
- package/templates/addon/src/index.tsx +9 -0
- package/templates/addon/tsconfig.json +8 -0
- package/templates/addon/wuira.config.mjs +4 -0
- package/tools/build-library.mjs +15 -0
- package/tools/module-loader.lua +3 -3
- package/tools/{wow-modules.cjs → wow-modules.mjs} +27 -11
- package/tsconfig.json +1 -1
- package/tsconfig.lib.json +8 -0
- package/addon.toc.in +0 -4
- package/cli/uira.cjs +0 -55
- package/src/Console.ts +0 -10
- package/src/UIra.ts +0 -18
- package/src/console-polyfill.ts +0 -25
- package/src/datasources/DataSource.ts +0 -42
- package/src/datasources/NativeNumberSource.ts +0 -7
- package/src/datasources/ProgressSource.ts +0 -46
- package/src/datasources/Resources.ts +0 -41
- package/src/examples/ProgressBars.tsx +0 -24
- package/src/index.tsx +0 -3
- package/src/playground/PlayerFrame.ts +0 -271
- package/src/types/Position.ts +0 -21
- package/src/types/Spacing.ts +0 -14
- package/src/ui/decorations/Background.ts +0 -29
- package/src/ui/decorations/Border.ts +0 -71
- package/src/ui/decorations/Decoration.ts +0 -59
- package/src/ui/decorations/Glow.ts +0 -25
- package/src/ui/decorations/backgroundTextures.ts +0 -10
- package/src/ui/decorations/borderTextures.ts +0 -13
- package/src/ui/decorations/statusBarTextures.ts +0 -11
- package/src/ui/layout/calculateFlow.ts +0 -33
- package/src/ui/layout/calculateGrid.ts +0 -22
- package/src/ui/layout/measureLayout.ts +0 -87
- package/src/ui/mount.ts +0 -138
- package/src/ui/primitives/jsx.ts +0 -75
- /package/{src/types/Color.ts → lib/types/Color.d.ts} +0 -0
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
// Experimental adapter for the client's existing player frame, not a replacement.
|
|
2
|
-
import { Console } from "../Console";
|
|
3
|
-
type NativeReturns<T extends unknown[]> = T & { readonly __tstlMultiReturn: unknown };
|
|
4
|
-
|
|
5
|
-
interface ExistingBar extends UiraNativeStatusBar {
|
|
6
|
-
SetStatusBarTexture(path: string | number | undefined): void;
|
|
7
|
-
GetStatusBarColor(): NativeReturns<[number, number, number, number]>;
|
|
8
|
-
GetStatusBarTexture(): ExistingTexture;
|
|
9
|
-
GetName(): string | undefined;
|
|
10
|
-
}
|
|
11
|
-
interface ExistingTexture extends UiraNativeTexture {
|
|
12
|
-
GetTexture(): string | number | undefined;
|
|
13
|
-
}
|
|
14
|
-
interface Artwork {
|
|
15
|
-
GetAlpha(): number;
|
|
16
|
-
SetAlpha(alpha: number): void;
|
|
17
|
-
}
|
|
18
|
-
interface StatusArtwork extends Artwork {
|
|
19
|
-
GetVertexColor(): NativeReturns<[number, number, number, number]>;
|
|
20
|
-
SetVertexColor(r: number, g: number, b: number, a: number): void;
|
|
21
|
-
}
|
|
22
|
-
interface ExistingPlayer extends UiraNativeFrame {
|
|
23
|
-
portrait?: Artwork;
|
|
24
|
-
Portrait?: Artwork;
|
|
25
|
-
PlayerFrameContainer?: { FrameTexture?: Artwork; Portrait?: Artwork };
|
|
26
|
-
healthBar?: ExistingBar;
|
|
27
|
-
healthbar?: ExistingBar;
|
|
28
|
-
HealthBar?: ExistingBar;
|
|
29
|
-
manabar?: ExistingBar;
|
|
30
|
-
ManaBar?: ExistingBar;
|
|
31
|
-
HealthBarsContainer?: { healthBar?: ExistingBar; HealthBar?: ExistingBar };
|
|
32
|
-
PlayerFrameContent?: { PlayerFrameContentMain?: {
|
|
33
|
-
HealthBarsContainer?: { healthBar?: ExistingBar; HealthBar?: ExistingBar };
|
|
34
|
-
ManaBarArea?: { ManaBar?: ExistingBar };
|
|
35
|
-
} };
|
|
36
|
-
GetScale(): number;
|
|
37
|
-
SetScale(scale: number): void;
|
|
38
|
-
GetAttribute(name: string): unknown;
|
|
39
|
-
SetAttribute(name: string, value: unknown): void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
declare const PlayerFrame: ExistingPlayer | undefined;
|
|
43
|
-
declare const PlayerFrameHealthBar: ExistingBar | undefined;
|
|
44
|
-
declare const PlayerFrameManaBar: ExistingBar | undefined;
|
|
45
|
-
declare const _G: Record<string, unknown>;
|
|
46
|
-
declare const SlashCmdList: Record<string, (this: void, message: string) => void>;
|
|
47
|
-
declare function InCombatLockdown(this: void): boolean;
|
|
48
|
-
declare function tonumber(this: void, value: string): number | undefined;
|
|
49
|
-
declare function SetPortraitTexture(this: void, texture: UiraNativeTexture, unit: string, noMask?: boolean): void;
|
|
50
|
-
|
|
51
|
-
interface BarSnapshot {
|
|
52
|
-
bar: ExistingBar;
|
|
53
|
-
texture: string | number | undefined;
|
|
54
|
-
color: [number, number, number, number];
|
|
55
|
-
width: number;
|
|
56
|
-
height: number;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const bindings = ["type1", "spell1", "unit1"];
|
|
60
|
-
|
|
61
|
-
export class PlayerFramePlayground {
|
|
62
|
-
private bars: BarSnapshot[] = [];
|
|
63
|
-
private originalScale?: number;
|
|
64
|
-
private originalBindings?: Record<string, unknown>;
|
|
65
|
-
private owner?: ExistingPlayer;
|
|
66
|
-
private artwork: { region: Artwork; alpha: number }[] = [];
|
|
67
|
-
private squarePortrait?: UiraNativeTexture;
|
|
68
|
-
private portraitEvents?: UiraNativeFrame;
|
|
69
|
-
private statusArtwork: { region: StatusArtwork; color: [number, number, number, number] }[] = [];
|
|
70
|
-
|
|
71
|
-
private HideStatusArt(): void {
|
|
72
|
-
for (const name of ["PlayerStatusTexture", "PlayerFrameFlash"]) {
|
|
73
|
-
const region = _G[name] as StatusArtwork | undefined;
|
|
74
|
-
if (region === undefined) continue;
|
|
75
|
-
if (!this.statusArtwork.some(saved => saved.region === region)) {
|
|
76
|
-
const [r, g, b, a] = region.GetVertexColor();
|
|
77
|
-
this.statusArtwork.push({ region, color: [r, g, b, a] });
|
|
78
|
-
}
|
|
79
|
-
// Status animations update region alpha; vertex alpha remains independent.
|
|
80
|
-
region.SetVertexColor(1, 1, 1, 0);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
public Portrait(): void {
|
|
85
|
-
const player = this.Capture();
|
|
86
|
-
this.HideStatusArt();
|
|
87
|
-
this.HideArt(_G.PlayerFrameTexture as Artwork | undefined);
|
|
88
|
-
this.HideArt(_G.PlayerPortrait as Artwork | undefined);
|
|
89
|
-
this.HideArt(player.portrait ?? player.Portrait);
|
|
90
|
-
this.HideArt(player.PlayerFrameContainer?.FrameTexture);
|
|
91
|
-
this.HideArt(player.PlayerFrameContainer?.Portrait);
|
|
92
|
-
if (this.squarePortrait === undefined) {
|
|
93
|
-
this.squarePortrait = player.CreateTexture(undefined, "OVERLAY");
|
|
94
|
-
this.squarePortrait.SetSize(64, 64);
|
|
95
|
-
this.squarePortrait.SetPoint("TOPLEFT", player, "TOPLEFT", 18, -16);
|
|
96
|
-
}
|
|
97
|
-
const texture = this.squarePortrait;
|
|
98
|
-
const refresh = () => {
|
|
99
|
-
// The client's portrait API supports an unmasked texture; no secure handlers are replaced.
|
|
100
|
-
SetPortraitTexture(texture, "player", true);
|
|
101
|
-
texture.SetTexCoord(0, 1, 0, 1);
|
|
102
|
-
};
|
|
103
|
-
refresh();
|
|
104
|
-
texture.Show();
|
|
105
|
-
if (this.portraitEvents === undefined) {
|
|
106
|
-
this.portraitEvents = CreateFrame("Frame", undefined, player);
|
|
107
|
-
}
|
|
108
|
-
this.portraitEvents.RegisterEvent("UNIT_PORTRAIT_UPDATE");
|
|
109
|
-
this.portraitEvents.RegisterEvent("PORTRAITS_UPDATED");
|
|
110
|
-
this.portraitEvents.SetScript("OnEvent", (_frame, event, unit) => {
|
|
111
|
-
if (event === "PORTRAITS_UPDATED" || unit === "player") refresh();
|
|
112
|
-
});
|
|
113
|
-
Console.Echo("square player portrait enabled (64x64); /uira restore reverts it");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private HideArt(region: Artwork | undefined): void {
|
|
117
|
-
if (region === undefined) return;
|
|
118
|
-
if (!this.artwork.some(saved => saved.region === region)) {
|
|
119
|
-
this.artwork.push({ region, alpha: region.GetAlpha() });
|
|
120
|
-
}
|
|
121
|
-
region.SetAlpha(0);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
private Player(): ExistingPlayer {
|
|
125
|
-
if (InCombatLockdown()) error("Uira playground: run this command outside combat");
|
|
126
|
-
if (PlayerFrame === undefined) error("Uira playground: PlayerFrame is not available");
|
|
127
|
-
if (this.owner !== undefined && this.owner !== PlayerFrame) error("PlayerFrame changed; reload before continuing");
|
|
128
|
-
return PlayerFrame;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private FindBars(player: ExistingPlayer): { hp?: ExistingBar; mana?: ExistingBar } {
|
|
132
|
-
const main = player.PlayerFrameContent?.PlayerFrameContentMain;
|
|
133
|
-
return {
|
|
134
|
-
hp: player.healthBar ?? player.healthbar ?? player.HealthBar
|
|
135
|
-
?? player.HealthBarsContainer?.healthBar ?? player.HealthBarsContainer?.HealthBar
|
|
136
|
-
?? main?.HealthBarsContainer?.healthBar ?? main?.HealthBarsContainer?.HealthBar
|
|
137
|
-
?? PlayerFrameHealthBar,
|
|
138
|
-
mana: player.manabar ?? player.ManaBar ?? main?.ManaBarArea?.ManaBar ?? PlayerFrameManaBar,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private Capture(): ExistingPlayer {
|
|
143
|
-
const player = this.Player();
|
|
144
|
-
if (this.originalScale !== undefined) return player;
|
|
145
|
-
const found = this.FindBars(player);
|
|
146
|
-
if (found.hp === undefined || found.mana === undefined) {
|
|
147
|
-
error("Uira playground: HP/mana bars not found. Run /uira info and report the output");
|
|
148
|
-
}
|
|
149
|
-
this.owner = player;
|
|
150
|
-
this.originalScale = player.GetScale();
|
|
151
|
-
for (const bar of [found.hp, found.mana]) {
|
|
152
|
-
const [r, g, b, a] = bar.GetStatusBarColor();
|
|
153
|
-
this.bars.push({ bar, texture: bar.GetStatusBarTexture().GetTexture(), color: [r, g, b, a],
|
|
154
|
-
width: bar.GetWidth(), height: bar.GetHeight() });
|
|
155
|
-
}
|
|
156
|
-
return player;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public Info(): void {
|
|
160
|
-
const player = this.Player();
|
|
161
|
-
const found = this.FindBars(player);
|
|
162
|
-
Console.Echo(`PlayerFrame present; HP=${found.hp?.GetName() ?? (found.hp === undefined ? "missing" : "unnamed")}; Mana=${found.mana?.GetName() ?? (found.mana === undefined ? "missing" : "unnamed")}`);
|
|
163
|
-
Console.Echo("skin | portrait | restore | scale 1.2 | size 180 24 | heal <spell name> | unheal");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public Skin(): void {
|
|
167
|
-
const player = this.Capture();
|
|
168
|
-
this.bars[0].bar.SetStatusBarTexture("Interface\\Buttons\\WHITE8X8");
|
|
169
|
-
this.bars[0].bar.SetStatusBarColor(1, 0.15, 0.8, 1);
|
|
170
|
-
this.bars[1].bar.SetStatusBarTexture("Interface\\Buttons\\WHITE8X8");
|
|
171
|
-
this.bars[1].bar.SetStatusBarColor(0.15, 0.8, 1, 1);
|
|
172
|
-
this.HideArt(_G.PlayerFrameTexture as Artwork | undefined);
|
|
173
|
-
this.HideArt(_G.PlayerFrameBackground as Artwork | undefined);
|
|
174
|
-
this.HideArt(_G.PlayerPortrait as Artwork | undefined);
|
|
175
|
-
this.HideArt(player.portrait ?? player.Portrait);
|
|
176
|
-
this.HideArt(player.PlayerFrameContainer?.FrameTexture);
|
|
177
|
-
this.HideArt(player.PlayerFrameContainer?.Portrait);
|
|
178
|
-
Console.Echo(`native bars restyled: HP pink, mana cyan; ${this.artwork.length} artwork regions hidden. Original events and click handlers preserved`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
public Scale(scale: number): void {
|
|
182
|
-
if (!Number.isFinite(scale) || scale < 0.25 || scale > 3) error("Scale must be between 0.25 and 3");
|
|
183
|
-
this.Capture().SetScale(scale);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
public Size(width: number, height: number): void {
|
|
187
|
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) error("Width and height must be positive numbers");
|
|
188
|
-
this.Capture();
|
|
189
|
-
for (const item of this.bars) item.bar.SetSize(width, height);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
public Heal(spell: string): void {
|
|
193
|
-
const player = this.Player();
|
|
194
|
-
if (spell.trim() === "") error("Usage: /uira heal <known healing spell name>");
|
|
195
|
-
if (this.owner === undefined) this.owner = player;
|
|
196
|
-
if (this.originalBindings === undefined) {
|
|
197
|
-
this.originalBindings = {};
|
|
198
|
-
for (const key of bindings) this.originalBindings[key] = player.GetAttribute(key);
|
|
199
|
-
}
|
|
200
|
-
// Opt-in test only. Keep the client's secure OnClick handler intact.
|
|
201
|
-
player.SetAttribute("type1", "spell");
|
|
202
|
-
player.SetAttribute("spell1", spell);
|
|
203
|
-
player.SetAttribute("unit1", "player");
|
|
204
|
-
Console.Echo(`left-click self-heal test set to '${spell}'. Test on PlayerFrame; /uira unheal restores it`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
public Unheal(): void {
|
|
208
|
-
const player = this.Player();
|
|
209
|
-
if (this.originalBindings !== undefined) {
|
|
210
|
-
for (const key of bindings) player.SetAttribute(key, this.originalBindings[key]);
|
|
211
|
-
this.originalBindings = undefined;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
public Restore(): void {
|
|
216
|
-
const player = this.Player();
|
|
217
|
-
this.Unheal();
|
|
218
|
-
this.squarePortrait?.Hide();
|
|
219
|
-
this.portraitEvents?.UnregisterAllEvents();
|
|
220
|
-
this.portraitEvents?.SetScript("OnEvent", undefined);
|
|
221
|
-
for (const saved of this.bars) {
|
|
222
|
-
saved.bar.SetStatusBarTexture(saved.texture);
|
|
223
|
-
saved.bar.SetStatusBarColor(saved.color[0], saved.color[1], saved.color[2], saved.color[3]);
|
|
224
|
-
saved.bar.SetSize(saved.width, saved.height);
|
|
225
|
-
}
|
|
226
|
-
if (this.originalScale !== undefined) player.SetScale(this.originalScale);
|
|
227
|
-
for (const saved of this.artwork) saved.region.SetAlpha(saved.alpha);
|
|
228
|
-
for (const saved of this.statusArtwork) {
|
|
229
|
-
const c = saved.color;
|
|
230
|
-
saved.region.SetVertexColor(c[0], c[1], c[2], c[3]);
|
|
231
|
-
}
|
|
232
|
-
this.statusArtwork = [];
|
|
233
|
-
this.artwork = [];
|
|
234
|
-
this.bars = [];
|
|
235
|
-
this.originalScale = undefined;
|
|
236
|
-
this.owner = undefined;
|
|
237
|
-
Console.Echo("playground changes restored");
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function RegisterPlayerFramePlayground(): void {
|
|
242
|
-
const playground = new PlayerFramePlayground();
|
|
243
|
-
// Wait for the client's initial layout. Combat reloads defer until combat ends.
|
|
244
|
-
const startup = CreateFrame("Frame", undefined, UIParent);
|
|
245
|
-
startup.RegisterEvent("PLAYER_ENTERING_WORLD");
|
|
246
|
-
startup.RegisterEvent("PLAYER_REGEN_ENABLED");
|
|
247
|
-
startup.SetScript("OnEvent", () => {
|
|
248
|
-
if (InCombatLockdown() || PlayerFrame === undefined) return;
|
|
249
|
-
playground.Skin();
|
|
250
|
-
playground.Portrait();
|
|
251
|
-
playground.Scale(1.2);
|
|
252
|
-
playground.Heal("Holy Light");
|
|
253
|
-
startup.UnregisterAllEvents();
|
|
254
|
-
startup.SetScript("OnEvent", undefined);
|
|
255
|
-
startup.Hide();
|
|
256
|
-
});
|
|
257
|
-
_G.SLASH_UIRAPLAYGROUND1 = "/uira";
|
|
258
|
-
SlashCmdList.UIRAPLAYGROUND = message => {
|
|
259
|
-
const parts = message.trim().split(" ");
|
|
260
|
-
const command = parts[0];
|
|
261
|
-
if (command === "skin") playground.Skin();
|
|
262
|
-
else if (command === "portrait") playground.Portrait();
|
|
263
|
-
else if (command === "restore") playground.Restore();
|
|
264
|
-
else if (command === "scale") playground.Scale(tonumber(parts[1] ?? "") ?? 0);
|
|
265
|
-
else if (command === "size") playground.Size(tonumber(parts[1] ?? "") ?? 0, tonumber(parts[2] ?? "") ?? 0);
|
|
266
|
-
else if (command === "heal") playground.Heal(parts.slice(1).join(" "));
|
|
267
|
-
else if (command === "unheal") playground.Unheal();
|
|
268
|
-
else playground.Info();
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
package/src/types/Position.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type Anchor = "TOPLEFT" | "TOP" | "TOPRIGHT" | "LEFT" | "CENTER" | "RIGHT" | "BOTTOMLEFT" | "BOTTOM" | "BOTTOMRIGHT";
|
|
2
|
-
|
|
3
|
-
/** Defaults to TOPLEFT; positive y moves down from the selected anchor. */
|
|
4
|
-
export type Position<TReference> = {
|
|
5
|
-
anchor?: Anchor;
|
|
6
|
-
relativeAnchor?: Anchor;
|
|
7
|
-
} & (
|
|
8
|
-
| {
|
|
9
|
-
mode: "absolute";
|
|
10
|
-
x: number;
|
|
11
|
-
y: number;
|
|
12
|
-
offset?: never;
|
|
13
|
-
relativeTo?: never;
|
|
14
|
-
}
|
|
15
|
-
| {
|
|
16
|
-
mode: "relative";
|
|
17
|
-
offset: { x: number; y: number };
|
|
18
|
-
relativeTo?: TReference;
|
|
19
|
-
x?: never;
|
|
20
|
-
y?: never;
|
|
21
|
-
});
|
package/src/types/Spacing.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/** A uniform spacing value or individually specified edges, in UI units. */
|
|
2
|
-
export type Spacing = number | { top?: number; right?: number; bottom?: number; left?: number };
|
|
3
|
-
|
|
4
|
-
export interface Insets { top: number; right: number; bottom: number; left: number; }
|
|
5
|
-
|
|
6
|
-
export function resolveSpacing(value: Spacing = 0): Insets {
|
|
7
|
-
const result = typeof value === "number"
|
|
8
|
-
? { top: value, right: value, bottom: value, left: value }
|
|
9
|
-
: { top: value.top ?? 0, right: value.right ?? 0, bottom: value.bottom ?? 0, left: value.left ?? 0 };
|
|
10
|
-
for (const edge of [result.top, result.right, result.bottom, result.left]) {
|
|
11
|
-
if (!Number.isFinite(edge) || edge < 0) error("Spacing must be finite and non-negative");
|
|
12
|
-
}
|
|
13
|
-
return result;
|
|
14
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { TextureOptions } from "../../types/Texture";
|
|
2
|
-
import { Decoration } from "./Decoration";
|
|
3
|
-
import { backgroundTextures } from "./backgroundTextures";
|
|
4
|
-
import type { BackgroundTexture } from "./backgroundTextures";
|
|
5
|
-
|
|
6
|
-
export interface BackgroundProps extends TextureOptions<BackgroundTexture> {}
|
|
7
|
-
|
|
8
|
-
export class Background extends Decoration {
|
|
9
|
-
public constructor(public readonly props: BackgroundProps) { super(); }
|
|
10
|
-
|
|
11
|
-
protected draw(frame: UiraNativeFrame): void {
|
|
12
|
-
const texture = this.texture(frame, "BACKGROUND");
|
|
13
|
-
const color = this.props.color ?? { r: 1, g: 1, b: 1 };
|
|
14
|
-
texture.SetAllPoints(frame);
|
|
15
|
-
if (this.props.texture !== undefined) {
|
|
16
|
-
const selected = this.props.texture;
|
|
17
|
-
const path = typeof selected === "string" ? backgroundTextures[selected].path : selected.path;
|
|
18
|
-
texture.SetTexture(path);
|
|
19
|
-
texture.SetVertexColor(color.r, color.g, color.b, color.a ?? 1);
|
|
20
|
-
} else {
|
|
21
|
-
texture.SetColorTexture(color.r, color.g, color.b, color.a ?? 1);
|
|
22
|
-
}
|
|
23
|
-
texture.SetHorizTile(this.props.tileX ?? false);
|
|
24
|
-
texture.SetVertTile(this.props.tileY ?? false);
|
|
25
|
-
texture.SetBlendMode(this.props.blendMode ?? "BLEND");
|
|
26
|
-
const uv = this.props.texCoords ?? [0, 1, 0, 1];
|
|
27
|
-
texture.SetTexCoord(uv[0], uv[1], uv[2], uv[3]);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import type { Color } from "../../types/Color";
|
|
2
|
-
import { Decoration } from "./Decoration";
|
|
3
|
-
import { borderTextures, BorderTexture } from "./borderTextures";
|
|
4
|
-
|
|
5
|
-
export interface BorderProps {
|
|
6
|
-
thickness?: number;
|
|
7
|
-
color: Color;
|
|
8
|
-
texture?: BorderTexture;
|
|
9
|
-
/** Rounded solid borders only. Native textures supply their own corner shape. */
|
|
10
|
-
radius?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class Border extends Decoration {
|
|
14
|
-
public constructor(public readonly props: BorderProps) { super(); }
|
|
15
|
-
|
|
16
|
-
protected draw(frame: UiraNativeFrame, width: number, height: number): void {
|
|
17
|
-
const selected = this.props.texture ?? "solid";
|
|
18
|
-
const style = typeof selected === "string" ? borderTextures[selected] : selected;
|
|
19
|
-
const color = this.props.color;
|
|
20
|
-
const thickness = this.props.thickness ?? style.edgeSize;
|
|
21
|
-
if (thickness <= 0) return;
|
|
22
|
-
if (style.path !== "") {
|
|
23
|
-
if ((this.props.radius ?? 0) > 0) error("Native border textures define their own corners; radius requires solid");
|
|
24
|
-
const border = this.backdrop(frame);
|
|
25
|
-
border.SetBackdrop({ edgeFile: style.path, edgeSize: thickness });
|
|
26
|
-
border.SetBackdropBorderColor(color.r, color.g, color.b, color.a ?? 1);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const t = Math.min(thickness, width / 2, height / 2);
|
|
30
|
-
const radius = Math.min(Math.max(0, this.props.radius ?? 0), width / 2, height / 2);
|
|
31
|
-
if (radius > 0) {
|
|
32
|
-
this.rounded(frame, width, height, t, radius);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
this.strip(frame, 0, 0, width, t);
|
|
36
|
-
this.strip(frame, 0, height - t, width, t);
|
|
37
|
-
this.strip(frame, 0, t, t, height - 2 * t);
|
|
38
|
-
this.strip(frame, width - t, t, t, height - 2 * t);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Bounded scanline approximation, without masks or external image assets. */
|
|
42
|
-
private rounded(frame: UiraNativeFrame, width: number, height: number, t: number, radius: number): void {
|
|
43
|
-
const extent = Math.max(radius, t);
|
|
44
|
-
const rows = Math.min(32, Math.ceil(extent));
|
|
45
|
-
const step = extent / rows;
|
|
46
|
-
const innerRadius = Math.max(0, radius - t);
|
|
47
|
-
for (let row = 0; row < rows; row += 1) {
|
|
48
|
-
const y = (row + 0.5) * step;
|
|
49
|
-
const distance = Math.max(0, radius - y);
|
|
50
|
-
const outer = y < radius ? radius - Math.sqrt(Math.max(0, radius * radius - distance * distance)) : 0;
|
|
51
|
-
const inner = y < t ? width / 2
|
|
52
|
-
: y < radius ? t + innerRadius - Math.sqrt(Math.max(0, innerRadius * innerRadius - distance * distance)) : t;
|
|
53
|
-
const span = Math.max(0, inner - outer);
|
|
54
|
-
this.strip(frame, outer, row * step, span, step);
|
|
55
|
-
this.strip(frame, width - inner, row * step, span, step);
|
|
56
|
-
this.strip(frame, outer, height - (row + 1) * step, span, step);
|
|
57
|
-
this.strip(frame, width - inner, height - (row + 1) * step, span, step);
|
|
58
|
-
}
|
|
59
|
-
this.strip(frame, 0, extent, t, height - 2 * extent);
|
|
60
|
-
this.strip(frame, width - t, extent, t, height - 2 * extent);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private strip(frame: UiraNativeFrame, x: number, y: number, width: number, height: number): void {
|
|
64
|
-
if (width <= 0 || height <= 0) return;
|
|
65
|
-
const texture = this.texture(frame, "OVERLAY");
|
|
66
|
-
const color = this.props.color;
|
|
67
|
-
texture.SetSize(width, height);
|
|
68
|
-
texture.SetPoint("TOPLEFT", frame, "TOPLEFT", x, -y);
|
|
69
|
-
texture.SetColorTexture(color.r, color.g, color.b, color.a ?? 1);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/** Owns reusable native regions. One instance belongs to one mounted Cell. */
|
|
2
|
-
export abstract class Decoration {
|
|
3
|
-
private active = false;
|
|
4
|
-
private textures: UiraNativeTexture[] = [];
|
|
5
|
-
private frames: UiraNativeFrame[] = [];
|
|
6
|
-
private textureIndex = 0;
|
|
7
|
-
private frameIndex = 0;
|
|
8
|
-
|
|
9
|
-
public mount(frame: UiraNativeFrame, width: number, height: number): void {
|
|
10
|
-
if (this.active) error("Decoration is already mounted; create a separate instance per Cell");
|
|
11
|
-
this.active = true;
|
|
12
|
-
this.textureIndex = 0;
|
|
13
|
-
this.frameIndex = 0;
|
|
14
|
-
try {
|
|
15
|
-
this.draw(frame, width, height);
|
|
16
|
-
} catch (failure) {
|
|
17
|
-
this.dispose();
|
|
18
|
-
throw failure;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public dispose(): void {
|
|
23
|
-
for (const texture of this.textures) texture.Hide();
|
|
24
|
-
for (const frame of this.frames) frame.Hide();
|
|
25
|
-
this.active = false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected texture(parent: UiraNativeFrame, layer: UiraDrawLayer): UiraNativeTexture {
|
|
29
|
-
let texture = this.textures[this.textureIndex];
|
|
30
|
-
if (texture === undefined) {
|
|
31
|
-
texture = parent.CreateTexture(undefined, layer);
|
|
32
|
-
this.textures.push(texture);
|
|
33
|
-
} else {
|
|
34
|
-
texture.SetParent(parent);
|
|
35
|
-
texture.ClearAllPoints();
|
|
36
|
-
}
|
|
37
|
-
this.textureIndex += 1;
|
|
38
|
-
texture.Show();
|
|
39
|
-
return texture;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
protected backdrop(parent: UiraNativeFrame): UiraNativeFrame {
|
|
43
|
-
let frame = this.frames[this.frameIndex];
|
|
44
|
-
if (frame === undefined) {
|
|
45
|
-
frame = CreateFrame("Frame", undefined, parent, "BackdropTemplate");
|
|
46
|
-
this.frames.push(frame);
|
|
47
|
-
} else {
|
|
48
|
-
frame.SetParent(parent);
|
|
49
|
-
frame.ClearAllPoints();
|
|
50
|
-
}
|
|
51
|
-
this.frameIndex += 1;
|
|
52
|
-
frame.EnableMouse(false);
|
|
53
|
-
frame.SetAllPoints(parent);
|
|
54
|
-
frame.Show();
|
|
55
|
-
return frame;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
protected abstract draw(frame: UiraNativeFrame, width: number, height: number): void;
|
|
59
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { Color } from "../../types/Color";
|
|
2
|
-
import { Decoration } from "./Decoration";
|
|
3
|
-
|
|
4
|
-
export interface GlowProps {
|
|
5
|
-
/** A glow sprite with transparent center; not an arbitrary filled image. */
|
|
6
|
-
texture: string;
|
|
7
|
-
color: Color;
|
|
8
|
-
spread?: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Static additive glow; no per-frame update or animation. */
|
|
12
|
-
export class Glow extends Decoration {
|
|
13
|
-
public constructor(public readonly props: GlowProps) { super(); }
|
|
14
|
-
|
|
15
|
-
protected draw(frame: UiraNativeFrame, width: number, height: number): void {
|
|
16
|
-
const texture = this.texture(frame, "OVERLAY");
|
|
17
|
-
const spread = Math.max(0, this.props.spread ?? 8);
|
|
18
|
-
const color = this.props.color;
|
|
19
|
-
texture.SetPoint("TOPLEFT", frame, "TOPLEFT", -spread, spread);
|
|
20
|
-
texture.SetSize(width + 2 * spread, height + 2 * spread);
|
|
21
|
-
texture.SetTexture(this.props.texture);
|
|
22
|
-
texture.SetVertexColor(color.r, color.g, color.b, color.a ?? 1);
|
|
23
|
-
texture.SetBlendMode("ADD");
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Known background paths registered by local Forever LibSharedMedia.
|
|
2
|
-
export const backgroundTextures = {
|
|
3
|
-
solid: { label: "Solid", path: "Interface\\Buttons\\WHITE8X8" },
|
|
4
|
-
tooltip: { label: "Blizzard Tooltip", path: "Interface\\Tooltips\\UI-Tooltip-Background" },
|
|
5
|
-
dialog: { label: "Blizzard Dialog", path: "Interface\\DialogFrame\\UI-DialogBox-Background" },
|
|
6
|
-
dialogDark: { label: "Blizzard Dialog Dark", path: "Interface\\DialogFrame\\UI-DialogBox-Background-Dark" },
|
|
7
|
-
dialogGold: { label: "Blizzard Dialog Gold", path: "Interface\\DialogFrame\\UI-DialogBox-Gold-Background" },
|
|
8
|
-
} as const;
|
|
9
|
-
|
|
10
|
-
export type BackgroundTexture = keyof typeof backgroundTextures | { path: string };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Paths registered by LibSharedMedia in locally installed Forever addons.
|
|
2
|
-
// This is a known catalog, not an exhaustive inventory of client assets.
|
|
3
|
-
export const borderTextures = {
|
|
4
|
-
solid: { label: "Solid", path: "", edgeSize: 1 },
|
|
5
|
-
tooltip: { label: "Blizzard Tooltip", path: "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize: 16 },
|
|
6
|
-
dialog: { label: "Blizzard Dialog", path: "Interface\\DialogFrame\\UI-DialogBox-Border", edgeSize: 32 },
|
|
7
|
-
dialogGold: { label: "Blizzard Dialog Gold", path: "Interface\\DialogFrame\\UI-DialogBox-Gold-Border", edgeSize: 32 },
|
|
8
|
-
achievementWood: { label: "Blizzard Achievement Wood", path: "Interface\\AchievementFrame\\UI-Achievement-WoodBorder", edgeSize: 16 },
|
|
9
|
-
chatBubble: { label: "Blizzard Chat Bubble", path: "Interface\\Tooltips\\ChatBubble-Backdrop", edgeSize: 16 },
|
|
10
|
-
party: { label: "Blizzard Party", path: "Interface\\CHARACTERFRAME\\UI-Party-Border", edgeSize: 16 },
|
|
11
|
-
} as const;
|
|
12
|
-
|
|
13
|
-
export type BorderTexture = keyof typeof borderTextures | { path: string; edgeSize: number };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Blizzard's status bar texture is registered in local Forever LibSharedMedia.
|
|
2
|
-
export const statusBarTextures = {
|
|
3
|
-
solid: { label: "Solid", path: "Interface\\Buttons\\WHITE8X8" },
|
|
4
|
-
blizzard: { label: "Blizzard Status Bar", path: "Interface\\TargetingFrame\\UI-StatusBar" },
|
|
5
|
-
} as const;
|
|
6
|
-
|
|
7
|
-
/** Accepts catalog names or a native/custom texture path. */
|
|
8
|
-
export function resolveStatusBarTexture(texture: string): string {
|
|
9
|
-
const preset = statusBarTextures[texture as keyof typeof statusBarTextures];
|
|
10
|
-
return preset === undefined ? texture : preset.path;
|
|
11
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { FlowLayoutProps } from "../FlowLayout";
|
|
2
|
-
|
|
3
|
-
/** Pure layout calculation in top-left UI coordinates. */
|
|
4
|
-
export function calculateFlow(props: FlowLayoutProps & {width: number; height: number}, children: readonly {width: number; height: number}[]): { x: number; y: number }[] {
|
|
5
|
-
const horizontal = props.direction === "horizontal";
|
|
6
|
-
const requestedLimit = horizontal ? props.width : props.height;
|
|
7
|
-
const limit = props.sizing === "content" && requestedLimit === 0 ? Infinity : requestedLimit;
|
|
8
|
-
const gap = props.gap ?? 0;
|
|
9
|
-
const lineGap = props.lineGap ?? gap;
|
|
10
|
-
if (gap < 0 || lineGap < 0) error("FlowLayout gaps must be non-negative");
|
|
11
|
-
const result: { x: number; y: number }[] = [];
|
|
12
|
-
let cursor = 0;
|
|
13
|
-
let cross = 0;
|
|
14
|
-
let lineSize = 0;
|
|
15
|
-
let count = 0;
|
|
16
|
-
for (const child of children) {
|
|
17
|
-
const size = horizontal ? child.width : child.height;
|
|
18
|
-
const crossSize = horizontal ? child.height : child.width;
|
|
19
|
-
const next = count === 0 ? 0 : cursor + gap;
|
|
20
|
-
if (props.grow && count > 0 && next + size > limit) {
|
|
21
|
-
cross += lineSize + lineGap;
|
|
22
|
-
cursor = 0;
|
|
23
|
-
lineSize = 0;
|
|
24
|
-
count = 0;
|
|
25
|
-
}
|
|
26
|
-
const position = count === 0 ? 0 : cursor + gap;
|
|
27
|
-
result.push(horizontal ? { x: position, y: cross } : { x: cross, y: position });
|
|
28
|
-
cursor = position + size;
|
|
29
|
-
lineSize = Math.max(lineSize, crossSize);
|
|
30
|
-
count += 1;
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { GridLayoutProps } from "../GridLayout";
|
|
2
|
-
|
|
3
|
-
/** Row-major placement into uniform slots; children keep their own sizes. */
|
|
4
|
-
export function calculateGrid(props: GridLayoutProps, children: readonly {width: number; height: number}[]): { x: number; y: number }[] {
|
|
5
|
-
if (!Number.isFinite(props.columns) || props.columns < 1 || Math.floor(props.columns) !== props.columns) {
|
|
6
|
-
error("GridLayout columns must be a positive integer");
|
|
7
|
-
}
|
|
8
|
-
const rowGap = props.rowGap ?? props.gap ?? 0;
|
|
9
|
-
const columnGap = props.columnGap ?? props.gap ?? 0;
|
|
10
|
-
if (rowGap < 0 || columnGap < 0) error("GridLayout gaps must be non-negative");
|
|
11
|
-
let width = props.cellWidth ?? 0;
|
|
12
|
-
let height = props.cellHeight ?? 0;
|
|
13
|
-
for (const child of children) {
|
|
14
|
-
if (props.cellWidth === undefined) width = Math.max(width, child.width);
|
|
15
|
-
if (props.cellHeight === undefined) height = Math.max(height, child.height);
|
|
16
|
-
}
|
|
17
|
-
if (width < 0 || height < 0) error("GridLayout cell sizes must be non-negative");
|
|
18
|
-
return children.map((_, index) => ({
|
|
19
|
-
x: (index % props.columns) * (width + columnGap),
|
|
20
|
-
y: Math.floor(index / props.columns) * (height + rowGap),
|
|
21
|
-
}));
|
|
22
|
-
}
|