@gjsify/adwaita-nativescript 0.64.12 → 0.64.13
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/lib/esm/builder/index.js +1 -1
- package/lib/esm/widgets/adw-about-dialog.js +3 -3
- package/lib/esm/widgets/adw-action-row.js +1 -1
- package/lib/esm/widgets/adw-avatar.js +1 -1
- package/lib/esm/widgets/adw-banner.js +1 -1
- package/lib/esm/widgets/adw-bottom-sheet.js +1 -1
- package/lib/esm/widgets/adw-carousel.js +1 -1
- package/lib/esm/widgets/adw-combo-row.js +1 -1
- package/lib/esm/widgets/adw-data-grid.js +1 -1
- package/lib/esm/widgets/adw-dialog.js +1 -1
- package/lib/esm/widgets/adw-image-button.js +1 -1
- package/lib/esm/widgets/adw-navigation-page.js +1 -1
- package/lib/esm/widgets/adw-navigation-split-view.js +1 -1
- package/lib/esm/widgets/adw-navigation-view.js +1 -1
- package/lib/esm/widgets/adw-overlay-split-view.js +1 -1
- package/lib/esm/widgets/adw-password-entry-row.js +1 -1
- package/lib/esm/widgets/adw-preferences-dialog.js +1 -1
- package/lib/esm/widgets/adw-preferences-page.js +1 -1
- package/lib/esm/widgets/adw-sidebar.js +1 -1
- package/lib/esm/widgets/adw-slider-row.js +1 -1
- package/lib/esm/widgets/adw-spin-row.js +1 -1
- package/lib/esm/widgets/adw-split-button.js +1 -1
- package/lib/esm/widgets/adw-tab-view.js +1 -1
- package/lib/esm/widgets/adw-toggle-group.js +1 -1
- package/lib/esm/widgets/adw-toolbar-view.js +1 -1
- package/lib/esm/widgets/adw-view-switcher-bar.js +1 -1
- package/lib/esm/widgets/adw-wrap-box.js +1 -1
- package/lib/esm/widgets/construct-props.js +1 -1
- package/lib/esm/widgets/gtk-action-bar.js +1 -1
- package/lib/esm/widgets/gtk-box.js +1 -1
- package/lib/esm/widgets/gtk-button.js +1 -1
- package/lib/esm/widgets/gtk-check-button.js +1 -1
- package/lib/esm/widgets/gtk-drawing-area.js +1 -1
- package/lib/esm/widgets/gtk-drop-down.js +1 -1
- package/lib/esm/widgets/gtk-entry.js +1 -1
- package/lib/esm/widgets/gtk-grid.js +1 -1
- package/lib/esm/widgets/gtk-icon-size.js +1 -1
- package/lib/esm/widgets/gtk-image.js +1 -1
- package/lib/esm/widgets/gtk-label.js +2 -2
- package/lib/esm/widgets/gtk-list-box.js +1 -1
- package/lib/esm/widgets/gtk-menu-button.js +1 -1
- package/lib/esm/widgets/gtk-revealer.js +1 -1
- package/lib/esm/widgets/gtk-scrolled-window.js +1 -1
- package/lib/esm/widgets/gtk-stack.js +1 -1
- package/lib/esm/widgets/gtk-text-view.js +1 -1
- package/lib/esm/widgets/gtk-toggle-button.js +1 -1
- package/lib/esm/widgets/gtk-window.js +1 -1
- package/lib/esm/widgets/label-markup.js +1 -0
- package/lib/esm/widgets/scrolled-window-policy.js +1 -1
- package/lib/esm/widgets/signals.js +1 -1
- package/lib/esm/widgets/single-child-base.js +1 -1
- package/lib/esm/widgets/single-child-cell.js +1 -0
- package/lib/esm/widgets/stack-page.js +1 -1
- package/lib/esm/widgets/transition-type.js +1 -1
- package/lib/esm/widgets/view-stack-page.js +1 -1
- package/lib/esm/widgets/widget-layout.js +1 -1
- package/lib/esm/widgets/xml-values.js +1 -1
- package/lib/types/builder/index.d.ts +4 -2
- package/lib/types/clamped-bin.spec.d.ts +2 -0
- package/lib/types/combo-row-properties.spec.d.ts +2 -0
- package/lib/types/default-translate.spec.d.ts +1 -0
- package/lib/types/drop-down-properties.spec.d.ts +2 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/label-markup.spec.d.ts +1 -0
- package/lib/types/single-child-cell.spec.d.ts +2 -0
- package/lib/types/testing/ns-core.d.mts +115 -3
- package/lib/types/widgets/adw-banner.d.ts +2 -2
- package/lib/types/widgets/adw-combo-row.d.ts +42 -26
- package/lib/types/widgets/gtk-drop-down.d.ts +41 -33
- package/lib/types/widgets/gtk-label.d.ts +29 -8
- package/lib/types/widgets/index.d.ts +1 -2
- package/lib/types/widgets/label-markup.d.ts +43 -0
- package/lib/types/widgets/signals.d.ts +54 -0
- package/lib/types/widgets/single-child-base.d.ts +2 -0
- package/lib/types/widgets/single-child-cell.d.ts +8 -0
- package/lib/types/widgets/xml-values.d.ts +12 -0
- package/package.json +12 -12
- package/src/testing/ns-core.mts +231 -3
|
@@ -21,6 +21,43 @@ type Declared<P> = P & {
|
|
|
21
21
|
* here, since a write that resolves to the value already held is not a change.
|
|
22
22
|
*/
|
|
23
23
|
readonly normalize?: (value: unknown, self: object) => unknown;
|
|
24
|
+
/**
|
|
25
|
+
* Replaces the equal-write check for this property: `written` is the value as the caller
|
|
26
|
+
* spelled it, `next` what {@link Declared.normalize} made of it, `held` what the store holds
|
|
27
|
+
* — and the answer decides whether the write notifies at all.
|
|
28
|
+
*
|
|
29
|
+
* WHY. A C setter guards on `g_strcmp0 (value, adw_…_get_…(self))`, against the GETTER
|
|
30
|
+
* READBACK rather than the stored field. Where storage normalises, the two differ and the
|
|
31
|
+
* guard can never fire: `GtkImage` turns an empty icon name into "no icon", so writing `''`
|
|
32
|
+
* to a button that already reads back `null` notifies EVERY time (measured on GJS 1.88.1 /
|
|
33
|
+
* libadwaita 1.9.4). Comparing the held value says the opposite, and only the property whose
|
|
34
|
+
* `normalize` collapses distinct writes onto one held value can tell the difference.
|
|
35
|
+
*
|
|
36
|
+
* RUNS ON BOTH LEGS. It used to run on the pure one alone, on the reasoning that "there the
|
|
37
|
+
* guard IS the C one" — which holds for a widget wrapping a real C object and not for a
|
|
38
|
+
* foreign-parent class, which has no C setter at all. On route D node-gi's generated setter
|
|
39
|
+
* is an identity guard and nothing more (`installForeignPropertyAccessors` in `gi.js`), so a
|
|
40
|
+
* property that left this to the real leg got the identity answer there and the measured one
|
|
41
|
+
* off-device — the per-leg difference this seam exists to close.
|
|
42
|
+
*/
|
|
43
|
+
readonly changed?: (next: unknown, held: unknown, written: unknown, self: object) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The rest of the C setter: runs once this property's own `notify::<name>` is out, so a
|
|
46
|
+
* setter that also drives OTHER properties emits in the order C does. For a property with
|
|
47
|
+
* {@link ForeignScalarProperty.explicitNotify} it runs INSTEAD of that notify, and owes every
|
|
48
|
+
* emission the write makes, its own included.
|
|
49
|
+
*
|
|
50
|
+
* WHY IT IS NOT `normalize`. A normalizer runs AHEAD of the store, so a cross-write made
|
|
51
|
+
* there notifies before the property that caused it. `Adw.SplitButton` measures the other way
|
|
52
|
+
* round — `label = 'Open'` on an icon button emits `label` first and the slots it clears
|
|
53
|
+
* after — and the two cannot both be one hook. The SHAPE of a value still belongs to
|
|
54
|
+
* `normalize`; what the write does to its siblings belongs here.
|
|
55
|
+
*
|
|
56
|
+
* RUNS ON BOTH LEGS, for the reason {@link Declared.changed} states: the foreign-parent class
|
|
57
|
+
* route D registers has no C setter to do this instead. `adw-split-button`'s label/icon-name
|
|
58
|
+
* mutual clear is the one that was silently missing on Android because of it.
|
|
59
|
+
*/
|
|
60
|
+
readonly afterNotify?: (self: object) => void;
|
|
24
61
|
};
|
|
25
62
|
/** A declared property of any kind, with its optional {@link Declared.normalize}. */
|
|
26
63
|
export type DeclaredProperty = Declared<ForeignScalarProperty> | Declared<ForeignObjectProperty>;
|
|
@@ -194,4 +231,21 @@ export declare function withProperties<TBase extends ObservableConstructor>(Base
|
|
|
194
231
|
/** Only what this class ADDS; `emittedSignalsOf` merges it with the base's. */
|
|
195
232
|
readonly emittedSignals: readonly string[];
|
|
196
233
|
}) & TBase;
|
|
234
|
+
/**
|
|
235
|
+
* Write a declared property's held value WITHOUT notifying, and without running `normalize`,
|
|
236
|
+
* `changed` or `afterNotify` — the PUSH half of a property whose widget owns the emission.
|
|
237
|
+
*
|
|
238
|
+
* WHY A WIDGET NEEDS IT. A property whose value also moves from UNDERNEATH — the selection of an
|
|
239
|
+
* `Adw.ComboRow` when its model is replaced, or when the held model splices — cannot be kept in
|
|
240
|
+
* sync by writing it, because the write is the thing that emits and the emission order is
|
|
241
|
+
* measured and is not the order a write would produce. `model = null` is the sharpest case:
|
|
242
|
+
* `selected` goes to the sentinel and `selected-item` to null with NO notify at all, and `model`
|
|
243
|
+
* alone notifies. So the widget pushes the new value in here and emits what it owes with
|
|
244
|
+
* `notify('<name>')`, in the order it measured.
|
|
245
|
+
*
|
|
246
|
+
* Only a {@link isSeamOwned} property may be pushed, and a name that is not one THROWS rather
|
|
247
|
+
* than writing a store route D would never read back: there the value lives in the GObject, and
|
|
248
|
+
* a silent push would be correct off-device and invisible on the device.
|
|
249
|
+
*/
|
|
250
|
+
export declare function pushDeclared(self: object, name: string, value: unknown): void;
|
|
197
251
|
export {};
|
|
@@ -5,6 +5,8 @@ export declare abstract class AdwSingleChildBase extends AdwStyledLayoutBase {
|
|
|
5
5
|
constructor();
|
|
6
6
|
private _columnIsStar;
|
|
7
7
|
onLoaded(): void;
|
|
8
|
+
onUnloaded(): void;
|
|
9
|
+
private _onWidthChangeSyncColumn;
|
|
8
10
|
private _syncColumnToAlignment;
|
|
9
11
|
/** The child, or `null`. */
|
|
10
12
|
get child(): View | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the single cell of a one-child container is `star` rather than `auto`.
|
|
3
|
+
*
|
|
4
|
+
* `star` when the container's width is settled — either because it stretches to its parent,
|
|
5
|
+
* or because a definite `width` was written on it. `auto` otherwise, so the container can
|
|
6
|
+
* still report its child's natural width and be placed by its alignment.
|
|
7
|
+
*/
|
|
8
|
+
export declare function singleChildCellIsStar(horizontalAlignment: unknown, width: unknown): boolean;
|
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
* caller can keep validating a RANGE without also having to re-check the type.
|
|
7
7
|
*/
|
|
8
8
|
export declare function xmlNumber(value: unknown, fallback: number): number;
|
|
9
|
+
/**
|
|
10
|
+
* The integer GtkBuilder reads out of an enum property's value, or `undefined` when the
|
|
11
|
+
* value is a nick or name.
|
|
12
|
+
*
|
|
13
|
+
* `_gtk_builder_enum_from_string` (gtk/gtkbuilder.c) tries the number FIRST
|
|
14
|
+
* (`g_ascii_strtoull (string, &endptr, 0)`, so base 0: decimal, `0x` hex, leading-`0` octal)
|
|
15
|
+
* and only then the nick and the name — which is why `halign="1"` and `halign="start"` are
|
|
16
|
+
* the same attribute in a `.ui` file. A JS number is the same reading, already parsed.
|
|
17
|
+
* GTK also accepts a numeric PREFIX (`"2abc"` is 2); the whole string must be a number here,
|
|
18
|
+
* so a nick that merely starts with a digit stays a nick.
|
|
19
|
+
*/
|
|
20
|
+
export declare function gtkBuilderInteger(value: unknown): number | undefined;
|
|
9
21
|
/**
|
|
10
22
|
* An attribute as a boolean.
|
|
11
23
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/adwaita-nativescript",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.13",
|
|
4
4
|
"description": "Native Adwaita widgets for the NativeScript runtime — real NativeScript components (StackLayout/GridLayout/Switch/Label) styled like Libadwaita, not a webview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -73,15 +73,15 @@
|
|
|
73
73
|
"design-system"
|
|
74
74
|
],
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@gjsify/adwaita-core": "^0.64.
|
|
77
|
-
"@gjsify/adwaita-fonts": "^0.64.
|
|
78
|
-
"@gjsify/adwaita-icons": "^0.64.
|
|
79
|
-
"@gjsify/canvas2d-core": "^0.64.
|
|
80
|
-
"@gjsify/native-platform": "^0.64.
|
|
76
|
+
"@gjsify/adwaita-core": "^0.64.13",
|
|
77
|
+
"@gjsify/adwaita-fonts": "^0.64.13",
|
|
78
|
+
"@gjsify/adwaita-icons": "^0.64.13",
|
|
79
|
+
"@gjsify/canvas2d-core": "^0.64.13",
|
|
80
|
+
"@gjsify/native-platform": "^0.64.13"
|
|
81
81
|
},
|
|
82
82
|
"peerDependencies": {
|
|
83
83
|
"@nativescript/core": "*",
|
|
84
|
-
"@gjsify/app-settings": "^0.64.
|
|
84
|
+
"@gjsify/app-settings": "^0.64.13"
|
|
85
85
|
},
|
|
86
86
|
"peerDependenciesMeta": {
|
|
87
87
|
"@nativescript/core": {
|
|
@@ -92,11 +92,11 @@
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@gjsify/app-settings": "^0.64.
|
|
96
|
-
"@gjsify/blueprint": "^0.64.
|
|
97
|
-
"@gjsify/cli": "^0.64.
|
|
98
|
-
"@gjsify/unit": "^0.64.
|
|
99
|
-
"@gjsify/vite-plugin-blueprint": "^0.64.
|
|
95
|
+
"@gjsify/app-settings": "^0.64.13",
|
|
96
|
+
"@gjsify/blueprint": "^0.64.13",
|
|
97
|
+
"@gjsify/cli": "^0.64.13",
|
|
98
|
+
"@gjsify/unit": "^0.64.13",
|
|
99
|
+
"@gjsify/vite-plugin-blueprint": "^0.64.13",
|
|
100
100
|
"@types/node": "^25.9.2",
|
|
101
101
|
"typescript": "^6.0.3"
|
|
102
102
|
},
|
package/src/testing/ns-core.mts
CHANGED
|
@@ -173,8 +173,12 @@ function unloadView(view: View): void {
|
|
|
173
173
|
*/
|
|
174
174
|
export class Style extends Observable {
|
|
175
175
|
direction?: 'ltr' | 'rtl' | null = null;
|
|
176
|
+
/** In DIPs, and unset until written: `fontSizeProperty` carries no `defaultValue`. */
|
|
177
|
+
fontSize?: number;
|
|
176
178
|
readonly viewRef: WeakRef<View>;
|
|
177
179
|
#visibility = 'visible';
|
|
180
|
+
#width: number | string = 'auto';
|
|
181
|
+
#height: number | string = 'auto';
|
|
178
182
|
|
|
179
183
|
constructor(view: View) {
|
|
180
184
|
super();
|
|
@@ -191,6 +195,33 @@ export class Style extends Observable {
|
|
|
191
195
|
this.#visibility = value;
|
|
192
196
|
this.notify({ eventName: 'visibilityChange', object: this, propertyName: 'visibility', value, oldValue });
|
|
193
197
|
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The inline width, in DIPs or as a percentage string, `'auto'` until written — and the
|
|
201
|
+
* `widthChange` a one-child container listens for. A CSS property notifies on the style
|
|
202
|
+
* and not on the view, which is the whole reason this pair lives here.
|
|
203
|
+
*/
|
|
204
|
+
get width(): number | string {
|
|
205
|
+
return this.#width;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
set width(value: number | string) {
|
|
209
|
+
const oldValue = this.#width;
|
|
210
|
+
if (value === oldValue) return;
|
|
211
|
+
this.#width = value;
|
|
212
|
+
this.notify({ eventName: 'widthChange', object: this, propertyName: 'width', value, oldValue });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
get height(): number | string {
|
|
216
|
+
return this.#height;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
set height(value: number | string) {
|
|
220
|
+
const oldValue = this.#height;
|
|
221
|
+
if (value === oldValue) return;
|
|
222
|
+
this.#height = value;
|
|
223
|
+
this.notify({ eventName: 'heightChange', object: this, propertyName: 'height', value, oldValue });
|
|
224
|
+
}
|
|
194
225
|
}
|
|
195
226
|
|
|
196
227
|
export class View extends Observable {
|
|
@@ -232,8 +263,32 @@ export class View extends Observable {
|
|
|
232
263
|
* missing so the first `className` write died inside a constructor.
|
|
233
264
|
*/
|
|
234
265
|
readonly cssClasses: Set<string> = new Set();
|
|
235
|
-
|
|
236
|
-
|
|
266
|
+
/**
|
|
267
|
+
* `'auto'` until written, and HELD ON THE {@link Style} as `widthProperty` /
|
|
268
|
+
* `heightProperty` are (`ui/styling/style-properties.ts` registers both on `Style`;
|
|
269
|
+
* `view-common.ts`'s `get width()` is a one-line forward to `this.style.width`).
|
|
270
|
+
*
|
|
271
|
+
* This double held them as plain fields on the view, which made it STRUCTURALLY BLIND to
|
|
272
|
+
* the one thing a reader needs from them: the notification. `AdwSingleChildBase` learns
|
|
273
|
+
* that a parent settled its width from `widthChange` on the style — on a device the only
|
|
274
|
+
* place it is emitted — and against a field there is no event at all, so a spec would
|
|
275
|
+
* have passed while the widget sat waiting for a message that never came.
|
|
276
|
+
*/
|
|
277
|
+
get width(): number | string {
|
|
278
|
+
return this._style.width;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
set width(value: number | string) {
|
|
282
|
+
this._style.width = value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
get height(): number | string {
|
|
286
|
+
return this._style.height;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
set height(value: number | string) {
|
|
290
|
+
this._style.height = value;
|
|
291
|
+
}
|
|
237
292
|
horizontalAlignment = 'stretch';
|
|
238
293
|
verticalAlignment = 'stretch';
|
|
239
294
|
/**
|
|
@@ -663,8 +718,181 @@ export class GridLayout extends LayoutBase {
|
|
|
663
718
|
}
|
|
664
719
|
}
|
|
665
720
|
|
|
721
|
+
/**
|
|
722
|
+
* A `Span`'s style — NOT a {@link View}'s {@link Style}.
|
|
723
|
+
*
|
|
724
|
+
* `Span extends ViewBase` upstream, so it has none of `View`'s shortcuts and carries only
|
|
725
|
+
* what a span can be given. Every member is unset until written, which is what the font
|
|
726
|
+
* properties answer on a device: `fontSizeProperty` and `fontFamilyProperty` are registered
|
|
727
|
+
* with no `defaultValue` (`ui/styling/style-properties.ts`).
|
|
728
|
+
*/
|
|
729
|
+
export class SpanStyle extends Observable {
|
|
730
|
+
fontFamily: string | undefined;
|
|
731
|
+
fontSize: number | undefined;
|
|
732
|
+
fontStyle: string | undefined;
|
|
733
|
+
fontWeight: string | undefined;
|
|
734
|
+
textDecoration: string | undefined;
|
|
735
|
+
verticalAlignment: string | undefined;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* One styled stretch of a {@link FormattedString}.
|
|
740
|
+
*
|
|
741
|
+
* The font and decoration members are ACCESSORS OVER `style`, as `ui/text-base/span.ts`
|
|
742
|
+
* has them — a double that made them own fields could not report a widget that wrote one
|
|
743
|
+
* and expected the other to see it.
|
|
744
|
+
*/
|
|
745
|
+
export class Span extends Observable {
|
|
746
|
+
static readonly linkTapEvent = 'linkTap';
|
|
747
|
+
|
|
748
|
+
readonly _style: SpanStyle = new SpanStyle();
|
|
749
|
+
|
|
750
|
+
get style(): SpanStyle {
|
|
751
|
+
return this._style;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/** NativeScript unescapes the two whitespace spellings a markup file can carry. */
|
|
755
|
+
private _text = '';
|
|
756
|
+
|
|
757
|
+
get text(): string {
|
|
758
|
+
return this._text;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
set text(value: string) {
|
|
762
|
+
this._text = String(value ?? '')
|
|
763
|
+
.replace(/\\n/g, '\n')
|
|
764
|
+
.replace(/\\t/g, '\t');
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
get fontFamily(): string | undefined {
|
|
768
|
+
return this._style.fontFamily;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
set fontFamily(value: string | undefined) {
|
|
772
|
+
this._style.fontFamily = value;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
get fontSize(): number | undefined {
|
|
776
|
+
return this._style.fontSize;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
set fontSize(value: number | undefined) {
|
|
780
|
+
this._style.fontSize = value;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
get fontStyle(): string | undefined {
|
|
784
|
+
return this._style.fontStyle;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
set fontStyle(value: string | undefined) {
|
|
788
|
+
this._style.fontStyle = value;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
get fontWeight(): string | undefined {
|
|
792
|
+
return this._style.fontWeight;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
set fontWeight(value: string | undefined) {
|
|
796
|
+
this._style.fontWeight = value;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
get textDecoration(): string | undefined {
|
|
800
|
+
return this._style.textDecoration;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
set textDecoration(value: string | undefined) {
|
|
804
|
+
this._style.textDecoration = value;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* DERIVED, never written — upstream flips it from `addEventListener` /
|
|
809
|
+
* `removeEventListener` through `_setTappable(this.hasListeners(Span.linkTapEvent))`,
|
|
810
|
+
* and `setSpanModifiers` attaches the native `ClickableSpan` only for a tappable span.
|
|
811
|
+
* So subscribing IS what makes a span a link, and this reads the listeners rather than
|
|
812
|
+
* a flag a caller could set without one.
|
|
813
|
+
*/
|
|
814
|
+
get tappable(): boolean {
|
|
815
|
+
return (listenersOf(this).get(Span.linkTapEvent)?.length ?? 0) > 0;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* The `ObservableArray<Span>` behind {@link FormattedString.spans}, as the four members
|
|
821
|
+
* that collection really offers.
|
|
822
|
+
*
|
|
823
|
+
* NOT A PLAIN ARRAY, and a double that used one would be the easier lie: `ObservableArray`
|
|
824
|
+
* reads by `getItem(pos)` and has no index access at all, so `spans[0]` is `undefined` on a
|
|
825
|
+
* device. A spec written against an array would pass here and read nothing there.
|
|
826
|
+
*/
|
|
827
|
+
export class SpanList {
|
|
828
|
+
private readonly _items: Span[] = [];
|
|
829
|
+
|
|
830
|
+
get length(): number {
|
|
831
|
+
return this._items.length;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
push(span: Span): number {
|
|
835
|
+
return this._items.push(span);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/** A negative `pos` counts from the end, as upstream's does. */
|
|
839
|
+
getItem(pos: number): Span {
|
|
840
|
+
return this._items[pos < 0 ? this._items.length + pos : pos]!;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
*[Symbol.iterator](): Generator<Span, void, unknown> {
|
|
844
|
+
yield* this._items;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/** The rich-text value of a {@link Label} — a FLAT list of spans, no nesting. */
|
|
849
|
+
export class FormattedString extends Observable {
|
|
850
|
+
readonly spans: SpanList = new SpanList();
|
|
851
|
+
|
|
852
|
+
/** `formatted-string.ts`: the span texts concatenated, nothing between them. */
|
|
853
|
+
toString(): string {
|
|
854
|
+
let text = '';
|
|
855
|
+
for (const span of this.spans) text += span.text;
|
|
856
|
+
return text;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
666
860
|
export class Label extends View {
|
|
667
|
-
|
|
861
|
+
private _text = '';
|
|
862
|
+
private _formattedText: FormattedString | null | undefined;
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* A write is a NO-OP while {@link formattedText} is set, as on a device:
|
|
866
|
+
* `[textProperty.setNative]` returns early when the label carries rich text
|
|
867
|
+
* (`ui/text-base/index.android.ts:192-195`), so the spans are what shows and this
|
|
868
|
+
* property is whatever the spans last said.
|
|
869
|
+
*/
|
|
870
|
+
get text(): string {
|
|
871
|
+
return this._text;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
set text(value: string) {
|
|
875
|
+
if (this._formattedText === null || this._formattedText === undefined) this._text = value;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Unset until something writes it — `formattedTextProperty` carries no `defaultValue`
|
|
880
|
+
* (`ui/text-base/text-base-common.ts:194`), so a label that was never given rich text
|
|
881
|
+
* reads `undefined`, not `null`.
|
|
882
|
+
*/
|
|
883
|
+
get formattedText(): FormattedString | null | undefined {
|
|
884
|
+
return this._formattedText;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
set formattedText(value: FormattedString | null | undefined) {
|
|
888
|
+
this._formattedText = value;
|
|
889
|
+
// The device re-derives the plain text from the spans on BOTH legs:
|
|
890
|
+
// `[formattedTextProperty.setNative]` calls
|
|
891
|
+
// `textProperty.nativeValueChange(this, value?.toString() ?? '')`, which is why a
|
|
892
|
+
// `text` written BEFORE the clear would be wiped to the empty string.
|
|
893
|
+
this._text = value?.toString() ?? '';
|
|
894
|
+
}
|
|
895
|
+
|
|
668
896
|
textWrap = false;
|
|
669
897
|
textAlignment = 'initial';
|
|
670
898
|
textOverflow = 'initial';
|