@gjsify/adwaita-web 0.64.0 → 0.64.1-next.241
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/dist/adwaita-web.css +3 -0
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/elements/adw-action-row.d.ts +3 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +3 -0
- package/lib/types/elements/adw-navigation-view.d.ts +7 -0
- package/lib/types/elements/adw-switch-row.d.ts +3 -0
- package/lib/types/elements/adw-window-features.d.ts +6 -1
- package/lib/types/elements/gtk-application-window.d.ts +2 -0
- package/lib/types/elements/gtk-window.d.ts +29 -3
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +13 -12
- package/scss/_window.scss +7 -0
- package/src/adw-action-rows.spec.ts +20 -0
- package/src/adw-alert-dialog.spec.ts +15 -0
- package/src/adw-application-window.spec.ts +2 -1
- package/src/adw-window.spec.ts +22 -3
- package/src/bottom-sheet.spec.ts +11 -0
- package/src/elements/adw-action-row.ts +4 -0
- package/src/elements/adw-alert-dialog.ts +6 -0
- package/src/elements/adw-application-window.ts +9 -2
- package/src/elements/adw-bottom-sheet.ts +4 -0
- package/src/elements/adw-navigation-view.ts +19 -0
- package/src/elements/adw-switch-row.ts +4 -0
- package/src/elements/adw-window-features.ts +19 -4
- package/src/elements/adw-window.ts +2 -2
- package/src/elements/gtk-application-window.ts +6 -0
- package/src/elements/gtk-window.ts +82 -3
- package/src/gobject-elements.ts +21 -2
- package/src/gtk-window.spec.ts +97 -1
- package/src/navigation-view.spec.ts +26 -0
- package/src/styles.generated.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/adwaita-web",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.1-next.241",
|
|
4
4
|
"description": "Adwaita/Libadwaita web components for browser targets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
"@codemirror/language": "^6.12.4",
|
|
59
59
|
"@codemirror/state": "^6.7.0",
|
|
60
60
|
"@codemirror/view": "^6.43.4",
|
|
61
|
-
"@gjsify/adwaita-core": "
|
|
62
|
-
"@gjsify/adwaita-fonts": "
|
|
63
|
-
"@gjsify/adwaita-icons": "
|
|
64
|
-
"@gjsify/gtksource-core": "
|
|
61
|
+
"@gjsify/adwaita-core": "0.64.1-next.241",
|
|
62
|
+
"@gjsify/adwaita-fonts": "0.64.1-next.241",
|
|
63
|
+
"@gjsify/adwaita-icons": "0.64.1-next.241",
|
|
64
|
+
"@gjsify/gtksource-core": "0.64.1-next.241",
|
|
65
65
|
"@lezer/highlight": "^1.2.3"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@gjsify/app-settings": "
|
|
68
|
+
"@gjsify/app-settings": "0.64.1-next.241"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|
|
71
71
|
"@gjsify/app-settings": {
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@gjsify/app-settings": "
|
|
77
|
-
"@gjsify/blueprint": "
|
|
78
|
-
"@gjsify/cli": "
|
|
79
|
-
"@gjsify/unit": "
|
|
80
|
-
"@gjsify/vite-plugin-blueprint": "
|
|
76
|
+
"@gjsify/app-settings": "0.64.1-next.241",
|
|
77
|
+
"@gjsify/blueprint": "0.64.1-next.241",
|
|
78
|
+
"@gjsify/cli": "0.64.1-next.241",
|
|
79
|
+
"@gjsify/unit": "0.64.1-next.241",
|
|
80
|
+
"@gjsify/vite-plugin-blueprint": "0.64.1-next.241",
|
|
81
81
|
"sass": "^1.101.0",
|
|
82
82
|
"typescript": "^6.0.3"
|
|
83
83
|
},
|
|
@@ -114,5 +114,6 @@
|
|
|
114
114
|
"bugs": {
|
|
115
115
|
"url": "https://github.com/gjsify/gjsify/issues"
|
|
116
116
|
},
|
|
117
|
-
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/adwaita-web#readme"
|
|
117
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/adwaita-web#readme",
|
|
118
|
+
"gitHead": "9937e8d27d18d159e35471b6669bdca6dff0cd05"
|
|
118
119
|
}
|
package/scss/_window.scss
CHANGED
|
@@ -26,6 +26,13 @@ adw-application-window {
|
|
|
26
26
|
outline: 1px solid rgb(255 255 255 / 7%);
|
|
27
27
|
outline-offset: -1px;
|
|
28
28
|
|
|
29
|
+
// No authored size (the element sets an inline one from `width`/`default-width`): a page
|
|
30
|
+
// has no window manager to size a toplevel, so it fills the viewport. The 360×200
|
|
31
|
+
// minimum (Adw.Window's size request) is the inline min-size the element sets.
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 100dvh;
|
|
34
|
+
box-sizing: border-box;
|
|
35
|
+
|
|
29
36
|
// `window.maximized` / `window.fullscreen` zero the radius and drop the shadow
|
|
30
37
|
// (_window.scss:66-72). A page-sized window IS the maximised one here: the frame
|
|
31
38
|
// is in-page, so there is nothing outside it to cast a shadow on.
|
|
@@ -674,4 +674,24 @@ export const AdwActionRowsTest = async () => {
|
|
|
674
674
|
host.remove();
|
|
675
675
|
});
|
|
676
676
|
});
|
|
677
|
+
|
|
678
|
+
await describe('declared GTK signals reach connect()', async () => {
|
|
679
|
+
await it('<adw-action-row> connects `activated`', () => {
|
|
680
|
+
const { el, host } = mount<AdwActionRow>('adw-action-row');
|
|
681
|
+
let seen = 0;
|
|
682
|
+
(el as unknown as { connect(s: string, h: () => void): number }).connect('activated', () => seen++);
|
|
683
|
+
el.dispatchEvent(new CustomEvent('activated'));
|
|
684
|
+
expect(seen).toBe(1);
|
|
685
|
+
host.remove();
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
await it('<adw-switch-row> connects `notify::active`', () => {
|
|
689
|
+
const { el, host } = mount<AdwSwitchRow>('adw-switch-row');
|
|
690
|
+
let seen = 0;
|
|
691
|
+
(el as unknown as { connect(s: string, h: () => void): number }).connect('notify::active', () => seen++);
|
|
692
|
+
el.setAttribute('active', '');
|
|
693
|
+
expect(seen).toBe(1);
|
|
694
|
+
host.remove();
|
|
695
|
+
});
|
|
696
|
+
});
|
|
677
697
|
};
|
|
@@ -143,4 +143,19 @@ export const AdwAlertDialogTest = async () => {
|
|
|
143
143
|
host.remove();
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
|
+
|
|
147
|
+
await describe('adw-alert-dialog responses before the first connect', async () => {
|
|
148
|
+
await it('buttons added while detached appear once connected', () => {
|
|
149
|
+
const el = document.createElement('adw-alert-dialog') as AdwAlertDialog;
|
|
150
|
+
el.addResponse('cancel', 'Cancel');
|
|
151
|
+
el.addResponse('ok', 'OK');
|
|
152
|
+
expect(el.getResponseLabel('ok')).toBe('OK');
|
|
153
|
+
const host = document.createElement('div');
|
|
154
|
+
document.body.appendChild(host);
|
|
155
|
+
host.appendChild(el);
|
|
156
|
+
const labels = [...el.querySelectorAll('.adw-alert-dialog-response')].map((b) => b.textContent);
|
|
157
|
+
expect(labels.join(',')).toBe('Cancel,OK');
|
|
158
|
+
host.remove();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
146
161
|
};
|
|
@@ -47,7 +47,8 @@ export const AdwApplicationWindowTest = async () => {
|
|
|
47
47
|
|
|
48
48
|
await it('keeps the inherited size request', async () => {
|
|
49
49
|
const win = makeWindow();
|
|
50
|
-
expect(win.style.
|
|
50
|
+
expect(win.style.minWidth).toBe('360px');
|
|
51
|
+
expect(win.style.width).toBe('');
|
|
51
52
|
win.setAttribute('width', '520');
|
|
52
53
|
expect(win.style.width).toBe('520px');
|
|
53
54
|
win.remove();
|
package/src/adw-window.spec.ts
CHANGED
|
@@ -53,13 +53,32 @@ export const AdwWindowTest = async () => {
|
|
|
53
53
|
win.remove();
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
await it('
|
|
56
|
+
await it('keeps 360×200 as a minimum and fills the viewport without an authored size', async () => {
|
|
57
57
|
const win = makeWindow();
|
|
58
|
-
expect(win.style.
|
|
59
|
-
expect(win.style.
|
|
58
|
+
expect(win.style.minWidth).toBe('360px');
|
|
59
|
+
expect(win.style.minHeight).toBe('200px');
|
|
60
|
+
expect(win.style.width).toBe('');
|
|
61
|
+
expect(win.style.height).toBe('');
|
|
62
|
+
expect(Math.round(win.getBoundingClientRect().width)).toBe(document.documentElement.clientWidth);
|
|
63
|
+
expect(Math.round(win.getBoundingClientRect().height)).toBe(window.innerHeight);
|
|
60
64
|
win.remove();
|
|
61
65
|
});
|
|
62
66
|
|
|
67
|
+
await it('width-request/height-request override the minimum; gtk-window has none', async () => {
|
|
68
|
+
const win = document.createElement('adw-window') as AdwWindow;
|
|
69
|
+
win.setAttribute('width-request', '100');
|
|
70
|
+
win.setAttribute('height-request', '50');
|
|
71
|
+
document.body.appendChild(win);
|
|
72
|
+
expect(win.style.minWidth).toBe('100px');
|
|
73
|
+
expect(win.style.minHeight).toBe('50px');
|
|
74
|
+
win.remove();
|
|
75
|
+
const plain = document.createElement('gtk-window');
|
|
76
|
+
document.body.appendChild(plain);
|
|
77
|
+
expect(plain.style.minWidth).toBe('');
|
|
78
|
+
expect(plain.style.minHeight).toBe('');
|
|
79
|
+
plain.remove();
|
|
80
|
+
});
|
|
81
|
+
|
|
63
82
|
await it('width/height attributes override the default size', async () => {
|
|
64
83
|
const win = document.createElement('adw-window') as AdwWindow;
|
|
65
84
|
win.setAttribute('width', '500');
|
package/src/bottom-sheet.spec.ts
CHANGED
|
@@ -577,4 +577,15 @@ export const AdwBottomSheetTest = async () => {
|
|
|
577
577
|
sheet.remove();
|
|
578
578
|
});
|
|
579
579
|
});
|
|
580
|
+
|
|
581
|
+
await describe('<adw-bottom-sheet> declared signals', async () => {
|
|
582
|
+
await it('connects `notify::open`', () => {
|
|
583
|
+
const sheet = mountSheet();
|
|
584
|
+
let seen = 0;
|
|
585
|
+
(sheet as unknown as { connect(s: string, h: () => void): number }).connect('notify::open', () => seen++);
|
|
586
|
+
sheet.open = true;
|
|
587
|
+
expect(seen).toBe(1);
|
|
588
|
+
sheet.remove();
|
|
589
|
+
});
|
|
590
|
+
});
|
|
580
591
|
};
|
|
@@ -27,6 +27,7 @@ import { ActionRowState } from '@gjsify/adwaita-core';
|
|
|
27
27
|
import { bindEmptySections } from '../empty-sections.js';
|
|
28
28
|
import { applyRowTooltip } from '../row-tooltip.js';
|
|
29
29
|
import { bindSlottedChildren } from '../slotted-children.js';
|
|
30
|
+
import type { DispatchedSignals } from '../signals.js';
|
|
30
31
|
import { type AdwRowActivation, attachRowActivation } from './row-activation.js';
|
|
31
32
|
import { applyRowLineClamp, parseRowLinesAttribute } from '../row-line-clamp.js';
|
|
32
33
|
|
|
@@ -44,6 +45,9 @@ function isSensitive(widget: HTMLElement): boolean {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export class AdwActionRow extends HTMLElement {
|
|
48
|
+
/** The GTK signals this element dispatches, each with the DOM event it arrives as (ADR 0093). */
|
|
49
|
+
static readonly signals: DispatchedSignals = { activated: 'activated' };
|
|
50
|
+
|
|
47
51
|
private _titleEl!: HTMLSpanElement;
|
|
48
52
|
private _subtitleEl!: HTMLSpanElement;
|
|
49
53
|
private _suffixEl!: HTMLDivElement;
|
|
@@ -222,6 +222,10 @@ export class AdwAlertDialog extends HTMLElement {
|
|
|
222
222
|
this._responseAreaEl = document.createElement('div');
|
|
223
223
|
this._responseAreaEl.className = 'adw-alert-dialog-responses';
|
|
224
224
|
|
|
225
|
+
// Responses added before the first connect already own a button (`addResponse` is
|
|
226
|
+
// callable at any point in the dialog's life); only the area to hang them in was missing.
|
|
227
|
+
this._responseAreaEl.append(...this._buttons.values());
|
|
228
|
+
|
|
225
229
|
this._dialogEl.append(messageArea, this._responseAreaEl);
|
|
226
230
|
|
|
227
231
|
// Two slots, both LIVE. An `<adw-alert-response>` is DATA — read for its id, label
|
|
@@ -463,6 +467,8 @@ export class AdwAlertDialog extends HTMLElement {
|
|
|
463
467
|
}
|
|
464
468
|
|
|
465
469
|
private _render(): void {
|
|
470
|
+
// Before the first connect there is no DOM to render into; `connectedCallback` renders.
|
|
471
|
+
if (!this._initialized) return;
|
|
466
472
|
this.classList.toggle('open', this.open);
|
|
467
473
|
|
|
468
474
|
const heading = this.heading;
|
|
@@ -57,7 +57,14 @@ export class AdwApplicationWindow extends GtkApplicationWindow {
|
|
|
57
57
|
// resolves a `[...Base.observedAttributes]` spread across files, and reports a
|
|
58
58
|
// `super` one as unreadable rather than as empty).
|
|
59
59
|
static get observedAttributes(): string[] {
|
|
60
|
-
return [
|
|
60
|
+
return [
|
|
61
|
+
...GtkApplicationWindow.observedAttributes,
|
|
62
|
+
'width',
|
|
63
|
+
'height',
|
|
64
|
+
'width-request',
|
|
65
|
+
'height-request',
|
|
66
|
+
'menu-model',
|
|
67
|
+
];
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
/** The content area — the Adw.Window:content property. */
|
|
@@ -131,7 +138,7 @@ export class AdwApplicationWindow extends GtkApplicationWindow {
|
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
attributeChangedCallback(name: string) {
|
|
134
|
-
if (name === 'width' || name === 'height') {
|
|
141
|
+
if (name === 'width' || name === 'height' || name === 'width-request' || name === 'height-request') {
|
|
135
142
|
this.renderFrameSize();
|
|
136
143
|
return;
|
|
137
144
|
}
|
|
@@ -64,6 +64,7 @@ import type {
|
|
|
64
64
|
} from '@gjsify/adwaita-core';
|
|
65
65
|
|
|
66
66
|
import { bindSlottedChildren, type AdwSlot } from '../slotted-children.js';
|
|
67
|
+
import type { DispatchedSignals } from '../signals.js';
|
|
67
68
|
|
|
68
69
|
/** The persistent content. Child of <adw-bottom-sheet>; consumed at connect time. */
|
|
69
70
|
export class AdwBottomSheetContent extends HTMLElement {}
|
|
@@ -75,6 +76,9 @@ export class AdwBottomSheetSheet extends HTMLElement {}
|
|
|
75
76
|
export class AdwBottomSheetBottomBar extends HTMLElement {}
|
|
76
77
|
|
|
77
78
|
export class AdwBottomSheet extends HTMLElement {
|
|
79
|
+
/** The GTK signals this element dispatches, each with the DOM event it arrives as (ADR 0093). */
|
|
80
|
+
static readonly signals: DispatchedSignals = { 'notify::open': 'notify::open' };
|
|
81
|
+
|
|
78
82
|
private _initialized = false;
|
|
79
83
|
private _contentEl!: HTMLDivElement;
|
|
80
84
|
private _dimmingEl!: HTMLDivElement;
|
|
@@ -55,6 +55,7 @@ import { BACK_BUTTON_FALLBACK_TOOLTIP, NavigationViewState, describeNavigationDi
|
|
|
55
55
|
import type { AdwNavigationPageProps, NavigationDiagnostic, NavigationStackChange } from '@gjsify/adwaita-core';
|
|
56
56
|
|
|
57
57
|
import { bindSlottedChildren } from '../slotted-children.js';
|
|
58
|
+
import type { DispatchedSignals } from '../signals.js';
|
|
58
59
|
|
|
59
60
|
/** The page properties the core owns, read off the element's attributes. */
|
|
60
61
|
function readPageProps(page: AdwNavigationPage): AdwNavigationPageProps {
|
|
@@ -135,6 +136,14 @@ export class AdwNavigationPage extends HTMLElement {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
export class AdwNavigationView extends HTMLElement {
|
|
139
|
+
/** The GTK signals this element dispatches, each with the DOM event it arrives as (ADR 0093). */
|
|
140
|
+
static readonly signals: DispatchedSignals = {
|
|
141
|
+
pushed: 'pushed',
|
|
142
|
+
popped: 'popped',
|
|
143
|
+
replaced: 'replaced',
|
|
144
|
+
'notify::visible-page': 'notify::visible-page',
|
|
145
|
+
};
|
|
146
|
+
|
|
138
147
|
private _pagesEl!: HTMLDivElement;
|
|
139
148
|
// The back button injected into each visible page (so we can remove it).
|
|
140
149
|
private readonly _backButtons = new WeakMap<AdwNavigationPage, HTMLElement>();
|
|
@@ -178,6 +187,11 @@ export class AdwNavigationView extends HTMLElement {
|
|
|
178
187
|
return this._state.visiblePageTag;
|
|
179
188
|
}
|
|
180
189
|
|
|
190
|
+
/** `adw_navigation_view_get_visible_page_tag`. */
|
|
191
|
+
get_visible_page_tag(): string | null {
|
|
192
|
+
return this.visiblePageTag;
|
|
193
|
+
}
|
|
194
|
+
|
|
181
195
|
get depth(): number {
|
|
182
196
|
return this._state.depth;
|
|
183
197
|
}
|
|
@@ -303,6 +317,11 @@ export class AdwNavigationView extends HTMLElement {
|
|
|
303
317
|
return pushed;
|
|
304
318
|
}
|
|
305
319
|
|
|
320
|
+
/** `adw_navigation_view_push_by_tag`, under the name a GJS caller uses. */
|
|
321
|
+
push_by_tag(tag: string): boolean {
|
|
322
|
+
return this.pushByTag(tag);
|
|
323
|
+
}
|
|
324
|
+
|
|
306
325
|
/** Push the page carrying `tag` (mirrors AdwNavigationView.push_by_tag). */
|
|
307
326
|
pushByTag(tag: string): boolean {
|
|
308
327
|
const pushed = this._state.pushByTag(tag);
|
|
@@ -29,6 +29,7 @@ import { SwitchRowState, deriveRowLabels } from '@gjsify/adwaita-core';
|
|
|
29
29
|
import { bindEmptySections } from '../empty-sections.js';
|
|
30
30
|
import { applyRowTooltip } from '../row-tooltip.js';
|
|
31
31
|
import { bindSlottedChildren } from '../slotted-children.js';
|
|
32
|
+
import type { DispatchedSignals } from '../signals.js';
|
|
32
33
|
import { applyRowLineClamp, parseRowLinesAttribute } from '../row-line-clamp.js';
|
|
33
34
|
|
|
34
35
|
// SIDE-EFFECT import, deliberately separate from the type import below: it guarantees
|
|
@@ -42,6 +43,9 @@ import { attachRowActivation } from './row-activation.js';
|
|
|
42
43
|
import type { GtkSwitch } from './gtk-switch.js';
|
|
43
44
|
|
|
44
45
|
export class AdwSwitchRow extends HTMLElement {
|
|
46
|
+
/** The GTK signals this element dispatches, each with the DOM event it arrives as (ADR 0093). */
|
|
47
|
+
static readonly signals: DispatchedSignals = { 'notify::active': 'notify::active' };
|
|
48
|
+
|
|
45
49
|
private _switchEl!: GtkSwitch;
|
|
46
50
|
private _prefixEl!: HTMLDivElement;
|
|
47
51
|
/** The label column — kept for `tooltip-text`, which the browser reads off a PART. */
|
|
@@ -13,10 +13,16 @@
|
|
|
13
13
|
import { AdwBreakpoint } from '@gjsify/adwaita-core';
|
|
14
14
|
import { addBreakpoints } from '../breakpoints.js';
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/** `adw_window_init`'s size request: a MINIMUM, overridable by `width-request`/`height-request`. */
|
|
17
17
|
const DEFAULT_WIDTH = 360;
|
|
18
18
|
const DEFAULT_HEIGHT = 200;
|
|
19
19
|
|
|
20
|
+
/** A `width-request`/`height-request` attribute as px; absent, unparsable or -1 means unset. */
|
|
21
|
+
function requested(host: HTMLElement, name: string, fallback: number): number {
|
|
22
|
+
const value = Number.parseFloat(host.getAttribute(name) ?? '');
|
|
23
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
export class AdwWindowFeatures {
|
|
21
27
|
private _contentEl: HTMLDivElement | null = null;
|
|
22
28
|
private _breakpoints: AdwBreakpoint[] = [];
|
|
@@ -117,12 +123,21 @@ export class AdwWindowFeatures {
|
|
|
117
123
|
this._disposeBreakpoints = null;
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* The window's size. `Adw.Window` defaults to a 360×200 MINIMUM (a size request, not the
|
|
128
|
+
* size; `width-request`/`height-request` override it). `default-width/height` is only
|
|
129
|
+
* a default: an authored size is the box, and an absent one leaves the box to the
|
|
130
|
+
* stylesheet, which fills the viewport — a page has no window manager to pick a size.
|
|
131
|
+
*/
|
|
121
132
|
applySizeRequest(): void {
|
|
122
133
|
const host = this._host;
|
|
134
|
+
host.style.minWidth = `${requested(host, 'width-request', DEFAULT_WIDTH)}px`;
|
|
135
|
+
host.style.minHeight = `${requested(host, 'height-request', DEFAULT_HEIGHT)}px`;
|
|
123
136
|
const width = host.getAttribute('default-width') ?? host.getAttribute('width');
|
|
124
137
|
const height = host.getAttribute('default-height') ?? host.getAttribute('height');
|
|
125
|
-
host.style.width =
|
|
126
|
-
host.style.
|
|
138
|
+
if (width) host.style.width = `${width}px`;
|
|
139
|
+
else host.style.removeProperty('width');
|
|
140
|
+
if (height) host.style.height = `${height}px`;
|
|
141
|
+
else host.style.removeProperty('height');
|
|
127
142
|
}
|
|
128
143
|
}
|
|
@@ -48,7 +48,7 @@ export class AdwWindow extends GtkWindow {
|
|
|
48
48
|
private readonly _features = new AdwWindowFeatures(this);
|
|
49
49
|
|
|
50
50
|
static get observedAttributes() {
|
|
51
|
-
return [...GtkWindow.observedAttributes, 'width', 'height'];
|
|
51
|
+
return [...GtkWindow.observedAttributes, 'width', 'height', 'width-request', 'height-request'];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/** The content area — the Adw.Window:content property. */
|
|
@@ -105,7 +105,7 @@ export class AdwWindow extends GtkWindow {
|
|
|
105
105
|
attributeChangedCallback(name: string) {
|
|
106
106
|
// The size request is read from the ATTRIBUTE, so it is applied on every change —
|
|
107
107
|
// including one that arrived before the element was connected.
|
|
108
|
-
if (name === 'width' || name === 'height') {
|
|
108
|
+
if (name === 'width' || name === 'height' || name === 'width-request' || name === 'height-request') {
|
|
109
109
|
this.renderFrameSize();
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
import type { ActionLike } from '@gjsify/adwaita-core';
|
|
30
30
|
import type { GtkApplication } from '../application.js';
|
|
31
|
+
import type { DispatchedSignals } from '../signals.js';
|
|
31
32
|
import type { AdwSlot } from '../slotted-children.js';
|
|
32
33
|
import { GtkWindow } from './gtk-window.js';
|
|
33
34
|
import { defineWindowApplication } from '../application.js';
|
|
@@ -45,6 +46,11 @@ export class GtkApplicationWindow extends GtkWindow {
|
|
|
45
46
|
private _menubar: HTMLDivElement | null = null;
|
|
46
47
|
private _menubarObserver: MutationObserver | null = null;
|
|
47
48
|
|
|
49
|
+
static override readonly signals: DispatchedSignals = {
|
|
50
|
+
...GtkWindow.signals,
|
|
51
|
+
'notify::show-menubar': 'notify::show-menubar',
|
|
52
|
+
};
|
|
53
|
+
|
|
48
54
|
static get observedAttributes(): string[] {
|
|
49
55
|
return [...GtkWindow.observedAttributes, 'show-menubar'];
|
|
50
56
|
}
|
|
@@ -73,6 +73,13 @@
|
|
|
73
73
|
// maximize its browser window. `is_maximized()` reads it.
|
|
74
74
|
// `present()` — un-hides the frame; a page cannot raise or focus a window it does not own.
|
|
75
75
|
//
|
|
76
|
+
// THE GOBJECT DOOR (ADR 0096 § 3, ADR 0098 Amendment 3): `connect` / `disconnect` / `emit` /
|
|
77
|
+
// `notify` / `get_property` / `set_property` come from `installElementApi`, over the events in
|
|
78
|
+
// `static signals`; an undeclared signal throws. A `close-request` handler that returns true is
|
|
79
|
+
// `preventDefault()` and stops later listeners. `emit('close-request')` throws: the signal is
|
|
80
|
+
// `close()`'s, which also runs the default handler. `is_active` and `focus_widget` are PROPERTIES,
|
|
81
|
+
// as in GJS and `@girs/gtk-4.0` (GTK 4 has no `gtk_window_is_active()`); `is_active()` is gone.
|
|
82
|
+
//
|
|
76
83
|
// Events: `notify::<prop>` (CustomEvent, bubbles, detail `{ <prop>: value }`) on every real
|
|
77
84
|
// change, and `close-request` (CustomEvent, bubbles, CANCELLABLE, no detail), which is
|
|
78
85
|
// `Gtk.Window::close-request`: a listener that calls `preventDefault()` is a GTK handler that
|
|
@@ -85,6 +92,8 @@
|
|
|
85
92
|
|
|
86
93
|
import { CloseRequestState, WindowMemberState, type WindowMemberProperty } from '@gjsify/adwaita-core';
|
|
87
94
|
|
|
95
|
+
import { installElementApi } from '../gobject-elements.js';
|
|
96
|
+
import type { DispatchedSignals } from '../signals.js';
|
|
88
97
|
import { bindSlottedChildren, type AdwSlot } from '../slotted-children.js';
|
|
89
98
|
|
|
90
99
|
// Registers <gtk-window-controls>: a frame with no titlebar of its own draws the two of them
|
|
@@ -120,6 +129,64 @@ export class GtkWindow extends HTMLElement {
|
|
|
120
129
|
this._members.reportFocus(null);
|
|
121
130
|
};
|
|
122
131
|
|
|
132
|
+
/**
|
|
133
|
+
* The GObject door (ADR 0096 § 3): `connect` / `disconnect` / `emit` / `notify` / `get_property` /
|
|
134
|
+
* `set_property` over these events, the same ones every element declares in `static signals`.
|
|
135
|
+
* A `close-request` handler that returns true is `preventDefault()`.
|
|
136
|
+
*/
|
|
137
|
+
static readonly signals: DispatchedSignals = {
|
|
138
|
+
'close-request': 'close-request',
|
|
139
|
+
...Object.fromEntries(
|
|
140
|
+
[
|
|
141
|
+
'decorated',
|
|
142
|
+
'default-height',
|
|
143
|
+
'default-width',
|
|
144
|
+
'deletable',
|
|
145
|
+
'focus-widget',
|
|
146
|
+
'fullscreened',
|
|
147
|
+
'hide-on-close',
|
|
148
|
+
'icon-name',
|
|
149
|
+
'is-active',
|
|
150
|
+
'maximized',
|
|
151
|
+
'modal',
|
|
152
|
+
'resizable',
|
|
153
|
+
'title',
|
|
154
|
+
'transient-for',
|
|
155
|
+
].map((name) => [`notify::${name}`, `notify::${name}`]),
|
|
156
|
+
),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/** `GObject.Object.connect`: installed per instance by `installElementApi`; refuses an undeclared signal. */
|
|
160
|
+
declare connect: (signal: string, handler: (emitter: this, ...args: any[]) => unknown) => number;
|
|
161
|
+
/** `GObject.Object.disconnect`: throws for an id that is not connected. */
|
|
162
|
+
declare disconnect: (id: number) => void;
|
|
163
|
+
/** `GObject.Object.emit`; `close-request` is refused, call `close()`. */
|
|
164
|
+
declare emit: (signal: string, ...args: unknown[]) => void;
|
|
165
|
+
/** `GObject.Object.notify`. */
|
|
166
|
+
declare notify: (name: string) => void;
|
|
167
|
+
/** `GObject.Object.get_property`, by GTK's dashed name. */
|
|
168
|
+
declare get_property: (name: string) => unknown;
|
|
169
|
+
/** `GObject.Object.set_property`; a read-only property throws. */
|
|
170
|
+
declare set_property: (name: string, value: unknown) => void;
|
|
171
|
+
|
|
172
|
+
constructor() {
|
|
173
|
+
super();
|
|
174
|
+
installElementApi(this);
|
|
175
|
+
// `Gtk.Window::close-request` is emitted by `close()`, which also runs the default handler
|
|
176
|
+
// (hide or destroy). Emitting it bare would be a different thing under the same name.
|
|
177
|
+
const emit = (this as unknown as { emit: (signal: string, ...args: unknown[]) => void }).emit;
|
|
178
|
+
Object.defineProperty(this, 'emit', {
|
|
179
|
+
configurable: true,
|
|
180
|
+
writable: true,
|
|
181
|
+
value: (signal: string, ...args: unknown[]): void => {
|
|
182
|
+
if (signal === 'close-request') {
|
|
183
|
+
throw new Error("<gtk-window>.emit('close-request') is not supported; call close()");
|
|
184
|
+
}
|
|
185
|
+
emit(signal, ...args);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
123
190
|
static get observedAttributes() {
|
|
124
191
|
return [
|
|
125
192
|
'decorated',
|
|
@@ -460,13 +527,16 @@ export class GtkWindow extends HTMLElement {
|
|
|
460
527
|
this.hidden = false;
|
|
461
528
|
}
|
|
462
529
|
|
|
463
|
-
/**
|
|
530
|
+
/**
|
|
531
|
+
* `Gtk.Window:is-active` — see the header: the document's focus, while this frame is shown.
|
|
532
|
+
* A PROPERTY, as in GJS and `@girs/gtk-4.0`: GTK 4 has no `gtk_window_is_active`, so
|
|
533
|
+
* `window.is_active` is the value and `window.is_active()` does not exist.
|
|
534
|
+
*/
|
|
464
535
|
get isActive(): boolean {
|
|
465
536
|
return this._members.active;
|
|
466
537
|
}
|
|
467
538
|
|
|
468
|
-
|
|
469
|
-
is_active(): boolean {
|
|
539
|
+
get is_active(): boolean {
|
|
470
540
|
return this._members.active;
|
|
471
541
|
}
|
|
472
542
|
|
|
@@ -475,6 +545,15 @@ export class GtkWindow extends HTMLElement {
|
|
|
475
545
|
return this.get_focus();
|
|
476
546
|
}
|
|
477
547
|
|
|
548
|
+
/** `Gtk.Window:focus-widget` under its GJS spelling; writing it is `set_focus`. */
|
|
549
|
+
get focus_widget(): Element | null {
|
|
550
|
+
return this.get_focus();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
set focus_widget(widget: Element | null) {
|
|
554
|
+
this.set_focus(widget);
|
|
555
|
+
}
|
|
556
|
+
|
|
478
557
|
/** `gtk_window_get_focus`: the element inside the frame that has focus, or `null`. */
|
|
479
558
|
get_focus(): Element | null {
|
|
480
559
|
const active = document.activeElement;
|
package/src/gobject-elements.ts
CHANGED
|
@@ -113,7 +113,13 @@ export function listenElement(el: HTMLElement, signal: string, handler: Handler)
|
|
|
113
113
|
const listener = (domEvent: Event) => {
|
|
114
114
|
// A notification bubbles, and a parent must not hear its child's property as its own.
|
|
115
115
|
if (notification && domEvent.target !== el) return;
|
|
116
|
-
handler(el, ...(EMITTED.has(domEvent) ? ((domEvent as CustomEvent).detail as unknown[]) : []));
|
|
116
|
+
const handled = handler(el, ...(EMITTED.has(domEvent) ? ((domEvent as CustomEvent).detail as unknown[]) : []));
|
|
117
|
+
// A GTK handler of a boolean-handled signal (`close-request`) returns true to take it over,
|
|
118
|
+
// which on a cancelable DOM event is `preventDefault()`; the handlers after it do not run.
|
|
119
|
+
if (handled === true && domEvent.cancelable) {
|
|
120
|
+
domEvent.preventDefault();
|
|
121
|
+
domEvent.stopImmediatePropagation();
|
|
122
|
+
}
|
|
117
123
|
};
|
|
118
124
|
el.addEventListener(event, listener);
|
|
119
125
|
return () => el.removeEventListener(event, listener);
|
|
@@ -148,13 +154,26 @@ export function installElementApi(el: HTMLElement): void {
|
|
|
148
154
|
});
|
|
149
155
|
define('emit', (signal: string, ...args: unknown[]): void => dispatchElement(el, signal, args));
|
|
150
156
|
define('notify', (name: string): void => notifyElement(el, name));
|
|
157
|
+
define('get_property', (name: string): unknown => {
|
|
158
|
+
eventOf(el, `notify::${name}`);
|
|
159
|
+
return (el as unknown as Record<string, unknown>)[propertyOf(name)];
|
|
160
|
+
});
|
|
161
|
+
define('set_property', (name: string, value: unknown): void => {
|
|
162
|
+
eventOf(el, `notify::${name}`);
|
|
163
|
+
const member = propertyOf(name);
|
|
164
|
+
const descriptor = inheritedDescriptor(el, member);
|
|
165
|
+
if (descriptor !== undefined && descriptor.get !== undefined && descriptor.set === undefined) {
|
|
166
|
+
throw new Error(`Property '${name}' of <${el.localName}> is read-only`);
|
|
167
|
+
}
|
|
168
|
+
(el as unknown as Record<string, unknown>)[member] = value;
|
|
169
|
+
});
|
|
151
170
|
// The `Gtk.Widget` CSS-class calls a template's TypeScript makes on its children.
|
|
152
171
|
define('add_css_class', (name: string): void => el.classList.add(name));
|
|
153
172
|
define('remove_css_class', (name: string): void => el.classList.remove(name));
|
|
154
173
|
define('has_css_class', (name: string): boolean => el.classList.contains(name));
|
|
155
174
|
}
|
|
156
175
|
|
|
157
|
-
const API_MEMBERS = new Set(['connect', 'disconnect', 'emit', 'notify']);
|
|
176
|
+
const API_MEMBERS = new Set(['connect', 'disconnect', 'emit', 'notify', 'get_property', 'set_property']);
|
|
158
177
|
|
|
159
178
|
/**
|
|
160
179
|
* The end of a bind the core's engine drives. An element speaks camelCase members where a `.blp`
|