@kurot/ui 3.1.0 → 3.3.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,69 @@ All notable changes to `@kurot/ui` are documented here.
4
4
 
5
5
  ---
6
6
 
7
+ ## [3.3.0] — 2026-10-08
8
+
9
+ ### Added
10
+
11
+ - Export RichLabel as an independent Component wrapping one native TextField.
12
+ Its only content interface is textFlow; ordinary Label text, appearance fields,
13
+ fitting and style presets are not exposed. Per-run styling stays in the flow.
14
+ - Support continuous Unicode wrapping across runs, hard breaks, automatic bounds,
15
+ parent width constraints, maximum width, line spacing and alignment.
16
+ - Copy assigned/read runs and styles, notify textFlow bindings, clear with [],
17
+ cache unchanged measurements and invalidate metrics after late font readiness.
18
+
19
+ ### Fixed
20
+
21
+ - Apply BitmapLabel.maxWidth when measuring automatic bounds without an explicit
22
+ or parent-supplied width, keeping wrapped height consistent with rendering.
23
+ - Remeasure BitmapLabel and RichLabel automatic height after an authored width
24
+ changes, including after the component has already completed layout.
25
+
26
+ ### Dependencies and migration
27
+
28
+ - Require Core ^2.3.0 for independent rich-text measurement. UI development
29
+ dependencies and the lockfile resolve published Core 2.3.1 without local
30
+ overrides; the compatible patch does not raise the peer minimum.
31
+ - Native code and programmatic skins can use both text components. KUI catalogs,
32
+ CLI compilation, ui-runtime materialization and Editor authoring are separate
33
+ follow-up work; this release does not migrate projects or edit those packages.
34
+
35
+ ### Verification
36
+
37
+ - Cover content ownership, binding, measurement, layout, late font readiness and
38
+ per-run Canvas styling. Add minified Canvas 2D, WebGL 1 and WebGL 2 browser tests
39
+ for rich text and bitmap labels, including width changes and clearing content.
40
+
41
+ ## [3.2.0] — 2026-10-08
42
+
43
+ ### Added
44
+
45
+ - Export BitmapLabel, a native UI component wrapping Core BitmapText with normal
46
+ measurement, parent layout constraints, alignment, spacing and text bindings.
47
+ - Accept a caller-owned BitmapFont or a configured font-resource name. Resolve
48
+ preloaded fonts synchronously and load missing fonts through Core's resource
49
+ singleton, reporting completion and loading/type errors through events.
50
+ - Ignore superseded asynchronous font results, including A-to-B-to-A changes.
51
+ Font ownership remains with the resource cache or caller.
52
+
53
+ ### Dependencies and migration
54
+
55
+ - Require `@kurot/core ^2.2.0` in peer and development dependencies for rendered
56
+ BitmapText, FontAnalyzer and pure text measurement. Upgrade older Core installs
57
+ and lockfiles before adopting UI 3.2.0.
58
+ - Remove the local Core dependency override and build against published Core.
59
+ UI continues to depend only on Core; bitmap-font is Core's dependency.
60
+ - BitmapLabel supports native code and programmatic skins. The ui-document
61
+ catalog, CLI KUI tags, Editor and ui-runtime materializers do not yet adopt it.
62
+
63
+ ### Tests and documentation
64
+
65
+ - Cover layout and measurement, alignment, binding notifications, cached and
66
+ asynchronous font resolution, stale requests, invalid resources and errors.
67
+ - Translate architecture documentation to English and correct outdated Skin,
68
+ theme-adapter, validation and virtual-layout descriptions against source.
69
+
7
70
  ## [3.1.0] — 2026-10-05
8
71
 
9
72
  ### Added
package/README.md CHANGED
@@ -2,10 +2,35 @@
2
2
 
3
3
  UI component framework for [@kurot/core](https://github.com/kurot-engine/kurot/tree/main/packages/core). Migrated from Egret EUI, rewritten in modern TypeScript with clean class inheritance — no namespace hacks, no prototype manipulation.
4
4
 
5
- > **Current release: 3.1.0.** Requires `@kurot/core@^2.1.0`. Targets ES2022 + evergreen browsers, same as core.
5
+ > **Package version: 3.3.0.** Requires `@kurot/core@^2.3.0`. Targets ES2022 + evergreen browsers, same as core.
6
6
 
7
7
  For the full list of changes in this release, see [CHANGELOG.md](./CHANGELOG.md).
8
8
 
9
+ ## RichLabel in 3.3.0
10
+
11
+ ```ts
12
+ import { RichLabel } from '@kurot/ui';
13
+
14
+ const message = new RichLabel([
15
+ { text: 'Important: ', style: { size: 20, bold: true, textColor: 0xff9900 } },
16
+ { text: 'all runs wrap together.', style: { size: 20, italic: true } },
17
+ ]);
18
+ message.maxWidth = 240;
19
+ group.addChild(message);
20
+ ```
21
+
22
+ RichLabel extends Component independently. Its content interface is `textFlow`,
23
+ with no whole-component Label text, color, font, fitting or preset properties.
24
+ It supports multiline layout, automatic bounds, parent constraints and alignment.
25
+ Assigned/read runs and styles are copied; reassign an edited snapshot to update.
26
+ See [RichLabel](docs/rich-label.md) for defaults, measurement and font readiness.
27
+ BitmapLabel now also honors maxWidth during automatic measurement. Both text
28
+ components remeasure automatic height when an authored width changes.
29
+
30
+ UI 3.3.0 builds and tests against published Core 2.3.1 without local overrides.
31
+ These native APIs do
32
+ not add KUI tags or an Editor rich-text authoring interface by themselves.
33
+
9
34
  ## Label text fitting in 3.1.0
10
35
 
11
36
  Single-line Labels can shrink their drawing size to fit the available region:
@@ -33,22 +58,44 @@ intended to wrap. Files are not migrated automatically. Load fonts before
33
58
  measurement; after late loading, call `label.invalidateSize()` to refresh
34
59
  metrics even if the family name is unchanged.
35
60
 
36
- See the [text layout and fitting contract](docs/label-text-layout.md). KUI
37
- authoring requires ui-document 0.9.x and matching CLI/ui-runtime releases;
38
- programmatic UI depends only on Core.
61
+ See the [text layout and fitting contract](docs/label-text-layout.md). Fitting
62
+ metadata was introduced in ui-document 0.9.0 and remains supported in 0.11.0.
63
+ Current CLI-built KUI projects use published CLI 3.3.0 with ui-document `^0.11.0`;
64
+ Published ui-runtime 0.8.2 uses the same kernel range. Programmatic UI depends
65
+ only on Core.
39
66
 
40
67
  ## Installation
41
68
 
42
69
  ```bash
43
- pnpm add @kurot/ui@^3.1.0 @kurot/core@^2.1.0
70
+ pnpm add @kurot/ui@^3.3.0 @kurot/core@^2.3.0
44
71
  ```
45
72
 
46
73
  `@kurot/ui` declares `@kurot/core` as a peer dependency. Install both packages explicitly so the application controls the resolved core version.
47
74
 
48
- UI 3.1 requires Core 2.1.0 or later within Core 2.x. Refresh old sheet manifests with
75
+ UI 3.3 requires Core 2.3.0 or later within Core 2.x for independent rich-text
76
+ measurement as well as BitmapText rendering and font resources. Core installs its published bitmap-font dependency automatically.
77
+ Refresh older Core/UI lockfiles and old sheet manifests with
49
78
  string-valued `subkeys` in Kurot Editor before launching the application.
50
79
  See the [resource migration guide](../ui-document/docs/resource-nine-slice.md).
51
80
 
81
+ ## Project style colors
82
+
83
+ CLI 3.3.0 resolves Label presets and named colors from the optional
84
+ `resource/config/style.json` into native UI properties, including state overrides:
85
+
86
+ ```xml
87
+ <Label id="labelDisplay" textColor="#FF9900"
88
+ textColor.disabled="@style:colors:disabled-text" />
89
+ ```
90
+
91
+ The Label belongs to a Skin declaring the `disabled` state. Native Skin state
92
+ handling applies the compiled numeric color and restores the base color on
93
+ leaving that state. The same reference syntax works for base color properties.
94
+ UI does not read style.json or evaluate XML references itself; programmatic
95
+ components assign numeric colors. This release does not introduce complete
96
+ typography presets or automatic live theme switching. See the
97
+ [project style contract](../ui-document/docs/project-styles.md).
98
+
52
99
  ## Quick Start
53
100
 
54
101
  ```ts
@@ -204,19 +251,21 @@ dynamic properties; use `this.skinParts.<name>` during the ready lifecycle.
204
251
 
205
252
  ### Basic Controls
206
253
 
207
- | Component | Description |
208
- | -------------- | ------------------------------------------------------------------------------ |
209
- | `Label` | Text display. Wraps `TextField` in the UI lifecycle. |
210
- | `Button` | Tappable button with `up`/`down`/`disabled` states and `labelDisplay` part. |
211
- | `CheckBox` | Toggle button. Dispatches `Event.CHANGE` on selection change. |
212
- | `RadioButton` | Mutually exclusive toggle. Use `groupName` to link buttons. |
213
- | `ToggleButton` | Base for toggle-style buttons. |
214
- | `ToggleSwitch` | Binary on/off switch (visual variant of `ToggleButton`). |
215
- | `ProgressBar` | Value indicator. Inject `thumb` (a `Component`) as the fill part. |
216
- | `HSlider` | Horizontal slider. Inject `thumb` and `track` skin parts; default range 0–100. |
217
- | `VSlider` | Vertical slider. Inject `thumb` and `track` skin parts; default range 0–100. |
218
- | `Rect` | Filled/stroked rectangle. Supports `fillColor`, `strokeColor`, `fillAlpha`. |
219
- | `Image` | Bitmap display. Supports URL string or `Texture`, `scale9Grid`, `fillMode`. |
254
+ | Component | Description |
255
+ | -------------- | ------------------------------------------------------------------------------- |
256
+ | `Label` | Text display. Wraps `TextField` in the UI lifecycle. |
257
+ | `RichLabel` | Styled runs in one continuous TextField layout; independent textFlow content. |
258
+ | `BitmapLabel` | Bitmap-font display. Wraps BitmapText with font resources, layout and bindings. |
259
+ | `Button` | Tappable button with `up`/`down`/`disabled` states and `labelDisplay` part. |
260
+ | `CheckBox` | Toggle button. Dispatches `Event.CHANGE` on selection change. |
261
+ | `RadioButton` | Mutually exclusive toggle. Use `groupName` to link buttons. |
262
+ | `ToggleButton` | Base for toggle-style buttons. |
263
+ | `ToggleSwitch` | Binary on/off switch (visual variant of `ToggleButton`). |
264
+ | `ProgressBar` | Value indicator. Inject `thumb` (a `Component`) as the fill part. |
265
+ | `HSlider` | Horizontal slider. Inject `thumb` and `track` skin parts; default range 0–100. |
266
+ | `VSlider` | Vertical slider. Inject `thumb` and `track` skin parts; default range 0–100. |
267
+ | `Rect` | Filled/stroked rectangle. Supports `fillColor`, `strokeColor`, `fillAlpha`. |
268
+ | `Image` | Bitmap display. Supports URL string or `Texture`, `scale9Grid`, `fillMode`. |
220
269
 
221
270
  Both sliders inherit Range's default `minimum = 0` and `maximum = 100`.
222
271
  Set either bound explicitly when a control needs a different range.
@@ -281,10 +330,12 @@ data.addItem('Durian');
281
330
  data.addItemAt('Elderberry', 1);
282
331
  data.removeItemAt(0);
283
332
  data.replaceItemAt('Fig', 0);
284
- data.sortOn('name'); // sort by field
285
- data.sort((a, b) => (a > b ? 1 : -1)); // custom sort
333
+ data.sort((a, b) => String(a).localeCompare(String(b)));
286
334
  data.filterFunction(item => item !== 'Fig');
287
335
  data.refresh(); // notify view after manual source changes
336
+
337
+ const records = new ArrayCollection([{ name: 'Pear' }, { name: 'Apple' }]);
338
+ records.sortOn('name'); // sort records by field
288
339
  ```
289
340
 
290
341
  ## Virtual Layout
@@ -335,20 +386,37 @@ component.percentHeight = 100;
335
386
  ## View States
336
387
 
337
388
  ```ts
338
- import { State, SetProperty, AddItems } from '@kurot/ui';
389
+ import { Label, Rect, Skin, State, SetProperty } from '@kurot/ui';
339
390
 
340
- class MyButtonSkin extends Skin {
341
- states = [
342
- new State('up'),
343
- new State('down', [new SetProperty('bg', 'fillColor', 0x5a4bd1)]),
344
- new State('disabled', [
345
- new SetProperty('bg', 'fillColor', 0x636e72),
346
- new SetProperty('lbl', 'textColor', 0xb2bec3),
347
- ]),
348
- ];
391
+ class StatefulButtonSkin extends Skin {
392
+ constructor() {
393
+ super();
394
+ const bg = new Rect(120, 36, 0x6c5ce7);
395
+ const labelDisplay = new Label();
396
+ labelDisplay.textColor = 0xffffff;
397
+ labelDisplay.horizontalCenter = 0;
398
+ labelDisplay.verticalCenter = 0;
399
+ this.setPart('bg', bg);
400
+ this.setPart('labelDisplay', labelDisplay);
401
+ this.skinParts = ['labelDisplay'];
402
+ this.elementsContent = [bg, labelDisplay];
403
+ this.states = [
404
+ new State('up'),
405
+ new State('down', [new SetProperty('bg', 'fillColor', 0x5a4bd1)]),
406
+ new State('disabled', [
407
+ new SetProperty('bg', 'fillColor', 0x636e72),
408
+ new SetProperty('labelDisplay', 'textColor', 0xb2bec3),
409
+ ]),
410
+ ];
411
+ }
349
412
  }
350
413
  ```
351
414
 
415
+ Button selects `up`, `down` or `disabled` through its native behavior. Each
416
+ state restores the previous property values before applying the next overrides.
417
+ CLI-built skins generate the same `State`/`SetProperty` objects from XML
418
+ attributes such as `textColor.disabled`.
419
+
352
420
  ## Events
353
421
 
354
422
  | Event | Dispatched by | Description |
@@ -405,14 +473,39 @@ cross-framework ranking.
405
473
 
406
474
  ## Differences from Egret EUI
407
475
 
408
- | | Egret EUI | @kurot/ui |
409
- | -------------- | --------------------------- | ------------------------------------ |
410
- | Namespace | `eui.*` global | ES Module named exports |
411
- | Component base | `namespace` + `mixin` | Standard class inheritance |
412
- | Layout state | Prototype-injected | `UIState` delegation |
413
- | Authored skins | EXML runtime parser | KUI XML compiled by `@kurot/cli` |
414
- | Skin parts | Incremental dynamic fields | Atomic typed `skinParts` |
415
- | Skin lifecycle | `partAdded` / `partRemoved` | `onSkinReady` / `onSkinRemoved` |
416
- | `thisObject` | Required in event listeners | Not needed — use arrow functions |
417
- | Virtual layout | Default on | Opt-in via `useVirtualLayout = true` |
418
- | i18n | Built-in | Not supported |
476
+ | | Egret EUI | @kurot/ui |
477
+ | -------------- | --------------------------- | ----------------------------------------------------------- |
478
+ | Namespace | `eui.*` global | ES Module named exports |
479
+ | Component base | `namespace` + `mixin` | Standard class inheritance |
480
+ | Layout state | Prototype-injected | `UIState` delegation |
481
+ | Authored skins | EXML runtime parser | KUI XML compiled by `@kurot/cli` |
482
+ | Skin parts | Incremental dynamic fields | Atomic typed `skinParts` |
483
+ | Skin lifecycle | `partAdded` / `partRemoved` | `onSkinReady` / `onSkinRemoved` |
484
+ | `thisObject` | Required in event listeners | Not needed — use arrow functions |
485
+ | Virtual layout | Default on | Opt-in via `useVirtualLayout = true` |
486
+ | i18n | Built-in | Project-managed translations; Core handles Unicode wrapping |
487
+
488
+ ## Bitmap labels in 3.2.0
489
+
490
+ [BitmapLabel](docs/bitmap-label.md) wraps Core BitmapText with native UI layout,
491
+ spacing, alignment and configured font-resource names:
492
+
493
+ ```ts
494
+ import { BitmapLabel } from '@kurot/ui';
495
+
496
+ const amount = new BitmapLabel('1,234.56');
497
+ amount.font = 'number_font_fnt';
498
+ amount.multiline = false;
499
+ amount.height = 100;
500
+ group.addChild(amount);
501
+ ```
502
+
503
+ Register the font descriptor in Core's resource configuration before assigning
504
+ its name, or assign a BitmapFont directly. The label borrows its font; the
505
+ resource cache or caller retains ownership. Superseded async requests cannot
506
+ replace a newer assignment. Successful named loads dispatch Event.COMPLETE;
507
+ failed or incorrectly typed resources dispatch IOErrorEvent.IO_ERROR.
508
+
509
+ The component supports native code and programmatic skins. Installed KUI
510
+ compilers, the ui-document catalog, Editor and ui-runtime materializers do not
511
+ yet adopt the BitmapLabel tag. Published UI 3.1.0 does not include the component.
@@ -0,0 +1,48 @@
1
+ import { BitmapFont } from '@kurot/core';
2
+ import { Component } from './Component.js';
3
+ import type { IDisplayText } from '../core/IDisplayText.js';
4
+ /**
5
+ * UI layout wrapper around BitmapText. String fonts are configured resource
6
+ * names; FontAnalyzer loads the descriptor and its page. Font ownership stays
7
+ * with the resource cache or the caller that supplied a BitmapFont.
8
+ */
9
+ export declare class BitmapLabel extends Component implements IDisplayText {
10
+ private readonly _bitmapText;
11
+ private _font?;
12
+ private _fontChanged;
13
+ private _fontRequest;
14
+ private _widthConstraint;
15
+ constructor(text?: string);
16
+ get width(): number;
17
+ set width(value: number);
18
+ get text(): string;
19
+ set text(value: string);
20
+ get font(): string | BitmapFont | undefined;
21
+ set font(value: string | BitmapFont | undefined);
22
+ get textAlign(): 'left' | 'center' | 'right';
23
+ set textAlign(value: 'left' | 'center' | 'right');
24
+ get verticalAlign(): 'top' | 'middle' | 'bottom';
25
+ set verticalAlign(value: 'top' | 'middle' | 'bottom');
26
+ get letterSpacing(): number;
27
+ set letterSpacing(value: number);
28
+ get lineSpacing(): number;
29
+ set lineSpacing(value: number);
30
+ get multiline(): boolean;
31
+ set multiline(value: boolean);
32
+ get smoothing(): boolean;
33
+ set smoothing(value: boolean);
34
+ /**
35
+ * Content metrics under the label's width constraint, before height clipping.
36
+ */
37
+ get textWidth(): number;
38
+ get textHeight(): number;
39
+ createChildren(): void;
40
+ commitProperties(): void;
41
+ measure(): void;
42
+ setLayoutBoundsSize(layoutWidth: number, layoutHeight: number): void;
43
+ updateDisplayList(unscaledWidth: number, unscaledHeight: number): void;
44
+ private _invalidateText;
45
+ private _measureText;
46
+ private _resolveFont;
47
+ }
48
+ //# sourceMappingURL=BitmapLabel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BitmapLabel.d.ts","sourceRoot":"","sources":["../../../src/kurot/components/BitmapLabel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA6C,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,SAAU,YAAW,YAAY;IAEjE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,KAAK,CAAC,CAAsB;IACpC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAO;gBAGZ,IAAI,SAAK;IAO5B,IAAoB,KAAK,IAAI,MAAM,CAElC;IACD,IAAoB,KAAK,CAAC,KAAK,EAAE,MAAM,EAKtC;IAED,IAAW,IAAI,IAAI,MAAM,CAExB;IACD,IAAW,IAAI,CAAC,KAAK,EAAE,MAAM,EAK5B;IACD,IAAW,IAAI,IAAI,MAAM,GAAG,UAAU,GAAG,SAAS,CAEjD;IACD,IAAW,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,EAQrD;IACD,IAAW,SAAS,IAAI,MAAM,GAAG,QAAQ,GAAG,OAAO,CAElD;IACD,IAAW,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,EAItD;IACD,IAAW,aAAa,IAAI,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAEtD;IACD,IAAW,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,EAI1D;IACD,IAAW,aAAa,IAAI,MAAM,CAEjC;IACD,IAAW,aAAa,CAAC,KAAK,EAAE,MAAM,EAIrC;IACD,IAAW,WAAW,IAAI,MAAM,CAE/B;IACD,IAAW,WAAW,CAAC,KAAK,EAAE,MAAM,EAInC;IACD,IAAW,SAAS,IAAI,OAAO,CAE9B;IACD,IAAW,SAAS,CAAC,KAAK,EAAE,OAAO,EAIlC;IACD,IAAW,SAAS,IAAI,OAAO,CAE9B;IACD,IAAW,SAAS,CAAC,KAAK,EAAE,OAAO,EAIlC;IACD;;OAEG;IACH,IAAW,SAAS,IAAI,MAAM,CAE7B;IACD,IAAW,UAAU,IAAI,MAAM,CAE9B;IAGe,cAAc,IAAI,IAAI;IAItB,gBAAgB,IAAI,IAAI;IAMxB,OAAO,IAAI,IAAI;IAIf,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAOpE,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI;IAOtF,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,YAAY;CA0BpB"}
@@ -0,0 +1,196 @@
1
+ import { BitmapFont, BitmapText, Event, IOErrorEvent, resource } from '@kurot/core';
2
+ import { Component } from './Component.js';
3
+ import { PropertyEvent } from '../events/PropertyEvent.js';
4
+ /**
5
+ * UI layout wrapper around BitmapText. String fonts are configured resource
6
+ * names; FontAnalyzer loads the descriptor and its page. Font ownership stays
7
+ * with the resource cache or the caller that supplied a BitmapFont.
8
+ */
9
+ export class BitmapLabel extends Component {
10
+ // ── Instance fields ──────────────────────────────────────────────────────
11
+ _bitmapText = new BitmapText();
12
+ _font;
13
+ _fontChanged = false;
14
+ _fontRequest = 0;
15
+ _widthConstraint = NaN;
16
+ // ── Constructor ──────────────────────────────────────────────────────────
17
+ constructor(text = '') {
18
+ super();
19
+ this.touchChildren = false;
20
+ this.text = text;
21
+ }
22
+ // ── Getters / Setters ─────────────────────────────────────────────────────
23
+ get width() {
24
+ return super.width;
25
+ }
26
+ set width(value) {
27
+ if (Object.is(this.$explicitWidth, value))
28
+ return;
29
+ super.width = value;
30
+ // Component width changes alone do not invalidate automatic text height.
31
+ this._invalidateText();
32
+ }
33
+ get text() {
34
+ return this._bitmapText.text;
35
+ }
36
+ set text(value) {
37
+ if (this.text === value)
38
+ return;
39
+ this._bitmapText.text = value;
40
+ PropertyEvent.dispatchPropertyEvent(this, 'text');
41
+ this._invalidateText();
42
+ }
43
+ get font() {
44
+ return this._font;
45
+ }
46
+ set font(value) {
47
+ if (this._font === value)
48
+ return;
49
+ this._font = value;
50
+ this._fontRequest++;
51
+ this._fontChanged = typeof value === 'string';
52
+ this._bitmapText.font = value instanceof BitmapFont ? value : undefined;
53
+ this.invalidateProperties();
54
+ this._invalidateText();
55
+ }
56
+ get textAlign() {
57
+ return this._bitmapText.textAlign;
58
+ }
59
+ set textAlign(value) {
60
+ if (this.textAlign === value)
61
+ return;
62
+ this._bitmapText.textAlign = value;
63
+ this.invalidateDisplayList();
64
+ }
65
+ get verticalAlign() {
66
+ return this._bitmapText.verticalAlign;
67
+ }
68
+ set verticalAlign(value) {
69
+ if (this.verticalAlign === value)
70
+ return;
71
+ this._bitmapText.verticalAlign = value;
72
+ this.invalidateDisplayList();
73
+ }
74
+ get letterSpacing() {
75
+ return this._bitmapText.letterSpacing;
76
+ }
77
+ set letterSpacing(value) {
78
+ if (this.letterSpacing === value)
79
+ return;
80
+ this._bitmapText.letterSpacing = value;
81
+ this._invalidateText();
82
+ }
83
+ get lineSpacing() {
84
+ return this._bitmapText.lineSpacing;
85
+ }
86
+ set lineSpacing(value) {
87
+ if (this.lineSpacing === value)
88
+ return;
89
+ this._bitmapText.lineSpacing = value;
90
+ this._invalidateText();
91
+ }
92
+ get multiline() {
93
+ return this._bitmapText.multiline;
94
+ }
95
+ set multiline(value) {
96
+ if (this.multiline === value)
97
+ return;
98
+ this._bitmapText.multiline = value;
99
+ this._invalidateText();
100
+ }
101
+ get smoothing() {
102
+ return this._bitmapText.smoothing;
103
+ }
104
+ set smoothing(value) {
105
+ if (this.smoothing === value)
106
+ return;
107
+ this._bitmapText.smoothing = value;
108
+ this.invalidateDisplayList();
109
+ }
110
+ /**
111
+ * Content metrics under the label's width constraint, before height clipping.
112
+ */
113
+ get textWidth() {
114
+ return this._measureText().width;
115
+ }
116
+ get textHeight() {
117
+ return this._measureText().height;
118
+ }
119
+ // ── Override methods ─────────────────────────────────────────────────────
120
+ createChildren() {
121
+ super.createChildren();
122
+ this.addChild(this._bitmapText);
123
+ }
124
+ commitProperties() {
125
+ super.commitProperties();
126
+ if (!this._fontChanged)
127
+ return;
128
+ this._fontChanged = false;
129
+ this._resolveFont();
130
+ }
131
+ measure() {
132
+ const measured = this._measureText();
133
+ this.setMeasuredSize(measured.width, measured.height);
134
+ }
135
+ setLayoutBoundsSize(layoutWidth, layoutHeight) {
136
+ super.setLayoutBoundsSize(layoutWidth, layoutHeight);
137
+ const width = Number.isFinite(layoutWidth) ? this.width : NaN;
138
+ if (Object.is(this._widthConstraint, width))
139
+ return;
140
+ this._widthConstraint = width;
141
+ this._invalidateText();
142
+ }
143
+ updateDisplayList(unscaledWidth, unscaledHeight) {
144
+ super.updateDisplayList(unscaledWidth, unscaledHeight);
145
+ this._bitmapText.width = unscaledWidth;
146
+ this._bitmapText.height = unscaledHeight;
147
+ }
148
+ // ── Private methods ──────────────────────────────────────────────────────
149
+ _invalidateText() {
150
+ this.invalidateSize();
151
+ this.invalidateDisplayList();
152
+ }
153
+ _measureText() {
154
+ const width = Number.isFinite(this._widthConstraint)
155
+ ? this._widthConstraint
156
+ : Number.isFinite(this.$explicitWidth)
157
+ ? this.$explicitWidth
158
+ : this.maxWidth !== 100000
159
+ ? this.maxWidth
160
+ : NaN;
161
+ const layout = this._bitmapText.measureText(width);
162
+ return { width: layout.width, height: layout.height };
163
+ }
164
+ _resolveFont() {
165
+ const name = this._font;
166
+ if (typeof name !== 'string')
167
+ return;
168
+ const request = this._fontRequest;
169
+ const apply = (font) => {
170
+ if (request !== this._fontRequest)
171
+ return;
172
+ if (!(font instanceof BitmapFont))
173
+ throw new TypeError(`Resource ${name} is not a bitmap font.`);
174
+ this._bitmapText.font = font;
175
+ this._invalidateText();
176
+ this.dispatchEventWith(Event.COMPLETE);
177
+ };
178
+ const cached = resource.get(name);
179
+ const report = (error) => {
180
+ if (request !== this._fontRequest)
181
+ return;
182
+ this.dispatchEventWith(IOErrorEvent.IO_ERROR, false, { source: name, error });
183
+ };
184
+ if (cached !== undefined) {
185
+ try {
186
+ apply(cached);
187
+ }
188
+ catch (error) {
189
+ report(error);
190
+ }
191
+ return;
192
+ }
193
+ void resource.load(name).then(apply).catch(report);
194
+ }
195
+ }
196
+ //# sourceMappingURL=BitmapLabel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BitmapLabel.js","sourceRoot":"","sources":["../../../src/kurot/components/BitmapLabel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { ITextElement } from '@kurot/core';
2
+ import { Component } from './Component.js';
3
+ /**
4
+ * Displays styled text runs in one continuous layout. Content is assigned only
5
+ * through textFlow; ordinary Label text and appearance presets are not exposed.
6
+ * Unspecified run styles use the TextField defaults captured at construction.
7
+ */
8
+ export declare class RichLabel extends Component {
9
+ private readonly _textField;
10
+ private _widthConstraint;
11
+ private _measurement?;
12
+ private _measurementWidth;
13
+ constructor(textFlow?: readonly ITextElement[]);
14
+ get width(): number;
15
+ set width(value: number);
16
+ /**
17
+ * Assignment and reads copy the runs and their styles. Reassign an edited
18
+ * snapshot to update the component; [] clears all content.
19
+ */
20
+ get textFlow(): ITextElement[];
21
+ set textFlow(value: readonly ITextElement[]);
22
+ get multiline(): boolean;
23
+ set multiline(value: boolean);
24
+ /**
25
+ * True uses Unicode line-break opportunities; false wraps by graphemes.
26
+ * Explicit multiline=false displays only the first unwrapped hard line.
27
+ */
28
+ get wordWrap(): boolean;
29
+ set wordWrap(value: boolean);
30
+ get lineSpacing(): number;
31
+ set lineSpacing(value: number);
32
+ get textAlign(): 'left' | 'center' | 'right';
33
+ set textAlign(value: 'left' | 'center' | 'right');
34
+ get verticalAlign(): 'top' | 'middle' | 'bottom';
35
+ set verticalAlign(value: 'top' | 'middle' | 'bottom');
36
+ /**
37
+ * Complete content metrics under the current width constraint, before
38
+ * height clipping. Empty content has zero width and height.
39
+ */
40
+ get textWidth(): number;
41
+ get textHeight(): number;
42
+ invalidateSize(): void;
43
+ createChildren(): void;
44
+ measure(): void;
45
+ setLayoutBoundsSize(layoutWidth: number, layoutHeight: number): void;
46
+ updateDisplayList(unscaledWidth: number, unscaledHeight: number): void;
47
+ private _copyTextFlow;
48
+ private _invalidateText;
49
+ private _measureText;
50
+ }
51
+ //# sourceMappingURL=RichLabel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RichLabel.d.ts","sourceRoot":"","sources":["../../../src/kurot/components/RichLabel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,YAAY,EAAiB,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,SAAS;IAGvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,gBAAgB,CAAO;IAC/B,OAAO,CAAC,YAAY,CAAC,CAAoC;IACzD,OAAO,CAAC,iBAAiB,CAAO;gBAIb,QAAQ,GAAE,SAAS,YAAY,EAAO;IASzD,IAAoB,KAAK,IAAI,MAAM,CAElC;IACD,IAAoB,KAAK,CAAC,KAAK,EAAE,MAAM,EAKtC;IAED;;;OAGG;IACH,IAAW,QAAQ,IAAI,YAAY,EAAE,CAEpC;IACD,IAAW,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,EAKjD;IAED,IAAW,SAAS,IAAI,OAAO,CAE9B;IACD,IAAW,SAAS,CAAC,KAAK,EAAE,OAAO,EAIlC;IAED;;;OAGG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IACD,IAAW,QAAQ,CAAC,KAAK,EAAE,OAAO,EAIjC;IAED,IAAW,WAAW,IAAI,MAAM,CAE/B;IACD,IAAW,WAAW,CAAC,KAAK,EAAE,MAAM,EAOnC;IAED,IAAW,SAAS,IAAI,MAAM,GAAG,QAAQ,GAAG,OAAO,CAElD;IACD,IAAW,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,EAOtD;IAED,IAAW,aAAa,IAAI,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAEtD;IACD,IAAW,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,EAO1D;IAED;;;OAGG;IACH,IAAW,SAAS,IAAI,MAAM,CAE7B;IACD,IAAW,UAAU,IAAI,MAAM,CAE9B;IAIe,cAAc,IAAI,IAAI;IAOtB,cAAc,IAAI,IAAI;IAKtB,OAAO,IAAI,IAAI;IAKf,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAQpE,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI;IAQtF,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,YAAY;CAgBpB"}