@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
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// `gtk_widget_compute_expand` in a box (gtkwidget.c:8387-8423, gtkboxlayout.c:793): a widget
|
|
2
|
+
// that authored no `hexpand` grows when a descendant does, an explicit value stops the walk, and
|
|
3
|
+
// the answer follows the tree as it changes. Plus the two size facts the same app layout broke
|
|
4
|
+
// on: a scrolled window requests 0 on a scrolling axis (gtkscrolledwindow.c:1821-1960), and a
|
|
5
|
+
// page leaving a view stack takes none of the stack's marks with it (adw-view-stack.c:1184-1205).
|
|
6
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
7
|
+
|
|
8
|
+
import type { AdwViewStack } from './elements/adw-view-stack.js';
|
|
9
|
+
|
|
10
|
+
function mount(html: string, width = 600, height = 600): { host: HTMLElement; box: (sel: string) => DOMRect } {
|
|
11
|
+
const host = document.createElement('div');
|
|
12
|
+
host.style.cssText = `width:${width}px;height:${height}px;display:flex;flex-direction:column`;
|
|
13
|
+
host.innerHTML = html;
|
|
14
|
+
document.body.appendChild(host);
|
|
15
|
+
const box = (sel: string) => (host.querySelector(sel) as HTMLElement).getBoundingClientRect();
|
|
16
|
+
return { host, box };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The box writes its computed values from a MutationObserver: one microtask, then a frame. */
|
|
20
|
+
const settle = () => new Promise((resolve) => requestAnimationFrame(resolve));
|
|
21
|
+
|
|
22
|
+
export const ComputeExpandTest = async () => {
|
|
23
|
+
await describe('computed expand in a <gtk-box>', async () => {
|
|
24
|
+
await it('a bin with an hexpand descendant fills a horizontal box', async () => {
|
|
25
|
+
const { host, box } = mount(
|
|
26
|
+
`<gtk-box id="row" style="width:600px">
|
|
27
|
+
<div id="fixed" style="width:100px"></div>
|
|
28
|
+
<adw-bin id="bin"><gtk-box><div id="leaf" hexpand></div></gtk-box></adw-bin>
|
|
29
|
+
</gtk-box>`,
|
|
30
|
+
);
|
|
31
|
+
await settle();
|
|
32
|
+
expect(host.querySelector('#bin')!.hasAttribute('data-computed-hexpand')).toBe(true);
|
|
33
|
+
expect(box('#bin').width).toBe(500);
|
|
34
|
+
host.remove();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await it('an explicit hexpand=false on an intermediate stops propagation', async () => {
|
|
38
|
+
const { host, box } = mount(
|
|
39
|
+
`<gtk-box style="width:600px">
|
|
40
|
+
<adw-bin id="bin"><gtk-box hexpand="false"><div style="width:10px" hexpand></div></gtk-box></adw-bin>
|
|
41
|
+
</gtk-box>`,
|
|
42
|
+
);
|
|
43
|
+
await settle();
|
|
44
|
+
expect(host.querySelector('#bin')!.hasAttribute('data-computed-hexpand')).toBe(false);
|
|
45
|
+
expect(box('#bin').width).toBe(10);
|
|
46
|
+
host.remove();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await it('a leaf widget does not propagate, and a hidden one never expands', async () => {
|
|
50
|
+
const { host } = mount(
|
|
51
|
+
`<gtk-box>
|
|
52
|
+
<gtk-label id="label"><div hexpand></div></gtk-label>
|
|
53
|
+
<adw-bin id="bin"><gtk-box hidden><div hexpand></div></gtk-box></adw-bin>
|
|
54
|
+
</gtk-box>`,
|
|
55
|
+
);
|
|
56
|
+
await settle();
|
|
57
|
+
expect(host.querySelector('#label')!.hasAttribute('data-computed-hexpand')).toBe(false);
|
|
58
|
+
expect(host.querySelector('#bin')!.hasAttribute('data-computed-hexpand')).toBe(false);
|
|
59
|
+
host.remove();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await it('propagation updates after a runtime reparent', async () => {
|
|
63
|
+
const { host, box } = mount(
|
|
64
|
+
`<gtk-box style="width:600px">
|
|
65
|
+
<div style="width:100px"></div>
|
|
66
|
+
<adw-bin id="bin"><div style="width:20px"></div></adw-bin>
|
|
67
|
+
</gtk-box>
|
|
68
|
+
<div id="parking"><div id="editor" hexpand></div></div>`,
|
|
69
|
+
);
|
|
70
|
+
await settle();
|
|
71
|
+
expect(box('#bin').width).toBe(20);
|
|
72
|
+
const bin = host.querySelector('#bin')!;
|
|
73
|
+
bin.replaceChildren(host.querySelector('#editor')!);
|
|
74
|
+
await settle();
|
|
75
|
+
expect(bin.hasAttribute('data-computed-hexpand')).toBe(true);
|
|
76
|
+
expect(box('#bin').width).toBe(500);
|
|
77
|
+
host.querySelector('#parking')!.append(host.querySelector('#editor')!);
|
|
78
|
+
await settle();
|
|
79
|
+
expect(bin.hasAttribute('data-computed-hexpand')).toBe(false);
|
|
80
|
+
host.remove();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await it('rewriting a label text does not recompute, adding a widget does', async () => {
|
|
84
|
+
const { host } = mount(
|
|
85
|
+
`<gtk-box>
|
|
86
|
+
<adw-bin id="bin"><gtk-box id="inner"><gtk-label id="label">0000</gtk-label><div hexpand></div></gtk-box></adw-bin>
|
|
87
|
+
</gtk-box>`,
|
|
88
|
+
);
|
|
89
|
+
await settle();
|
|
90
|
+
const bin = host.querySelector('#bin')!;
|
|
91
|
+
expect(bin.hasAttribute('data-computed-hexpand')).toBe(true);
|
|
92
|
+
// Drop the mark by hand: only a recompute would write it back.
|
|
93
|
+
bin.removeAttribute('data-computed-hexpand');
|
|
94
|
+
host.querySelector('#label')!.textContent = 'FFFF';
|
|
95
|
+
await settle();
|
|
96
|
+
expect(bin.hasAttribute('data-computed-hexpand')).toBe(false);
|
|
97
|
+
host.querySelector('#inner')!.append(document.createElement('gtk-label'));
|
|
98
|
+
await settle();
|
|
99
|
+
expect(bin.hasAttribute('data-computed-hexpand')).toBe(true);
|
|
100
|
+
host.remove();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await it('a registered subclass of a bin propagates as its base does', async () => {
|
|
104
|
+
const Bin = customElements.get('adw-bin')!;
|
|
105
|
+
const tag = 'compute-expand-test-bin';
|
|
106
|
+
if (!customElements.get(tag)) customElements.define(tag, class extends Bin {});
|
|
107
|
+
const { host, box } = mount(
|
|
108
|
+
`<gtk-box style="width:600px">
|
|
109
|
+
<div style="width:100px"></div>
|
|
110
|
+
<${tag} id="sub"><div hexpand></div></${tag}>
|
|
111
|
+
</gtk-box>`,
|
|
112
|
+
);
|
|
113
|
+
await settle();
|
|
114
|
+
expect(box('#sub').width).toBe(500);
|
|
115
|
+
host.remove();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await describe('<gtk-scrolled-window> requests no content size on a scrolling axis', async () => {
|
|
120
|
+
await it('in a horizontal box inside a 600px-high box it stays 600 and scrolls', async () => {
|
|
121
|
+
const { host, box } = mount(
|
|
122
|
+
`<gtk-box orientation="vertical" style="height:600px">
|
|
123
|
+
<gtk-box id="row" vexpand>
|
|
124
|
+
<gtk-scrolled-window id="sw" hexpand>
|
|
125
|
+
<div style="height:2000px;width:50px"></div>
|
|
126
|
+
</gtk-scrolled-window>
|
|
127
|
+
</gtk-box>
|
|
128
|
+
</gtk-box>`,
|
|
129
|
+
);
|
|
130
|
+
await settle();
|
|
131
|
+
expect(box('#row').height).toBe(600);
|
|
132
|
+
expect(box('#sw').height).toBe(600);
|
|
133
|
+
const viewport = host.querySelector('#sw gtk-viewport') as HTMLElement;
|
|
134
|
+
expect(viewport.getBoundingClientRect().height).toBe(600);
|
|
135
|
+
expect(viewport.scrollHeight > 600).toBe(true);
|
|
136
|
+
host.remove();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await it('sizes the measured chain: split-view content fills, the editor bin gets width', async () => {
|
|
140
|
+
const { host, box } = mount(
|
|
141
|
+
`<adw-overlay-split-view id="osv" style="height:600px">
|
|
142
|
+
<div slot="sidebar" style="width:100px"></div>
|
|
143
|
+
<gtk-box id="centerRight">
|
|
144
|
+
<adw-bin id="editor"><div hexpand style="height:10px"></div></adw-bin>
|
|
145
|
+
<gtk-scrolled-window id="right" hscrollbar-policy="never" style="flex:0 1 auto">
|
|
146
|
+
<div style="width:150px;height:3000px"></div>
|
|
147
|
+
</gtk-scrolled-window>
|
|
148
|
+
</gtk-box>
|
|
149
|
+
</adw-overlay-split-view>`,
|
|
150
|
+
800,
|
|
151
|
+
600,
|
|
152
|
+
);
|
|
153
|
+
await settle();
|
|
154
|
+
expect(box('#centerRight').height).toBe(box('#osv').height);
|
|
155
|
+
expect(box('#editor').width > 0).toBe(true);
|
|
156
|
+
expect(box('#right').height).toBe(box('#centerRight').height);
|
|
157
|
+
host.remove();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await describe('<adw-view-stack> page removal', async () => {
|
|
162
|
+
await it('a child removed from a view stack loses the stack classes', async () => {
|
|
163
|
+
const { host } = mount('<adw-view-stack id="s"></adw-view-stack><div id="elsewhere"></div>');
|
|
164
|
+
const stack = host.querySelector('#s') as AdwViewStack;
|
|
165
|
+
const a = document.createElement('div');
|
|
166
|
+
const b = document.createElement('div');
|
|
167
|
+
stack.add(a, 'a');
|
|
168
|
+
stack.add(b, 'b');
|
|
169
|
+
expect(a.classList.contains('active-view')).toBe(true);
|
|
170
|
+
expect(b.hidden).toBe(true);
|
|
171
|
+
|
|
172
|
+
stack.removePage('a');
|
|
173
|
+
expect(a.classList.contains('adw-view-stack-child')).toBe(false);
|
|
174
|
+
expect(a.classList.contains('active-view')).toBe(false);
|
|
175
|
+
expect(a.dataset.name).toBe(undefined);
|
|
176
|
+
|
|
177
|
+
// A plain DOM reparent is `gtk_widget_unparent` reaching the stack too.
|
|
178
|
+
host.querySelector('#elsewhere')!.append(b);
|
|
179
|
+
await settle();
|
|
180
|
+
expect(b.classList.contains('adw-view-stack-child')).toBe(false);
|
|
181
|
+
expect(b.hidden).toBe(false);
|
|
182
|
+
expect(stack.pages.length).toBe(0);
|
|
183
|
+
host.remove();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// compute-expand.ts — `gtk_widget_compute_expand`: does a widget tree rooted here want spare space?
|
|
2
|
+
//
|
|
3
|
+
// GTK does not only read a widget's OWN `hexpand`: a widget that set none expands when one of
|
|
4
|
+
// its children does (gtkwidget.c:8387-8423, `gtk_widget_update_computed_expand`). An AdwBin
|
|
5
|
+
// whose child is an hexpand editor therefore grows in a horizontal box, where the `[hexpand]`
|
|
6
|
+
// rules in `_widget.scss` gave it nothing and the editor came out 0px wide.
|
|
7
|
+
//
|
|
8
|
+
// THREE RULES, all in the C:
|
|
9
|
+
//
|
|
10
|
+
// · a widget that is not visible never expands (gtkwidget.c:8508-8509);
|
|
11
|
+
// · an EXPLICIT value wins and stops the walk — `hexpand_set` is checked before the vfunc
|
|
12
|
+
// runs, so an intermediate `hexpand: false` hides every expanding descendant;
|
|
13
|
+
// · otherwise only a class with a `compute_expand` vfunc looks at its children at all. The
|
|
14
|
+
// base GtkWidget has none, so a leaf (a label, a button's icon) never propagates.
|
|
15
|
+
//
|
|
16
|
+
// WHY JS AND NOT `:has()`: a selector cannot say "and no widget between here and there set
|
|
17
|
+
// hexpand" at arbitrary depth, it cannot tell a container with the vfunc from a leaf without
|
|
18
|
+
// one, and a registered subclass (`GObject.registerClass` over AdwBin) is a NEW tag that only
|
|
19
|
+
// the prototype chain links back to its base. So the value is computed here, and the box that
|
|
20
|
+
// needs it writes it as `data-computed-hexpand` / `data-computed-vexpand` on its children
|
|
21
|
+
// (gtk-box.ts) — internal state, never an authored attribute.
|
|
22
|
+
//
|
|
23
|
+
// The DOM has wrapper elements GTK has no widget for (`.adw-overlay-child`, a view stack's
|
|
24
|
+
// page `div`): those are transparent, the walk goes through them to the widgets inside.
|
|
25
|
+
//
|
|
26
|
+
// Reference: refs/gtk/gtk/gtkwidget.c (gtk_widget_compute_expand, update_computed_expand)
|
|
27
|
+
// Reference: refs/gtk/gtk/gtkbox.c:181-194 (every child, OR-ed)
|
|
28
|
+
// Reference: refs/gtk/gtk/gtkstack.c:2342-2373 (every page, visible or not)
|
|
29
|
+
// Reference: refs/gtk/gtk/gtkoverlay.c:292-301 (the main child only)
|
|
30
|
+
// Reference: refs/gtk/gtk/gtkscrolledwindow.c:544-554, gtkviewport.c:211-220 (the child only)
|
|
31
|
+
// Reference: refs/libadwaita/src/adw-widget-utils.c:501-536 (adw_widget_compute_expand)
|
|
32
|
+
// Copyright (c) The GTK Team. LGPLv2.1+. Copyright (c) The libadwaita authors. LGPLv2.1+.
|
|
33
|
+
|
|
34
|
+
export type ExpandOrientation = 'horizontal' | 'vertical';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Per tag that has a `compute_expand` vfunc: the subtrees of its own that are NOT the children
|
|
38
|
+
* the vfunc reads, and whether it answers on one axis only. A tag missing here behaves as the
|
|
39
|
+
* base GtkWidget — it does not propagate.
|
|
40
|
+
*/
|
|
41
|
+
interface ExpandVfunc {
|
|
42
|
+
readonly skip?: string;
|
|
43
|
+
readonly horizontalOnly?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `adw_widget_compute_expand` (adw-widget-utils.c:501), by the class_init that installs it. */
|
|
47
|
+
const ADW_WIDGET: ExpandVfunc = {};
|
|
48
|
+
|
|
49
|
+
const VFUNCS: ReadonlyMap<string, ExpandVfunc> = new Map<string, ExpandVfunc>([
|
|
50
|
+
['gtk-box', {}],
|
|
51
|
+
['gtk-stack', {}],
|
|
52
|
+
// The overlay children are its children too, but `gtk_overlay_compute_expand` reads only `child`.
|
|
53
|
+
['gtk-overlay', { skip: '.adw-overlay-layer' }],
|
|
54
|
+
// The scrollbars are children the vfunc skips; they are leaves, so skipping them is exact.
|
|
55
|
+
['gtk-scrolled-window', { skip: 'gtk-scrollbar' }],
|
|
56
|
+
['gtk-viewport', {}],
|
|
57
|
+
['adw-bin', ADW_WIDGET], // adw-bin.c:108
|
|
58
|
+
['adw-bottom-sheet', ADW_WIDGET], // adw-bottom-sheet.c:795
|
|
59
|
+
['adw-breakpoint-bin', ADW_WIDGET], // adw-breakpoint-bin.c:623
|
|
60
|
+
['adw-clamp', ADW_WIDGET], // adw-clamp.c:174
|
|
61
|
+
['adw-clamp-scrollable', ADW_WIDGET], // adw-clamp-scrollable.c:244
|
|
62
|
+
['adw-layout-slot', ADW_WIDGET], // adw-layout-slot.c:128
|
|
63
|
+
['adw-multi-layout-view', ADW_WIDGET], // adw-multi-layout-view.c:354
|
|
64
|
+
['adw-navigation-view', ADW_WIDGET], // adw-navigation-view.c:1900
|
|
65
|
+
['adw-overlay-split-view', ADW_WIDGET], // adw-overlay-split-view.c:909
|
|
66
|
+
['adw-status-page', ADW_WIDGET], // adw-status-page.c:213
|
|
67
|
+
['adw-toast-overlay', ADW_WIDGET], // adw-toast-overlay.c:543
|
|
68
|
+
['adw-toolbar-view', ADW_WIDGET], // adw-toolbar-view.c:525
|
|
69
|
+
['adw-view-stack', ADW_WIDGET], // adw-view-stack.c:1520
|
|
70
|
+
['adw-header-bar', { horizontalOnly: true }], // adw-header-bar.c:853
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
const ATTRIBUTE: Record<ExpandOrientation, string> = { horizontal: 'hexpand', vertical: 'vexpand' };
|
|
74
|
+
|
|
75
|
+
/** The base tag whose vfunc `el` inherits — its own tag, or the one a registered subclass extends. */
|
|
76
|
+
function vfuncOf(el: Element): ExpandVfunc | undefined {
|
|
77
|
+
const own = VFUNCS.get(el.localName);
|
|
78
|
+
if (own !== undefined) return own;
|
|
79
|
+
if (typeof customElements.getName !== 'function') return undefined;
|
|
80
|
+
for (let proto = Object.getPrototypeOf(el); proto && proto !== HTMLElement.prototype;) {
|
|
81
|
+
const tag = customElements.getName(proto.constructor as CustomElementConstructor);
|
|
82
|
+
const found = tag === null ? undefined : VFUNCS.get(tag);
|
|
83
|
+
if (found !== undefined) return found;
|
|
84
|
+
proto = Object.getPrototypeOf(proto);
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A GTK widget, as opposed to a wrapper this renderer added: every element is a custom element,
|
|
91
|
+
* and a plain element an author gave an expand value stands in for one.
|
|
92
|
+
*/
|
|
93
|
+
function isWidget(el: Element): boolean {
|
|
94
|
+
return el.localName.includes('-') || el.hasAttribute('hexpand') || el.hasAttribute('vexpand');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A view stack writes `hidden` on every page but the selected one. That is
|
|
99
|
+
* `gtk_widget_set_child_visible` (adw-view-stack.c:1016-1023), not `GtkWidget:visible`, and
|
|
100
|
+
* `gtk_widget_compute_expand` reads only the latter — so a background page still counts.
|
|
101
|
+
*/
|
|
102
|
+
function isStackPage(el: Element): boolean {
|
|
103
|
+
return el.classList.contains('adw-view-stack-child') && el.parentElement?.localName === 'adw-view-stack';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The value of an authored `hexpand` / `vexpand`: `""` is true, `"false"` false, absent unset. */
|
|
107
|
+
export function explicitExpand(el: Element, orientation: ExpandOrientation): boolean | undefined {
|
|
108
|
+
const raw = el.getAttribute(ATTRIBUTE[orientation]);
|
|
109
|
+
return raw === null ? undefined : raw !== 'false';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** `gtk_widget_compute_expand (el, orientation)`. */
|
|
113
|
+
export function computeExpand(el: Element, orientation: ExpandOrientation): boolean {
|
|
114
|
+
if (el.hasAttribute('hidden') && !isStackPage(el)) return false;
|
|
115
|
+
const explicit = explicitExpand(el, orientation);
|
|
116
|
+
if (explicit !== undefined) return explicit;
|
|
117
|
+
const vfunc = vfuncOf(el);
|
|
118
|
+
if (vfunc === undefined) return false;
|
|
119
|
+
if (vfunc.horizontalOnly && orientation === 'vertical') return false;
|
|
120
|
+
return anyChildExpands(el, orientation, vfunc.skip);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The OR over the widget children of `parent`, reached through any wrapper that is not one. */
|
|
124
|
+
function anyChildExpands(parent: Element, orientation: ExpandOrientation, skip: string | undefined): boolean {
|
|
125
|
+
for (const child of parent.children) {
|
|
126
|
+
if (skip !== undefined && child.matches(skip)) continue;
|
|
127
|
+
if (isWidget(child)) {
|
|
128
|
+
if (computeExpand(child, orientation)) return true;
|
|
129
|
+
} else if (anyChildExpands(child, orientation, undefined)) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
@@ -13,7 +13,7 @@ import { describe, expect, it } from '@gjsify/unit';
|
|
|
13
13
|
|
|
14
14
|
import type { AdwAlertDialog } from './elements/adw-alert-dialog.js';
|
|
15
15
|
import { capabilities } from './capabilities.mjs';
|
|
16
|
-
import { buildSharedTree, mountSharedTree } from './shared-tree-builder.js';
|
|
16
|
+
import { buildSharedTree, buildWithSiblings, mountSharedTree } from './shared-tree-builder.js';
|
|
17
17
|
import { registerTemplateClass } from './template-classes.js';
|
|
18
18
|
|
|
19
19
|
const vector = (kind: string) => CONSTRUCT_VECTORS.find((each) => each.kind === kind)!.tree;
|
|
@@ -24,8 +24,21 @@ registerTemplateClass(EXTERN_VECTOR_CLASS, 'corpus-extern');
|
|
|
24
24
|
|
|
25
25
|
function observe(vector: ConstructVector): unknown {
|
|
26
26
|
if (vector.kind === 'sibling-object') {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
let feed: ((size: { width: number; height: number }) => void) | undefined;
|
|
28
|
+
const { root, siblings } = buildWithSiblings(vector.tree, {
|
|
29
|
+
observeSize: (_element, onSize) => {
|
|
30
|
+
feed = onSize;
|
|
31
|
+
return () => {};
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const beside = siblings.get('beside')!;
|
|
35
|
+
return {
|
|
36
|
+
insideRoot: root.contains(beside) || beside.parentElement !== null,
|
|
37
|
+
label: BREAKPOINT_VECTOR_SIZES.map((size) => {
|
|
38
|
+
feed!(size);
|
|
39
|
+
return beside.getAttribute('label');
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
29
42
|
}
|
|
30
43
|
if (vector.kind === 'layout') {
|
|
31
44
|
// CSS Grid lines count from 1, GTK cells from 0: `2 / span 2` is column 1, columnSpan 2.
|
|
@@ -93,15 +106,22 @@ function observe(vector: ConstructVector): unknown {
|
|
|
93
106
|
}
|
|
94
107
|
const { root, unmount } = mountSharedTree(vector.tree);
|
|
95
108
|
try {
|
|
109
|
+
if (vector.kind === 'accessibility') {
|
|
110
|
+
// Read off the inner `<button>`, the one that carries the role and is announced.
|
|
111
|
+
const control = (root.querySelector('#described') as unknown as { button: HTMLButtonElement }).button;
|
|
112
|
+
return [
|
|
113
|
+
{ label: control.getAttribute('aria-label'), description: control.getAttribute('aria-description') },
|
|
114
|
+
];
|
|
115
|
+
}
|
|
96
116
|
if (vector.kind === 'strings') {
|
|
97
117
|
return [...root.querySelectorAll('option, .adw-drop-down-item-label')].map((node) => node.textContent);
|
|
98
118
|
}
|
|
99
119
|
const dialog = root as unknown as AdwAlertDialog;
|
|
100
120
|
return (vector.tree.extensions?.responses ?? []).map(({ id }) => ({
|
|
101
121
|
id,
|
|
102
|
-
label: dialog.
|
|
103
|
-
appearance: dialog.
|
|
104
|
-
enabled: dialog.
|
|
122
|
+
label: dialog.get_response_label(id),
|
|
123
|
+
appearance: dialog.get_response_appearance(id),
|
|
124
|
+
enabled: dialog.get_response_enabled(id),
|
|
105
125
|
}));
|
|
106
126
|
} finally {
|
|
107
127
|
unmount();
|
|
@@ -139,7 +159,9 @@ export const AdwConstructVectorsTest = async () => {
|
|
|
139
159
|
children: [{ tag: 'GtkButton', signals: [signal] }],
|
|
140
160
|
});
|
|
141
161
|
const scope = { onX: () => {} };
|
|
142
|
-
expect(() => mountSharedTree(on({ name: '
|
|
162
|
+
expect(() => mountSharedTree(on({ name: 'toggled', handler: 'onX' }), { scope })).toThrow(
|
|
163
|
+
"no signal 'toggled'",
|
|
164
|
+
);
|
|
143
165
|
expect(() => mountSharedTree(on({ name: 'clicked', handler: 'onX', flags: ['after'] }), { scope })).toThrow(
|
|
144
166
|
'plain handlers only',
|
|
145
167
|
);
|
|
@@ -331,6 +353,74 @@ export const AdwConstructVectorsTest = async () => {
|
|
|
331
353
|
expect(() => mountSharedTree(withBreakpoint('template'))).toThrow("id 'template'");
|
|
332
354
|
expect(() => mountSharedTree(withBreakpoint('caption', 'wide please'))).toThrow('breakpoint condition');
|
|
333
355
|
});
|
|
356
|
+
await it('hands a sibling back by id, a dialog included, and builds none of them into the root', () => {
|
|
357
|
+
const tree: SharedTreeNode = {
|
|
358
|
+
tag: 'GtkBox',
|
|
359
|
+
children: [{ tag: 'GtkLabel', id: 'inside' }],
|
|
360
|
+
siblings: [
|
|
361
|
+
{ tag: 'AdwAlertDialog', id: 'confirm', props: { heading: 'Save?' } },
|
|
362
|
+
{ tag: EXTERN_VECTOR_CLASS, extern: true, id: 'screen' },
|
|
363
|
+
],
|
|
364
|
+
};
|
|
365
|
+
const { root, siblings, objects } = buildWithSiblings(tree);
|
|
366
|
+
expect([...siblings.keys()].join()).toBe('confirm,screen');
|
|
367
|
+
expect(siblings.get('screen') instanceof CorpusExtern).toBe(true);
|
|
368
|
+
expect(siblings.get('confirm')!.localName).toBe('adw-alert-dialog');
|
|
369
|
+
expect(siblings.get('confirm')!.getAttribute('heading')).toBe('Save?');
|
|
370
|
+
expect(root.querySelector('#screen')).toBe(null);
|
|
371
|
+
expect([...objects.keys()].sort().join()).toBe('confirm,inside,screen');
|
|
372
|
+
});
|
|
373
|
+
await it('refuses siblings through every one-root door, a sibling without an id, and a duplicate id', () => {
|
|
374
|
+
const withSiblings = (siblings: SharedTreeNode[]): SharedTreeNode => ({
|
|
375
|
+
tag: 'GtkBox',
|
|
376
|
+
children: [{ tag: 'GtkLabel', id: 'taken' }],
|
|
377
|
+
siblings,
|
|
378
|
+
});
|
|
379
|
+
const labelled = withSiblings([{ tag: 'GtkLabel', id: 'beside' }]);
|
|
380
|
+
expect(() => buildSharedTree(labelled)).toThrow('buildWithSiblings');
|
|
381
|
+
expect(() => mountSharedTree(labelled)).toThrow('buildWithSiblings');
|
|
382
|
+
expect(() => buildWithSiblings(withSiblings([{ tag: 'GtkLabel' }]))).toThrow('has no id');
|
|
383
|
+
expect(() => buildWithSiblings(withSiblings([{ tag: 'GtkLabel', id: 'taken' }]))).toThrow("id 'taken'");
|
|
384
|
+
});
|
|
385
|
+
await it('refuses a breakpoint setter that names a sibling the tree does not carry', () => {
|
|
386
|
+
expect(() => buildWithSiblings({ ...vector('sibling-object'), siblings: [] })).toThrow("id 'beside'");
|
|
387
|
+
});
|
|
388
|
+
await it('names a check button on its input, and lets an authored name beat the icon fallback', () => {
|
|
389
|
+
const { root, unmount } = mountSharedTree({
|
|
390
|
+
tag: 'GtkBox',
|
|
391
|
+
children: [
|
|
392
|
+
{ tag: 'GtkCheckButton', id: 'check', accessibility: { label: { value: 'Dark' } } },
|
|
393
|
+
{
|
|
394
|
+
tag: 'GtkButton',
|
|
395
|
+
id: 'icon',
|
|
396
|
+
props: { 'icon-name': 'edit-copy-symbolic', 'tooltip-text': 'Copy' },
|
|
397
|
+
accessibility: { label: { value: 'Copy the link' } },
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
});
|
|
401
|
+
try {
|
|
402
|
+
const check = root.querySelector('#check')!;
|
|
403
|
+
const input = check.querySelector('.adw-check-input')!;
|
|
404
|
+
expect(input.getAttribute('aria-label')).toBe('Dark');
|
|
405
|
+
expect(input.hasAttribute('aria-description')).toBe(false);
|
|
406
|
+
check.removeAttribute('aria-label');
|
|
407
|
+
expect(input.hasAttribute('aria-label')).toBe(false);
|
|
408
|
+
const icon = root.querySelector('#icon') as unknown as HTMLElement & { button: HTMLButtonElement };
|
|
409
|
+
expect(icon.button.getAttribute('aria-label')).toBe('Copy the link');
|
|
410
|
+
icon.removeAttribute('aria-label');
|
|
411
|
+
expect(icon.button.getAttribute('aria-label')).toBe('Copy');
|
|
412
|
+
} finally {
|
|
413
|
+
unmount();
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
await it('refuses an accessibility name it has no door for, naming it', () => {
|
|
417
|
+
const tree: SharedTreeNode = {
|
|
418
|
+
tag: 'GtkBox',
|
|
419
|
+
children: [{ tag: 'GtkCheckButton', accessibility: { checked: { value: true } } }],
|
|
420
|
+
};
|
|
421
|
+
expect(() => buildSharedTree(tree)).toThrow("'checked'");
|
|
422
|
+
expect(() => mountSharedTree(tree)).toThrow('cannot be rendered by adwaita-web');
|
|
423
|
+
});
|
|
334
424
|
await it('does not read a registered name for a node that is not extern', () => {
|
|
335
425
|
const el = buildSharedTree({ tag: 'GtkBox', children: [{ tag: 'GtkLabel' }] });
|
|
336
426
|
expect(el.children[0]!.localName).toBe('gtk-label');
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Single-child containers hand their child the WHOLE allocation, on both axes: GtkOverlay's main
|
|
2
|
+
// child (gtkoverlay.c size_allocate), AdwToastOverlay's child, GtkStack's visible page
|
|
3
|
+
// (gtkstack.c:2778-2831) and AdwBin's child (gtkbinlayout.c:81). A block box gives a child only
|
|
4
|
+
// its content height, and a percentage resolves against an indefinite one — these specs pin the
|
|
5
|
+
// chain a real window builds: overlay > toast-overlay > stack > bin > view-stack.
|
|
6
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
7
|
+
|
|
8
|
+
function mount(html: string, width = 400, height = 300): { host: HTMLElement; box: (sel: string) => DOMRect } {
|
|
9
|
+
const host = document.createElement('div');
|
|
10
|
+
host.style.cssText = `width:${width}px;height:${height}px;display:flex;flex-direction:column`;
|
|
11
|
+
host.innerHTML = html;
|
|
12
|
+
document.body.appendChild(host);
|
|
13
|
+
const box = (sel: string) => (host.querySelector(sel) as HTMLElement).getBoundingClientRect();
|
|
14
|
+
return { host, box };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const ContainerFillTest = async () => {
|
|
18
|
+
await describe('single-child containers fill', async () => {
|
|
19
|
+
await it('<gtk-overlay> gives its main child the whole sized overlay', () => {
|
|
20
|
+
const { host, box } = mount('<gtk-overlay style="flex:1"><div id="main"></div></gtk-overlay>');
|
|
21
|
+
expect(box('#main').width).toBe(400);
|
|
22
|
+
expect(box('#main').height).toBe(300);
|
|
23
|
+
host.remove();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await it('<gtk-overlay> still measures as its main child when unsized', () => {
|
|
27
|
+
const { host, box } = mount('<gtk-overlay id="o"><div style="height:70px"></div></gtk-overlay>');
|
|
28
|
+
expect(box('#o').height).toBe(70);
|
|
29
|
+
host.remove();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await it('<adw-toast-overlay> gives its child the whole overlay', () => {
|
|
33
|
+
const { host, box } = mount('<adw-toast-overlay style="flex:1"><div id="c"></div></adw-toast-overlay>');
|
|
34
|
+
expect(box('#c').width).toBe(400);
|
|
35
|
+
expect(box('#c').height).toBe(300);
|
|
36
|
+
host.remove();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await it('a default <gtk-stack> page fills both axes, even with a zero-intrinsic child', () => {
|
|
40
|
+
const { host, box } = mount(
|
|
41
|
+
'<gtk-stack style="flex:1"><div id="p" name="a"><div id="z"></div></div></gtk-stack>',
|
|
42
|
+
);
|
|
43
|
+
expect(box('#p').width).toBe(400);
|
|
44
|
+
expect(box('#p').height).toBe(300);
|
|
45
|
+
host.remove();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await it('hhomogeneous="false" sizes a page to max-content', () => {
|
|
49
|
+
const { host, box } = mount(
|
|
50
|
+
'<gtk-stack hhomogeneous="false" style="flex:1"><div id="p" name="a" style="width:max-content"><span>hi</span></div></gtk-stack>',
|
|
51
|
+
);
|
|
52
|
+
expect(box('#p').width < 400).toBe(true);
|
|
53
|
+
host.remove();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await it('overlay > toast-overlay > stack > bin > view-stack fills the box', async () => {
|
|
57
|
+
const { host, box } = mount(
|
|
58
|
+
`<gtk-overlay style="flex:1"><adw-toast-overlay><gtk-stack>
|
|
59
|
+
<adw-bin id="b" name="a"><adw-view-stack id="v" style="flex:1"></adw-view-stack></adw-bin>
|
|
60
|
+
</gtk-stack></adw-toast-overlay></gtk-overlay>`,
|
|
61
|
+
);
|
|
62
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
63
|
+
for (const sel of ['adw-toast-overlay', 'gtk-stack', '#b', '#v']) {
|
|
64
|
+
expect(box(sel).width).toBe(400);
|
|
65
|
+
expect(box(sel).height).toBe(300);
|
|
66
|
+
}
|
|
67
|
+
host.remove();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|