@gjsify/adwaita-web 0.64.1-next.240 → 0.64.1-next.243
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 +42 -4
- package/dist/adwaita-web.css +12 -1
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/canvas2d/canvas2d-bridge.d.ts +18 -0
- package/lib/types/canvas2d-bridge.spec.d.ts +1 -0
- 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-image.d.ts +12 -3
- package/lib/types/elements/gtk-scrolled-window.d.ts +10 -0
- package/lib/types/elements/gtk-window.d.ts +29 -3
- package/lib/types/gobject-elements.d.ts +7 -0
- package/lib/types/icon-registry.d.ts +3 -1
- package/lib/types/icon-theme.d.ts +53 -0
- package/lib/types/icon-theme.spec.d.ts +1 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +12 -12
- package/scss/_about_dialog.scss +2 -2
- package/scss/_icon.scss +18 -0
- 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/canvas2d/canvas2d-bridge.ts +51 -1
- package/src/canvas2d-bridge.spec.ts +47 -0
- package/src/elements/adw-about-dialog.ts +4 -2
- 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-image.ts +55 -24
- package/src/elements/gtk-scrolled-window.ts +37 -1
- package/src/elements/gtk-window.ts +82 -3
- package/src/gobject-door.spec.ts +44 -0
- package/src/gobject-elements.ts +43 -2
- package/src/gtk-scrolled-window.spec.ts +74 -0
- package/src/gtk-window.spec.ts +97 -1
- package/src/icon-registry.ts +13 -7
- package/src/icon-theme.spec.ts +182 -0
- package/src/icon-theme.ts +178 -0
- package/src/index.ts +3 -0
- package/src/navigation-view.spec.ts +26 -0
- package/src/shared-tree-builder.ts +2 -1
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/adwaita-web",
|
|
3
|
-
"version": "0.64.1-next.
|
|
3
|
+
"version": "0.64.1-next.243",
|
|
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": "0.64.1-next.
|
|
62
|
-
"@gjsify/adwaita-fonts": "0.64.1-next.
|
|
63
|
-
"@gjsify/adwaita-icons": "0.64.1-next.
|
|
64
|
-
"@gjsify/gtksource-core": "0.64.1-next.
|
|
61
|
+
"@gjsify/adwaita-core": "0.64.1-next.243",
|
|
62
|
+
"@gjsify/adwaita-fonts": "0.64.1-next.243",
|
|
63
|
+
"@gjsify/adwaita-icons": "0.64.1-next.243",
|
|
64
|
+
"@gjsify/gtksource-core": "0.64.1-next.243",
|
|
65
65
|
"@lezer/highlight": "^1.2.3"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@gjsify/app-settings": "0.64.1-next.
|
|
68
|
+
"@gjsify/app-settings": "0.64.1-next.243"
|
|
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": "0.64.1-next.
|
|
77
|
-
"@gjsify/blueprint": "0.64.1-next.
|
|
78
|
-
"@gjsify/cli": "0.64.1-next.
|
|
79
|
-
"@gjsify/unit": "0.64.1-next.
|
|
80
|
-
"@gjsify/vite-plugin-blueprint": "0.64.1-next.
|
|
76
|
+
"@gjsify/app-settings": "0.64.1-next.243",
|
|
77
|
+
"@gjsify/blueprint": "0.64.1-next.243",
|
|
78
|
+
"@gjsify/cli": "0.64.1-next.243",
|
|
79
|
+
"@gjsify/unit": "0.64.1-next.243",
|
|
80
|
+
"@gjsify/vite-plugin-blueprint": "0.64.1-next.243",
|
|
81
81
|
"sass": "^1.101.0",
|
|
82
82
|
"typescript": "^6.0.3"
|
|
83
83
|
},
|
|
@@ -115,5 +115,5 @@
|
|
|
115
115
|
"url": "https://github.com/gjsify/gjsify/issues"
|
|
116
116
|
},
|
|
117
117
|
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/adwaita-web#readme",
|
|
118
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "f88c7b1074d454e211094963a9f4dc7d0982bf32"
|
|
119
119
|
}
|
package/scss/_about_dialog.scss
CHANGED
|
@@ -149,10 +149,10 @@ adw-about-dialog {
|
|
|
149
149
|
height: 96px;
|
|
150
150
|
margin-top: var(--spacing-s);
|
|
151
151
|
margin-bottom: var(--spacing-m);
|
|
152
|
-
//
|
|
152
|
+
// A symbolic glyph scales with the box; the fill stays `.adw-icon`'s `currentColor`,
|
|
153
|
+
// which `.adw-icon-color` turns off for a registered colour application icon.
|
|
153
154
|
-webkit-mask-size: contain;
|
|
154
155
|
mask-size: contain;
|
|
155
|
-
background-color: currentColor;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// --- App name (title-1) ---
|
package/scss/_icon.scss
CHANGED
|
@@ -96,3 +96,21 @@
|
|
|
96
96
|
.adw-icon:not([class*='adw-icon--']) {
|
|
97
97
|
background-color: transparent;
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
// A FULL-COLOUR icon — what `registerColorIcon()` registers and `<gtk-image>` resolves a
|
|
101
|
+
// name to before (or, for `-symbolic` names, after) the symbolic glyph, in the order GTK's
|
|
102
|
+
// theme tries them. It is drawn as-is, the way GTK draws `hicolor/…/apps/<app-id>.svg`: no
|
|
103
|
+
// mask and no `currentColor` fill, only the image the element puts in `background-image`.
|
|
104
|
+
//
|
|
105
|
+
// The modifier is NOT `adw-icon--…`, so the rule above never sees it as a mask class.
|
|
106
|
+
// Two classes beat both `:where(.adw-icon)` and the one-class sizing rules a context sets
|
|
107
|
+
// (`.adw-about-dialog-icon`'s mask-size), so an icon box drawn in colour never picks up a
|
|
108
|
+
// leftover mask from them.
|
|
109
|
+
.adw-icon.adw-icon-color {
|
|
110
|
+
mask-image: none;
|
|
111
|
+
-webkit-mask-image: none;
|
|
112
|
+
background-color: transparent;
|
|
113
|
+
background-position: center;
|
|
114
|
+
background-repeat: no-repeat;
|
|
115
|
+
background-size: contain;
|
|
116
|
+
}
|
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
|
};
|
|
@@ -63,7 +63,55 @@ export class Canvas2DBridgeElement extends HostElement {
|
|
|
63
63
|
private _drawFrame: number | null = null;
|
|
64
64
|
|
|
65
65
|
static get observedAttributes() {
|
|
66
|
-
return ['content-width', 'content-height'];
|
|
66
|
+
return ['content-width', 'content-height', 'focusable', 'can-focus'];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `Gtk.Widget:focusable`, whether the widget can hold the keyboard focus. `Gtk.DrawingArea`
|
|
71
|
+
* leaves it FALSE, so it is read by PRESENCE; a blueprint's `focusable: true` writes it.
|
|
72
|
+
*/
|
|
73
|
+
get focusable(): boolean {
|
|
74
|
+
return this.hasAttribute('focusable');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
set focusable(v: boolean) {
|
|
78
|
+
this.toggleAttribute('focusable', !!v);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** `Gtk.Widget:can-focus`, TRUE by default, so it is read by VALUE. */
|
|
82
|
+
get canFocus(): boolean {
|
|
83
|
+
return this.getAttribute('can-focus') !== 'false';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
set canFocus(v: boolean) {
|
|
87
|
+
this.setAttribute('can-focus', v ? 'true' : 'false');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get can_focus(): boolean {
|
|
91
|
+
return this.canFocus;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
set can_focus(v: boolean) {
|
|
95
|
+
this.canFocus = v;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* `gtk_widget_grab_focus`: moves the keyboard focus here and says whether it landed.
|
|
100
|
+
* FALSE when the widget is not focusable or cannot focus, as in GTK.
|
|
101
|
+
*/
|
|
102
|
+
grab_focus(): boolean {
|
|
103
|
+
if (!this.focusable || !this.canFocus) return false;
|
|
104
|
+
this.focus();
|
|
105
|
+
return this.ownerDocument.activeElement === this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
grabFocus(): boolean {
|
|
109
|
+
return this.grab_focus();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private _syncFocus(): void {
|
|
113
|
+
if (this.focusable && this.canFocus) this.tabIndex = 0;
|
|
114
|
+
else this.removeAttribute('tabindex');
|
|
67
115
|
}
|
|
68
116
|
|
|
69
117
|
/** `Gtk.DrawingArea:content-width`, the minimum and natural width. Defaults to 0. */
|
|
@@ -161,6 +209,7 @@ export class Canvas2DBridgeElement extends HostElement {
|
|
|
161
209
|
this.style.overflow = 'hidden';
|
|
162
210
|
this.style.position = 'relative';
|
|
163
211
|
this._publishSize();
|
|
212
|
+
this._syncFocus();
|
|
164
213
|
this._observe();
|
|
165
214
|
}
|
|
166
215
|
|
|
@@ -171,6 +220,7 @@ export class Canvas2DBridgeElement extends HostElement {
|
|
|
171
220
|
|
|
172
221
|
attributeChangedCallback(name: string) {
|
|
173
222
|
if (name === 'content-width' || name === 'content-height') this._publishSize();
|
|
223
|
+
else this._syncFocus();
|
|
174
224
|
}
|
|
175
225
|
|
|
176
226
|
private _publishSize(): void {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// DOM-level tests for <gjsify-canvas2d-bridge>: the keyboard focus a game console needs for its keys.
|
|
2
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
3
|
+
|
|
4
|
+
import type { Canvas2DBridgeElement } from './canvas2d/canvas2d-bridge.js';
|
|
5
|
+
|
|
6
|
+
function mount(attrs: Record<string, string> = {}): { el: Canvas2DBridgeElement; host: HTMLElement } {
|
|
7
|
+
const host = document.createElement('div');
|
|
8
|
+
document.body.appendChild(host);
|
|
9
|
+
const el = document.createElement('gjsify-canvas2d-bridge') as Canvas2DBridgeElement;
|
|
10
|
+
for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
|
|
11
|
+
host.appendChild(el);
|
|
12
|
+
return { el, host };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Canvas2DBridgeTest = async () => {
|
|
16
|
+
await describe('<gjsify-canvas2d-bridge> focus', async () => {
|
|
17
|
+
await it('is not focusable by default, so grab_focus returns false', () => {
|
|
18
|
+
const { el, host } = mount();
|
|
19
|
+
expect(el.focusable).toBe(false);
|
|
20
|
+
expect(el.grab_focus()).toBe(false);
|
|
21
|
+
expect(document.activeElement === el).toBe(false);
|
|
22
|
+
host.remove();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await it('grab_focus takes the keyboard focus when focusable', () => {
|
|
26
|
+
const { el, host } = mount({ focusable: '' });
|
|
27
|
+
expect(el.grab_focus()).toBe(true);
|
|
28
|
+
expect(document.activeElement === el).toBe(true);
|
|
29
|
+
host.remove();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await it('a focusable set after connect makes the element focusable', () => {
|
|
33
|
+
const { el, host } = mount();
|
|
34
|
+
el.focusable = true;
|
|
35
|
+
expect(el.grab_focus()).toBe(true);
|
|
36
|
+
host.remove();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await it('can-focus false refuses the focus', () => {
|
|
40
|
+
const { el, host } = mount({ focusable: '' });
|
|
41
|
+
el.canFocus = false;
|
|
42
|
+
expect(el.can_focus).toBe(false);
|
|
43
|
+
expect(el.grab_focus()).toBe(false);
|
|
44
|
+
host.remove();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
// Properties / attributes (all reflected, mirroring the AdwAboutDialog GObject
|
|
20
20
|
// properties of the same name, hyphenated):
|
|
21
21
|
// application-name the application's display name (shown as a title-1)
|
|
22
|
-
// application-icon
|
|
23
|
-
//
|
|
22
|
+
// application-icon an icon name, by convention the application ID; drawn in
|
|
23
|
+
// colour once `registerColorIcon(id, …)` registered it, else
|
|
24
|
+
// as a symbolic glyph or `image-missing` (`<gtk-image>`'s
|
|
25
|
+
// lookup); absent one, no icon is drawn
|
|
24
26
|
// developer-name the developer / vendor line below the name
|
|
25
27
|
// version the version, shown as a clickable pill
|
|
26
28
|
// comments a longer description (Details page)
|
|
@@ -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
|
}
|