@gjsify/adwaita-web 0.64.1 → 0.64.2-next.254
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 +22 -88
- package/dist/adwaita-web.css +91 -12
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/accessible.d.ts +8 -0
- package/lib/types/application.d.ts +1 -1
- package/lib/types/child-slot.spec.d.ts +1 -0
- package/lib/types/compute-expand.d.ts +5 -0
- package/lib/types/compute-expand.spec.d.ts +1 -0
- package/lib/types/container-fill.spec.d.ts +1 -0
- package/lib/types/elements/adw-about-dialog.d.ts +28 -1
- package/lib/types/elements/adw-action-row.d.ts +2 -1
- package/lib/types/elements/adw-alert-dialog.d.ts +80 -19
- package/lib/types/elements/adw-avatar.d.ts +2 -1
- package/lib/types/elements/adw-banner.d.ts +2 -1
- package/lib/types/elements/adw-bin.d.ts +2 -1
- package/lib/types/elements/adw-bottom-sheet.d.ts +5 -4
- package/lib/types/elements/adw-breakpoint-bin.d.ts +2 -1
- package/lib/types/elements/adw-button-content.d.ts +2 -1
- package/lib/types/elements/adw-button-row.d.ts +2 -1
- package/lib/types/elements/adw-card.d.ts +2 -1
- package/lib/types/elements/adw-carousel.d.ts +20 -3
- package/lib/types/elements/adw-clamp-scrollable.d.ts +2 -1
- package/lib/types/elements/adw-clamp.d.ts +2 -1
- package/lib/types/elements/adw-combo-row.d.ts +6 -1
- package/lib/types/elements/adw-data-grid.d.ts +2 -1
- package/lib/types/elements/adw-dialog.d.ts +2 -1
- package/lib/types/elements/adw-entry-row.d.ts +2 -1
- package/lib/types/elements/adw-expander-row.d.ts +2 -1
- package/lib/types/elements/adw-header-bar.d.ts +2 -1
- package/lib/types/elements/adw-inline-view-switcher.d.ts +3 -2
- package/lib/types/elements/adw-layout-slot.d.ts +2 -1
- package/lib/types/elements/adw-multi-layout-view.d.ts +2 -1
- package/lib/types/elements/adw-navigation-split-view.d.ts +2 -1
- package/lib/types/elements/adw-navigation-view.d.ts +3 -2
- package/lib/types/elements/adw-overlay-split-view.d.ts +2 -1
- package/lib/types/elements/adw-preferences-dialog.d.ts +5 -4
- package/lib/types/elements/adw-preferences-group.d.ts +2 -1
- package/lib/types/elements/adw-preferences-row.d.ts +2 -1
- package/lib/types/elements/adw-shortcut-label.d.ts +2 -1
- package/lib/types/elements/adw-shortcuts-item.d.ts +2 -1
- package/lib/types/elements/adw-shortcuts-section.d.ts +2 -1
- package/lib/types/elements/adw-sidebar.d.ts +4 -3
- package/lib/types/elements/adw-spin-row.d.ts +2 -1
- package/lib/types/elements/adw-spinner.d.ts +2 -1
- package/lib/types/elements/adw-split-button.d.ts +2 -1
- package/lib/types/elements/adw-status-page.d.ts +2 -1
- package/lib/types/elements/adw-switch-row.d.ts +4 -1
- package/lib/types/elements/adw-tab-bar.d.ts +2 -1
- package/lib/types/elements/adw-tab-button.d.ts +2 -1
- package/lib/types/elements/adw-tab-overview.d.ts +2 -1
- package/lib/types/elements/adw-tab-view.d.ts +3 -2
- package/lib/types/elements/adw-toast-overlay.d.ts +10 -30
- package/lib/types/elements/adw-toggle-group.d.ts +3 -2
- package/lib/types/elements/adw-toolbar-view.d.ts +2 -1
- package/lib/types/elements/adw-view-stack.d.ts +3 -1
- package/lib/types/elements/adw-view-switcher-bar.d.ts +2 -1
- package/lib/types/elements/adw-view-switcher-sidebar.d.ts +2 -1
- package/lib/types/elements/adw-view-switcher.d.ts +3 -2
- package/lib/types/elements/adw-window-features.d.ts +8 -0
- package/lib/types/elements/adw-window-title.d.ts +2 -1
- package/lib/types/elements/adw-wrap-box.d.ts +2 -1
- package/lib/types/elements/checks.d.ts +16 -1
- package/lib/types/elements/gtk-action-bar.d.ts +2 -1
- package/lib/types/elements/gtk-application-window.d.ts +1 -4
- package/lib/types/elements/gtk-aspect-frame.d.ts +2 -1
- package/lib/types/elements/gtk-box.d.ts +5 -1
- package/lib/types/elements/gtk-button.d.ts +5 -7
- package/lib/types/elements/gtk-calendar.d.ts +2 -1
- package/lib/types/elements/gtk-center-box.d.ts +2 -1
- package/lib/types/elements/gtk-color-dialog-button.d.ts +2 -1
- package/lib/types/elements/gtk-drag-icon.d.ts +2 -1
- package/lib/types/elements/gtk-drawing-area.d.ts +2 -1
- package/lib/types/elements/gtk-drop-down.d.ts +2 -1
- package/lib/types/elements/gtk-editable-label.d.ts +2 -1
- package/lib/types/elements/gtk-emoji-chooser.d.ts +2 -0
- package/lib/types/elements/gtk-entry.d.ts +2 -1
- package/lib/types/elements/gtk-expander.d.ts +2 -1
- package/lib/types/elements/gtk-fixed.d.ts +2 -1
- package/lib/types/elements/gtk-flow-box-child.d.ts +2 -1
- package/lib/types/elements/gtk-flow-box.d.ts +2 -1
- package/lib/types/elements/gtk-font-dialog-button.d.ts +2 -1
- package/lib/types/elements/gtk-frame.d.ts +2 -1
- package/lib/types/elements/gtk-gl-area.d.ts +2 -1
- package/lib/types/elements/gtk-graphics-offload.d.ts +2 -1
- package/lib/types/elements/gtk-grid.d.ts +2 -1
- package/lib/types/elements/gtk-header-bar.d.ts +2 -1
- package/lib/types/elements/gtk-image.d.ts +5 -6
- package/lib/types/elements/gtk-inscription.d.ts +2 -1
- package/lib/types/elements/gtk-label.d.ts +10 -3
- package/lib/types/elements/gtk-level-bar.d.ts +2 -1
- package/lib/types/elements/gtk-list-box-row.d.ts +2 -1
- package/lib/types/elements/gtk-list-box.d.ts +2 -1
- package/lib/types/elements/gtk-list-view.d.ts +2 -1
- package/lib/types/elements/gtk-media-controls.d.ts +2 -1
- package/lib/types/elements/gtk-menu-button.d.ts +2 -1
- package/lib/types/elements/gtk-notebook.d.ts +2 -1
- package/lib/types/elements/gtk-overlay.d.ts +2 -1
- package/lib/types/elements/gtk-paned.d.ts +2 -1
- package/lib/types/elements/gtk-picture.d.ts +2 -1
- package/lib/types/elements/gtk-popover-bin.d.ts +2 -1
- package/lib/types/elements/gtk-popover-menu-bar.d.ts +2 -1
- package/lib/types/elements/gtk-popover-menu.d.ts +2 -0
- package/lib/types/elements/gtk-popover.d.ts +7 -1
- package/lib/types/elements/gtk-progress-bar.d.ts +2 -1
- package/lib/types/elements/gtk-revealer.d.ts +2 -1
- package/lib/types/elements/gtk-scale-button.d.ts +2 -1
- package/lib/types/elements/gtk-scale.d.ts +2 -1
- package/lib/types/elements/gtk-scrollbar.d.ts +2 -1
- package/lib/types/elements/gtk-scrolled-window.d.ts +2 -1
- package/lib/types/elements/gtk-search-bar.d.ts +2 -1
- package/lib/types/elements/gtk-separator.d.ts +2 -1
- package/lib/types/elements/gtk-spin-button.d.ts +2 -1
- package/lib/types/elements/gtk-spinner.d.ts +2 -1
- package/lib/types/elements/gtk-stack-page.d.ts +29 -0
- package/lib/types/elements/gtk-stack-sidebar.d.ts +2 -1
- package/lib/types/elements/gtk-stack-switcher.d.ts +2 -1
- package/lib/types/elements/gtk-stack.d.ts +13 -1
- package/lib/types/elements/gtk-switch.d.ts +2 -1
- package/lib/types/elements/gtk-text-view.d.ts +2 -1
- package/lib/types/elements/gtk-text.d.ts +2 -1
- package/lib/types/elements/gtk-tree-expander.d.ts +2 -1
- package/lib/types/elements/gtk-video.d.ts +2 -1
- package/lib/types/elements/gtk-viewport.d.ts +2 -1
- package/lib/types/elements/gtk-window-controls.d.ts +2 -1
- package/lib/types/elements/gtk-window-handle.d.ts +2 -1
- package/lib/types/elements/gtk-window.d.ts +18 -2
- package/lib/types/gobject-elements.d.ts +15 -0
- package/lib/types/gtksource/gtk-source-view.d.ts +2 -1
- package/lib/types/{icon-registry.spec.d.ts → icon-masks.spec.d.ts} +1 -1
- package/lib/types/icon-theme.d.ts +15 -35
- package/lib/types/index.d.ts +3 -4
- package/lib/types/learn6502-window.spec.d.ts +1 -0
- package/lib/types/namespace/adw.d.ts +1 -1
- package/lib/types/namespace/gdk.d.ts +1 -0
- package/lib/types/namespace/gio.d.ts +1 -0
- package/lib/types/namespace/glib.d.ts +2 -1
- package/lib/types/namespace/gtk.d.ts +2 -0
- package/lib/types/shared-tree-builder.d.ts +31 -0
- package/lib/types/signals.d.ts +5 -1
- package/lib/types/source-view/adw-source-view.d.ts +2 -1
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/widget-element.d.ts +25 -0
- package/lib/types/widget-map.spec.d.ts +1 -0
- package/lib/types/widget-mapping.d.ts +7 -0
- 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/_about_dialog.scss +46 -1
- package/scss/_bin.scss +6 -3
- package/scss/_checks.scss +5 -2
- package/scss/_icon.scss +3 -4
- package/scss/_labels.scss +2 -2
- package/scss/_overlay.scss +11 -1
- package/scss/_overlay_split_view.scss +10 -0
- package/scss/_reset.scss +1 -1
- package/scss/_scrolled_window.scss +15 -0
- package/scss/_stack.scss +8 -2
- package/scss/_toast.scss +13 -3
- package/scss/_widget.scss +5 -1
- package/scss/_window.scss +16 -0
- package/src/accessible.ts +29 -0
- package/src/adw-about-dialog.spec.ts +81 -12
- package/src/adw-action-rows.spec.ts +2 -2
- package/src/adw-alert-dialog.spec.ts +71 -5
- package/src/adw-bin.spec.ts +4 -3
- package/src/adw-button-content.spec.ts +1 -1
- package/src/adw-carousel.spec.ts +49 -1
- 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 +2 -3
- package/src/checks.spec.ts +51 -1
- package/src/child-slot.spec.ts +71 -0
- package/src/compute-expand.spec.ts +186 -0
- package/src/compute-expand.ts +134 -0
- package/src/construct-vectors.spec.ts +97 -7
- package/src/container-fill.spec.ts +70 -0
- package/src/elements/adw-about-dialog.ts +152 -9
- package/src/elements/adw-action-row.ts +2 -1
- package/src/elements/adw-alert-dialog.ts +177 -46
- package/src/elements/adw-application-window.ts +0 -3
- package/src/elements/adw-avatar.ts +2 -1
- package/src/elements/adw-banner.ts +2 -1
- package/src/elements/adw-bin.ts +2 -1
- package/src/elements/adw-bottom-sheet.ts +5 -4
- package/src/elements/adw-breakpoint-bin.ts +5 -1
- package/src/elements/adw-button-content.ts +2 -1
- package/src/elements/adw-button-row.ts +2 -1
- package/src/elements/adw-card.ts +2 -1
- package/src/elements/adw-carousel.ts +62 -3
- package/src/elements/adw-clamp-scrollable.ts +2 -1
- package/src/elements/adw-clamp.ts +2 -1
- package/src/elements/adw-combo-row.ts +15 -1
- package/src/elements/adw-data-grid.ts +2 -1
- package/src/elements/adw-dialog.ts +5 -2
- package/src/elements/adw-entry-row.ts +2 -1
- package/src/elements/adw-expander-row.ts +2 -1
- package/src/elements/adw-header-bar.ts +6 -1
- package/src/elements/adw-inline-view-switcher.ts +3 -2
- package/src/elements/adw-layout-slot.ts +2 -1
- package/src/elements/adw-multi-layout-view.ts +2 -1
- package/src/elements/adw-navigation-split-view.ts +2 -1
- package/src/elements/adw-navigation-view.ts +3 -2
- package/src/elements/adw-overlay-split-view.ts +2 -1
- package/src/elements/adw-preferences-dialog.ts +6 -4
- package/src/elements/adw-preferences-group.ts +2 -1
- package/src/elements/adw-preferences-row.ts +2 -1
- package/src/elements/adw-shortcut-label.ts +2 -1
- package/src/elements/adw-shortcuts-item.ts +2 -1
- package/src/elements/adw-shortcuts-section.ts +2 -1
- package/src/elements/adw-sidebar.ts +4 -3
- package/src/elements/adw-spin-row.ts +2 -1
- package/src/elements/adw-spinner.ts +2 -1
- package/src/elements/adw-split-button.ts +2 -1
- package/src/elements/adw-status-page.ts +2 -1
- package/src/elements/adw-switch-row.ts +10 -1
- package/src/elements/adw-tab-bar.ts +2 -1
- package/src/elements/adw-tab-button.ts +2 -1
- package/src/elements/adw-tab-overview.ts +2 -1
- package/src/elements/adw-tab-view.ts +3 -2
- package/src/elements/adw-toast-overlay.ts +37 -70
- package/src/elements/adw-toggle-group.ts +3 -2
- package/src/elements/adw-toolbar-view.ts +2 -1
- package/src/elements/adw-view-stack.ts +39 -1
- package/src/elements/adw-view-switcher-bar.ts +2 -1
- package/src/elements/adw-view-switcher-sidebar.ts +2 -1
- package/src/elements/adw-view-switcher.ts +3 -2
- package/src/elements/adw-window-features.ts +14 -2
- package/src/elements/adw-window-title.ts +2 -1
- package/src/elements/adw-wrap-box.ts +2 -1
- package/src/elements/checks.ts +85 -6
- package/src/elements/gtk-action-bar.ts +2 -1
- package/src/elements/gtk-application-window.ts +3 -8
- package/src/elements/gtk-aspect-frame.ts +2 -1
- package/src/elements/gtk-box.ts +56 -1
- package/src/elements/gtk-button.ts +10 -22
- package/src/elements/gtk-calendar.ts +2 -1
- package/src/elements/gtk-center-box.ts +2 -1
- package/src/elements/gtk-color-dialog-button.ts +2 -1
- package/src/elements/gtk-drag-icon.ts +6 -1
- package/src/elements/gtk-drawing-area.ts +2 -1
- package/src/elements/gtk-drop-down.ts +2 -1
- package/src/elements/gtk-editable-label.ts +2 -1
- package/src/elements/gtk-emoji-chooser.ts +5 -0
- package/src/elements/gtk-entry.ts +2 -1
- package/src/elements/gtk-expander.ts +2 -1
- package/src/elements/gtk-fixed.ts +2 -1
- package/src/elements/gtk-flow-box-child.ts +5 -2
- package/src/elements/gtk-flow-box.ts +2 -1
- package/src/elements/gtk-font-dialog-button.ts +2 -1
- package/src/elements/gtk-frame.ts +2 -1
- package/src/elements/gtk-gl-area.ts +2 -1
- package/src/elements/gtk-graphics-offload.ts +5 -2
- package/src/elements/gtk-grid.ts +2 -1
- package/src/elements/gtk-header-bar.ts +2 -1
- package/src/elements/gtk-image.ts +14 -15
- package/src/elements/gtk-inscription.ts +2 -1
- package/src/elements/gtk-label.ts +12 -5
- package/src/elements/gtk-level-bar.ts +2 -1
- package/src/elements/gtk-list-box-row.ts +5 -2
- package/src/elements/gtk-list-box.ts +2 -1
- package/src/elements/gtk-list-view.ts +2 -1
- package/src/elements/gtk-media-controls.ts +2 -1
- package/src/elements/gtk-menu-button.ts +2 -1
- package/src/elements/gtk-notebook.ts +2 -1
- package/src/elements/gtk-overlay.ts +7 -5
- package/src/elements/gtk-paned.ts +9 -2
- package/src/elements/gtk-picture.ts +2 -1
- package/src/elements/gtk-popover-bin.ts +5 -1
- package/src/elements/gtk-popover-menu-bar.ts +2 -1
- package/src/elements/gtk-popover-menu.ts +5 -0
- package/src/elements/gtk-popover.ts +14 -1
- package/src/elements/gtk-progress-bar.ts +2 -1
- package/src/elements/gtk-revealer.ts +2 -1
- package/src/elements/gtk-scale-button.ts +2 -1
- package/src/elements/gtk-scale.ts +2 -1
- package/src/elements/gtk-scrollbar.ts +2 -1
- package/src/elements/gtk-scrolled-window.ts +8 -5
- package/src/elements/gtk-search-bar.ts +7 -6
- package/src/elements/gtk-separator.ts +2 -1
- package/src/elements/gtk-spin-button.ts +2 -1
- package/src/elements/gtk-spinner.ts +2 -1
- package/src/elements/gtk-stack-page.ts +112 -0
- package/src/elements/gtk-stack-sidebar.ts +2 -1
- package/src/elements/gtk-stack-switcher.ts +2 -1
- package/src/elements/gtk-stack.ts +52 -6
- package/src/elements/gtk-switch.ts +2 -1
- package/src/elements/gtk-text-view.ts +2 -1
- package/src/elements/gtk-text.ts +2 -1
- package/src/elements/gtk-tree-expander.ts +5 -1
- package/src/elements/gtk-video.ts +2 -1
- package/src/elements/gtk-viewport.ts +6 -1
- package/src/elements/gtk-window-controls.ts +2 -1
- package/src/elements/gtk-window-handle.ts +6 -1
- package/src/elements/gtk-window.ts +57 -7
- package/src/gio.spec.ts +13 -3
- package/src/glib.spec.ts +9 -0
- package/src/gobject-elements.ts +44 -2
- package/src/gtk-label.spec.ts +39 -0
- package/src/gtk-scrolled-window.spec.ts +6 -6
- package/src/gtk-stack.spec.ts +170 -0
- package/src/gtk-value-doors.spec.ts +19 -0
- package/src/gtk-window.spec.ts +2 -2
- package/src/gtksource/gtk-source-view.ts +2 -1
- package/src/{icon-registry.spec.ts → icon-masks.spec.ts} +4 -100
- package/src/icon-theme.spec.ts +69 -124
- package/src/icon-theme.ts +29 -119
- package/src/index.ts +5 -9
- 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/gdk.ts +1 -0
- package/src/namespace/gio.ts +8 -0
- package/src/namespace/glib.ts +4 -0
- package/src/namespace/gtk.ts +2 -0
- package/src/shared-tree-builder.ts +131 -13
- package/src/shared-trees.spec.ts +15 -1
- package/src/signals.ts +11 -2
- package/src/slotted-children.spec.ts +24 -0
- package/src/slotted-children.ts +18 -2
- package/src/source-view/adw-source-view.ts +2 -1
- package/src/split-button.spec.ts +1 -1
- package/src/style-manager.spec.ts +13 -0
- package/src/style-manager.ts +6 -0
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +16 -2
- package/src/value-lists.spec.ts +3 -3
- package/src/widget-element.ts +75 -0
- package/src/widget-map.spec.ts +116 -0
- package/src/widget-mapping.ts +100 -0
- package/src/widget-visible.spec.ts +148 -0
- package/src/window-maximize.spec.ts +93 -0
- package/lib/types/icon-registry.d.ts +0 -37
- package/src/icon-registry.ts +0 -163
|
@@ -124,8 +124,8 @@ export const AdwAlertDialogTest = async () => {
|
|
|
124
124
|
// directly would assert a contract this element does not have.
|
|
125
125
|
const { el, host } = mount({ 'close-response': 'cancel', open: '' });
|
|
126
126
|
let seen: string | null = null;
|
|
127
|
-
el.
|
|
128
|
-
seen =
|
|
127
|
+
el.connect('response', (_dialog, response: string) => {
|
|
128
|
+
seen = response;
|
|
129
129
|
});
|
|
130
130
|
(el.querySelector('.adw-alert-dialog-scrim') as HTMLElement).click();
|
|
131
131
|
expect(seen).toBe('cancel');
|
|
@@ -147,9 +147,9 @@ export const AdwAlertDialogTest = async () => {
|
|
|
147
147
|
await describe('adw-alert-dialog responses before the first connect', async () => {
|
|
148
148
|
await it('buttons added while detached appear once connected', () => {
|
|
149
149
|
const el = document.createElement('adw-alert-dialog') as AdwAlertDialog;
|
|
150
|
-
el.
|
|
151
|
-
el.
|
|
152
|
-
expect(el.
|
|
150
|
+
el.add_response('cancel', 'Cancel');
|
|
151
|
+
el.add_response('ok', 'OK');
|
|
152
|
+
expect(el.get_response_label('ok')).toBe('OK');
|
|
153
153
|
const host = document.createElement('div');
|
|
154
154
|
document.body.appendChild(host);
|
|
155
155
|
host.appendChild(el);
|
|
@@ -158,4 +158,70 @@ export const AdwAlertDialogTest = async () => {
|
|
|
158
158
|
host.remove();
|
|
159
159
|
});
|
|
160
160
|
});
|
|
161
|
+
|
|
162
|
+
// The calls a GJS app makes (Learn6502's `notification.service.ts`), run as written.
|
|
163
|
+
await describe('adw-alert-dialog in the GJS construct-props form', async () => {
|
|
164
|
+
const Dialog = customElements.get('adw-alert-dialog') as unknown as new (
|
|
165
|
+
props?: Record<string, unknown>,
|
|
166
|
+
) => AdwAlertDialog;
|
|
167
|
+
|
|
168
|
+
const confirmDialog = () => {
|
|
169
|
+
const dialog = new Dialog({
|
|
170
|
+
heading: 'Reset?',
|
|
171
|
+
body: 'This clears the memory.',
|
|
172
|
+
close_response: 'cancel',
|
|
173
|
+
default_response: 'confirm',
|
|
174
|
+
});
|
|
175
|
+
dialog.add_response('cancel', 'Cancel');
|
|
176
|
+
dialog.add_response('confirm', 'Confirm');
|
|
177
|
+
return dialog;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
await it('takes the snake_case bag', () => {
|
|
181
|
+
const dialog = confirmDialog();
|
|
182
|
+
expect(dialog.heading).toBe('Reset?');
|
|
183
|
+
expect(dialog.body).toBe('This clears the memory.');
|
|
184
|
+
expect(dialog.get_close_response()).toBe('cancel');
|
|
185
|
+
expect(dialog.get_default_response()).toBe('confirm');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await it('refuses a key that is no Adw.AlertDialog property', () => {
|
|
189
|
+
expect(() => new Dialog({ no_such_prop: 1 })).toThrow();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
await it('present() attaches it, a click emits response with the id, and closing detaches it', () => {
|
|
193
|
+
const dialog = confirmDialog();
|
|
194
|
+
const seen: unknown[][] = [];
|
|
195
|
+
dialog.connect('response', (emitter, response: string) => seen.push([emitter, response]));
|
|
196
|
+
dialog.present(null);
|
|
197
|
+
expect(dialog.isConnected).toBe(true);
|
|
198
|
+
expect(dialog.open).toBe(true);
|
|
199
|
+
const buttons = [...dialog.querySelectorAll('.adw-alert-dialog-response')] as HTMLButtonElement[];
|
|
200
|
+
buttons.find((b) => b.textContent === 'Confirm')!.click();
|
|
201
|
+
expect(seen.length).toBe(1);
|
|
202
|
+
expect(seen[0]![0]).toBe(dialog);
|
|
203
|
+
expect(seen[0]![1]).toBe('confirm');
|
|
204
|
+
expect(dialog.open).toBe(false);
|
|
205
|
+
expect(dialog.isConnected).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await it('a dismissal emits the close_response once, not the clicked one too', () => {
|
|
209
|
+
const dialog = confirmDialog();
|
|
210
|
+
const seen: string[] = [];
|
|
211
|
+
dialog.connect('response', (_dialog, response: string) => seen.push(response));
|
|
212
|
+
dialog.present(null);
|
|
213
|
+
(dialog.querySelector('.adw-alert-dialog-scrim') as HTMLElement).click();
|
|
214
|
+
expect(seen.join(',')).toBe('cancel');
|
|
215
|
+
expect(dialog.isConnected).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
await it('remove_response drops the response and its button', () => {
|
|
219
|
+
const dialog = confirmDialog();
|
|
220
|
+
dialog.present(null);
|
|
221
|
+
dialog.remove_response('confirm');
|
|
222
|
+
expect(dialog.has_response('confirm')).toBe(false);
|
|
223
|
+
expect(dialog.querySelectorAll('.adw-alert-dialog-response').length).toBe(1);
|
|
224
|
+
dialog.remove();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
161
227
|
};
|
package/src/adw-bin.spec.ts
CHANGED
|
@@ -61,12 +61,13 @@ export const AdwBinTest = async () => {
|
|
|
61
61
|
other.remove();
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
await it('
|
|
64
|
+
await it('a bin is a column flex box, and its only child fills it', () => {
|
|
65
65
|
const { el, host } = mount([document.createElement('div')]);
|
|
66
66
|
// A bin takes the size it is GIVEN and hands all of it to the child, which
|
|
67
|
-
// `GtkBinLayout` does on both axes (gtkbinlayout.c:81).
|
|
67
|
+
// `GtkBinLayout` does on both axes (gtkbinlayout.c:81). Flex stretch, not a
|
|
68
|
+
// percentage, so it also holds when the bin is itself a flex item.
|
|
68
69
|
el.style.height = '120px';
|
|
69
|
-
expect(getComputedStyle(el).display).toBe('
|
|
70
|
+
expect(getComputedStyle(el).display).toBe('flex');
|
|
70
71
|
const child = el.firstElementChild as HTMLElement;
|
|
71
72
|
expect(child.getBoundingClientRect().height).toBe(120);
|
|
72
73
|
host.remove();
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
BUTTON_CONTENT_TEXT_VECTORS,
|
|
21
21
|
} from '@gjsify/adwaita-core/conformance';
|
|
22
22
|
import type { AdwButtonContent } from './elements/adw-button-content.js';
|
|
23
|
-
import { fallbackMask, maskOf } from './icon-
|
|
23
|
+
import { fallbackMask, maskOf } from './icon-masks.spec.js';
|
|
24
24
|
|
|
25
25
|
/** Mount a button content inside a plain Adwaita button, the way the stories do. */
|
|
26
26
|
function mount(attributes: Record<string, string> = {}): {
|
package/src/adw-carousel.spec.ts
CHANGED
|
@@ -11,11 +11,13 @@ import { describe, expect, it } from '@gjsify/unit';
|
|
|
11
11
|
|
|
12
12
|
import type { CarouselPageMeasurement } from '@gjsify/adwaita-core';
|
|
13
13
|
import {
|
|
14
|
+
CAROUSEL_DOOR_VECTORS,
|
|
14
15
|
CAROUSEL_PAGE_ALLOCATION_VECTORS,
|
|
15
16
|
CAROUSEL_PAGE_AT_POSITION_VECTORS,
|
|
16
17
|
CAROUSEL_PAGE_LIST_VECTORS,
|
|
18
|
+
driveCarouselDoorVectors,
|
|
17
19
|
} from '@gjsify/adwaita-core/conformance';
|
|
18
|
-
import type { CarouselPageOp } from '@gjsify/adwaita-core/conformance';
|
|
20
|
+
import type { CarouselDoorLike, CarouselPageOp } from '@gjsify/adwaita-core/conformance';
|
|
19
21
|
|
|
20
22
|
/** The carousel surface this suite drives — the element's own public API. */
|
|
21
23
|
interface CarouselElement extends HTMLElement {
|
|
@@ -513,4 +515,50 @@ export const AdwCarouselTest = async () => {
|
|
|
513
515
|
host.remove();
|
|
514
516
|
});
|
|
515
517
|
});
|
|
518
|
+
|
|
519
|
+
// The GIR doors, on the vectors real `gi://Adw` passes. `mount` makes the animated
|
|
520
|
+
// scroll instant too, so `settle` waits for the rAF-throttled read-back only.
|
|
521
|
+
const hosts: HTMLElement[] = [];
|
|
522
|
+
await driveCarouselDoorVectors(
|
|
523
|
+
{
|
|
524
|
+
name: 'adw-carousel',
|
|
525
|
+
isOracle: false,
|
|
526
|
+
makeCarousel(count) {
|
|
527
|
+
const { carousel, host } = mount(Array.from({ length: count }, (_unused, i) => `p${i}`));
|
|
528
|
+
hosts.push(host);
|
|
529
|
+
const pages = Array.from(carousel.querySelectorAll('.adw-carousel-page')).map(
|
|
530
|
+
(slot) => slot.firstElementChild as HTMLElement,
|
|
531
|
+
);
|
|
532
|
+
return { carousel: carousel as unknown as CarouselDoorLike, pages };
|
|
533
|
+
},
|
|
534
|
+
makeStranger: () => card('stranger'),
|
|
535
|
+
async settle() {
|
|
536
|
+
await flush();
|
|
537
|
+
await flush();
|
|
538
|
+
},
|
|
539
|
+
dispose() {
|
|
540
|
+
for (const host of hosts.splice(0)) host.remove();
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
{ describe, it, expect },
|
|
544
|
+
CAROUSEL_DOOR_VECTORS,
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
await describe('adw-carousel scroll_to before the pages are adopted', async () => {
|
|
548
|
+
await it('lands where a declared position would, as upstream defers it to an idle', async () => {
|
|
549
|
+
const carousel = document.createElement('adw-carousel') as CarouselElement & CarouselDoorLike;
|
|
550
|
+
carousel.style.cssText = 'display:block;width:200px;height:80px;';
|
|
551
|
+
const pages = ['a', 'b', 'c'].map((label) => card(label));
|
|
552
|
+
for (const page of pages) carousel.appendChild(page);
|
|
553
|
+
expect(carousel.get_n_pages()).toBe(3);
|
|
554
|
+
expect(carousel.get_nth_page(2)).toBe(pages[2]);
|
|
555
|
+
carousel.scroll_to(pages[2], false);
|
|
556
|
+
|
|
557
|
+
document.body.appendChild(carousel);
|
|
558
|
+
await flush();
|
|
559
|
+
await flush();
|
|
560
|
+
expect(carousel.get_position()).toBe(2);
|
|
561
|
+
carousel.remove();
|
|
562
|
+
});
|
|
563
|
+
});
|
|
516
564
|
};
|
|
@@ -233,6 +233,24 @@ export const AdwHeaderBarTest = async () => {
|
|
|
233
233
|
|
|
234
234
|
// `windowtitle .title, .subtitle { padding: 0 12px }` (_header-bar.scss:214-227).
|
|
235
235
|
// Measured beside a GTK render of the gallery's window-title `.blp`: 27px narrower here.
|
|
236
|
+
await describe('<adw-header-bar> packing', async () => {
|
|
237
|
+
await it('packs an untyped child from the start, declared or appended', async () => {
|
|
238
|
+
// adw-header-bar.c:1040-1041 — `type == NULL` is pack_start. A Blueprint `[start]`
|
|
239
|
+
// tags only the next child, so the buttons after it arrive untyped.
|
|
240
|
+
const tagged = document.createElement('button');
|
|
241
|
+
tagged.setAttribute('slot', 'start');
|
|
242
|
+
const declared = document.createElement('button');
|
|
243
|
+
const bar = mountHeaderBar({ title: 'T' }, [tagged, declared]);
|
|
244
|
+
const late = document.createElement('button');
|
|
245
|
+
bar.appendChild(late);
|
|
246
|
+
await Promise.resolve();
|
|
247
|
+
const start = bar.querySelector('.adw-header-bar-start');
|
|
248
|
+
const report = [tagged, declared, late].map((el) => el.parentElement === start);
|
|
249
|
+
unmountAll();
|
|
250
|
+
expect(report).toStrictEqual([true, true, true]);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
236
254
|
await describe('adw-window-title label sides', async () => {
|
|
237
255
|
await it('pads its title and subtitle 12px on each side', () => {
|
|
238
256
|
const title = document.createElement('adw-window-title');
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// DOM-level behaviour tests for <adw-toast-overlay>, in a real browser via the
|
|
2
2
|
// @gjsify/adwaita-web browser test axis.
|
|
3
3
|
//
|
|
4
|
-
// `Adw.ToastOverlay` shows exactly ONE toast at a time and queues the rest
|
|
4
|
+
// `Adw.ToastOverlay` shows exactly ONE toast at a time and queues the rest,
|
|
5
5
|
// advancing only when the visible one is dismissed (manually or by its timeout) — the
|
|
6
|
-
// policy `
|
|
6
|
+
// policy `ToastOverlayQueue` owns in `@gjsify/adwaita-core` and this element composes.
|
|
7
|
+
// A toast is an `Adw.Toast`, added with `add_toast(toast)` as on GTK.
|
|
7
8
|
//
|
|
8
9
|
// Timing runs through core's injected `ToastScheduler` seam (a virtual clock), so the
|
|
9
10
|
// auto-dismiss lifecycle is deterministic with no real timers.
|
|
11
|
+
import { Toast } from '@gjsify/adwaita-core';
|
|
10
12
|
import { describe, expect, it } from '@gjsify/unit';
|
|
11
13
|
|
|
12
14
|
import type { AdwToastOverlay, ToastScheduler, ToastTimerHandle } from './elements/adw-toast-overlay.js';
|
|
@@ -65,12 +67,10 @@ export const AdwToastOverlayTest = async () => {
|
|
|
65
67
|
await it('shows exactly one toast at a time, in FIFO order', async () => {
|
|
66
68
|
const { overlay, clock, host } = mountOverlay();
|
|
67
69
|
|
|
68
|
-
overlay.
|
|
69
|
-
overlay.
|
|
70
|
-
overlay.
|
|
70
|
+
overlay.add_toast(new Toast({ title: 'First', timeout: 3 }));
|
|
71
|
+
overlay.add_toast(new Toast({ title: 'Second', timeout: 3 }));
|
|
72
|
+
overlay.add_toast(new Toast({ title: 'Third', timeout: 3 }));
|
|
71
73
|
|
|
72
|
-
// BEFORE the fix this was ['First', 'Second', 'Third'] — all three
|
|
73
|
-
// strips mounted at once.
|
|
74
74
|
expect(toastTitles(overlay)).toStrictEqual(['First']);
|
|
75
75
|
expect(overlay.pendingToasts).toBe(2);
|
|
76
76
|
expect(overlay.currentToast?.title).toBe('First');
|
|
@@ -96,8 +96,9 @@ export const AdwToastOverlayTest = async () => {
|
|
|
96
96
|
await it('holds the queue while a sticky toast (timeout 0) is shown', async () => {
|
|
97
97
|
const { overlay, clock, host } = mountOverlay();
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
overlay.
|
|
99
|
+
const sticky = new Toast({ title: 'Sticky', timeout: 0 });
|
|
100
|
+
overlay.add_toast(sticky);
|
|
101
|
+
overlay.add_toast(new Toast({ title: 'Next', timeout: 3 }));
|
|
101
102
|
|
|
102
103
|
// No timer was scheduled for the sticky toast, so time does not advance it.
|
|
103
104
|
clock.advance(60_000);
|
|
@@ -105,7 +106,7 @@ export const AdwToastOverlayTest = async () => {
|
|
|
105
106
|
expect(overlay.pendingToasts).toBe(1);
|
|
106
107
|
|
|
107
108
|
// Dismissing it hands the slot to the queued toast.
|
|
108
|
-
|
|
109
|
+
sticky.dismiss();
|
|
109
110
|
expect(toastTitles(overlay)).toStrictEqual(['Next']);
|
|
110
111
|
expect(overlay.pendingToasts).toBe(0);
|
|
111
112
|
|
|
@@ -115,30 +116,34 @@ export const AdwToastOverlayTest = async () => {
|
|
|
115
116
|
await it('advances the queue when the close button is pressed', async () => {
|
|
116
117
|
const { overlay, host } = mountOverlay();
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
const first = new Toast({ title: 'First', timeout: 3 });
|
|
120
|
+
let dismissed = 0;
|
|
121
|
+
first.connect('dismissed', () => {
|
|
122
|
+
dismissed += 1;
|
|
123
|
+
});
|
|
124
|
+
overlay.add_toast(first);
|
|
125
|
+
overlay.add_toast(new Toast({ title: 'Second', timeout: 3 }));
|
|
120
126
|
|
|
121
127
|
const close = overlay.querySelector('.adw-toast-close-button') as HTMLButtonElement;
|
|
122
128
|
close.click();
|
|
123
129
|
|
|
130
|
+
expect(dismissed).toBe(1);
|
|
124
131
|
expect(toastTitles(overlay)).toStrictEqual(['Second']);
|
|
125
132
|
expect(overlay.pendingToasts).toBe(0);
|
|
126
133
|
|
|
127
134
|
host.remove();
|
|
128
135
|
});
|
|
129
136
|
|
|
130
|
-
await it('
|
|
137
|
+
await it('emits button-clicked and advances the queue', async () => {
|
|
131
138
|
const { overlay, host } = mountOverlay();
|
|
132
139
|
|
|
140
|
+
const toast = new Toast({ title: 'File moved to Trash', timeout: 3, button_label: 'Undo' });
|
|
133
141
|
let undone = 0;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
buttonLabel: 'Undo',
|
|
137
|
-
onAction: () => {
|
|
138
|
-
undone += 1;
|
|
139
|
-
},
|
|
142
|
+
toast.connect('button-clicked', () => {
|
|
143
|
+
undone += 1;
|
|
140
144
|
});
|
|
141
|
-
overlay.
|
|
145
|
+
overlay.add_toast(toast);
|
|
146
|
+
overlay.add_toast(new Toast({ title: 'Second', timeout: 3 }));
|
|
142
147
|
|
|
143
148
|
const action = overlay.querySelector('.adw-toast-action-button') as HTMLButtonElement;
|
|
144
149
|
expect(action.textContent).toBe('Undo');
|
|
@@ -150,12 +155,51 @@ export const AdwToastOverlayTest = async () => {
|
|
|
150
155
|
host.remove();
|
|
151
156
|
});
|
|
152
157
|
|
|
153
|
-
await it('
|
|
158
|
+
await it('draws no action button without a button_label', async () => {
|
|
159
|
+
const { overlay, host } = mountOverlay();
|
|
160
|
+
|
|
161
|
+
overlay.add_toast(new Toast({ title: 'Saved' }));
|
|
162
|
+
expect(overlay.querySelector('.adw-toast-action-button')).toBeNull();
|
|
163
|
+
|
|
164
|
+
host.remove();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await it('renders the title as markup unless use_markup is off', async () => {
|
|
168
|
+
const { overlay, host } = mountOverlay();
|
|
169
|
+
|
|
170
|
+
overlay.add_toast(new Toast({ title: '<b>Bold</b>', timeout: 0 }));
|
|
171
|
+
expect(overlay.querySelector('.adw-toast-title b')?.textContent).toBe('Bold');
|
|
172
|
+
overlay.dismiss_all();
|
|
173
|
+
|
|
174
|
+
const { overlay: plain, host: plainHost } = mountOverlay();
|
|
175
|
+
plain.add_toast(new Toast({ title: '<b>Bold</b>', timeout: 0, use_markup: false }));
|
|
176
|
+
expect(plain.querySelector('.adw-toast-title b')).toBeNull();
|
|
177
|
+
expect(toastTitles(plain)).toStrictEqual(['<b>Bold</b>']);
|
|
178
|
+
|
|
179
|
+
host.remove();
|
|
180
|
+
plainHost.remove();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await it('shows a HIGH priority toast ahead of the current one', async () => {
|
|
184
|
+
const { overlay, host } = mountOverlay();
|
|
185
|
+
|
|
186
|
+
overlay.add_toast(new Toast({ title: 'Normal', timeout: 0 }));
|
|
187
|
+
const urgent = new Toast({ title: 'Urgent', timeout: 0, priority: 1 });
|
|
188
|
+
overlay.add_toast(urgent);
|
|
189
|
+
|
|
190
|
+
expect(toastTitles(overlay)).toStrictEqual(['Urgent']);
|
|
191
|
+
urgent.dismiss();
|
|
192
|
+
expect(toastTitles(overlay)).toStrictEqual(['Normal']);
|
|
193
|
+
|
|
194
|
+
host.remove();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
await it('dismisses the visible toast and the queue on dismiss_all()', async () => {
|
|
154
198
|
const { overlay, clock, host } = mountOverlay();
|
|
155
199
|
|
|
156
|
-
overlay.
|
|
157
|
-
overlay.
|
|
158
|
-
overlay.
|
|
200
|
+
overlay.add_toast(new Toast({ title: 'First', timeout: 3 }));
|
|
201
|
+
overlay.add_toast(new Toast({ title: 'Second', timeout: 3 }));
|
|
202
|
+
overlay.dismiss_all();
|
|
159
203
|
|
|
160
204
|
expect(overlay.currentToast).toBeNull();
|
|
161
205
|
expect(overlay.pendingToasts).toBe(0);
|
package/src/application.spec.ts
CHANGED
|
@@ -95,6 +95,50 @@ export const ApplicationTest = async () => {
|
|
|
95
95
|
(window as unknown as { application: object | null }).application = null;
|
|
96
96
|
expect(press({ key: 's', ctrlKey: true })).toBe(false);
|
|
97
97
|
});
|
|
98
|
+
await it('inhibit(LOGOUT) cancels beforeunload until uninhibit', () => {
|
|
99
|
+
const app = new Adw.Application({ application_id: 'org.gjsify.vec.WebInhibit' });
|
|
100
|
+
const unload = (): boolean => {
|
|
101
|
+
const event = new Event('beforeunload', { cancelable: true });
|
|
102
|
+
window.dispatchEvent(event);
|
|
103
|
+
return event.defaultPrevented;
|
|
104
|
+
};
|
|
105
|
+
expect(unload()).toBe(false);
|
|
106
|
+
const cookie = app.inhibit(null, Gtk.ApplicationInhibitFlags.LOGOUT, 'unsaved changes');
|
|
107
|
+
expect(cookie > 0).toBe(true);
|
|
108
|
+
expect(unload()).toBe(true);
|
|
109
|
+
app.uninhibit(cookie);
|
|
110
|
+
expect(unload()).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
await it('every window class has application, get_application and set_application', () => {
|
|
113
|
+
const app = new Adw.Application({ application_id: 'org.gjsify.vec.WebWindowApp' });
|
|
114
|
+
const other = new Adw.Application({ application_id: 'org.gjsify.vec.WebWindowOther' });
|
|
115
|
+
for (const tag of ['gtk-window', 'gtk-application-window', 'adw-window', 'adw-application-window']) {
|
|
116
|
+
const window = document.createElement(tag) as unknown as Gtk.WindowCommon<Element>;
|
|
117
|
+
const notified: unknown[] = [];
|
|
118
|
+
window.connect('notify::application', () => notified.push(window.application));
|
|
119
|
+
expect(window.get_application()).toBe(null);
|
|
120
|
+
window.set_application(app as never);
|
|
121
|
+
expect(window.get_application()).toBe(app);
|
|
122
|
+
expect(window.application).toBe(app);
|
|
123
|
+
expect(app.get_windows().includes(window)).toBe(true);
|
|
124
|
+
window.set_application(other as never);
|
|
125
|
+
expect(app.get_windows().includes(window)).toBe(false);
|
|
126
|
+
expect(other.get_windows().includes(window)).toBe(true);
|
|
127
|
+
window.application = null;
|
|
128
|
+
expect(window.get_application()).toBe(null);
|
|
129
|
+
expect(other.get_windows().includes(window)).toBe(false);
|
|
130
|
+
expect(notified).toStrictEqual([app, other, null]);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
await it('a window reaches inhibit through get_application()', () => {
|
|
134
|
+
const app = new Adw.Application({ application_id: 'org.gjsify.vec.WebWindowInhibit' });
|
|
135
|
+
const window = document.createElement('gtk-window') as unknown as Gtk.WindowCommon<Element>;
|
|
136
|
+
window.set_application(app as never);
|
|
137
|
+
const cookie = window.get_application()?.inhibit(window, Gtk.ApplicationInhibitFlags.LOGOUT, 'unsaved');
|
|
138
|
+
expect((cookie ?? 0) > 0).toBe(true);
|
|
139
|
+
window.get_application()?.uninhibit(cookie ?? 0);
|
|
140
|
+
window.set_application(null);
|
|
141
|
+
});
|
|
98
142
|
await it('quit() takes the windows it added off the page', async () => {
|
|
99
143
|
const app = new Adw.Application({ application_id: 'org.gjsify.vec.WebQuit' });
|
|
100
144
|
const window = scene.window(app as never) as Element;
|
package/src/application.ts
CHANGED
|
@@ -10,10 +10,9 @@ import {
|
|
|
10
10
|
withGtkApplication,
|
|
11
11
|
activateWidgetAction as activateIn,
|
|
12
12
|
activateMenuItem,
|
|
13
|
-
|
|
13
|
+
defineWindowApplicationMembers,
|
|
14
14
|
dispatchAccelerator,
|
|
15
15
|
menuActionsFor,
|
|
16
|
-
installActionMap,
|
|
17
16
|
type AdwMenuItem,
|
|
18
17
|
type AdwMenuActions,
|
|
19
18
|
type AdwMenuModel,
|
|
@@ -40,8 +39,18 @@ export const webHost: ApplicationHost = {
|
|
|
40
39
|
},
|
|
41
40
|
exit() {},
|
|
42
41
|
iconTheme: () => IconTheme.get_for_display(null),
|
|
42
|
+
guardUnload(active) {
|
|
43
|
+
if (active) window.addEventListener('beforeunload', confirmUnload);
|
|
44
|
+
else window.removeEventListener('beforeunload', confirmUnload);
|
|
45
|
+
},
|
|
43
46
|
};
|
|
44
47
|
|
|
48
|
+
/** The only hook a page has on Ctrl+Q, closing the tab and reload; the browser words the prompt. */
|
|
49
|
+
function confirmUnload(event: BeforeUnloadEvent): void {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
event.returnValue = '';
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
/** An element's parent for action resolution; a popover is rooted at the widget that owns it. */
|
|
46
55
|
export function parentOfElement(node: object): object | null {
|
|
47
56
|
if (!(node instanceof Element)) return null;
|
|
@@ -88,23 +97,9 @@ function followAccelerators(window: object, application: WindowApplication | nul
|
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
/**
|
|
91
|
-
* `Gtk.Window:application` and
|
|
100
|
+
* `Gtk.Window:application`, `get_application()` and `set_application()` on a window element class.
|
|
92
101
|
* Setting the property is `add_window`, as on GTK; setting it to `null` is `remove_window`.
|
|
93
102
|
*/
|
|
94
103
|
export function defineWindowApplication(proto: object): void {
|
|
95
|
-
|
|
96
|
-
Object.defineProperty(proto, 'application', {
|
|
97
|
-
configurable: true,
|
|
98
|
-
enumerable: true,
|
|
99
|
-
get(this: object) {
|
|
100
|
-
return applicationOfWindow(this);
|
|
101
|
-
},
|
|
102
|
-
set(this: object, value: WindowApplication | null) {
|
|
103
|
-
const current = applicationOfWindow(this) as unknown as WindowApplication | null;
|
|
104
|
-
if (current === value) return;
|
|
105
|
-
current?.remove_window(this);
|
|
106
|
-
value?.add_window(this);
|
|
107
|
-
followAccelerators(this, value);
|
|
108
|
-
},
|
|
109
|
-
});
|
|
104
|
+
defineWindowApplicationMembers(proto, followAccelerators);
|
|
110
105
|
}
|
package/src/capabilities.mjs
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
/** @type {import('./capabilities.d.mts').capabilities} */
|
|
6
6
|
export const capabilities = {
|
|
7
7
|
layout: 'implemented',
|
|
8
|
+
accessibility: 'implemented',
|
|
8
9
|
strings: 'implemented',
|
|
9
10
|
responses: 'implemented',
|
|
10
11
|
page: 'implemented',
|
|
@@ -12,7 +13,5 @@ export const capabilities = {
|
|
|
12
13
|
signal: 'implemented',
|
|
13
14
|
bind: 'implemented',
|
|
14
15
|
breakpoint: 'implemented',
|
|
15
|
-
'sibling-object':
|
|
16
|
-
refused: 'the web builder mounts one root element and has no consumer for a second root yet (ADR 0093)',
|
|
17
|
-
},
|
|
16
|
+
'sibling-object': 'implemented',
|
|
18
17
|
};
|
package/src/checks.spec.ts
CHANGED
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
// so two scenarios sharing the literal name `colour` would share a selection.
|
|
13
13
|
|
|
14
14
|
import { describe, expect, it } from '@gjsify/unit';
|
|
15
|
-
import { RADIO_GROUP_VECTORS } from '@gjsify/adwaita-core/conformance';
|
|
15
|
+
import { RADIO_GROUP_VECTORS, driveCheckGroupVectors, type CheckButtonLike } from '@gjsify/adwaita-core/conformance';
|
|
16
16
|
|
|
17
17
|
import type { GtkCheckButton, AdwRadio } from './elements/checks.js';
|
|
18
|
+
import { buildSharedTree } from './shared-tree-builder.js';
|
|
18
19
|
|
|
19
20
|
function host(): HTMLElement {
|
|
20
21
|
const el = document.createElement('div');
|
|
@@ -354,4 +355,53 @@ export const AdwChecksTest = async () => {
|
|
|
354
355
|
parent.remove();
|
|
355
356
|
});
|
|
356
357
|
});
|
|
358
|
+
|
|
359
|
+
await describe('<gtk-check-button> group (CHECK_GROUP_VECTORS)', async () => {
|
|
360
|
+
await driveCheckGroupVectors(
|
|
361
|
+
{
|
|
362
|
+
name: 'adwaita-web',
|
|
363
|
+
isOracle: false,
|
|
364
|
+
make: () => mountCheckbox().el as unknown as CheckButtonLike,
|
|
365
|
+
activate: (button) => parts(button as unknown as HTMLElement).indicator.click(),
|
|
366
|
+
countToggled(button) {
|
|
367
|
+
let count = 0;
|
|
368
|
+
(button as unknown as HTMLElement).addEventListener('toggled', () => void (count += 1));
|
|
369
|
+
return () => count;
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
{ describe, it, expect },
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
await it('a grouped button draws the radio and its input announces role=radio', () => {
|
|
376
|
+
const [a, b] = [mountCheckbox().el, mountCheckbox().el];
|
|
377
|
+
b.set_group(a);
|
|
378
|
+
expect([a.hasAttribute('radio'), parts(b).input.getAttribute('role')].join()).toBe('true,radio');
|
|
379
|
+
b.set_group(null);
|
|
380
|
+
expect([a.hasAttribute('radio'), b.hasAttribute('radio'), parts(b).input.hasAttribute('role')].join()).toBe(
|
|
381
|
+
'true,false,false',
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
await it('a projected `group: follow` resolves by id once the tree is in the document', () => {
|
|
386
|
+
const tree = buildSharedTree({
|
|
387
|
+
tag: 'GtkBox',
|
|
388
|
+
children: [
|
|
389
|
+
{ tag: 'GtkCheckButton', id: 'group-follow', props: { active: true } },
|
|
390
|
+
{ tag: 'GtkCheckButton', id: 'group-dark', props: { group: 'group-follow' } },
|
|
391
|
+
],
|
|
392
|
+
});
|
|
393
|
+
host().appendChild(tree);
|
|
394
|
+
const [follow, dark] = ['group-follow', 'group-dark'].map(
|
|
395
|
+
(id) => document.getElementById(id) as GtkCheckButton,
|
|
396
|
+
);
|
|
397
|
+
dark.set_active(true);
|
|
398
|
+
expect([follow.get_active(), dark.get_active()].join()).toBe('false,true');
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
await it('a group id nothing carries is refused', () => {
|
|
402
|
+
const el = document.createElement('gtk-check-button') as GtkCheckButton;
|
|
403
|
+
el.setAttribute('group', 'group-nowhere');
|
|
404
|
+
expect(() => (el as unknown as { connectedCallback(): void }).connectedCallback()).toThrow();
|
|
405
|
+
});
|
|
406
|
+
});
|
|
357
407
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// A `.blp`'s `child:` (and every other widget-valued property) becomes `slot: '<property>'` in the
|
|
2
|
+
// shared tree (`packages/infra/blueprint/src/project.mjs`). An element that routed only its
|
|
3
|
+
// DEFAULT slot refused that name on mount, and before the refusal existed it left the child
|
|
4
|
+
// beside the structure it had built — Learn6502's `Overlay { child: Adw.ToastOverlay { … } }`
|
|
5
|
+
// was the measured case. Every element whose GTK class has such a property is mounted here
|
|
6
|
+
// with it, and the child has to land where the element's own binding says that slot goes.
|
|
7
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
8
|
+
import type { SharedTreeNode } from '@gjsify/adwaita-core/conformance';
|
|
9
|
+
|
|
10
|
+
import { mountSharedTree } from './shared-tree-builder.js';
|
|
11
|
+
import { slottedChildrenOf } from './slotted-children.js';
|
|
12
|
+
|
|
13
|
+
/** The GIR class, and the widget-valued property the tree writes as its child's slot. */
|
|
14
|
+
const CASES: ReadonlyArray<readonly [string, string]> = [
|
|
15
|
+
['GtkOverlay', 'child'],
|
|
16
|
+
['AdwToastOverlay', 'child'],
|
|
17
|
+
['AdwDialog', 'child'],
|
|
18
|
+
['AdwAlertDialog', 'extra-child'],
|
|
19
|
+
['GtkListBoxRow', 'child'],
|
|
20
|
+
['GtkFlowBoxChild', 'child'],
|
|
21
|
+
['GtkGraphicsOffload', 'child'],
|
|
22
|
+
['GtkPaned', 'start-child'],
|
|
23
|
+
['GtkPaned', 'end-child'],
|
|
24
|
+
['GtkWindow', 'child'],
|
|
25
|
+
['GtkWindowHandle', 'child'],
|
|
26
|
+
['GtkPopover', 'child'],
|
|
27
|
+
['AdwBreakpointBin', 'child'],
|
|
28
|
+
['GtkPopoverBin', 'child'],
|
|
29
|
+
['GtkViewport', 'child'],
|
|
30
|
+
['GtkTreeExpander', 'child'],
|
|
31
|
+
['GtkDragIcon', 'child'],
|
|
32
|
+
['GtkSearchBar', 'child'],
|
|
33
|
+
['AdwBin', 'child'],
|
|
34
|
+
['AdwClamp', 'child'],
|
|
35
|
+
['GtkScrolledWindow', 'child'],
|
|
36
|
+
['GtkRevealer', 'child'],
|
|
37
|
+
['GtkFrame', 'child'],
|
|
38
|
+
['GtkExpander', 'child'],
|
|
39
|
+
['AdwStatusPage', 'child'],
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/** Where `child` ended up, read against the slot the parent itself declares. */
|
|
43
|
+
function report(root: HTMLElement, child: Element, slot: string): string {
|
|
44
|
+
const declared = slottedChildrenOf(root)?.slots.find((candidate) => candidate.name === slot);
|
|
45
|
+
if (declared === undefined) return 'undeclared';
|
|
46
|
+
if (!root.contains(child)) return 'outside';
|
|
47
|
+
if (!('into' in declared)) return 'consumed';
|
|
48
|
+
return child.parentElement === declared.into ? 'routed' : `beside, under <${child.parentElement?.localName}>`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const AdwChildSlotTest = async () => {
|
|
52
|
+
await describe('adwaita-web: a widget-valued property in a tree is the slot it names', async () => {
|
|
53
|
+
for (const [tag, slot] of CASES) {
|
|
54
|
+
await it(`${tag} { ${slot}: … }`, () => {
|
|
55
|
+
const tree: SharedTreeNode = {
|
|
56
|
+
tag,
|
|
57
|
+
children: [{ tag: 'GtkBox', id: 'probe', slot }],
|
|
58
|
+
};
|
|
59
|
+
let outcome: string;
|
|
60
|
+
try {
|
|
61
|
+
const { root, unmount } = mountSharedTree(tree);
|
|
62
|
+
outcome = report(root, root.querySelector('#probe')!, slot);
|
|
63
|
+
unmount();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
outcome = `refused: ${(error as Error).message}`;
|
|
66
|
+
}
|
|
67
|
+
expect(`${tag} ${slot}: ${outcome}`).toBe(`${tag} ${slot}: routed`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
};
|