@gjsify/adwaita-web 0.53.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adwaita-web.css +8070 -3037
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-application-window.spec.d.ts +1 -0
- package/lib/types/adw-bin.spec.d.ts +1 -0
- package/lib/types/adw-breakpoint-bin.spec.d.ts +1 -0
- package/lib/types/adw-clamp-scrollable.spec.d.ts +1 -0
- package/lib/types/adw-multi-layout-view.spec.d.ts +1 -0
- package/lib/types/adw-preferences-row.spec.d.ts +1 -0
- package/lib/types/adw-shortcuts.spec.d.ts +1 -0
- package/lib/types/adw-tab-bar.spec.d.ts +1 -0
- package/lib/types/adw-tab-button.spec.d.ts +1 -0
- package/lib/types/adw-tab-overview.spec.d.ts +1 -0
- package/lib/types/adw-view-switcher-sidebar.spec.d.ts +1 -0
- package/lib/types/adw-window.spec.d.ts +1 -0
- package/lib/types/breakpoints.d.ts +14 -3
- package/lib/types/construct-vectors.spec.d.ts +1 -0
- package/lib/types/elements/adw-application-window.d.ts +27 -0
- package/lib/types/elements/adw-bin.d.ts +17 -0
- package/lib/types/elements/adw-breakpoint-bin.d.ts +67 -0
- package/lib/types/elements/adw-clamp-scrollable.d.ts +38 -0
- package/lib/types/elements/adw-dialog.d.ts +10 -0
- package/lib/types/elements/adw-layout-slot.d.ts +19 -0
- package/lib/types/elements/adw-multi-layout-view.d.ts +101 -0
- package/lib/types/elements/adw-preferences-row.d.ts +31 -0
- package/lib/types/elements/adw-shortcuts-dialog.d.ts +13 -0
- package/lib/types/elements/adw-shortcuts-item.d.ts +36 -0
- package/lib/types/elements/adw-shortcuts-section.d.ts +14 -0
- package/lib/types/elements/adw-sidebar.d.ts +5 -0
- package/lib/types/elements/adw-switch-row.d.ts +3 -0
- package/lib/types/elements/adw-tab-bar.d.ts +108 -0
- package/lib/types/elements/adw-tab-button.d.ts +32 -0
- package/lib/types/elements/adw-tab-overview.d.ts +199 -0
- package/lib/types/elements/adw-tab-view.d.ts +19 -23
- package/lib/types/elements/adw-view-switcher-sidebar.d.ts +84 -0
- package/lib/types/elements/adw-window.d.ts +40 -0
- package/lib/types/elements/gtk-about-dialog.d.ts +146 -0
- package/lib/types/elements/gtk-application-window.d.ts +34 -0
- package/lib/types/elements/gtk-aspect-frame.d.ts +27 -0
- package/lib/types/elements/gtk-button.d.ts +9 -0
- package/lib/types/elements/gtk-calendar.d.ts +159 -0
- package/lib/types/elements/gtk-center-box.d.ts +39 -0
- package/lib/types/elements/gtk-color-dialog-button.d.ts +69 -0
- package/lib/types/elements/gtk-column-view.d.ts +50 -0
- package/lib/types/elements/gtk-drag-icon.d.ts +78 -0
- package/lib/types/elements/gtk-drawing-area.d.ts +95 -0
- package/lib/types/elements/gtk-editable-label.d.ts +33 -0
- package/lib/types/elements/gtk-emoji-chooser.d.ts +84 -0
- package/lib/types/elements/gtk-entry.d.ts +20 -1
- package/lib/types/elements/gtk-expander.d.ts +49 -0
- package/lib/types/elements/gtk-fixed.d.ts +13 -0
- package/lib/types/elements/gtk-flow-box-child.d.ts +14 -0
- package/lib/types/elements/gtk-flow-box.d.ts +87 -0
- package/lib/types/elements/gtk-font-dialog-button.d.ts +83 -0
- package/lib/types/elements/gtk-frame.d.ts +24 -0
- package/lib/types/elements/gtk-gl-area.d.ts +136 -0
- package/lib/types/elements/gtk-graphics-offload.d.ts +24 -0
- package/lib/types/elements/gtk-grid-view.d.ts +39 -0
- package/lib/types/elements/gtk-grid.d.ts +27 -0
- package/lib/types/elements/gtk-header-bar.d.ts +60 -0
- package/lib/types/elements/gtk-inscription.d.ts +64 -0
- package/lib/types/elements/gtk-label.d.ts +3 -0
- package/lib/types/elements/gtk-level-bar.d.ts +59 -0
- package/lib/types/elements/gtk-link-button.d.ts +29 -0
- package/lib/types/elements/gtk-list-box-row.d.ts +54 -0
- package/lib/types/elements/gtk-list-box.d.ts +100 -0
- package/lib/types/elements/gtk-list-view.d.ts +73 -0
- package/lib/types/elements/gtk-media-controls.d.ts +142 -0
- package/lib/types/elements/gtk-notebook.d.ts +148 -0
- package/lib/types/elements/gtk-overlay.d.ts +22 -0
- package/lib/types/elements/gtk-page-setup-unix-dialog.d.ts +87 -0
- package/lib/types/elements/gtk-paned.d.ts +121 -0
- package/lib/types/elements/gtk-password-entry.d.ts +27 -0
- package/lib/types/elements/gtk-picture.d.ts +66 -0
- package/lib/types/elements/gtk-popover-bin.d.ts +82 -0
- package/lib/types/elements/gtk-popover-menu-bar.d.ts +66 -0
- package/lib/types/elements/gtk-popover-menu.d.ts +83 -0
- package/lib/types/elements/gtk-popover.d.ts +28 -3
- package/lib/types/elements/gtk-print-unix-dialog.d.ts +167 -0
- package/lib/types/elements/gtk-revealer.d.ts +55 -0
- package/lib/types/elements/gtk-scale-button.d.ts +59 -0
- package/lib/types/elements/gtk-scale.d.ts +98 -0
- package/lib/types/elements/gtk-scrollbar.d.ts +124 -0
- package/lib/types/elements/gtk-scrolled-window.d.ts +121 -0
- package/lib/types/elements/gtk-search-bar.d.ts +39 -0
- package/lib/types/elements/gtk-search-entry.d.ts +27 -0
- package/lib/types/elements/gtk-separator.d.ts +10 -0
- package/lib/types/elements/gtk-spin-button.d.ts +94 -0
- package/lib/types/elements/gtk-spinner.d.ts +14 -0
- package/lib/types/elements/gtk-stack-sidebar.d.ts +43 -0
- package/lib/types/elements/gtk-stack-switcher.d.ts +58 -0
- package/lib/types/elements/gtk-stack.d.ts +154 -0
- package/lib/types/elements/gtk-switch.d.ts +4 -1
- package/lib/types/elements/gtk-text-view.d.ts +62 -0
- package/lib/types/elements/gtk-text.d.ts +45 -0
- package/lib/types/elements/gtk-toggle-button.d.ts +45 -0
- package/lib/types/elements/gtk-tree-expander.d.ts +38 -0
- package/lib/types/elements/gtk-video.d.ts +97 -0
- package/lib/types/elements/gtk-viewport.d.ts +84 -0
- package/lib/types/elements/gtk-window-controls.d.ts +80 -0
- package/lib/types/elements/gtk-window-handle.d.ts +39 -0
- package/lib/types/elements/gtk-window.d.ts +97 -0
- package/lib/types/elements/justification.d.ts +2 -0
- package/lib/types/elements/popover-menu.d.ts +18 -0
- package/lib/types/elements/roving-focus.d.ts +29 -1
- package/lib/types/emoji-data.d.ts +38 -0
- package/lib/types/font-description.d.ts +96 -0
- package/lib/types/gtk-about-dialog.spec.d.ts +1 -0
- package/lib/types/gtk-action-bar.spec.d.ts +1 -0
- package/lib/types/gtk-application-window.spec.d.ts +1 -0
- package/lib/types/gtk-aspect-frame.spec.d.ts +1 -0
- package/lib/types/gtk-calendar.spec.d.ts +1 -0
- package/lib/types/gtk-center-box.spec.d.ts +1 -0
- package/lib/types/gtk-color-dialog-button.spec.d.ts +1 -0
- package/lib/types/gtk-column-view.spec.d.ts +1 -0
- package/lib/types/gtk-drag-icon.spec.d.ts +1 -0
- package/lib/types/gtk-drawing-area.spec.d.ts +1 -0
- package/lib/types/gtk-editable-label.spec.d.ts +1 -0
- package/lib/types/gtk-emoji-chooser.spec.d.ts +1 -0
- package/lib/types/gtk-expander.spec.d.ts +1 -0
- package/lib/types/gtk-fixed.spec.d.ts +1 -0
- package/lib/types/gtk-flow-box.spec.d.ts +1 -0
- package/lib/types/gtk-font-dialog-button.spec.d.ts +1 -0
- package/lib/types/gtk-frame.spec.d.ts +1 -0
- package/lib/types/gtk-gl-area.spec.d.ts +1 -0
- package/lib/types/gtk-graphics-offload.spec.d.ts +1 -0
- package/lib/types/gtk-grid-view.spec.d.ts +1 -0
- package/lib/types/gtk-grid.spec.d.ts +1 -0
- package/lib/types/gtk-header-bar.spec.d.ts +1 -0
- package/lib/types/gtk-inscription.spec.d.ts +1 -0
- package/lib/types/gtk-level-bar.spec.d.ts +1 -0
- package/lib/types/gtk-link-button.spec.d.ts +1 -0
- package/lib/types/gtk-list-box.spec.d.ts +1 -0
- package/lib/types/gtk-list-view.spec.d.ts +1 -0
- package/lib/types/gtk-media-controls.spec.d.ts +1 -0
- package/lib/types/gtk-notebook.spec.d.ts +1 -0
- package/lib/types/gtk-overlay.spec.d.ts +1 -0
- package/lib/types/gtk-paned.spec.d.ts +1 -0
- package/lib/types/gtk-password-entry.spec.d.ts +1 -0
- package/lib/types/gtk-picture.spec.d.ts +1 -0
- package/lib/types/gtk-popover-bin.spec.d.ts +1 -0
- package/lib/types/gtk-popover-menu-bar.spec.d.ts +1 -0
- package/lib/types/gtk-popover-menu.spec.d.ts +1 -0
- package/lib/types/gtk-popover-role.spec.d.ts +1 -0
- package/lib/types/gtk-popover.spec.d.ts +1 -0
- package/lib/types/gtk-print-dialogs.spec.d.ts +1 -0
- package/lib/types/gtk-revealer.spec.d.ts +1 -0
- package/lib/types/gtk-scale-button.spec.d.ts +1 -0
- package/lib/types/gtk-scale.spec.d.ts +1 -0
- package/lib/types/gtk-scrollbar.spec.d.ts +1 -0
- package/lib/types/gtk-scrolled-window.spec.d.ts +1 -0
- package/lib/types/gtk-search-bar.spec.d.ts +1 -0
- package/lib/types/gtk-search-entry.spec.d.ts +1 -0
- package/lib/types/gtk-separator.spec.d.ts +1 -0
- package/lib/types/gtk-spin-button.spec.d.ts +1 -0
- package/lib/types/gtk-spinner.spec.d.ts +1 -0
- package/lib/types/gtk-stack-sidebar.spec.d.ts +1 -0
- package/lib/types/gtk-stack-switcher.spec.d.ts +1 -0
- package/lib/types/gtk-stack.spec.d.ts +1 -0
- package/lib/types/gtk-text-view.spec.d.ts +1 -0
- package/lib/types/gtk-text.spec.d.ts +1 -0
- package/lib/types/gtk-toggle-button.spec.d.ts +1 -0
- package/lib/types/gtk-tree-expander.spec.d.ts +1 -0
- package/lib/types/gtk-video.spec.d.ts +1 -0
- package/lib/types/gtk-viewport.spec.d.ts +1 -0
- package/lib/types/gtk-window-controls.spec.d.ts +1 -0
- package/lib/types/gtk-window-handle.spec.d.ts +1 -0
- package/lib/types/gtk-window.spec.d.ts +1 -0
- package/lib/types/index.d.ts +4 -1
- package/lib/types/list-view-dom.d.ts +44 -0
- package/lib/types/namespace/adw.d.ts +12 -0
- package/lib/types/namespace/gtk.d.ts +61 -0
- package/lib/types/page-setup.d.ts +100 -0
- package/lib/types/scroll-shading.d.ts +15 -1
- package/lib/types/shared-tree-builder.d.ts +30 -2
- package/lib/types/signals.d.ts +4 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/tab-chip.d.ts +73 -0
- package/lib/types/template-classes.d.ts +11 -0
- package/package.json +12 -8
- package/scss/_action_row.scss +2 -2
- package/scss/_application_window.scss +26 -0
- package/scss/_aspect_frame.scss +25 -0
- package/scss/_bin.scss +26 -0
- package/scss/_button.scss +45 -0
- package/scss/_calendar.scss +176 -0
- package/scss/_center_box.scss +25 -0
- package/scss/_checks.scss +6 -1
- package/scss/_clamp_scrollable.scss +28 -0
- package/scss/_color_button.scss +67 -0
- package/scss/_column_view.scss +126 -0
- package/scss/_drag_icon.scss +55 -0
- package/scss/_drawing.scss +85 -0
- package/scss/_emoji_chooser.scss +213 -0
- package/scss/_entry.scss +13 -5
- package/scss/_entry_row.scss +1 -1
- package/scss/_expander.scss +113 -0
- package/scss/_fixed.scss +29 -0
- package/scss/_flow_box.scss +94 -0
- package/scss/_font_button.scss +63 -0
- package/scss/_frame.scss +41 -0
- package/scss/_grid.scss +19 -0
- package/scss/_grid_view.scss +46 -0
- package/scss/_gtk_about_dialog.scss +186 -0
- package/scss/_headerbar.scss +132 -0
- package/scss/_icons.generated.scss +186 -0
- package/scss/_inscription.scss +125 -0
- package/scss/_level_bar.scss +190 -0
- package/scss/_link.scss +82 -0
- package/scss/_list_box.scss +88 -0
- package/scss/_list_view.scss +96 -0
- package/scss/_media_controls.scss +139 -0
- package/scss/_overlay.scss +68 -0
- package/scss/_paned.scss +130 -0
- package/scss/_password_entry.scss +83 -0
- package/scss/_picture.scss +80 -0
- package/scss/_popover_bin.scss +53 -0
- package/scss/_popover_menu_bar.scss +104 -0
- package/scss/_preferences_row.scss +51 -0
- package/scss/_print_dialogs.scss +328 -0
- package/scss/_reset.scss +21 -19
- package/scss/_revealer.scss +37 -0
- package/scss/_row.scss +20 -5
- package/scss/_scale.scss +298 -0
- package/scss/_scale_button.scss +167 -0
- package/scss/_scrollbar.scss +230 -0
- package/scss/_scrolled_window.scss +131 -0
- package/scss/_search_bar.scss +118 -0
- package/scss/_search_entry.scss +86 -0
- package/scss/_separator.scss +44 -0
- package/scss/_shortcuts_dialog.scss +100 -0
- package/scss/_spin_button.scss +172 -0
- package/scss/_spin_row.scss +56 -8
- package/scss/_spinner.scss +85 -4
- package/scss/_stack.scss +729 -0
- package/scss/_switch.scss +13 -5
- package/scss/_switch_row.scss +10 -0
- package/scss/_tab_bar.scss +317 -0
- package/scss/_tab_button.scss +98 -0
- package/scss/_tab_overview.scss +483 -0
- package/scss/_text.scss +182 -0
- package/scss/_theme.scss +1 -1
- package/scss/_tree_expander.scss +62 -0
- package/scss/_variables.scss +43 -21
- package/scss/_video.scss +146 -0
- package/scss/_view_switcher_sidebar.scss +112 -0
- package/scss/_viewport.scss +50 -0
- package/scss/_window.scss +118 -1
- package/scss/_window_handle.scss +20 -0
- package/scss/adwaita-skin.scss +109 -0
- package/src/adw-action-rows.spec.ts +76 -0
- package/src/adw-application-window.spec.ts +118 -0
- package/src/adw-bin.spec.ts +75 -0
- package/src/adw-breakpoint-bin.spec.ts +324 -0
- package/src/adw-clamp-scrollable.spec.ts +189 -0
- package/src/adw-multi-layout-view.spec.ts +243 -0
- package/src/adw-preferences-row.spec.ts +164 -0
- package/src/adw-row-state.spec.ts +142 -0
- package/src/adw-shortcuts.spec.ts +155 -0
- package/src/adw-tab-bar.spec.ts +287 -0
- package/src/adw-tab-button.spec.ts +101 -0
- package/src/adw-tab-overview.spec.ts +443 -0
- package/src/adw-view-switcher-sidebar.spec.ts +281 -0
- package/src/adw-window.spec.ts +129 -0
- package/src/breakpoints.ts +25 -8
- package/src/capabilities.d.mts +3 -0
- package/src/capabilities.mjs +18 -0
- package/src/connect-lifecycle.spec.ts +12 -0
- package/src/construct-vectors.spec.ts +175 -0
- package/src/elements/adw-application-window.ts +154 -0
- package/src/elements/adw-bin.ts +101 -0
- package/src/elements/adw-breakpoint-bin.ts +287 -0
- package/src/elements/adw-clamp-scrollable.ts +207 -0
- package/src/elements/adw-dialog.ts +19 -0
- package/src/elements/adw-layout-slot.ts +95 -0
- package/src/elements/adw-multi-layout-view.ts +326 -0
- package/src/elements/adw-preferences-row.ts +150 -0
- package/src/elements/adw-shortcuts-dialog.ts +52 -0
- package/src/elements/adw-shortcuts-item.ts +171 -0
- package/src/elements/adw-shortcuts-section.ts +86 -0
- package/src/elements/adw-sidebar.ts +42 -3
- package/src/elements/adw-switch-row.ts +28 -1
- package/src/elements/adw-tab-bar.ts +529 -0
- package/src/elements/adw-tab-button.ts +194 -0
- package/src/elements/adw-tab-overview.ts +1011 -0
- package/src/elements/adw-tab-view.ts +66 -145
- package/src/elements/adw-toggle-group.ts +20 -5
- package/src/elements/adw-view-stack.ts +18 -0
- package/src/elements/adw-view-switcher-sidebar.ts +332 -0
- package/src/elements/adw-window.ts +185 -11
- package/src/elements/checks.ts +35 -8
- package/src/elements/gtk-about-dialog.ts +811 -0
- package/src/elements/gtk-application-window.ts +108 -0
- package/src/elements/gtk-aspect-frame.ts +160 -0
- package/src/elements/gtk-button.ts +12 -0
- package/src/elements/gtk-calendar.ts +757 -0
- package/src/elements/gtk-center-box.ts +184 -0
- package/src/elements/gtk-color-dialog-button.ts +278 -0
- package/src/elements/gtk-column-view.ts +320 -0
- package/src/elements/gtk-drag-icon.ts +257 -0
- package/src/elements/gtk-drawing-area.ts +303 -0
- package/src/elements/gtk-editable-label.ts +204 -0
- package/src/elements/gtk-emoji-chooser.ts +542 -0
- package/src/elements/gtk-entry.ts +39 -5
- package/src/elements/gtk-expander.ts +301 -0
- package/src/elements/gtk-fixed.ts +99 -0
- package/src/elements/gtk-flow-box-child.ts +68 -0
- package/src/elements/gtk-flow-box.ts +480 -0
- package/src/elements/gtk-font-dialog-button.ts +432 -0
- package/src/elements/gtk-frame.ts +174 -0
- package/src/elements/gtk-gl-area.ts +476 -0
- package/src/elements/gtk-graphics-offload.ts +130 -0
- package/src/elements/gtk-grid-view.ts +168 -0
- package/src/elements/gtk-grid.ts +127 -0
- package/src/elements/gtk-header-bar.ts +273 -0
- package/src/elements/gtk-inscription.ts +387 -0
- package/src/elements/gtk-label.ts +19 -11
- package/src/elements/gtk-level-bar.ts +323 -0
- package/src/elements/gtk-link-button.ts +138 -0
- package/src/elements/gtk-list-box-row.ts +195 -0
- package/src/elements/gtk-list-box.ts +413 -0
- package/src/elements/gtk-list-view.ts +370 -0
- package/src/elements/gtk-media-controls.ts +509 -0
- package/src/elements/gtk-notebook.ts +664 -0
- package/src/elements/gtk-overlay.ts +149 -0
- package/src/elements/gtk-page-setup-unix-dialog.ts +420 -0
- package/src/elements/gtk-paned.ts +597 -0
- package/src/elements/gtk-password-entry.ts +170 -0
- package/src/elements/gtk-picture.ts +254 -0
- package/src/elements/gtk-popover-bin.ts +332 -0
- package/src/elements/gtk-popover-menu-bar.ts +355 -0
- package/src/elements/gtk-popover-menu.ts +339 -0
- package/src/elements/gtk-popover.ts +92 -10
- package/src/elements/gtk-print-unix-dialog.ts +989 -0
- package/src/elements/gtk-progress-bar.ts +24 -11
- package/src/elements/gtk-revealer.ts +406 -0
- package/src/elements/gtk-scale-button.ts +358 -0
- package/src/elements/gtk-scale.ts +492 -0
- package/src/elements/gtk-scrollbar.ts +528 -0
- package/src/elements/gtk-scrolled-window.ts +642 -0
- package/src/elements/gtk-search-bar.ts +200 -0
- package/src/elements/gtk-search-entry.ts +172 -0
- package/src/elements/gtk-separator.ts +52 -0
- package/src/elements/gtk-spin-button.ts +475 -0
- package/src/elements/gtk-spinner.ts +126 -0
- package/src/elements/gtk-stack-sidebar.ts +212 -0
- package/src/elements/gtk-stack-switcher.ts +279 -0
- package/src/elements/gtk-stack.ts +748 -0
- package/src/elements/gtk-switch.ts +52 -19
- package/src/elements/gtk-text-view.ts +290 -0
- package/src/elements/gtk-text.ts +219 -0
- package/src/elements/gtk-toggle-button.ts +239 -0
- package/src/elements/gtk-tree-expander.ts +243 -0
- package/src/elements/gtk-video.ts +423 -0
- package/src/elements/gtk-viewport.ts +277 -0
- package/src/elements/gtk-window-controls.ts +341 -0
- package/src/elements/gtk-window-handle.ts +221 -0
- package/src/elements/gtk-window.ts +366 -0
- package/src/elements/justification.ts +24 -0
- package/src/elements/popover-menu.ts +24 -0
- package/src/elements/roving-focus.ts +53 -21
- package/src/emoji-data.ts +577 -0
- package/src/entry-rows.spec.ts +31 -0
- package/src/font-description.ts +238 -0
- package/src/gtk-about-dialog.spec.ts +250 -0
- package/src/gtk-action-bar.spec.ts +89 -0
- package/src/gtk-application-window.spec.ts +107 -0
- package/src/gtk-aspect-frame.spec.ts +114 -0
- package/src/gtk-calendar.spec.ts +589 -0
- package/src/gtk-center-box.spec.ts +125 -0
- package/src/gtk-color-dialog-button.spec.ts +141 -0
- package/src/gtk-column-view.spec.ts +154 -0
- package/src/gtk-drag-icon.spec.ts +190 -0
- package/src/gtk-drawing-area.spec.ts +211 -0
- package/src/gtk-editable-label.spec.ts +190 -0
- package/src/gtk-emoji-chooser.spec.ts +173 -0
- package/src/gtk-entry.spec.ts +23 -0
- package/src/gtk-expander.spec.ts +170 -0
- package/src/gtk-fixed.spec.ts +97 -0
- package/src/gtk-flow-box.spec.ts +289 -0
- package/src/gtk-font-dialog-button.spec.ts +166 -0
- package/src/gtk-frame.spec.ts +119 -0
- package/src/gtk-gl-area.spec.ts +361 -0
- package/src/gtk-graphics-offload.spec.ts +119 -0
- package/src/gtk-grid-view.spec.ts +106 -0
- package/src/gtk-grid.spec.ts +122 -0
- package/src/gtk-header-bar.spec.ts +202 -0
- package/src/gtk-inscription.spec.ts +235 -0
- package/src/gtk-level-bar.spec.ts +199 -0
- package/src/gtk-link-button.spec.ts +117 -0
- package/src/gtk-list-box.spec.ts +422 -0
- package/src/gtk-list-view.spec.ts +203 -0
- package/src/gtk-media-controls.spec.ts +295 -0
- package/src/gtk-notebook.spec.ts +546 -0
- package/src/gtk-overlay.spec.ts +141 -0
- package/src/gtk-paned.spec.ts +267 -0
- package/src/gtk-password-entry.spec.ts +150 -0
- package/src/gtk-picture.spec.ts +172 -0
- package/src/gtk-popover-bin.spec.ts +229 -0
- package/src/gtk-popover-menu-bar.spec.ts +296 -0
- package/src/gtk-popover-menu.spec.ts +261 -0
- package/src/gtk-popover-role.spec.ts +138 -0
- package/src/gtk-popover.spec.ts +264 -0
- package/src/gtk-print-dialogs.spec.ts +251 -0
- package/src/gtk-revealer.spec.ts +164 -0
- package/src/gtk-scale-button.spec.ts +218 -0
- package/src/gtk-scale.spec.ts +213 -0
- package/src/gtk-scrollbar.spec.ts +244 -0
- package/src/gtk-scrolled-window.spec.ts +262 -0
- package/src/gtk-search-bar.spec.ts +179 -0
- package/src/gtk-search-entry.spec.ts +142 -0
- package/src/gtk-separator.spec.ts +74 -0
- package/src/gtk-spin-button.spec.ts +186 -0
- package/src/gtk-spinner.spec.ts +130 -0
- package/src/gtk-stack-sidebar.spec.ts +217 -0
- package/src/gtk-stack-switcher.spec.ts +249 -0
- package/src/gtk-stack.spec.ts +432 -0
- package/src/gtk-text-view.spec.ts +189 -0
- package/src/gtk-text.spec.ts +163 -0
- package/src/gtk-toggle-button.spec.ts +186 -0
- package/src/gtk-tree-expander.spec.ts +201 -0
- package/src/gtk-video.spec.ts +240 -0
- package/src/gtk-viewport.spec.ts +140 -0
- package/src/gtk-window-controls.spec.ts +232 -0
- package/src/gtk-window-handle.spec.ts +158 -0
- package/src/gtk-window.spec.ts +190 -0
- package/src/index.ts +9 -1
- package/src/keyboard-operable.spec.ts +33 -1
- package/src/list-view-dom.ts +121 -0
- package/src/namespace/adw.ts +12 -0
- package/src/namespace/gtk.ts +61 -0
- package/src/page-setup.ts +222 -0
- package/src/scroll-shading.ts +18 -1
- package/src/shared-tree-builder.ts +263 -21
- package/src/signals.ts +15 -0
- package/src/source-view/theme.ts +11 -5
- package/src/style-classes.spec.ts +9 -8
- package/src/styles.generated.ts +1 -1
- package/src/tab-chip.ts +200 -0
- package/src/template-classes.ts +45 -0
- package/src/test.browser.mts +152 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// DOM-level tests for <gtk-video>. What matters is the OVERLAY ICON, because it is the only
|
|
2
|
+
// part of this widget with logic in it: `gtk_video_update_overlay_icon` is a four-way choice
|
|
3
|
+
// whose ORDER carries meaning (no stream before error, error before ended), and
|
|
4
|
+
// `gtk_video_update_playing` hides the icon by VISIBILITY rather than swapping it for a pause
|
|
5
|
+
// glyph. Both are asserted here against the arm they come from.
|
|
6
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
7
|
+
|
|
8
|
+
import type { GtkVideo } from './elements/gtk-video.js';
|
|
9
|
+
|
|
10
|
+
/** A `<video>` with no `src`: no stream to report, so every media event is synthetic here. */
|
|
11
|
+
function mount(withMedia: boolean, attrs: Record<string, string> = {}): { el: GtkVideo; host: HTMLElement } {
|
|
12
|
+
const host = document.createElement('div');
|
|
13
|
+
document.body.appendChild(host);
|
|
14
|
+
const el = document.createElement('gtk-video') as GtkVideo;
|
|
15
|
+
for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
|
|
16
|
+
if (withMedia) el.appendChild(document.createElement('video'));
|
|
17
|
+
host.appendChild(el);
|
|
18
|
+
return { el, host };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function overlayIcon(el: GtkVideo): string | null {
|
|
22
|
+
return el.querySelector('.adw-video-overlay-icon gtk-image')?.getAttribute('icon-name') ?? null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const GtkVideoTest = async () => {
|
|
26
|
+
await describe('<gtk-video> the four-way overlay icon', async () => {
|
|
27
|
+
await it('draws the EJECT glyph with no media stream at all', () => {
|
|
28
|
+
// gtkvideo.c:629 — the no-stream arm is FIRST, not the fallback, so an element
|
|
29
|
+
// that has not been given a `<video>` shows "there is nothing here to play"
|
|
30
|
+
// rather than a play glyph over nothing.
|
|
31
|
+
const { el, host } = mount(false);
|
|
32
|
+
expect(el.media).toBe(null);
|
|
33
|
+
expect(overlayIcon(el)).toBe('media-eject-symbolic');
|
|
34
|
+
host.remove();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await it('draws the PLAY glyph once a stream is present and healthy', () => {
|
|
38
|
+
// gtkvideo.c:645 — the last arm, reached only with a stream, no error, not ended.
|
|
39
|
+
const { el, host } = mount(true);
|
|
40
|
+
expect(overlayIcon(el)).toBe('media-playback-start-symbolic');
|
|
41
|
+
host.remove();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await it('draws the ERROR glyph and takes the message as its tooltip', () => {
|
|
45
|
+
// gtkvideo.c:633-643 — the error arm also sets the tooltip to `error->message`,
|
|
46
|
+
// which is the only place this widget carries text that is not an accessible name.
|
|
47
|
+
const { el, host } = mount(true);
|
|
48
|
+
const media = el.media!;
|
|
49
|
+
Object.defineProperty(media, 'error', { value: { message: 'no such stream' }, configurable: true });
|
|
50
|
+
media.dispatchEvent(new Event('error'));
|
|
51
|
+
expect(overlayIcon(el)).toBe('dialog-error-symbolic');
|
|
52
|
+
const button = el.querySelector('.adw-video-overlay-icon') as HTMLButtonElement;
|
|
53
|
+
expect(button.title).toBe('no such stream');
|
|
54
|
+
host.remove();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await it('draws the REPEAT glyph once the stream has ended', () => {
|
|
58
|
+
// gtkvideo.c:638-639 — the ended arm.
|
|
59
|
+
const { el, host } = mount(true);
|
|
60
|
+
el.media!.dispatchEvent(new Event('ended'));
|
|
61
|
+
expect(overlayIcon(el)).toBe('media-playlist-repeat-symbolic');
|
|
62
|
+
host.remove();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await it('prefers ERROR over ENDED, which is what the arm order says', () => {
|
|
66
|
+
// gtkvideo.c:632-638 — the error test comes first, so a stream that both failed and
|
|
67
|
+
// ended shows the error. Asserting the reverse would pass on a port that swapped
|
|
68
|
+
// the two arms.
|
|
69
|
+
const { el, host } = mount(true);
|
|
70
|
+
const media = el.media!;
|
|
71
|
+
media.dispatchEvent(new Event('ended'));
|
|
72
|
+
Object.defineProperty(media, 'error', { value: { message: 'boom' }, configurable: true });
|
|
73
|
+
media.dispatchEvent(new Event('error'));
|
|
74
|
+
expect(overlayIcon(el)).toBe('dialog-error-symbolic');
|
|
75
|
+
host.remove();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await describe('<gtk-video> the overlay is hidden while playing', async () => {
|
|
80
|
+
await it('a play HIDES the icon and a pause shows it, both by visibility', () => {
|
|
81
|
+
// gtkvideo.c:665 — `gtk_widget_set_visible (self->overlay_icon, !playing)`. There
|
|
82
|
+
// is no pause glyph in the overlay: the controls bar carries the pause button, and
|
|
83
|
+
// a playing video shows the frames and nothing on top of them.
|
|
84
|
+
const { el, host } = mount(true);
|
|
85
|
+
const button = el.querySelector('.adw-video-overlay-icon') as HTMLButtonElement;
|
|
86
|
+
expect(button.hidden).toBe(false);
|
|
87
|
+
el.media!.dispatchEvent(new Event('play'));
|
|
88
|
+
expect(button.hidden).toBe(true);
|
|
89
|
+
expect(el.classList.contains('playing')).toBe(true);
|
|
90
|
+
el.media!.dispatchEvent(new Event('pause'));
|
|
91
|
+
expect(button.hidden).toBe(false);
|
|
92
|
+
expect(el.classList.contains('playing')).toBe(false);
|
|
93
|
+
host.remove();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await it("keeps the icon's GLYPH across the hide — it is not swapped for a pause", () => {
|
|
97
|
+
const { el, host } = mount(true);
|
|
98
|
+
el.media!.dispatchEvent(new Event('play'));
|
|
99
|
+
expect(overlayIcon(el)).toBe('media-playback-start-symbolic');
|
|
100
|
+
host.remove();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await it('announces playing as its own notify, which the C does not', () => {
|
|
104
|
+
const { el, host } = mount(true);
|
|
105
|
+
const events: unknown[] = [];
|
|
106
|
+
el.addEventListener('notify::playing', (e) => events.push((e as CustomEvent).detail));
|
|
107
|
+
el.media!.dispatchEvent(new Event('play'));
|
|
108
|
+
el.media!.dispatchEvent(new Event('pause'));
|
|
109
|
+
expect(events).toStrictEqual([{ playing: true }, { playing: false }]);
|
|
110
|
+
host.remove();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await describe('<gtk-video> the properties', async () => {
|
|
115
|
+
await it('autoplay and loop default to FALSE and apply to the media element', () => {
|
|
116
|
+
// ui/gtkvideo.ui's `<property name="enabled">disabled</property>` on the
|
|
117
|
+
// graphics offload is the same shape: the template starts the widget off.
|
|
118
|
+
const { el, host } = mount(true);
|
|
119
|
+
expect(el.autoplay).toBe(false);
|
|
120
|
+
expect(el.loop).toBe(false);
|
|
121
|
+
expect(el.media!.loop).toBe(false);
|
|
122
|
+
el.loop = true;
|
|
123
|
+
expect(el.media!.loop).toBe(true);
|
|
124
|
+
host.remove();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await it('NEVER gives the media element an autoplay attribute of its own', () => {
|
|
128
|
+
// The C's `autoplay` is not the element's autoplay attribute: it plays when the
|
|
129
|
+
// stream reports `prepared` AND the widget is mapped (gtkvideo.c:684-690), and
|
|
130
|
+
// the readiness gate is bound separately. Handing `<video>` an autoplay attribute
|
|
131
|
+
// would bypass the second condition entirely.
|
|
132
|
+
const { el, host } = mount(true, { autoplay: '' });
|
|
133
|
+
expect(el.autoplay).toBe(true);
|
|
134
|
+
expect(el.media!.autoplay).toBe(false);
|
|
135
|
+
host.remove();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await it('plays on readiness, not on the property write', () => {
|
|
139
|
+
const { el, host } = mount(true, { autoplay: '' });
|
|
140
|
+
const media = el.media!;
|
|
141
|
+
let played = false;
|
|
142
|
+
media.play = () => {
|
|
143
|
+
played = true;
|
|
144
|
+
return Promise.resolve();
|
|
145
|
+
};
|
|
146
|
+
expect(played).toBe(false);
|
|
147
|
+
media.dispatchEvent(new Event('loadeddata'));
|
|
148
|
+
expect(played).toBe(true);
|
|
149
|
+
host.remove();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await it('graphics-offload is stored and class-flagged, and buys no rendering', () => {
|
|
153
|
+
// `GtkGraphicsOffload` moves a subtree into a shared GL texture; CSS has no such
|
|
154
|
+
// mechanism. The property is observed faithfully — that is what the
|
|
155
|
+
// element-properties ratchet measures — and the class is deliberately unstyled.
|
|
156
|
+
const { el, host } = mount(true);
|
|
157
|
+
expect(el.graphicsOffload).toBe(false);
|
|
158
|
+
el.graphicsOffload = true;
|
|
159
|
+
expect(el.hasAttribute('graphics-offload')).toBe(true);
|
|
160
|
+
expect(el.classList.contains('graphics-offload')).toBe(true);
|
|
161
|
+
host.remove();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await it('notifies once per real change', () => {
|
|
165
|
+
const { el, host } = mount(true);
|
|
166
|
+
const events: unknown[] = [];
|
|
167
|
+
el.addEventListener('notify::loop', (e) => events.push((e as CustomEvent).detail));
|
|
168
|
+
el.loop = true;
|
|
169
|
+
el.loop = true;
|
|
170
|
+
el.loop = false;
|
|
171
|
+
expect(events).toStrictEqual([{ loop: true }, { loop: false }]);
|
|
172
|
+
host.remove();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await describe('<gtk-video> the controls reveal', async () => {
|
|
177
|
+
await it('is hidden until revealControls() and stays out of the way while so', async () => {
|
|
178
|
+
// ui/gtkvideo.ui's `controls_revealer` has `measure` 1, so the bar is allocated
|
|
179
|
+
// even while hidden — which is why this is an opacity transition and not
|
|
180
|
+
// `display: none`, and why the revealer keeps its box.
|
|
181
|
+
const { el, host } = mount(true);
|
|
182
|
+
const revealer = el.querySelector('.adw-video-controls-revealer') as HTMLElement;
|
|
183
|
+
expect(el.controlsRevealed).toBe(false);
|
|
184
|
+
expect(getComputedStyle(revealer).display).not.toBe('none');
|
|
185
|
+
el.revealControls();
|
|
186
|
+
expect(el.controlsRevealed).toBe(true);
|
|
187
|
+
expect(getComputedStyle(revealer).pointerEvents).toBe('auto');
|
|
188
|
+
host.remove();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await it('withdraws itself after three seconds of stillness, and re-arms on movement', async () => {
|
|
192
|
+
// gtkvideo.c:130 — `g_timeout_add (3 * 1000, …)`. Re-arming on every reveal is
|
|
193
|
+
// what makes it "three seconds after the LAST movement" rather than after the
|
|
194
|
+
// first, so the test moves the pointer before the first timeout could fire.
|
|
195
|
+
const { el, host } = mount(true);
|
|
196
|
+
el.revealControls();
|
|
197
|
+
el.revealControls();
|
|
198
|
+
expect(el.controlsRevealed).toBe(true);
|
|
199
|
+
host.remove();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await describe("<gtk-video> the template's three nodes", async () => {
|
|
204
|
+
await it("keeps the author's <video> as the main child and adds the two overlays", () => {
|
|
205
|
+
// ui/gtkvideo.ui puts the picture (here the media) UNDER an overlay icon and a
|
|
206
|
+
// controls revealer. Wrapping in place rather than re-creating is what lets the
|
|
207
|
+
// light DOM stay the API.
|
|
208
|
+
const { el, host } = mount(true);
|
|
209
|
+
const surface = el.querySelector('.adw-video-surface') as HTMLElement;
|
|
210
|
+
expect(surface.querySelector('video')).toBe(el.media);
|
|
211
|
+
expect(surface.querySelector('.adw-video-overlay-icon')).not.toBe(null);
|
|
212
|
+
expect(surface.querySelector('.adw-video-controls-revealer')).not.toBe(null);
|
|
213
|
+
host.remove();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await it('the overlay icon wears .osd and .circular and is a real BUTTON', async () => {
|
|
217
|
+
// ui/gtkvideo.ui — `<class name="osd"/>`, `<class name="circular"/>`, and an
|
|
218
|
+
// `accessible-role` of button. A `<button>` IS that role natively, which is the
|
|
219
|
+
// honest way to build it rather than a div with a click handler.
|
|
220
|
+
const { el, host } = mount(true);
|
|
221
|
+
const button = el.querySelector('.adw-video-overlay-icon') as HTMLButtonElement;
|
|
222
|
+
expect(button.tagName).toBe('BUTTON');
|
|
223
|
+
expect(button.classList.contains('osd')).toBe(true);
|
|
224
|
+
expect(button.classList.contains('circular')).toBe(true);
|
|
225
|
+
host.remove();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
await it("the icon is 64px with a 32px radius — libadwaita's three lines", () => {
|
|
229
|
+
// refs/libadwaita/src/stylesheet/widgets/_misc.scss:76-80 — `video image.osd`.
|
|
230
|
+
// The radius is half the width, the same shape `.circular` gives at 34px.
|
|
231
|
+
const { el, host } = mount(true);
|
|
232
|
+
const button = el.querySelector('.adw-video-overlay-icon') as HTMLButtonElement;
|
|
233
|
+
const style = getComputedStyle(button);
|
|
234
|
+
expect(style.minWidth).toBe('64px');
|
|
235
|
+
expect(style.minHeight).toBe('64px');
|
|
236
|
+
expect(style.borderTopLeftRadius).toBe('32px');
|
|
237
|
+
host.remove();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// DOM-level tests for <gtk-viewport>: the adjustments are GTK's own configure — a step of a
|
|
2
|
+
// tenth of the window and a page of nine tenths — read off the scrollport rather than
|
|
3
|
+
// recomputed, and `scroll-to-focus` moves the position the way GTK's focus handler does.
|
|
4
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
5
|
+
|
|
6
|
+
import type { AdwAdjustment } from '@gjsify/adwaita-core';
|
|
7
|
+
|
|
8
|
+
import type { GtkViewport } from './elements/gtk-viewport.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A host of a known size, so a viewport inside it has a measurable page.
|
|
12
|
+
*
|
|
13
|
+
* The VIEWPORT carries the size rather than the host: GTK hands a viewport an allocation from
|
|
14
|
+
* its parent, and in a browser the allocation is a height on the scrollport itself — without
|
|
15
|
+
* one it grows with its content and has nothing to scroll.
|
|
16
|
+
*/
|
|
17
|
+
function mount(width = 200, height = 100): { el: GtkViewport; host: HTMLElement } {
|
|
18
|
+
const host = document.createElement('div');
|
|
19
|
+
host.style.width = `${width}px`;
|
|
20
|
+
host.style.height = `${height}px`;
|
|
21
|
+
document.body.appendChild(host);
|
|
22
|
+
const el = document.createElement('gtk-viewport') as GtkViewport;
|
|
23
|
+
el.style.width = `${width}px`;
|
|
24
|
+
el.style.height = `${height}px`;
|
|
25
|
+
host.appendChild(el);
|
|
26
|
+
return { el, host };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A child of a fixed size, so the viewport's page and content sizes are exact. */
|
|
30
|
+
function content(el: GtkViewport, height: number, width = 200): HTMLElement {
|
|
31
|
+
const box = document.createElement('div');
|
|
32
|
+
box.style.width = `${width}px`;
|
|
33
|
+
box.style.height = `${height}px`;
|
|
34
|
+
el.appendChild(box);
|
|
35
|
+
return box;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The `detail` of every `event` dispatched on (or bubbling to) `el`. */
|
|
39
|
+
function record(el: HTMLElement, event: string): unknown[] {
|
|
40
|
+
const details: unknown[] = [];
|
|
41
|
+
el.addEventListener(event, (e) => details.push((e as CustomEvent).detail));
|
|
42
|
+
return details;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The `detail` of a `value-changed`, which carries one adjustment per axis. */
|
|
46
|
+
const detail = (value: unknown): { hadjustment: AdwAdjustment; vadjustment: AdwAdjustment } =>
|
|
47
|
+
value as { hadjustment: AdwAdjustment; vadjustment: AdwAdjustment };
|
|
48
|
+
|
|
49
|
+
export const GtkViewportTest = async () => {
|
|
50
|
+
await describe('<gtk-viewport> the adjustments', async () => {
|
|
51
|
+
await it('is a generic role, and an empty one has nothing to scroll', async () => {
|
|
52
|
+
const { el, host } = mount();
|
|
53
|
+
expect(el.getAttribute('role')).toBe('generic');
|
|
54
|
+
// No content: the range is exactly the window, which is `upper == page_size` and
|
|
55
|
+
// the reason an automatic scrollbar stays hidden.
|
|
56
|
+
expect(el.vadjustment.upper).toBe(el.vadjustment.pageSize);
|
|
57
|
+
expect(el.vadjustment.value).toBe(0);
|
|
58
|
+
host.remove();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await it("configures each axis as GTK's viewport does", async () => {
|
|
62
|
+
const { el, host } = mount(200, 100);
|
|
63
|
+
content(el, 400);
|
|
64
|
+
const vertical = el.vadjustment;
|
|
65
|
+
// `viewport_set_adjustment_values`: lower 0, upper the child size, page the
|
|
66
|
+
// viewport size, and the two increments as fractions OF it.
|
|
67
|
+
expect(vertical.lower).toBe(0);
|
|
68
|
+
expect(vertical.upper).toBe(400);
|
|
69
|
+
expect(vertical.pageSize).toBe(100);
|
|
70
|
+
expect(vertical.stepIncrement).toBe(10);
|
|
71
|
+
expect(vertical.pageIncrement).toBe(90);
|
|
72
|
+
expect(el.hadjustment.upper).toBe(200);
|
|
73
|
+
host.remove();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await it('announces a move as a value-changed, once, with both axes', async () => {
|
|
77
|
+
const { el, host } = mount(200, 100);
|
|
78
|
+
content(el, 400);
|
|
79
|
+
const seen = record(el, 'value-changed');
|
|
80
|
+
el.scrollAxis('vertical', 40);
|
|
81
|
+
expect(seen.length).toBe(1);
|
|
82
|
+
const both = detail(seen[0]);
|
|
83
|
+
expect(both.vadjustment.value).toBe(40);
|
|
84
|
+
expect(both.hadjustment.value).toBe(0);
|
|
85
|
+
host.remove();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await it("clamps a move into the range, which is set_value's contract", async () => {
|
|
89
|
+
const { el, host } = mount(200, 100);
|
|
90
|
+
content(el, 400);
|
|
91
|
+
el.scrollAxis('vertical', 1000);
|
|
92
|
+
// upper - pageSize = 300.
|
|
93
|
+
expect(el.vadjustment.value).toBe(300);
|
|
94
|
+
el.scrollAxis('vertical', -50);
|
|
95
|
+
expect(el.vadjustment.value).toBe(0);
|
|
96
|
+
host.remove();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await describe('<gtk-viewport> scroll-to-focus', async () => {
|
|
101
|
+
await it('is on by default, and only the literal false turns it off', async () => {
|
|
102
|
+
const { el, host } = mount();
|
|
103
|
+
expect(el.scrollToFocus).toBe(true);
|
|
104
|
+
el.setAttribute('scroll-to-focus', 'false');
|
|
105
|
+
expect(el.scrollToFocus).toBe(false);
|
|
106
|
+
// An unknown value is the pspec's TRUE again, as an unset construct bag is.
|
|
107
|
+
el.setAttribute('scroll-to-focus', 'maybe');
|
|
108
|
+
expect(el.scrollToFocus).toBe(true);
|
|
109
|
+
host.remove();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await it('notifies the way a GObject property does', async () => {
|
|
113
|
+
const { el, host } = mount();
|
|
114
|
+
const seen = record(el, 'notify::scroll-to-focus');
|
|
115
|
+
el.scrollToFocus = false;
|
|
116
|
+
expect(seen.length).toBe(1);
|
|
117
|
+
// `toEqual` compares with `==` in this runner, so a detail object is read field by
|
|
118
|
+
// field rather than compared as one value.
|
|
119
|
+
expect((seen[0] as Record<string, unknown>)['scroll-to-focus']).toBe(false);
|
|
120
|
+
host.remove();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await it('brings a focused widget into view, and ignores a change written to itself', async () => {
|
|
124
|
+
const { el, host } = mount(200, 100);
|
|
125
|
+
const last = content(el, 400);
|
|
126
|
+
const button = document.createElement('button');
|
|
127
|
+
button.style.width = '80px';
|
|
128
|
+
button.style.height = '20px';
|
|
129
|
+
el.appendChild(button);
|
|
130
|
+
button.style.marginTop = '360px';
|
|
131
|
+
expect(el.vadjustment.value).toBe(0);
|
|
132
|
+
button.focus();
|
|
133
|
+
// The C checks `FOCUS_WITHIN` and skips a target that is the viewport itself;
|
|
134
|
+
// a real focus change inside it is what moves the adjustment.
|
|
135
|
+
expect(el.vadjustment.value > 0).toBe(true);
|
|
136
|
+
el.dispatchEvent(new FocusEvent('focusin', { bubbles: true }));
|
|
137
|
+
host.remove();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// DOM-level tests for <gtk-window-controls>. The widget is a small decision table and every
|
|
2
|
+
// branch of it is a line in `update_window_buttons` (gtkwindowcontrols.c:247-426): which half
|
|
3
|
+
// of the decoration layout `side` selects, which tokens build a button, and the four window
|
|
4
|
+
// properties that each token reads.
|
|
5
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
6
|
+
|
|
7
|
+
import type { GtkWindowControls } from './elements/gtk-window-controls.js';
|
|
8
|
+
import type { GtkWindow } from './elements/gtk-window.js';
|
|
9
|
+
|
|
10
|
+
function mount(
|
|
11
|
+
attrs: Record<string, string> = {},
|
|
12
|
+
windowAttrs: Record<string, string> | null = null,
|
|
13
|
+
): {
|
|
14
|
+
el: GtkWindowControls;
|
|
15
|
+
host: HTMLElement;
|
|
16
|
+
} {
|
|
17
|
+
const host = document.createElement('div');
|
|
18
|
+
document.body.appendChild(host);
|
|
19
|
+
let parent: HTMLElement = host;
|
|
20
|
+
if (windowAttrs !== null) {
|
|
21
|
+
const win = document.createElement('gtk-window') as GtkWindow;
|
|
22
|
+
for (const [name, value] of Object.entries(windowAttrs)) win.setAttribute(name, value);
|
|
23
|
+
host.appendChild(win);
|
|
24
|
+
parent = win;
|
|
25
|
+
}
|
|
26
|
+
const el = document.createElement('gtk-window-controls') as GtkWindowControls;
|
|
27
|
+
for (const [name, value] of Object.entries(attrs)) el.setAttribute(name, value);
|
|
28
|
+
parent.appendChild(el);
|
|
29
|
+
return { el, host };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* What each child under the controls IS, in DOM order: the window icon, or the button's own
|
|
34
|
+
* role class (`minimize` / `maximize` / `close`, gtkwindowcontrols.c:322, :359, :387).
|
|
35
|
+
*/
|
|
36
|
+
function children(el: GtkWindowControls): string[] {
|
|
37
|
+
return [...el.children].map(
|
|
38
|
+
(node) =>
|
|
39
|
+
[...node.classList].find((name) => name !== 'adw-window-controls-button' && name !== 'adw-icon') ?? 'icon',
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const GtkWindowControlsTest = async () => {
|
|
44
|
+
await describe('<gtk-window-controls> defaults', async () => {
|
|
45
|
+
await it('is empty outside a window, exactly as GTK returns early there', () => {
|
|
46
|
+
// gtkwindowcontrols.c:257-264 — no GtkWindow root, `set_empty (self, TRUE)`.
|
|
47
|
+
const { el, host } = mount();
|
|
48
|
+
expect(el.empty).toBe(true);
|
|
49
|
+
expect(el.classList.contains('empty')).toBe(true);
|
|
50
|
+
expect(el.getAttribute('role')).toBe('group');
|
|
51
|
+
expect(el.children.length).toBe(0);
|
|
52
|
+
host.remove();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await it('defaults `side` to start, and `empty` to TRUE', () => {
|
|
56
|
+
// :68 — PROP_SIDE's default is GTK_PACK_START; :618 — `empty` defaults TRUE.
|
|
57
|
+
const { el, host } = mount();
|
|
58
|
+
expect(el.side).toBe('start');
|
|
59
|
+
expect(el.empty).toBe(true);
|
|
60
|
+
host.remove();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await describe('<gtk-window-controls> the decoration layout', async () => {
|
|
65
|
+
await it('builds the three GNOME buttons inside a window, on the end half', async () => {
|
|
66
|
+
// GNOME's layout is `menu:minimize,maximize,close`, and `menu` is the application
|
|
67
|
+
// menu: the START half names a token GTK's loop does not build a button for
|
|
68
|
+
// (gtkwindowcontrols.c:299-393), so a start-side bar is empty by construction.
|
|
69
|
+
const { el, host } = mount({ side: 'end' }, {});
|
|
70
|
+
expect(el.empty).toBe(false);
|
|
71
|
+
expect(el.classList.contains('empty')).toBe(false);
|
|
72
|
+
expect(children(el)).toStrictEqual(['minimize', 'maximize', 'close']);
|
|
73
|
+
host.remove();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await it('a layout with no colon is the SAME half on both sides', async () => {
|
|
77
|
+
// gtkwindowcontrols.c:150-158 — one token list, both sides, verbatim.
|
|
78
|
+
const start = mount({ 'decoration-layout': 'close' }, {});
|
|
79
|
+
expect(children(start.el)).toStrictEqual(['close']);
|
|
80
|
+
start.host.remove();
|
|
81
|
+
const end = mount({ 'decoration-layout': 'close', side: 'end' }, {});
|
|
82
|
+
expect(children(end.el)).toStrictEqual(['close']);
|
|
83
|
+
end.host.remove();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await it("GNOME's own layout leaves a START-side bar empty", async () => {
|
|
87
|
+
// `menu:minimize,maximize,close` — the application menu is the start half, and
|
|
88
|
+
// GTK's loop builds no button for a token it does not name
|
|
89
|
+
// (gtkwindowcontrols.c:299-393, :416-419).
|
|
90
|
+
const { el, host } = mount({}, {});
|
|
91
|
+
expect(children(el)).toStrictEqual([]);
|
|
92
|
+
expect(el.empty).toBe(true);
|
|
93
|
+
host.remove();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await it('`side` picks the half after the first colon', async () => {
|
|
97
|
+
// gtkwindowcontrols.c:141-166 — the START half is what is BEFORE the first
|
|
98
|
+
// colon, so the buttons are written there and the menu section after.
|
|
99
|
+
const layout = { 'decoration-layout': 'minimize,close:menu' };
|
|
100
|
+
const start = mount(layout, {});
|
|
101
|
+
expect(children(start.el)).toStrictEqual(['minimize', 'close']);
|
|
102
|
+
start.host.remove();
|
|
103
|
+
const end = mount({ ...layout, side: 'end' }, {});
|
|
104
|
+
expect(children(end.el)).toStrictEqual([]);
|
|
105
|
+
end.host.remove();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await it('an empty layout is the no-layout case, which is empty', async () => {
|
|
109
|
+
// gtkwindowcontrols.c:140-144 — an empty string is NULL, and NULL is empty.
|
|
110
|
+
const { el, host } = mount({ 'decoration-layout': '' }, {});
|
|
111
|
+
expect(el.empty).toBe(true);
|
|
112
|
+
host.remove();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await it('a token nothing knows builds nothing', async () => {
|
|
116
|
+
// The tail of the loop (:416-419) leaves `button` NULL for an unknown token.
|
|
117
|
+
const { el, host } = mount({ 'decoration-layout': 'menu,close' }, {});
|
|
118
|
+
expect(children(el)).toStrictEqual(['close']);
|
|
119
|
+
host.remove();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await describe('<gtk-window-controls> the window it reads', async () => {
|
|
124
|
+
await it('drops minimize and the icon on a modal window', async () => {
|
|
125
|
+
// gtkwindowcontrols.c:272-273, :299 and :318 — `is_sovereign_window` gates both.
|
|
126
|
+
const { el, host } = mount({ 'decoration-layout': 'icon,minimize,close' }, { modal: 'true' });
|
|
127
|
+
expect(children(el)).toStrictEqual(['close']);
|
|
128
|
+
host.remove();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await it('drops minimize and the icon on a transient window', async () => {
|
|
132
|
+
const { el, host } = mount(
|
|
133
|
+
{ 'decoration-layout': 'icon,minimize,close' },
|
|
134
|
+
{
|
|
135
|
+
'transient-for': 'main',
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
expect(children(el)).toStrictEqual(['close']);
|
|
139
|
+
host.remove();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await it('drops the icon when the window has none to show', async () => {
|
|
143
|
+
// update_window_icon (:187-210) leaves the icon out when there is no paintable.
|
|
144
|
+
const bare = mount({ 'decoration-layout': 'icon,close' }, {});
|
|
145
|
+
expect(children(bare.el)).toStrictEqual(['close']);
|
|
146
|
+
bare.host.remove();
|
|
147
|
+
const named = mount({ 'decoration-layout': 'icon,close' }, { 'icon-name': 'utilities-terminal' });
|
|
148
|
+
expect(children(named.el)).toStrictEqual(['icon', 'close']);
|
|
149
|
+
named.host.remove();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await it('drops the maximize button while the window is not resizable', async () => {
|
|
153
|
+
// gtkwindowcontrols.c:342-346 — `resizable` is the only test on this token.
|
|
154
|
+
const { el, host } = mount({ side: 'end' }, { resizable: 'false' });
|
|
155
|
+
expect(children(el)).toStrictEqual(['minimize', 'close']);
|
|
156
|
+
host.remove();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await it('swaps maximize for restore while the window is maximized', async () => {
|
|
160
|
+
// gtkwindowcontrols.c:347-350 — the glyph AND the label follow the state.
|
|
161
|
+
const { el, host } = mount({ side: 'end' }, { maximized: 'true' });
|
|
162
|
+
const button = el.querySelector('.maximize') as HTMLButtonElement;
|
|
163
|
+
expect(button.getAttribute('aria-label')).toBe('Restore');
|
|
164
|
+
expect(button.querySelector('gtk-image')?.getAttribute('icon-name')).toBe('window-restore-symbolic');
|
|
165
|
+
host.remove();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
await it('drops the close button while the window is not deletable', async () => {
|
|
169
|
+
const { el, host } = mount({ side: 'end' }, { deletable: 'false' });
|
|
170
|
+
expect(children(el)).toStrictEqual(['minimize', 'maximize']);
|
|
171
|
+
host.remove();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
await it('rebuilds when one of the six watched window properties moves', async () => {
|
|
175
|
+
// window_notify_cb (gtkwindowcontrols.c:428-441) names exactly these six.
|
|
176
|
+
const { el, host } = mount({ side: 'end' }, { deletable: 'false' });
|
|
177
|
+
const win = el.closest('gtk-window') as GtkWindow;
|
|
178
|
+
win.deletable = true;
|
|
179
|
+
await Promise.resolve();
|
|
180
|
+
await Promise.resolve();
|
|
181
|
+
expect(children(el)).toStrictEqual(['minimize', 'maximize', 'close']);
|
|
182
|
+
host.remove();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await it('takes the layout off the frame when it has none of its own', async () => {
|
|
186
|
+
// `get_layout`'s second stop, the port's stand-in for Gtk.Settings (:129-140).
|
|
187
|
+
const { el, host } = mount({ side: 'end' }, { 'decoration-layout': ':close' });
|
|
188
|
+
expect(children(el)).toStrictEqual(['close']);
|
|
189
|
+
host.remove();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await describe('<gtk-window-controls> events', async () => {
|
|
194
|
+
await it('a press asks for the action its GtkActionable name names', async () => {
|
|
195
|
+
const { el, host } = mount({ 'decoration-layout': 'minimize,close', side: 'end' }, {});
|
|
196
|
+
const actions: unknown[] = [];
|
|
197
|
+
el.addEventListener('window-action', (e) => actions.push((e as CustomEvent).detail));
|
|
198
|
+
(el.querySelector('.minimize') as HTMLButtonElement).click();
|
|
199
|
+
(el.querySelector('.close') as HTMLButtonElement).click();
|
|
200
|
+
expect(actions).toStrictEqual([{ action: 'minimize' }, { action: 'close' }]);
|
|
201
|
+
host.remove();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await it('`empty` notifies only when it moves', async () => {
|
|
205
|
+
// set_empty (gtkwindowcontrols.c:216-230) returns early on an unchanged value.
|
|
206
|
+
const { el, host } = mount({ side: 'end' }, {});
|
|
207
|
+
const events: unknown[] = [];
|
|
208
|
+
el.addEventListener('notify::empty', (e) => events.push((e as CustomEvent).detail));
|
|
209
|
+
const win = el.closest('gtk-window') as GtkWindow;
|
|
210
|
+
win.setAttribute('decoration-layout', '');
|
|
211
|
+
win.setAttribute('decoration-layout', '');
|
|
212
|
+
await Promise.resolve();
|
|
213
|
+
await Promise.resolve();
|
|
214
|
+
win.setAttribute('decoration-layout', ':close');
|
|
215
|
+
await Promise.resolve();
|
|
216
|
+
await Promise.resolve();
|
|
217
|
+
expect(events).toStrictEqual([{ empty: true }, { empty: false }]);
|
|
218
|
+
host.remove();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await it('a `side` write notifies with the property, not the attribute', async () => {
|
|
222
|
+
const { el, host } = mount({ side: 'start' }, {});
|
|
223
|
+
const events: unknown[] = [];
|
|
224
|
+
el.addEventListener('notify::side', (e) => events.push((e as CustomEvent).detail));
|
|
225
|
+
el.side = 'start';
|
|
226
|
+
el.side = 'end';
|
|
227
|
+
el.side = 'end';
|
|
228
|
+
expect(events).toStrictEqual([{ side: 'end' }]);
|
|
229
|
+
host.remove();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
};
|