@gjsify/adwaita-web 0.62.0 → 0.63.1-next.235
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 +31 -0
- package/dist/adwaita-web.css +4 -0
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/canvas2d/canvas2d-bridge.d.ts +26 -5
- package/lib/types/elements/adw-application-window.d.ts +26 -7
- package/lib/types/elements/adw-bottom-sheet.d.ts +3 -0
- package/lib/types/elements/adw-data-grid.d.ts +4 -0
- package/lib/types/elements/adw-window-features.d.ts +39 -0
- package/lib/types/elements/adw-window.d.ts +11 -24
- package/lib/types/elements/gtk-application-window.d.ts +12 -4
- package/lib/types/elements/gtk-window.d.ts +44 -0
- package/lib/types/gtk-window.spec.d.ts +6 -0
- package/lib/types/pango-markup.d.ts +10 -0
- package/lib/types/pango-markup.spec.d.ts +1 -0
- package/lib/types/source-view/copy-glyph.d.ts +6 -0
- package/lib/types/source-view/copy-glyph.spec.d.ts +1 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +13 -12
- package/scss/_window.scss +7 -2
- package/src/adw-application-window.spec.ts +8 -0
- package/src/adw-data-grid.spec.ts +11 -0
- package/src/adw-window.spec.ts +8 -0
- package/src/application.spec.ts +34 -1
- package/src/application.ts +16 -0
- package/src/bottom-sheet.spec.ts +9 -0
- package/src/canvas2d/canvas2d-bridge.ts +24 -8
- package/src/elements/adw-alert-dialog.ts +8 -5
- package/src/elements/adw-application-window.ts +90 -20
- package/src/elements/adw-banner.ts +14 -9
- package/src/elements/adw-bottom-sheet.ts +9 -0
- package/src/elements/adw-data-grid.ts +18 -0
- package/src/elements/adw-window-features.ts +128 -0
- package/src/elements/adw-window.ts +45 -108
- package/src/elements/gtk-application-window.ts +17 -6
- package/src/elements/gtk-window.ts +170 -18
- package/src/gtk-window.spec.ts +133 -0
- package/src/pango-markup.spec.ts +257 -0
- package/src/pango-markup.ts +266 -0
- package/src/source-view/adw-source-view.ts +2 -8
- package/src/source-view/copy-glyph.spec.ts +46 -0
- package/src/source-view/copy-glyph.ts +52 -0
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +4 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// What `Adw.Window` and `Adw.ApplicationWindow` add on top of their Gtk parent: the content
|
|
2
|
+
// area, the breakpoints and the open-dialog bookkeeping.
|
|
3
|
+
//
|
|
4
|
+
// A HELPER, NOT A BASE CLASS. Upstream `AdwWindow` extends `GtkWindow` and `AdwApplicationWindow`
|
|
5
|
+
// extends `GtkApplicationWindow` (Adw-1.gir), so the two cannot share an Adw base without one of
|
|
6
|
+
// them losing its Gtk parent. A custom element has one prototype chain, so the shared part is
|
|
7
|
+
// composed in: each class owns one of these and forwards to it.
|
|
8
|
+
//
|
|
9
|
+
// Reference: refs/libadwaita/src/adw-window.c
|
|
10
|
+
// Copyright (c) 2020-2023 Alice Mikhaylenko / Purism SPC (libadwaita). LGPLv2.1+
|
|
11
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
12
|
+
|
|
13
|
+
import { AdwBreakpoint } from '@gjsify/adwaita-core';
|
|
14
|
+
import { addBreakpoints } from '../breakpoints.js';
|
|
15
|
+
|
|
16
|
+
/** The default minimum size of an AdwWindow, from adw_window_init (C:344). */
|
|
17
|
+
const DEFAULT_WIDTH = 360;
|
|
18
|
+
const DEFAULT_HEIGHT = 200;
|
|
19
|
+
|
|
20
|
+
export class AdwWindowFeatures {
|
|
21
|
+
private _contentEl: HTMLDivElement | null = null;
|
|
22
|
+
private _breakpoints: AdwBreakpoint[] = [];
|
|
23
|
+
private _disposeBreakpoints: (() => void) | null = null;
|
|
24
|
+
private _openDialogs: HTMLElement[] = [];
|
|
25
|
+
|
|
26
|
+
constructor(private readonly _host: HTMLElement) {}
|
|
27
|
+
|
|
28
|
+
/** The content area, or `null` before the window is first connected. */
|
|
29
|
+
get contentArea(): HTMLDivElement | null {
|
|
30
|
+
return this._contentEl;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The content widget — the first child of the content area. */
|
|
34
|
+
get content(): HTMLElement | null {
|
|
35
|
+
return (this._contentEl?.firstElementChild as HTMLElement | null) ?? null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set content(value: HTMLElement | null) {
|
|
39
|
+
if (value) this._contentEl?.appendChild(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get breakpoints(): readonly AdwBreakpoint[] {
|
|
43
|
+
return this._breakpoints;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get currentBreakpoint(): AdwBreakpoint | null {
|
|
47
|
+
return this._breakpoints.find((bp) => bp.applied) ?? null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get dialogs(): readonly HTMLElement[] {
|
|
51
|
+
// A dialog taken out of the document never raises `notify::open` on the way
|
|
52
|
+
// out, so a dialog that was REMOVED while open would otherwise stay listed
|
|
53
|
+
// for the rest of the window's life.
|
|
54
|
+
this._openDialogs = this._openDialogs.filter((dialog) => dialog.isConnected);
|
|
55
|
+
return this._openDialogs;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get visibleDialog(): HTMLElement | null {
|
|
59
|
+
const dialogs = this.dialogs;
|
|
60
|
+
return dialogs.length > 0 ? (dialogs[dialogs.length - 1] as HTMLElement) : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Take the content area the Gtk parent built, once. Tracks open dialogs presented inside
|
|
65
|
+
* the window: an AdwDialog raises `notify::open`, which bubbles to the host.
|
|
66
|
+
*/
|
|
67
|
+
adopt(contentEl: HTMLDivElement): void {
|
|
68
|
+
if (this._contentEl !== null) return;
|
|
69
|
+
this._contentEl = contentEl;
|
|
70
|
+
contentEl.classList.add('adw-window-content');
|
|
71
|
+
this._host.addEventListener('notify::open', (e) => {
|
|
72
|
+
const dialog = e.target as HTMLElement;
|
|
73
|
+
if (dialog === this._host) return;
|
|
74
|
+
if (dialog.hasAttribute('open')) {
|
|
75
|
+
if (!this._openDialogs.includes(dialog)) this._openDialogs.push(dialog);
|
|
76
|
+
} else {
|
|
77
|
+
this._openDialogs = this._openDialogs.filter((d) => d !== dialog);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Add a breakpoint (adw_window_add_breakpoint). The breakpoint evaluates against the
|
|
84
|
+
* window's own box and sets a `data-breakpoint` attribute when applied.
|
|
85
|
+
*/
|
|
86
|
+
addBreakpoint(condition: string): AdwBreakpoint {
|
|
87
|
+
const host = this._host;
|
|
88
|
+
const bp = new AdwBreakpoint(condition, {
|
|
89
|
+
onApply: () => (host.dataset.breakpoint = condition),
|
|
90
|
+
onUnapply: () => {
|
|
91
|
+
if (host.dataset.breakpoint === condition) delete host.dataset.breakpoint;
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
this._breakpoints.push(bp);
|
|
95
|
+
// Re-evaluated against a box that may have changed since the last add.
|
|
96
|
+
this.bind();
|
|
97
|
+
return bp;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Bind the breakpoints to THIS box.
|
|
102
|
+
*
|
|
103
|
+
* NOT once per lifetime: `release` drops the disposer, so a window moved between
|
|
104
|
+
* parents would come back deaf — and a window is exactly the thing a client-side route
|
|
105
|
+
* change re-parents. Re-binding is cheap and idempotent, and it re-measures the new box.
|
|
106
|
+
*/
|
|
107
|
+
bind(): void {
|
|
108
|
+
this._disposeBreakpoints?.();
|
|
109
|
+
this._disposeBreakpoints = null;
|
|
110
|
+
if (this._contentEl !== null && this._breakpoints.length > 0) {
|
|
111
|
+
this._disposeBreakpoints = addBreakpoints(this._host, this._breakpoints);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
release(): void {
|
|
116
|
+
this._disposeBreakpoints?.();
|
|
117
|
+
this._disposeBreakpoints = null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The window's size request — GTK's, not CSS's, so an absent attribute means the default. */
|
|
121
|
+
applySizeRequest(): void {
|
|
122
|
+
const host = this._host;
|
|
123
|
+
const width = host.getAttribute('default-width') ?? host.getAttribute('width');
|
|
124
|
+
const height = host.getAttribute('default-height') ?? host.getAttribute('height');
|
|
125
|
+
host.style.width = width ? `${width}px` : `${DEFAULT_WIDTH}px`;
|
|
126
|
+
host.style.height = height ? `${height}px` : `${DEFAULT_HEIGHT}px`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
// <adw-window> — the web counterpart of Adw.Window: a
|
|
2
|
-
//
|
|
1
|
+
// <adw-window> — the web counterpart of Adw.Window: a Gtk.Window (so <gtk-window>'s state
|
|
2
|
+
// members — is-active, focus-widget, close-request, present(), maximized — are inherited)
|
|
3
|
+
// with no titlebar area. Adw.Window styles nothing itself — you pair it with
|
|
3
4
|
// Adw.ToolbarView and an Adw.HeaderBar (or a Gtk.HeaderBar) to get the chrome —
|
|
4
5
|
// so this element owns the FRAME and the window's own state, and the chrome is
|
|
5
6
|
// whatever you put in it.
|
|
@@ -38,159 +39,95 @@
|
|
|
38
39
|
// Copyright (c) 2020-2023 Alice Mikhaylenko / Purism SPC (libadwaita). LGPLv2.1+
|
|
39
40
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
40
41
|
|
|
41
|
-
import { AdwBreakpoint } from '@gjsify/adwaita-core';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
42
|
+
import type { AdwBreakpoint } from '@gjsify/adwaita-core';
|
|
43
|
+
import type { AdwSlot } from '../slotted-children.js';
|
|
44
|
+
import { AdwWindowFeatures } from './adw-window-features.js';
|
|
45
|
+
import { GtkWindow } from './gtk-window.js';
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const DEFAULT_HEIGHT = 200;
|
|
48
|
-
|
|
49
|
-
export class AdwWindow extends HTMLElement {
|
|
50
|
-
private _initialized = false;
|
|
51
|
-
private _contentEl!: HTMLDivElement;
|
|
52
|
-
private _breakpoints: AdwBreakpoint[] = [];
|
|
53
|
-
private _disposeBreakpoints: (() => void) | null = null;
|
|
54
|
-
private _openDialogs: HTMLElement[] = [];
|
|
47
|
+
export class AdwWindow extends GtkWindow {
|
|
48
|
+
private readonly _features = new AdwWindowFeatures(this);
|
|
55
49
|
|
|
56
50
|
static get observedAttributes() {
|
|
57
|
-
return [
|
|
51
|
+
return [...GtkWindow.observedAttributes, 'width', 'height'];
|
|
58
52
|
}
|
|
59
53
|
|
|
60
54
|
/** The content area — the Adw.Window:content property. */
|
|
61
55
|
get contentArea(): HTMLDivElement {
|
|
62
|
-
return this.
|
|
56
|
+
return this._content;
|
|
63
57
|
}
|
|
64
58
|
|
|
65
59
|
/** The content widget — the first child of the content area. */
|
|
66
60
|
get content(): HTMLElement | null {
|
|
67
|
-
return this.
|
|
61
|
+
return this._features.content;
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
set content(value: HTMLElement | null) {
|
|
71
|
-
|
|
72
|
-
this._contentEl.appendChild(value);
|
|
73
|
-
}
|
|
65
|
+
this._features.content = value;
|
|
74
66
|
}
|
|
75
67
|
|
|
76
68
|
/** The breakpoints added to this window. */
|
|
77
69
|
get breakpoints(): readonly AdwBreakpoint[] {
|
|
78
|
-
return this.
|
|
70
|
+
return this._features.breakpoints;
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
/** The currently applied breakpoint, or null when none is. */
|
|
82
74
|
get currentBreakpoint(): AdwBreakpoint | null {
|
|
83
|
-
return this.
|
|
75
|
+
return this._features.currentBreakpoint;
|
|
84
76
|
}
|
|
85
77
|
|
|
86
78
|
/** The open dialogs presented inside this window (Adw.Window:dialogs). */
|
|
87
79
|
get dialogs(): readonly HTMLElement[] {
|
|
88
|
-
|
|
89
|
-
// out, so a dialog that was REMOVED while open would otherwise stay listed
|
|
90
|
-
// for the rest of the window's life.
|
|
91
|
-
this._openDialogs = this._openDialogs.filter((dialog) => dialog.isConnected);
|
|
92
|
-
return this._openDialogs;
|
|
80
|
+
return this._features.dialogs;
|
|
93
81
|
}
|
|
94
82
|
|
|
95
83
|
/** The currently visible dialog, or null when none is (Adw.Window:visible-dialog). */
|
|
96
84
|
get visibleDialog(): HTMLElement | null {
|
|
97
|
-
|
|
98
|
-
return dialogs.length > 0 ? (dialogs[dialogs.length - 1] as HTMLElement) : null;
|
|
85
|
+
return this._features.visibleDialog;
|
|
99
86
|
}
|
|
100
87
|
|
|
101
|
-
/**
|
|
102
|
-
* Add a breakpoint to this window (adw_window_add_breakpoint).
|
|
103
|
-
* The breakpoint evaluates against the window's own box and sets a
|
|
104
|
-
* `data-breakpoint` attribute when applied.
|
|
105
|
-
*/
|
|
88
|
+
/** Add a breakpoint to this window (adw_window_add_breakpoint). */
|
|
106
89
|
addBreakpoint(condition: string): AdwBreakpoint {
|
|
107
|
-
|
|
108
|
-
onApply: () => (this.dataset.breakpoint = condition),
|
|
109
|
-
onUnapply: () => {
|
|
110
|
-
if (this.dataset.breakpoint === condition) delete this.dataset.breakpoint;
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
this._breakpoints.push(bp);
|
|
114
|
-
// Re-evaluated against a box that may have changed since the last add.
|
|
115
|
-
this._bindBreakpoints();
|
|
116
|
-
return bp;
|
|
90
|
+
return this._features.addBreakpoint(condition);
|
|
117
91
|
}
|
|
118
92
|
|
|
119
93
|
connectedCallback() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
94
|
+
super.connectedCallback();
|
|
95
|
+
this._features.adopt(this._content);
|
|
96
|
+
// EVERY connect, not only the first — see `AdwWindowFeatures.bind`.
|
|
97
|
+
this._features.bind();
|
|
123
98
|
}
|
|
124
99
|
|
|
125
100
|
disconnectedCallback() {
|
|
126
|
-
this.
|
|
127
|
-
|
|
101
|
+
this._features.release();
|
|
102
|
+
super.disconnectedCallback();
|
|
128
103
|
}
|
|
129
104
|
|
|
130
105
|
attributeChangedCallback(name: string) {
|
|
131
|
-
// The size request is read from the ATTRIBUTE, so it is applied on every
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
106
|
+
// The size request is read from the ATTRIBUTE, so it is applied on every change —
|
|
107
|
+
// including one that arrived before the element was connected.
|
|
108
|
+
if (name === 'width' || name === 'height') {
|
|
109
|
+
this.renderFrameSize();
|
|
110
|
+
return;
|
|
136
111
|
}
|
|
112
|
+
super.attributeChangedCallback(name);
|
|
137
113
|
}
|
|
138
114
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
// `src/shared-tree-builder.ts` throws on a slot a defined element does not
|
|
152
|
-
// declare, and `showcases/gtk/effect-adw-services/src/window.blp` is rooted at
|
|
153
|
-
// one. The content area becomes the host's own child, so a child appended later
|
|
154
|
-
// lands in it too (`bindSlottedChildren` is LIVE, see `src/slotted-children.ts`).
|
|
155
|
-
bindSlottedChildren(this, [{ name: 'content', into: this._contentEl }, { into: this._contentEl }]).install(
|
|
156
|
-
this._contentEl,
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
// Track open dialogs presented inside this window: an AdwDialog raises
|
|
160
|
-
// `notify::open`, which bubbles to here.
|
|
161
|
-
this.addEventListener('notify::open', (e) => {
|
|
162
|
-
const dialog = e.target as HTMLElement;
|
|
163
|
-
if (dialog === this) return;
|
|
164
|
-
if (dialog.hasAttribute('open')) {
|
|
165
|
-
if (!this._openDialogs.includes(dialog)) this._openDialogs.push(dialog);
|
|
166
|
-
} else {
|
|
167
|
-
this._openDialogs = this._openDialogs.filter((d) => d !== dialog);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Bind the breakpoints to THIS box.
|
|
174
|
-
*
|
|
175
|
-
* NOT once per lifetime: `disconnectedCallback` releases the disposer, so a
|
|
176
|
-
* window moved between parents would come back deaf — and a window is exactly
|
|
177
|
-
* the thing a client-side route change re-parents. Re-binding is cheap and
|
|
178
|
-
* idempotent, and it re-measures the new box on the way.
|
|
179
|
-
*/
|
|
180
|
-
private _bindBreakpoints() {
|
|
181
|
-
this._disposeBreakpoints?.();
|
|
182
|
-
this._disposeBreakpoints = null;
|
|
183
|
-
if (this._initialized && this._breakpoints.length > 0) {
|
|
184
|
-
this._disposeBreakpoints = addBreakpoints(this, this._breakpoints);
|
|
185
|
-
}
|
|
115
|
+
/** `Adw.Window` has no titlebar of its own: you pair it with a header bar. */
|
|
116
|
+
protected override hasTitlebar(): boolean {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
protected override strips(): Node[] {
|
|
121
|
+
return [this._content];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected override extraSlots(): AdwSlot[] {
|
|
125
|
+
// `content` is the content area's name too: a `.blp` writes `content:` on the window.
|
|
126
|
+
return [{ name: 'content', into: this._content }];
|
|
186
127
|
}
|
|
187
128
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const width = this.getAttribute('default-width') ?? this.getAttribute('width');
|
|
191
|
-
const height = this.getAttribute('default-height') ?? this.getAttribute('height');
|
|
192
|
-
this.style.width = width ? `${width}px` : `${DEFAULT_WIDTH}px`;
|
|
193
|
-
this.style.height = height ? `${height}px` : `${DEFAULT_HEIGHT}px`;
|
|
129
|
+
protected override renderFrameSize(): void {
|
|
130
|
+
this._features.applySizeRequest();
|
|
194
131
|
}
|
|
195
132
|
}
|
|
196
133
|
|
|
@@ -16,21 +16,32 @@
|
|
|
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.
|
|
20
|
-
//
|
|
19
|
+
// activate either. `defineWindowApplication` installs the `Gio.ActionMap` half on the prototype
|
|
20
|
+
// (`add_action`, `lookup_action`, `remove_action`, the `win.` registry); the `Gio.ActionGroup`
|
|
21
|
+
// half stays off because a window's `activate_action` is the widget's. Plain `<gtk-window>` has
|
|
22
|
+
// no ActionMap. What this element adds over it is that map, the strip and the property that shows it.
|
|
21
23
|
//
|
|
22
24
|
// Reference: refs/gtk/gtk/gtkapplicationwindow.c
|
|
23
25
|
// Reference: refs/libadwaita/src/stylesheet/widgets/_window.scss (GtkWindow)
|
|
24
26
|
// Copyright (c) The GTK Team. LGPLv2.1+.
|
|
25
27
|
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
26
28
|
|
|
29
|
+
import type { ActionLike } from '@gjsify/adwaita-core';
|
|
30
|
+
import type { GtkApplication } from '../application.js';
|
|
27
31
|
import type { AdwSlot } from '../slotted-children.js';
|
|
28
32
|
import { GtkWindow } from './gtk-window.js';
|
|
29
33
|
import { defineWindowApplication } from '../application.js';
|
|
30
34
|
|
|
31
35
|
export class GtkApplicationWindow extends GtkWindow {
|
|
32
36
|
/** `Gtk.Window:application`: setting it is `add_window` (ADR 0098), set by `defineWindowApplication`. */
|
|
33
|
-
declare application:
|
|
37
|
+
declare application: GtkApplication | null;
|
|
38
|
+
|
|
39
|
+
/** `Gio.ActionMap.add_action`: installed by `installActionMap`; the action is reachable as `win.<name>`. */
|
|
40
|
+
declare add_action: (action: ActionLike) => void;
|
|
41
|
+
/** `Gio.ActionMap.lookup_action`: the window's own action, or `null`. */
|
|
42
|
+
declare lookup_action: (name: string) => ActionLike | null;
|
|
43
|
+
/** `Gio.ActionMap.remove_action`: removing an unknown name does nothing. */
|
|
44
|
+
declare remove_action: (name: string) => void;
|
|
34
45
|
private _menubar: HTMLDivElement | null = null;
|
|
35
46
|
private _menubarObserver: MutationObserver | null = null;
|
|
36
47
|
|
|
@@ -51,7 +62,7 @@ export class GtkApplicationWindow extends GtkWindow {
|
|
|
51
62
|
* The menubar strip, created once and reused: `strips()` and `extraSlots()` both ask for
|
|
52
63
|
* it, so whichever runs first builds it and the other finds it.
|
|
53
64
|
*/
|
|
54
|
-
|
|
65
|
+
protected menubarSection(): HTMLDivElement {
|
|
55
66
|
if (this._menubar === null) {
|
|
56
67
|
const section = document.createElement('div');
|
|
57
68
|
section.className = 'adw-gtk-application-window-menubar';
|
|
@@ -95,14 +106,14 @@ export class GtkApplicationWindow extends GtkWindow {
|
|
|
95
106
|
* The observer is what makes the second half live: `bindSlottedChildren` adopts a
|
|
96
107
|
* `slot="menubar"` child after connect, and a bar that appeared then must appear.
|
|
97
108
|
*/
|
|
98
|
-
|
|
109
|
+
protected _followMenubar(): void {
|
|
99
110
|
this._menubarObserver?.disconnect();
|
|
100
111
|
this._menubarObserver = new MutationObserver(() => this._renderMenubar());
|
|
101
112
|
this._menubarObserver.observe(this.menubarSection(), { childList: true });
|
|
102
113
|
this._renderMenubar();
|
|
103
114
|
}
|
|
104
115
|
|
|
105
|
-
|
|
116
|
+
protected _renderMenubar(): void {
|
|
106
117
|
const menubar = this.menubarSection();
|
|
107
118
|
menubar.hidden = !this.showMenubar || menubar.childElementCount === 0;
|
|
108
119
|
}
|