@gjsify/adwaita-web 0.64.1 → 0.64.2-next.252
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 +12 -8
- package/dist/adwaita-web.css +39 -9
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/application.d.ts +1 -1
- package/lib/types/child-slot.spec.d.ts +1 -0
- package/lib/types/container-fill.spec.d.ts +1 -0
- package/lib/types/elements/adw-alert-dialog.d.ts +77 -17
- package/lib/types/elements/adw-toast-overlay.d.ts +8 -29
- package/lib/types/elements/adw-window-features.d.ts +8 -0
- package/lib/types/elements/gtk-application-window.d.ts +1 -4
- package/lib/types/elements/gtk-emoji-chooser.d.ts +2 -0
- package/lib/types/elements/gtk-popover-menu.d.ts +2 -0
- package/lib/types/elements/gtk-popover.d.ts +5 -0
- package/lib/types/elements/gtk-stack-page.d.ts +28 -0
- package/lib/types/elements/gtk-stack.d.ts +11 -0
- package/lib/types/elements/gtk-window.d.ts +16 -1
- package/lib/types/gobject-elements.d.ts +9 -0
- package/lib/types/index.d.ts +2 -2
- package/lib/types/learn6502-window.spec.d.ts +1 -0
- package/lib/types/namespace/adw.d.ts +1 -1
- package/lib/types/namespace/gtk.d.ts +2 -0
- package/lib/types/shared-tree-builder.d.ts +31 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/widget-visible.spec.d.ts +1 -0
- package/lib/types/window-maximize.spec.d.ts +1 -0
- package/package.json +13 -12
- package/scss/_bin.scss +6 -3
- package/scss/_overlay.scss +11 -1
- package/scss/_reset.scss +1 -1
- package/scss/_stack.scss +8 -2
- package/scss/_toast.scss +13 -3
- package/scss/_window.scss +16 -0
- package/src/adw-alert-dialog.spec.ts +71 -5
- package/src/adw-bin.spec.ts +4 -3
- package/src/adw-header-bar.spec.ts +18 -0
- package/src/adw-toast-overlay.spec.ts +68 -24
- package/src/application.spec.ts +44 -0
- package/src/application.ts +13 -18
- package/src/capabilities.mjs +1 -3
- package/src/child-slot.spec.ts +71 -0
- package/src/construct-vectors.spec.ts +51 -6
- package/src/container-fill.spec.ts +70 -0
- package/src/elements/adw-alert-dialog.ts +174 -44
- package/src/elements/adw-application-window.ts +0 -3
- package/src/elements/adw-breakpoint-bin.ts +3 -0
- package/src/elements/adw-dialog.ts +3 -1
- package/src/elements/adw-header-bar.ts +4 -0
- package/src/elements/adw-toast-overlay.ts +35 -69
- package/src/elements/adw-window-features.ts +14 -2
- package/src/elements/gtk-application-window.ts +3 -8
- package/src/elements/gtk-drag-icon.ts +4 -0
- package/src/elements/gtk-emoji-chooser.ts +5 -0
- package/src/elements/gtk-flow-box-child.ts +3 -1
- package/src/elements/gtk-graphics-offload.ts +3 -1
- package/src/elements/gtk-list-box-row.ts +3 -1
- package/src/elements/gtk-overlay.ts +5 -4
- package/src/elements/gtk-paned.ts +7 -1
- package/src/elements/gtk-popover-bin.ts +3 -0
- package/src/elements/gtk-popover-menu.ts +5 -0
- package/src/elements/gtk-popover.ts +12 -0
- package/src/elements/gtk-search-bar.ts +5 -5
- package/src/elements/gtk-stack-page.ts +111 -0
- package/src/elements/gtk-stack.ts +50 -5
- package/src/elements/gtk-tree-expander.ts +3 -0
- package/src/elements/gtk-viewport.ts +4 -0
- package/src/elements/gtk-window-handle.ts +4 -0
- package/src/elements/gtk-window.ts +55 -6
- package/src/gobject-elements.ts +28 -0
- package/src/gtk-stack.spec.ts +170 -0
- package/src/index.ts +2 -2
- package/src/keyboard-operable.spec.ts +3 -3
- package/src/learn6502-window.spec.ts +113 -0
- package/src/namespace/adw.ts +1 -1
- package/src/namespace/gtk.ts +2 -0
- package/src/shared-tree-builder.ts +105 -10
- package/src/shared-trees.spec.ts +15 -1
- package/src/slotted-children.spec.ts +24 -0
- package/src/slotted-children.ts +18 -2
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +10 -0
- package/src/value-lists.spec.ts +3 -3
- package/src/widget-visible.spec.ts +104 -0
- package/src/window-maximize.spec.ts +93 -0
|
@@ -2,44 +2,34 @@
|
|
|
2
2
|
// content and shows transient toast strips (title, optional action button, close
|
|
3
3
|
// affordance) layered over the bottom of its own bounds.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// A toast is an `Adw.Toast` and goes in through `add_toast(toast)`, exactly as on GTK, so a
|
|
6
|
+
// GNOME app's toast code runs here unchanged. The QUEUE — one toast at a time, priority,
|
|
7
|
+
// re-add, auto-dismiss — is HEADLESS and lives in `@gjsify/adwaita-core` (ADR 0004) as
|
|
8
|
+
// {@link ToastOverlayQueue}; this element keeps only the DOM half: build the strip on
|
|
9
|
+
// `onShow`, tear it down on `onHide`, run the enter/exit transitions.
|
|
9
10
|
//
|
|
10
11
|
// Adapted from Adwaita Web UI Framework (https://github.com/mclellac/adwaita-web).
|
|
11
12
|
// Reference: refs/adwaita-web/adwaita-web/scss/_toast.scss, _toast_overlay.scss
|
|
12
|
-
// Reference: refs/libadwaita/src/adw-toast-overlay.c, adw-toast.c (add_toast queues,
|
|
13
|
-
// one toast is shown at a time,
|
|
13
|
+
// Reference: refs/libadwaita/src/adw-toast-overlay.c, adw-toast-widget.c (add_toast queues,
|
|
14
|
+
// one toast is shown at a time, the button emits button-clicked, activates the action and
|
|
15
|
+
// dismisses)
|
|
14
16
|
// Copyright (c) 2025 csm (adwaita-web). MIT License.
|
|
15
17
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
16
|
-
// Modifications: Reimplemented as Web Component for @gjsify/adwaita-web;
|
|
17
|
-
// extended addToast() with action button + close button + options API; the
|
|
18
|
+
// Modifications: Reimplemented as Web Component for @gjsify/adwaita-web; the
|
|
18
19
|
// queue/auto-dismiss state machine composed from @gjsify/adwaita-core.
|
|
19
20
|
|
|
20
|
-
import {
|
|
21
|
+
import { ToastOverlayQueue } from '@gjsify/adwaita-core';
|
|
21
22
|
|
|
23
|
+
import { activateWidgetAction } from '../application.js';
|
|
24
|
+
import { renderPangoMarkup } from '../pango-markup.js';
|
|
22
25
|
import { bindSlottedChildren } from '../slotted-children.js';
|
|
23
26
|
import { createGtkImage } from './gtk-image.js';
|
|
24
|
-
import type { ToastScheduler, ToastTimerHandle } from '@gjsify/adwaita-core';
|
|
27
|
+
import type { Toast, ToastScheduler, ToastTimerHandle } from '@gjsify/adwaita-core';
|
|
25
28
|
|
|
26
29
|
// The timing seam is re-exported so a consumer can type a custom scheduler for
|
|
27
30
|
// {@link AdwToastOverlay.scheduler} without depending on the core package.
|
|
28
31
|
export type { ToastScheduler, ToastTimerHandle } from '@gjsify/adwaita-core';
|
|
29
32
|
|
|
30
|
-
/** Options for {@link AdwToastOverlay.addToast}, mirroring Adw.Toast's properties. */
|
|
31
|
-
export interface AdwToastOptions {
|
|
32
|
-
/** Seconds before the toast auto-dismisses; `0` keeps it until dismissed. Mirrors Adw.Toast:timeout (default 5). */
|
|
33
|
-
timeout?: number;
|
|
34
|
-
/** Label for the action button; omitted/empty means no button. Mirrors Adw.Toast:button-label. */
|
|
35
|
-
buttonLabel?: string;
|
|
36
|
-
/** Invoked when the action button is pressed. Mirrors the activation of Adw.Toast:action-name. */
|
|
37
|
-
onAction?: () => void;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Default toast lifetime in SECONDS — this element's public unit (Adw.Toast:timeout). */
|
|
41
|
-
const DEFAULT_TIMEOUT_SECONDS = 5;
|
|
42
|
-
|
|
43
33
|
/**
|
|
44
34
|
* Fallback budget in ms for the exit transition: must stay at or above the
|
|
45
35
|
* `.adw-toast.hiding` transition-duration in `_toast.scss` (0.15s today), because it
|
|
@@ -56,18 +46,16 @@ const domScheduler: ToastScheduler = {
|
|
|
56
46
|
export class AdwToastOverlay extends HTMLElement {
|
|
57
47
|
private _toasts!: HTMLDivElement;
|
|
58
48
|
private _initialized = false;
|
|
59
|
-
/** The headless
|
|
60
|
-
private readonly _queue:
|
|
49
|
+
/** The headless queue of `adw-toast-overlay.c` (ADR 0004). */
|
|
50
|
+
private readonly _queue: ToastOverlayQueue;
|
|
61
51
|
/** The timing source the queue's auto-dismiss runs on — swappable, see {@link scheduler}. */
|
|
62
52
|
private _scheduler: ToastScheduler = domScheduler;
|
|
63
|
-
/** The strip currently mounted for {@link
|
|
53
|
+
/** The strip currently mounted for {@link ToastOverlayQueue.current}, if any. */
|
|
64
54
|
private _currentEl: HTMLDivElement | null = null;
|
|
65
|
-
/** Per-toast action callbacks (the core toast descriptor carries only the label). */
|
|
66
|
-
private readonly _actions = new WeakMap<AdwToast, () => void>();
|
|
67
55
|
|
|
68
56
|
constructor() {
|
|
69
57
|
super();
|
|
70
|
-
this._queue = new
|
|
58
|
+
this._queue = new ToastOverlayQueue({
|
|
71
59
|
// Indirect through `this._scheduler` so the timing source stays
|
|
72
60
|
// swappable after construction (the queue takes its scheduler once).
|
|
73
61
|
scheduler: {
|
|
@@ -97,7 +85,7 @@ export class AdwToastOverlay extends HTMLElement {
|
|
|
97
85
|
this._toasts = document.createElement('div');
|
|
98
86
|
this._toasts.className = 'adw-toast-overlay-toasts';
|
|
99
87
|
|
|
100
|
-
bindSlottedChildren(this, [{ into: content }]).install(content, this._toasts);
|
|
88
|
+
bindSlottedChildren(this, [{ name: 'child', into: content }, { into: content }]).install(content, this._toasts);
|
|
101
89
|
}
|
|
102
90
|
|
|
103
91
|
/**
|
|
@@ -115,7 +103,7 @@ export class AdwToastOverlay extends HTMLElement {
|
|
|
115
103
|
}
|
|
116
104
|
|
|
117
105
|
/** The toast currently on screen, or `null` when nothing is shown. */
|
|
118
|
-
get currentToast():
|
|
106
|
+
get currentToast(): Toast | null {
|
|
119
107
|
return this._queue.current;
|
|
120
108
|
}
|
|
121
109
|
|
|
@@ -124,45 +112,21 @@ export class AdwToastOverlay extends HTMLElement {
|
|
|
124
112
|
return this._queue.pending;
|
|
125
113
|
}
|
|
126
114
|
|
|
127
|
-
/**
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
* immediately when the slot is free, otherwise it waits its turn.
|
|
131
|
-
*
|
|
132
|
-
* `options` is either an {@link AdwToastOptions} bag or, as a legacy shorthand, a
|
|
133
|
-
* timeout in MILLISECONDS.
|
|
134
|
-
*/
|
|
135
|
-
addToast(title: string, options: number | AdwToastOptions = {}): void {
|
|
136
|
-
// Ensure the toast container exists even if addToast() races a not-yet
|
|
115
|
+
/** `adw_toast_overlay_add_toast`: show `toast` now, or queue it by its priority. */
|
|
116
|
+
add_toast(toast: Toast): void {
|
|
117
|
+
// Ensure the toast container exists even if add_toast() races a not-yet
|
|
137
118
|
// connected element (mirrors Adw.ToastOverlay queuing before realize).
|
|
138
119
|
if (!this._toasts) this.connectedCallback();
|
|
139
|
-
|
|
140
|
-
// Back-compat: the original signature was addToast(title, timeoutMs).
|
|
141
|
-
const opts: AdwToastOptions = typeof options === 'number' ? { timeout: options / 1000 } : options;
|
|
142
|
-
|
|
143
|
-
// This element's public timeout unit is SECONDS (Adw.Toast:timeout); the
|
|
144
|
-
// headless descriptor counts milliseconds.
|
|
145
|
-
const toast = new AdwToast(title, {
|
|
146
|
-
timeout: (opts.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000,
|
|
147
|
-
buttonLabel: opts.buttonLabel,
|
|
148
|
-
});
|
|
149
|
-
if (opts.onAction) this._actions.set(toast, opts.onAction);
|
|
150
|
-
|
|
151
|
-
this._queue.add(toast);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Dismiss the visible toast early; the next queued toast takes its place. */
|
|
155
|
-
dismiss(): void {
|
|
156
|
-
this._queue.dismiss();
|
|
120
|
+
this._queue.add_toast(toast);
|
|
157
121
|
}
|
|
158
122
|
|
|
159
|
-
/**
|
|
160
|
-
|
|
161
|
-
this._queue.
|
|
123
|
+
/** `adw_toast_overlay_dismiss_all`: dismiss every queued toast, then the visible one. */
|
|
124
|
+
dismiss_all(): void {
|
|
125
|
+
this._queue.dismiss_all();
|
|
162
126
|
}
|
|
163
127
|
|
|
164
128
|
/** Build + attach the strip for `toast` — the DOM half of the queue's `onShow`. */
|
|
165
|
-
private _mountToast(toast:
|
|
129
|
+
private _mountToast(toast: Toast): void {
|
|
166
130
|
const el = document.createElement('div');
|
|
167
131
|
el.className = 'adw-toast';
|
|
168
132
|
|
|
@@ -171,18 +135,20 @@ export class AdwToastOverlay extends HTMLElement {
|
|
|
171
135
|
|
|
172
136
|
const titleEl = document.createElement('span');
|
|
173
137
|
titleEl.className = 'adw-toast-title';
|
|
174
|
-
|
|
138
|
+
if (toast.use_markup) renderPangoMarkup(titleEl, toast.title);
|
|
139
|
+
else titleEl.textContent = toast.title;
|
|
175
140
|
wrapper.appendChild(titleEl);
|
|
176
141
|
el.appendChild(wrapper);
|
|
177
142
|
|
|
178
|
-
if (toast.
|
|
143
|
+
if (toast.button_label) {
|
|
179
144
|
const button = document.createElement('button');
|
|
180
145
|
button.type = 'button';
|
|
181
146
|
button.className = 'adw-toast-action-button';
|
|
182
|
-
button.textContent = toast.
|
|
147
|
+
button.textContent = toast.button_label;
|
|
183
148
|
button.addEventListener('click', () => {
|
|
184
|
-
|
|
185
|
-
this.
|
|
149
|
+
toast.emit('button-clicked');
|
|
150
|
+
if (toast.action_name) activateWidgetAction(this, toast.action_name);
|
|
151
|
+
toast.dismiss();
|
|
186
152
|
});
|
|
187
153
|
el.appendChild(button);
|
|
188
154
|
}
|
|
@@ -197,7 +163,7 @@ export class AdwToastOverlay extends HTMLElement {
|
|
|
197
163
|
close.className = 'adw-toast-close-button';
|
|
198
164
|
close.setAttribute('aria-label', 'Close');
|
|
199
165
|
close.appendChild(createGtkImage('window-close'));
|
|
200
|
-
close.addEventListener('click', () =>
|
|
166
|
+
close.addEventListener('click', () => toast.dismiss());
|
|
201
167
|
el.appendChild(close);
|
|
202
168
|
|
|
203
169
|
this._toasts.appendChild(el);
|
|
@@ -23,6 +23,15 @@ function requested(host: HTMLElement, name: string, fallback: number): number {
|
|
|
23
23
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* A maximized or fullscreen `Gtk.Window` ignores its default size and fills the monitor's work
|
|
28
|
+
* area (gtkwindow.c: the default size is only the RESTORED size). The page's work area is the
|
|
29
|
+
* frame's containing block, which the skin sizes from these two attributes.
|
|
30
|
+
*/
|
|
31
|
+
export function fillsContainingBlock(host: HTMLElement): boolean {
|
|
32
|
+
return ['maximized', 'fullscreened'].some((name) => host.hasAttribute(name) && host.getAttribute(name) !== 'false');
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
export class AdwWindowFeatures {
|
|
27
36
|
private _contentEl: HTMLDivElement | null = null;
|
|
28
37
|
private _breakpoints: AdwBreakpoint[] = [];
|
|
@@ -128,13 +137,16 @@ export class AdwWindowFeatures {
|
|
|
128
137
|
* size; `width-request`/`height-request` override it). `default-width/height` is only
|
|
129
138
|
* a default: an authored size is the box, and an absent one leaves the box to the
|
|
130
139
|
* stylesheet, which fills the viewport — a page has no window manager to pick a size.
|
|
140
|
+
* While maximized or fullscreened the default size is not applied at all: the stylesheet
|
|
141
|
+
* fills the containing block, and the minimum still holds.
|
|
131
142
|
*/
|
|
132
143
|
applySizeRequest(): void {
|
|
133
144
|
const host = this._host;
|
|
134
145
|
host.style.minWidth = `${requested(host, 'width-request', DEFAULT_WIDTH)}px`;
|
|
135
146
|
host.style.minHeight = `${requested(host, 'height-request', DEFAULT_HEIGHT)}px`;
|
|
136
|
-
const
|
|
137
|
-
const
|
|
147
|
+
const fills = fillsContainingBlock(host);
|
|
148
|
+
const width = fills ? null : (host.getAttribute('default-width') ?? host.getAttribute('width'));
|
|
149
|
+
const height = fills ? null : (host.getAttribute('default-height') ?? host.getAttribute('height'));
|
|
138
150
|
if (width) host.style.width = `${width}px`;
|
|
139
151
|
else host.style.removeProperty('width');
|
|
140
152
|
if (height) host.style.height = `${height}px`;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
//
|
|
17
17
|
// THE `win.` / `app.` PREFIXES. `GtkApplicationWindow` exports window actions under `win.` and
|
|
18
18
|
// application-wide ones under `app.` (:48-56), and any `GtkActionable` inside the window can
|
|
19
|
-
// activate either. `
|
|
19
|
+
// activate either. `installActionMap` installs the `Gio.ActionMap` half on the prototype
|
|
20
20
|
// (`add_action`, `lookup_action`, `remove_action`, the `win.` registry); the `Gio.ActionGroup`
|
|
21
21
|
// half stays off because a window's `activate_action` is the widget's. Plain `<gtk-window>` has
|
|
22
22
|
// no ActionMap. What this element adds over it is that map, the strip and the property that shows it.
|
|
@@ -26,17 +26,12 @@
|
|
|
26
26
|
// Copyright (c) The GTK Team. LGPLv2.1+.
|
|
27
27
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
28
28
|
|
|
29
|
-
import type
|
|
30
|
-
import type { GtkApplication } from '../application.js';
|
|
29
|
+
import { installActionMap, type ActionLike } from '@gjsify/adwaita-core';
|
|
31
30
|
import type { DispatchedSignals } from '../signals.js';
|
|
32
31
|
import type { AdwSlot } from '../slotted-children.js';
|
|
33
32
|
import { GtkWindow } from './gtk-window.js';
|
|
34
|
-
import { defineWindowApplication } from '../application.js';
|
|
35
33
|
|
|
36
34
|
export class GtkApplicationWindow extends GtkWindow {
|
|
37
|
-
/** `Gtk.Window:application`: setting it is `add_window` (ADR 0098), set by `defineWindowApplication`. */
|
|
38
|
-
declare application: GtkApplication | null;
|
|
39
|
-
|
|
40
35
|
/** `Gio.ActionMap.add_action`: installed by `installActionMap`; the action is reachable as `win.<name>`. */
|
|
41
36
|
declare add_action: (action: ActionLike) => void;
|
|
42
37
|
/** `Gio.ActionMap.lookup_action`: the window's own action, or `null`. */
|
|
@@ -127,4 +122,4 @@ export class GtkApplicationWindow extends GtkWindow {
|
|
|
127
122
|
|
|
128
123
|
customElements.define('gtk-application-window', GtkApplicationWindow);
|
|
129
124
|
|
|
130
|
-
|
|
125
|
+
installActionMap(GtkApplicationWindow.prototype);
|
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
// Copyright (c) GNOME contributors (libadwaita). LGPLv2.1+.
|
|
54
54
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
55
55
|
|
|
56
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
57
|
+
|
|
56
58
|
/** The `rgba()` string a `GdkRGBA` value paints, as the colour swatch shows it. */
|
|
57
59
|
interface Rgba {
|
|
58
60
|
readonly red: number;
|
|
@@ -166,6 +168,8 @@ export class GtkDragIcon extends HTMLElement {
|
|
|
166
168
|
// `gtk_widget_set_can_target (self, FALSE)` (:386-390), in CSS: the icon is pointed
|
|
167
169
|
// at, and takes no pointer event of any kind.
|
|
168
170
|
this.style.pointerEvents = 'none';
|
|
171
|
+
// The NAME only, for the reason `adw-bin.ts` gives: the child already IS the content.
|
|
172
|
+
bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
169
173
|
if (this._dragging) this._observe();
|
|
170
174
|
}
|
|
171
175
|
|
|
@@ -103,6 +103,11 @@ export class GtkEmojiChooser extends GtkPopover {
|
|
|
103
103
|
*/
|
|
104
104
|
private _ctrl = false;
|
|
105
105
|
|
|
106
|
+
/** The chooser's template is the child (gtkemojichooser.ui). */
|
|
107
|
+
protected override takesChild(): boolean {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
106
111
|
connectedCallback(): void {
|
|
107
112
|
super.connectedCallback();
|
|
108
113
|
if (this._built) return;
|
|
@@ -38,7 +38,9 @@ export class GtkFlowBoxChild extends HTMLElement {
|
|
|
38
38
|
this._contentsEl = document.createElement('div');
|
|
39
39
|
this._contentsEl.className = 'adw-flow-box-child-contents';
|
|
40
40
|
|
|
41
|
-
bindSlottedChildren(this, [{ into: this._contentsEl }]).install(
|
|
41
|
+
bindSlottedChildren(this, [{ name: 'child', into: this._contentsEl }, { into: this._contentsEl }]).install(
|
|
42
|
+
this._contentsEl,
|
|
43
|
+
);
|
|
42
44
|
|
|
43
45
|
this.setAttribute('role', 'gridcell');
|
|
44
46
|
// `gtk_flow_box_child_init` makes the cell focusable (:578-580) — the box's cursor
|
|
@@ -98,7 +98,9 @@ export class GtkGraphicsOffload extends HTMLElement {
|
|
|
98
98
|
this._childEl.className = 'adw-graphics-offload-child';
|
|
99
99
|
// `GTK_TYPE_BIN_LAYOUT` gives the child the whole allocation, and `child` is the one
|
|
100
100
|
// GtkBuildable slot the widget has, so it is the default one here.
|
|
101
|
-
bindSlottedChildren(this, [{ into: this._childEl }]).install(
|
|
101
|
+
bindSlottedChildren(this, [{ name: 'child', into: this._childEl }, { into: this._childEl }]).install(
|
|
102
|
+
this._childEl,
|
|
103
|
+
);
|
|
102
104
|
this.replaceChildren(this._childEl);
|
|
103
105
|
this._render();
|
|
104
106
|
}
|
|
@@ -110,7 +110,9 @@ export class GtkListBoxRow extends HTMLElement {
|
|
|
110
110
|
// `child` is a WIDGET property, so it is a slot here (gtklistbox.c:3307-3331): the
|
|
111
111
|
// authored child lands in the same node `gtk_list_box_row_set_child` would put it in,
|
|
112
112
|
// and a child appended after connect lands there too.
|
|
113
|
-
bindSlottedChildren(this, [{ into: this._contentsEl }]).install(
|
|
113
|
+
bindSlottedChildren(this, [{ name: 'child', into: this._contentsEl }, { into: this._contentsEl }]).install(
|
|
114
|
+
this._contentsEl,
|
|
115
|
+
);
|
|
114
116
|
|
|
115
117
|
// GTK declares the accessible role unconditionally (gtklistbox.c:3955), so there is
|
|
116
118
|
// no `selection-mode` to condition it on here.
|
|
@@ -98,10 +98,11 @@ export class GtkOverlay extends HTMLElement {
|
|
|
98
98
|
// bare `<child>` — the default — is the main child, which is the pairing
|
|
99
99
|
// `packages/framework/gtk-host/src/descriptors/gtk.ts` curates for `GtkOverlay`
|
|
100
100
|
// (`slots: { child, overlay }`, `defaultSlot: 'child'`).
|
|
101
|
-
bindSlottedChildren(this, [
|
|
102
|
-
this._childEl,
|
|
103
|
-
this.
|
|
104
|
-
|
|
101
|
+
bindSlottedChildren(this, [
|
|
102
|
+
{ name: 'child', into: this._childEl },
|
|
103
|
+
{ into: this._childEl },
|
|
104
|
+
{ name: 'overlay', into: this._layerEl },
|
|
105
|
+
]).install(this._childEl, this._layerEl);
|
|
105
106
|
|
|
106
107
|
this._observe();
|
|
107
108
|
}
|
|
@@ -278,7 +278,13 @@ export class GtkPaned extends HTMLElement {
|
|
|
278
278
|
// (gtkpaned.c:816-826).
|
|
279
279
|
bindSlottedChildren(
|
|
280
280
|
this,
|
|
281
|
-
[
|
|
281
|
+
[
|
|
282
|
+
{ into: this._startEl },
|
|
283
|
+
{ name: 'start', into: this._startEl },
|
|
284
|
+
{ name: 'end', into: this._endEl },
|
|
285
|
+
{ name: 'start-child', into: this._startEl },
|
|
286
|
+
{ name: 'end-child', into: this._endEl },
|
|
287
|
+
],
|
|
282
288
|
() => this._observe(),
|
|
283
289
|
).install(this._startEl, this._handleEl, this._endEl);
|
|
284
290
|
|
|
@@ -75,6 +75,7 @@ import type { AdwMenuActions, AdwMenuInput, AdwMenuModel } from '@gjsify/adwaita
|
|
|
75
75
|
import './gtk-popover-menu.js';
|
|
76
76
|
|
|
77
77
|
import type { GtkPopoverMenu } from './gtk-popover-menu.js';
|
|
78
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
78
79
|
|
|
79
80
|
/** How long a press must last to count as a long press — GTK's own default. */
|
|
80
81
|
const LONG_PRESS_MS = 500;
|
|
@@ -150,6 +151,8 @@ export class GtkPopoverBin extends HTMLElement {
|
|
|
150
151
|
this._initialized = true;
|
|
151
152
|
// `gtk_widget_class_set_css_name (widget_class, "popoverbin")` (gtkpopoverbin.c:382).
|
|
152
153
|
this.classList.add('adw-popover-bin');
|
|
154
|
+
// The NAME only, for the reason `adw-bin.ts` gives: the child already IS the content.
|
|
155
|
+
bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
153
156
|
this.addEventListener('contextmenu', (event) => this._onContextMenu(event));
|
|
154
157
|
this.addEventListener('keydown', (event) => this._onKeyDown(event));
|
|
155
158
|
this._wireGestures();
|
|
@@ -161,6 +161,11 @@ export class GtkPopoverMenu extends GtkPopover {
|
|
|
161
161
|
this.openSubmenu(value);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/** The menu's own stack is the child (gtkpopovermenu.c `gtk_popover_menu_init`). */
|
|
165
|
+
protected override takesChild(): boolean {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
164
169
|
connectedCallback() {
|
|
165
170
|
super.connectedCallback();
|
|
166
171
|
if (this._menuInitialized) return;
|
|
@@ -82,6 +82,8 @@
|
|
|
82
82
|
|
|
83
83
|
import { PopoverState, resolvePopoverKey } from '@gjsify/adwaita-core';
|
|
84
84
|
|
|
85
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
86
|
+
|
|
85
87
|
/** Which side of the anchor the surface sits on. */
|
|
86
88
|
export type GtkPopoverPosition = 'bottom' | 'top' | 'start' | 'end';
|
|
87
89
|
|
|
@@ -257,10 +259,20 @@ export class GtkPopover extends HTMLElement {
|
|
|
257
259
|
if (this._state.open) this._bindDocument();
|
|
258
260
|
}
|
|
259
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Whether `Gtk.Popover:child` is the author's to set. A subclass that builds its own
|
|
264
|
+
* content (GtkPopoverMenu, GtkEmojiChooser) sets the child itself, as their C does.
|
|
265
|
+
*/
|
|
266
|
+
protected takesChild(): boolean {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
260
270
|
private _buildOnce() {
|
|
261
271
|
if (this._initialized) return;
|
|
262
272
|
this._initialized = true;
|
|
263
273
|
|
|
274
|
+
// The NAME only, for the reason `adw-bin.ts` gives: the child already IS the content.
|
|
275
|
+
if (this.takesChild()) bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
264
276
|
this.classList.add('adw-popover');
|
|
265
277
|
if (!this.hasAttribute('role')) this.setAttribute('role', 'menu');
|
|
266
278
|
// The `menu` variant is NOT defaulted or inferred here — it is GTK's `.menu`
|
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
// Copyright (c) The GTK Team, GNOME contributors. LGPLv2.1+.
|
|
38
38
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
39
39
|
|
|
40
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
41
|
+
|
|
40
42
|
export class GtkSearchBar extends HTMLElement {
|
|
41
43
|
private _box!: HTMLDivElement;
|
|
42
44
|
private _center!: HTMLDivElement;
|
|
@@ -93,10 +95,6 @@ export class GtkSearchBar extends HTMLElement {
|
|
|
93
95
|
|
|
94
96
|
this._center = document.createElement('div');
|
|
95
97
|
this._center.className = 'adw-search-bar-center';
|
|
96
|
-
// Whatever the author put inside the bar is the child, moved into the box's centre —
|
|
97
|
-
// the `GtkCenterBox` the C builds around it (gtksearchbar.c:378-386).
|
|
98
|
-
this._center.append(...this.childNodes);
|
|
99
|
-
|
|
100
98
|
this._close = document.createElement('button');
|
|
101
99
|
this._close.className = 'adw-search-bar-close';
|
|
102
100
|
this._close.type = 'button';
|
|
@@ -106,7 +104,9 @@ export class GtkSearchBar extends HTMLElement {
|
|
|
106
104
|
this._box = document.createElement('div');
|
|
107
105
|
this._box.className = 'adw-search-bar-box';
|
|
108
106
|
this._box.append(this._center, this._close);
|
|
109
|
-
|
|
107
|
+
// Whatever the author put inside the bar is the child, moved into the box's centre —
|
|
108
|
+
// the `GtkCenterBox` the C builds around it (gtksearchbar.c:378-386).
|
|
109
|
+
bindSlottedChildren(this, [{ name: 'child', into: this._center }, { into: this._center }]).install(this._box);
|
|
110
110
|
|
|
111
111
|
// `input-intercepted`: typing into the connected editable reveals the bar
|
|
112
112
|
// (gtksearchbar.c:210-218). Captured, because the editable may be nested two
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// <gtk-stack-page> — the page record a `<gtk-stack>` is AUTHORED with.
|
|
2
|
+
//
|
|
3
|
+
// NOT A WIDGET, AS UPSTREAM. `GtkStackPage` descends from `GObject.Object` (gtkstack.c:212-233):
|
|
4
|
+
// it carries a page's `name`, `title`, `icon-name` and attention state beside the `child` that IS
|
|
5
|
+
// the page. A Blueprint spells it `Stack { StackPage { name: "debugger"; child: … } }`, and
|
|
6
|
+
// without an element for it the shared-tree builder refused every such `.blp`.
|
|
7
|
+
//
|
|
8
|
+
// THE STACK ADOPTS IT AND KEEPS NO RECORD, the shape `<adw-view-stack>` gives
|
|
9
|
+
// `<adw-view-stack-page>`: `<gtk-stack>` already reads a page's six properties as attributes on
|
|
10
|
+
// its CHILD (see `gtk-stack.ts`), so adopting a record copies them onto the child and puts the
|
|
11
|
+
// child where the record was. The record leaves the tree, which is the browser half of what
|
|
12
|
+
// `@gjsify/adwaita-nativescript`'s `stack-page.ts` refuses after adoption: a write to a record
|
|
13
|
+
// the stack no longer reads would be a silent no-op.
|
|
14
|
+
// name / title / icon-name / needs-attention / use-underline — the same attributes
|
|
15
|
+
// visible — the DOM's own `hidden`, as on the child (`GtkStackPage:visible`, gtkstack.c:567)
|
|
16
|
+
// child — the slot `child`, or the record's one element child (a bare child IS the page)
|
|
17
|
+
//
|
|
18
|
+
// Reference: refs/gtk/gtk/gtkstack.c (GtkStackPage)
|
|
19
|
+
// Copyright (c) The GTK Team. LGPLv2.1+.
|
|
20
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
21
|
+
|
|
22
|
+
import { bindSlottedChildren } from '../slotted-children.js';
|
|
23
|
+
|
|
24
|
+
/** The attributes a record hands to its child on adoption — `GtkStackPage`'s string/boolean properties. */
|
|
25
|
+
export const STACK_PAGE_ATTRIBUTES = ['name', 'title', 'icon-name', 'needs-attention', 'use-underline'] as const;
|
|
26
|
+
|
|
27
|
+
export class GtkStackPage extends HTMLElement {
|
|
28
|
+
static get observedAttributes() {
|
|
29
|
+
return [...STACK_PAGE_ATTRIBUTES];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `GtkStackPage:name` — what `visible-child-name` selects by. */
|
|
33
|
+
get name(): string {
|
|
34
|
+
return this.getAttribute('name') ?? '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set name(value: string | null) {
|
|
38
|
+
if (value === null) this.removeAttribute('name');
|
|
39
|
+
else this.setAttribute('name', value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// `GtkStackPage:title` is `HTMLElement.title`, which already reflects the `title` attribute.
|
|
43
|
+
|
|
44
|
+
/** `GtkStackPage:icon-name`. */
|
|
45
|
+
get iconName(): string | null {
|
|
46
|
+
return this.getAttribute('icon-name');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set iconName(value: string | null) {
|
|
50
|
+
if (value === null) this.removeAttribute('icon-name');
|
|
51
|
+
else this.setAttribute('icon-name', value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `GtkStackPage:needs-attention`. */
|
|
55
|
+
get needsAttention(): boolean {
|
|
56
|
+
return this.hasAttribute('needs-attention');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
set needsAttention(value: boolean) {
|
|
60
|
+
this.toggleAttribute('needs-attention', !!value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** `GtkStackPage:use-underline`. */
|
|
64
|
+
get useUnderline(): boolean {
|
|
65
|
+
return this.hasAttribute('use-underline');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
set useUnderline(value: boolean) {
|
|
69
|
+
this.toggleAttribute('use-underline', !!value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `GtkStackPage:visible` — `hidden`, as on the child the stack reads it from. */
|
|
73
|
+
get visible(): boolean {
|
|
74
|
+
return !this.hasAttribute('hidden');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
set visible(value: boolean) {
|
|
78
|
+
this.toggleAttribute('hidden', !value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** `GtkStackPage:child` — the element the stack shows for this page, while not yet adopted. */
|
|
82
|
+
get child(): HTMLElement | null {
|
|
83
|
+
return pageChildOf(this);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
connectedCallback(): void {
|
|
87
|
+
// `child:` in a `.blp` authors `slot="child"`, and the mount refuses a name this element
|
|
88
|
+
// never declared. The stack adopts on ITS connect, which runs first, so by now this
|
|
89
|
+
// record is usually detached; enrolling the name is all that is left to do.
|
|
90
|
+
bindSlottedChildren(this, [{ name: 'child', into: this }]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The one element a record holds: its `slot="child"` child or its only element child. A record
|
|
96
|
+
* with none, or with more than one, is refused by name — GtkStackPage has exactly one `child`.
|
|
97
|
+
*/
|
|
98
|
+
export function pageChildOf(page: HTMLElement): HTMLElement | null {
|
|
99
|
+
const elements = Array.from(page.children) as HTMLElement[];
|
|
100
|
+
const slotted = elements.filter((child) => child.getAttribute('slot') === 'child');
|
|
101
|
+
const candidates = slotted.length > 0 ? slotted : elements;
|
|
102
|
+
if (candidates.length > 1) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`<gtk-stack-page name="${page.getAttribute('name') ?? ''}"> holds ${candidates.length} children, ` +
|
|
105
|
+
'and a GtkStackPage has one `child`.',
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return candidates[0] ?? null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
customElements.define('gtk-stack-page', GtkStackPage);
|