@gjsify/adwaita-web 0.54.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 +8066 -3033
- 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 +1 -1
- 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/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,509 @@
|
|
|
1
|
+
// <gtk-media-controls> — the transport bar under a video: play/pause, position, duration,
|
|
2
|
+
// volume. It owns NO playback. `GtkMediaControls` has exactly one GIR property and it is
|
|
3
|
+
// `media-stream`, an OBJECT (gtkmediacontrols.c:296-306) — so on this renderer the element
|
|
4
|
+
// has no scalar surface to observe at all, and every control it draws is a VIEW of the
|
|
5
|
+
// stream it was handed. That is a fact about the widget, not a shortfall in the port, and it
|
|
6
|
+
// is why this file has no `observedAttributes`.
|
|
7
|
+
//
|
|
8
|
+
// THE NODE TREE IS THE TEMPLATE, VERBATIM. refs/gtk/gtk/ui/gtkmediacontrols.ui builds
|
|
9
|
+
// box (a GtkBox, `sensitive` 0, `spacing` 6)
|
|
10
|
+
// ├── play_button (GtkButton, `has-frame` 0, media-playback-start-symbolic)
|
|
11
|
+
// ├── time_box (GtkBox)
|
|
12
|
+
// │ ├── time_label (GtkLabel, `font-features "tnum=1"`)
|
|
13
|
+
// │ ├── seek_scale (GtkScale, hexpand, aria-label "Position")
|
|
14
|
+
// │ └── duration_label (GtkLabel, `font-features "tnum=1"`)
|
|
15
|
+
// └── volume_button (GtkVolumeButton, aria-label "Volume")
|
|
16
|
+
// Every node above is a node here, in that order, with those two classes on it.
|
|
17
|
+
//
|
|
18
|
+
// `sensitive` 0 ON THE WHOLE BOX IS THE FIRST THING TO NOTICE. The template disables the
|
|
19
|
+
// entire container (ui/gtkmediacontrols.ui), and `gtk_media_controls_set_sensitive` is what
|
|
20
|
+
// re-enables it — the bar is inert until a stream is attached. So this element renders its
|
|
21
|
+
// controls INSENSITIVE by default and only becomes live once it has a media element to
|
|
22
|
+
// drive, which is the same observable consequence the C has: `gtk_media_controls_new()`
|
|
23
|
+
// produces a bar you cannot press.
|
|
24
|
+
//
|
|
25
|
+
// THE TIME FORMAT IS `totem_time_to_string`, AND IT IS NOT `Date`. The function
|
|
26
|
+
// (gtkmediacontrols.c:75-132) formats in seconds from a microsecond count, adds one second
|
|
27
|
+
// when it is showing what REMAINS, and then picks one of four shapes:
|
|
28
|
+
//
|
|
29
|
+
// • `hour > 0 || force_hour` and NOT remaining → `%d:%02d:%02d` ("9:05:02")
|
|
30
|
+
// • `hour > 0 || force_hour` and remaining → `-%d:%02d:%02d` ("-9:05:02")
|
|
31
|
+
// • remaining → `-%d:%02d` ("-5:02")
|
|
32
|
+
// • otherwise → `%d:%02d` ("5:02")
|
|
33
|
+
//
|
|
34
|
+
// Three details in that are load-bearing and all three are in the port. `remaining` bumps
|
|
35
|
+
// the SECOND COUNT UP by one (`:81-82`) because the C wants `current + remaining = total`
|
|
36
|
+
// to hold exactly; the hour/minute split uses INTEGER division on the already-decremented
|
|
37
|
+
// remainder, not a floating-point `mm:ss` (`:86-88`); and the two long formats exist only
|
|
38
|
+
// when an HOUR is present or forced, so a two-hour video shows `2:00:00` while a
|
|
39
|
+
// one-hour-fifty video shows `1:50:00` and a one-minute video shows `1:02`. GTK calls it
|
|
40
|
+
// with `force_hour` FALSE everywhere (`:358, :364, :389`), so nothing here forces it — the
|
|
41
|
+
// parameter is kept because it is part of the function's own contract and a port that
|
|
42
|
+
// dropped it would be unable to answer what it is for.
|
|
43
|
+
//
|
|
44
|
+
// WHICH LABEL SHOWS WHAT CHANGES WITH THE DURATION, AND THAT IS NOT A TASTE DECISION.
|
|
45
|
+
// With no duration yet, the DURATION label shows the elapsed time
|
|
46
|
+
// (`totem_time_to_string (timestamp, FALSE, FALSE)`, :358-359) — the position takes the
|
|
47
|
+
// single label, because there is no total to compare it against. Once a duration is known
|
|
48
|
+
// the DURATION label shows what REMAINS, negatively, and is VISIBLE only when the duration
|
|
49
|
+
// is greater than zero (`:389-391`). So "duration label" is a misnomer: it is the remaining
|
|
50
|
+
// label, and the position label is the elapsed one.
|
|
51
|
+
//
|
|
52
|
+
// THE SEEK SCALE'S ADJUSTMENT IS NOT 0…1. `time_adjustment` has `upper` 10 and
|
|
53
|
+
// `step-increment` 1 and `page-increment` 10 (ui/gtkmediacontrols.ui), and every real
|
|
54
|
+
// duration arrives through `gtk_media_controls_set_duration`, which resets the adjustment's
|
|
55
|
+
// range to the media's own — so the numbers on the scale are SECONDS, and
|
|
56
|
+
// `time_adjustment_changed` writes the position from them. That is why the `<input
|
|
57
|
+
// type="range">` here is `min="0"`, `max` set to the duration in seconds and `step="1"`,
|
|
58
|
+
// and why a range with no duration is disabled rather than scaled to a default of 10: the
|
|
59
|
+
// template's `upper` 10 is a placeholder for the pre-stream state, not a range a user can
|
|
60
|
+
// seek within.
|
|
61
|
+
//
|
|
62
|
+
// VOLUME IS 0…1 AND IT IS MUTED BY ZERO. `volume_adjustment` has `upper` 1,
|
|
63
|
+
// `step-increment` 0.1, `page-increment` 1 and `value` 1 (ui/gtkmediacontrols.ui) — full
|
|
64
|
+
// scale, tenth steps, and FULL volume as the initial value. A `GtkVolumeButton`'s icon is
|
|
65
|
+
// derived from that value by GTK itself rather than by this template, which is why the icon
|
|
66
|
+
// here changes with the level the way it does in C: the element picks the one of four
|
|
67
|
+
// `audio-volume-*` names `GtkVolumeButton` would, and does nothing else with it.
|
|
68
|
+
//
|
|
69
|
+
// AND THE FOUR NAMES ARE THE SYMBOLIC ONES, IN AN ORDER THAT IS NOT THE ORDER OF THE
|
|
70
|
+
// LEVELS. `GtkVolumeButton:use-symbolic` defaults TRUE (gtkvolumebutton.c:186-188) and is a
|
|
71
|
+
// CONSTRUCT property, so `gtk_volume_button_set_property` swaps the template's list for
|
|
72
|
+
// `icons_symbolic` (`:142-148`) before anything reads it — the names in play are
|
|
73
|
+
// `audio-volume-{muted,high,low,medium}-symbolic` (`:74-77`), in THAT order. It is a
|
|
74
|
+
// `GtkScaleButton`, and `gtk_scale_button_update_icon` (gtkscalebutton.c:1066-1104) indexes
|
|
75
|
+
// that list: the exact `lower` takes `[0]`, the exact `upper` takes `[1]`, and everything
|
|
76
|
+
// between is `(guint)((value − lower) / step) + 2` with
|
|
77
|
+
// `step = (upper − lower) / (num_icons − 2)` — which for 0…1 and four icons is a HALF. So
|
|
78
|
+
// `high` belongs to full scale alone, `muted` to silence alone, and `low`/`medium` split at
|
|
79
|
+
// one half, not at thirds. `normalizeIconName` accepts either spelling, and the emitted
|
|
80
|
+
// names keep the suffix the C's own list carries.
|
|
81
|
+
//
|
|
82
|
+
// Reference: refs/gtk/gtk/ui/gtkmediacontrols.ui (the whole node tree and both adjustments)
|
|
83
|
+
// Reference: refs/gtk/gtk/gtkmediacontrols.c (`totem_time_to_string` :75-132,
|
|
84
|
+
// `update_timestamp` :352-366, `update_duration` :380-392, the one object property
|
|
85
|
+
// :296-306, `play_button_clicked`)
|
|
86
|
+
// Copyright (c) The GTK Team. LGPLv2.1+.
|
|
87
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web.
|
|
88
|
+
|
|
89
|
+
// The play/pause pair `ui/gtkvideo.ui`'s `play_button` carries, as constants for the same
|
|
90
|
+
// reason the video's four overlay glyphs are constants — `check-adwaita-icon-masks.mjs` finds
|
|
91
|
+
// an emitted name through a SCREAMING_SNAKE `…ICON` constant holding a plain literal, and a
|
|
92
|
+
// ternary of two literals inside a `setAttribute` is invisible to it.
|
|
93
|
+
const PLAY_ICON = 'media-playback-start-symbolic';
|
|
94
|
+
/** The playing state, and the only pause glyph either element draws. */
|
|
95
|
+
const PAUSE_ICON = 'media-playback-pause-symbolic';
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The four `audio-volume-*` names `GtkVolumeButton` derives from a 0…1 level, one constant
|
|
99
|
+
* each rather than an array of them, in the ORDER `icons_symbolic` holds them
|
|
100
|
+
* (gtkvolumebutton.c:74-77) — which is muted, HIGH, low, medium, and not the order of the
|
|
101
|
+
* levels.
|
|
102
|
+
*
|
|
103
|
+
* The shape is load-bearing, not stylistic. `check-adwaita-icon-masks.mjs` reads an emitted
|
|
104
|
+
* icon name out of the SOURCE with a regex for a SCREAMING_SNAKE constant whose name ends in
|
|
105
|
+
* `ICON` or `ICON_NAME` and whose value is a plain string literal — that is how
|
|
106
|
+
* `PASSWORD_REVEAL_ICON_NAME` and the about dialog's glyph are found. Four literals inside an
|
|
107
|
+
* array are invisible to it, so its second arm would report four ICONS entries nothing emits
|
|
108
|
+
* while all four really are emitted. The `-symbolic` suffix is likewise kept on the value:
|
|
109
|
+
* `normalizeIconName` accepts either spelling, and dropping it buys nothing.
|
|
110
|
+
*/
|
|
111
|
+
export const VOLUME_MUTED_ICON = 'audio-volume-muted-symbolic';
|
|
112
|
+
/** The FULL-SCALE name — `[1]` in the list, so only the exact `upper` reaches it. */
|
|
113
|
+
export const VOLUME_HIGH_ICON = 'audio-volume-high-symbolic';
|
|
114
|
+
/** Below a half — `[2]`, the lower of the two middle names. */
|
|
115
|
+
export const VOLUME_LOW_ICON = 'audio-volume-low-symbolic';
|
|
116
|
+
/** From a half up to — but not at — full scale — `[3]`. */
|
|
117
|
+
export const VOLUME_MEDIUM_ICON = 'audio-volume-medium-symbolic';
|
|
118
|
+
|
|
119
|
+
/** `volume_adjustment`'s `lower` (ui/gtkmediacontrols.ui leaves it at the `GtkAdjustment`
|
|
120
|
+
* default of 0) and its `upper` 1 — the two ends `gtk_scale_button_update_icon` tests for. */
|
|
121
|
+
const VOLUME_LOWER = 0;
|
|
122
|
+
const VOLUME_UPPER = 1;
|
|
123
|
+
/** How many names `icons_symbolic` carries — `num_icons` in `gtk_scale_button_update_icon`. */
|
|
124
|
+
const VOLUME_ICON_COUNT = 4;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The icon `GtkVolumeButton` shows at `volume` (0…1), as `gtk_scale_button_update_icon`
|
|
128
|
+
* derives it (gtkscalebutton.c:1086-1104): the exact `lower` is `[0]` (muted), the exact
|
|
129
|
+
* `upper` is `[1]` (high), and a value between them indexes `[2]`/`[3]` by
|
|
130
|
+
* `(guint)((value − lower) / step) + 2` with `step = (upper − lower) / (num_icons − 2)` — a
|
|
131
|
+
* HALF for a 0…1 adjustment over four icons.
|
|
132
|
+
*/
|
|
133
|
+
export function volumeIconFor(volume: number): string {
|
|
134
|
+
const value = Math.min(VOLUME_UPPER, Math.max(VOLUME_LOWER, Number.isFinite(volume) ? volume : 1));
|
|
135
|
+
if (value === VOLUME_LOWER) return VOLUME_MUTED_ICON;
|
|
136
|
+
if (value === VOLUME_UPPER) return VOLUME_HIGH_ICON;
|
|
137
|
+
const step = (VOLUME_UPPER - VOLUME_LOWER) / (VOLUME_ICON_COUNT - 2);
|
|
138
|
+
// The C casts the quotient to `guint`, which truncates toward zero; the value is already
|
|
139
|
+
// inside the range here, so that is the same as a floor.
|
|
140
|
+
return Math.trunc((value - VOLUME_LOWER) / step) + 2 === 2 ? VOLUME_LOW_ICON : VOLUME_MEDIUM_ICON;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* `totem_time_to_string` (gtkmediacontrols.c:75-132), in seconds.
|
|
145
|
+
*
|
|
146
|
+
* The `remaining` bump is the C's own (`:81-82`) and it is not a rounding fudge: the C
|
|
147
|
+
* computes the remaining time as `duration - timestamp` and then increments `_time` — the
|
|
148
|
+
* WHOLE TOTAL, not the seconds field — so that `current + remaining = total` holds for a
|
|
149
|
+
* whole second of playback rather than being one second short at every boundary. That is why
|
|
150
|
+
* 300 s of remaining time reads `-5:01` and not `-5:00`: the seconds field only appears to
|
|
151
|
+
* move when the input's own seconds are zero, and the increment carries at 59.
|
|
152
|
+
*
|
|
153
|
+
* NOTHING HERE FLOORS A NEGATIVE, because the C does not. `_time % 60` in C99 keeps the sign
|
|
154
|
+
* of the dividend, so five seconds negative is zero minutes and `-5` seconds and the
|
|
155
|
+
* function returns the nonsense `0:-5`; the C's CALLERS guard the subtraction instead
|
|
156
|
+
* (`duration > timestamp ? duration - timestamp : 0`, :364), and so does every call site
|
|
157
|
+
* here. The C's arithmetic is transcribed rather than repaired, so a caller that skips the
|
|
158
|
+
* guard sees what GTK would have shown.
|
|
159
|
+
*
|
|
160
|
+
* The hour branch is `hour > 0 || force_hour` — an ELEVEN-MINUTE video is `11:03`, not
|
|
161
|
+
* `0:11:03` — and `force_hour` is FALSE at every call site in the C (`:358, :364, :389`).
|
|
162
|
+
* It stays a parameter because it is part of the function's contract, not because anything
|
|
163
|
+
* here passes it.
|
|
164
|
+
*
|
|
165
|
+
* @param seconds total or remaining time, in seconds; a negative is NOT floored here, exactly
|
|
166
|
+
* as it is not in the C — see above.
|
|
167
|
+
*/
|
|
168
|
+
export function mediaTimeToString(seconds: number, remaining = false, forceHour = false): string {
|
|
169
|
+
let time = Math.trunc(seconds);
|
|
170
|
+
if (remaining) time++;
|
|
171
|
+
|
|
172
|
+
const sec = time % 60;
|
|
173
|
+
time -= sec;
|
|
174
|
+
const min = (time % 3600) / 60;
|
|
175
|
+
time -= min * 60;
|
|
176
|
+
const hour = time / 3600;
|
|
177
|
+
|
|
178
|
+
if (hour > 0 || forceHour) {
|
|
179
|
+
// `%d:%02d:%02d` and its negated form — the two "long time format" cases.
|
|
180
|
+
return remaining
|
|
181
|
+
? `-${hour}:${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}`
|
|
182
|
+
: `${hour}:${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
|
183
|
+
}
|
|
184
|
+
if (remaining) return `-${min}:${String(sec).padStart(2, '0')}`;
|
|
185
|
+
return `${min}:${String(sec).padStart(2, '0')}`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export class GtkMediaControls extends HTMLElement {
|
|
189
|
+
private _initialized = false;
|
|
190
|
+
private _playButton!: HTMLButtonElement;
|
|
191
|
+
private _timeLabel!: HTMLSpanElement;
|
|
192
|
+
private _seekScale!: HTMLInputElement;
|
|
193
|
+
private _durationLabel!: HTMLSpanElement;
|
|
194
|
+
private _volumeButton!: HTMLButtonElement;
|
|
195
|
+
/** The icon node INSIDE the volume button — the popover is its sibling child. */
|
|
196
|
+
private _volumeIcon!: HTMLSpanElement;
|
|
197
|
+
private _volumePopover!: HTMLElement;
|
|
198
|
+
private _volumeScale!: HTMLInputElement;
|
|
199
|
+
private _media: HTMLVideoElement | null = null;
|
|
200
|
+
/** Whether the user is dragging the seek scale — the C's own re-entrancy guard. */
|
|
201
|
+
private _seeking = false;
|
|
202
|
+
private _volume = 1;
|
|
203
|
+
|
|
204
|
+
connectedCallback() {
|
|
205
|
+
if (this._initialized) {
|
|
206
|
+
this._adoptMedia();
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this._initialized = true;
|
|
210
|
+
|
|
211
|
+
// `box` — the template's GtkBox, `sensitive` 0 and `spacing` 6
|
|
212
|
+
// (ui/gtkmediacontrols.ui). `insensitive` is the state the C's `sensitive` 0 puts
|
|
213
|
+
// it in, and it is what makes every control inside inert until a stream is attached.
|
|
214
|
+
const box = document.createElement('div');
|
|
215
|
+
box.className = 'adw-media-controls-box';
|
|
216
|
+
|
|
217
|
+
// `play_button` — GtkButton with `has-frame` 0, which is libadwaita's flat button.
|
|
218
|
+
this._playButton = document.createElement('button');
|
|
219
|
+
this._playButton.className = 'adw-media-controls-button adw-flat';
|
|
220
|
+
this._playButton.type = 'button';
|
|
221
|
+
this._playButton.title = 'Play';
|
|
222
|
+
this._playButton.setAttribute('aria-label', 'Play');
|
|
223
|
+
this._playButton.addEventListener('click', () => this._togglePlay());
|
|
224
|
+
|
|
225
|
+
// `time_box` — the middle GtkBox holding the two labels and the scale between them.
|
|
226
|
+
const timeBox = document.createElement('div');
|
|
227
|
+
timeBox.className = 'adw-media-controls-time-box';
|
|
228
|
+
|
|
229
|
+
// Both labels carry `font-features "tnum=1"` (ui/gtkmediacontrols.ui): tabular
|
|
230
|
+
// figures, so a position that counts 9→10 does not shift the scale sideways. That is
|
|
231
|
+
// `_media_controls.scss`'s `font-variant-numeric: tabular-nums`, and it is the reason
|
|
232
|
+
// this is a real class rather than a comment.
|
|
233
|
+
this._timeLabel = document.createElement('span');
|
|
234
|
+
this._timeLabel.className = 'adw-media-controls-time-label';
|
|
235
|
+
|
|
236
|
+
// `seek_scale` — the GtkScale. `min` is the adjustment's 0 and `max` starts at the
|
|
237
|
+
// template's placeholder 10 (ui/gtkmediacontrols.ui), replaced by the real duration
|
|
238
|
+
// the moment one is known; `step` is the adjustment's `step-increment` of 1 second.
|
|
239
|
+
this._seekScale = document.createElement('input');
|
|
240
|
+
this._seekScale.className = 'adw-media-controls-seek-scale';
|
|
241
|
+
this._seekScale.type = 'range';
|
|
242
|
+
this._seekScale.min = '0';
|
|
243
|
+
this._seekScale.max = '10';
|
|
244
|
+
this._seekScale.step = '1';
|
|
245
|
+
this._seekScale.value = '0';
|
|
246
|
+
// The template's own accessibility block: `label` "Position" on the scale.
|
|
247
|
+
this._seekScale.setAttribute('aria-label', 'Position');
|
|
248
|
+
this._seekScale.addEventListener('input', () => this._onSeekInput());
|
|
249
|
+
|
|
250
|
+
this._durationLabel = document.createElement('span');
|
|
251
|
+
this._durationLabel.className = 'adw-media-controls-duration-label';
|
|
252
|
+
|
|
253
|
+
timeBox.append(this._timeLabel, this._seekScale, this._durationLabel);
|
|
254
|
+
|
|
255
|
+
// `volume_button` — a GtkVolumeButton, which is a GtkMenuButton whose popover holds
|
|
256
|
+
// the volume adjustment. Built as a button plus its own popover rather than
|
|
257
|
+
// `<gtk-menu-button>` so the popover is this widget's own surface and no second
|
|
258
|
+
// element's open/close machinery is involved.
|
|
259
|
+
this._volumeButton = document.createElement('button');
|
|
260
|
+
this._volumeButton.className = 'adw-media-controls-button adw-media-controls-volume-button adw-flat';
|
|
261
|
+
this._volumeButton.type = 'button';
|
|
262
|
+
// The template's own accessibility block: `label` "Volume" on the volume button.
|
|
263
|
+
this._volumeButton.setAttribute('aria-label', 'Volume');
|
|
264
|
+
this._volumeButton.addEventListener('click', () => this._toggleVolumePopover());
|
|
265
|
+
this._volumeIcon = document.createElement('span');
|
|
266
|
+
this._volumeIcon.className = 'adw-media-controls-volume-icon';
|
|
267
|
+
|
|
268
|
+
// The popover holds `volume_adjustment`: `upper` 1, `step-increment` 0.1,
|
|
269
|
+
// `page-increment` 1, `value` 1 (ui/gtkmediacontrols.ui).
|
|
270
|
+
// A real `<gtk-popover>`, not a div wearing a borrowed class: `_popover.scss` is the
|
|
271
|
+
// ONE popover surface in this package and its header records that three hand-rolled
|
|
272
|
+
// copies of the plate disagreed with libadwaita and with each other before it was
|
|
273
|
+
// lifted out. `GtkVolumeButton`'s adjustment popover is exactly that surface, so the
|
|
274
|
+
// volume slide goes in one.
|
|
275
|
+
this._volumePopover = document.createElement('gtk-popover');
|
|
276
|
+
this._volumePopover.className = 'adw-media-controls-volume-popover';
|
|
277
|
+
this._volumeScale = document.createElement('input');
|
|
278
|
+
this._volumeScale.className = 'adw-media-controls-volume-scale';
|
|
279
|
+
this._volumeScale.type = 'range';
|
|
280
|
+
this._volumeScale.min = '0';
|
|
281
|
+
this._volumeScale.max = '1';
|
|
282
|
+
this._volumeScale.step = '0.1';
|
|
283
|
+
this._volumeScale.value = '1';
|
|
284
|
+
this._volumeScale.setAttribute('aria-label', 'Volume');
|
|
285
|
+
this._volumeScale.addEventListener('input', () => this._onVolumeInput());
|
|
286
|
+
this._volumePopover.append(this._volumeScale);
|
|
287
|
+
// A click on the SLIDE is not a click on the ANCHOR: without this, dragging the
|
|
288
|
+
// volume would close the popover under the pointer on every input event.
|
|
289
|
+
this._volumePopover.addEventListener('click', (event) => event.stopPropagation());
|
|
290
|
+
|
|
291
|
+
this._volumeButton.append(this._volumeIcon, this._volumePopover);
|
|
292
|
+
|
|
293
|
+
box.append(this._playButton, timeBox, this._volumeButton);
|
|
294
|
+
this.replaceChildren(box);
|
|
295
|
+
this._renderVolume();
|
|
296
|
+
this._adoptMedia();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** The `<video>` these controls drive — the browser's answer to a `GtkMediaStream`. */
|
|
300
|
+
get media(): HTMLVideoElement | null {
|
|
301
|
+
return this._media;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** The current position in seconds. */
|
|
305
|
+
get timestamp(): number {
|
|
306
|
+
return this._media?.currentTime ?? 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** The media's duration in seconds, or 0 while unknown. */
|
|
310
|
+
get duration(): number {
|
|
311
|
+
const value = this._media?.duration ?? 0;
|
|
312
|
+
return Number.isFinite(value) ? value : 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** The volume level, 0…1, matching `volume_adjustment`'s range. */
|
|
316
|
+
get volume(): number {
|
|
317
|
+
return this._volume;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
set volume(value: number) {
|
|
321
|
+
this._setVolume(value);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Whether the bar is live — the C's `gtk_media_controls_set_sensitive`.
|
|
326
|
+
*
|
|
327
|
+
* Loosely typed on purpose: `_adoptMedia` coerces its optional chain to `null`, and this
|
|
328
|
+
* answers "is there a stream" for any field state rather than only the two the coercion
|
|
329
|
+
* produces. A `!== null` test here read `undefined` as a stream.
|
|
330
|
+
*/
|
|
331
|
+
get sensitive(): boolean {
|
|
332
|
+
return this._media != null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** `play_button_clicked` (ui/gtkmediacontrols.ui) — the button's only action. */
|
|
336
|
+
private _togglePlay(): void {
|
|
337
|
+
// `!= null`, not `!== null`: an `undefined` `_media` is "no stream" here for the same
|
|
338
|
+
// reason it is in `sensitive`.
|
|
339
|
+
const media = this._media ?? null;
|
|
340
|
+
if (media === null) return;
|
|
341
|
+
if (media.paused) void media.play().catch(() => {});
|
|
342
|
+
else media.pause();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* `time_adjustment_changed` (ui/gtkmediacontrols.ui): the scale's value IS the position,
|
|
347
|
+
* in seconds. The `_seeking` guard is the C's own — `update_timestamp` writes the scale
|
|
348
|
+
* from the stream, so without it a drag would fight the playback it is dragging through.
|
|
349
|
+
*/
|
|
350
|
+
private _onSeekInput(): void {
|
|
351
|
+
this._seeking = true;
|
|
352
|
+
if (this._media) this._media.currentTime = Number.parseFloat(this._seekScale.value);
|
|
353
|
+
this._seeking = false;
|
|
354
|
+
this._updateTimestamp();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private _onVolumeInput(): void {
|
|
358
|
+
this._setVolume(Number.parseFloat(this._volumeScale.value));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private _setVolume(value: number): void {
|
|
362
|
+
const level = Math.min(1, Math.max(0, Number.isFinite(value) ? value : 1));
|
|
363
|
+
this._volume = level;
|
|
364
|
+
if (this._media) this._media.volume = level;
|
|
365
|
+
this._renderVolume();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private _renderVolume(): void {
|
|
369
|
+
if (this._volumeScale) this._volumeScale.value = String(this._volume);
|
|
370
|
+
// The ICON node is replaced, never the button's children: the button also holds the
|
|
371
|
+
// popover (a `GtkVolumeButton` is a `GtkMenuButton`), and a `replaceChildren` here
|
|
372
|
+
// would take the volume slide with it on every level change.
|
|
373
|
+
const icon = document.createElement('gtk-image');
|
|
374
|
+
icon.setAttribute('icon-name', volumeIconFor(this._volume));
|
|
375
|
+
this._volumeIcon?.replaceChildren(icon);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private _toggleVolumePopover(): void {
|
|
379
|
+
if (!this._volumePopover) return;
|
|
380
|
+
this._volumePopover.hidden = !this._volumePopover.hidden;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* The media element these controls drive. A `<gtk-media-controls>` normally sits INSIDE a
|
|
385
|
+
* `<gtk-video>` (the template's `controls_revealer` child), so the nearest ancestor video
|
|
386
|
+
* is the stream — and a controls bar used on its own names one with a `for` attribute
|
|
387
|
+
* holding an id.
|
|
388
|
+
*
|
|
389
|
+
* The optional chain's `undefined` IS COERCED TO `null`, and that is not tidiness: `?? null`
|
|
390
|
+
* is what keeps `_media` in the domain `sensitive` and `_render` compare against. A bar with
|
|
391
|
+
* no enclosing video used to store `undefined` here, `undefined !== null` then reported the
|
|
392
|
+
* template's `sensitive` 0 bar as LIVE, and the `!this._media.paused` at the end of
|
|
393
|
+
* `_render` threw `can't access property "paused", this._media is undefined` — which is the
|
|
394
|
+
* uncaught error `tests/browser/specs/adwaita-upgrade-order.spec.ts` fails on. `null` is
|
|
395
|
+
* what `gtk_media_controls_new()` leaves behind: the template has no stream at all, and
|
|
396
|
+
* `sensitive` is 0 until one is set (ui/gtkmediacontrols.ui).
|
|
397
|
+
*/
|
|
398
|
+
private _adoptMedia(): void {
|
|
399
|
+
const id = this.getAttribute('for');
|
|
400
|
+
// Narrowed at BOTH lookups rather than after them: `getElementById` answers
|
|
401
|
+
// `HTMLElement | null` and `querySelector` answers `Element | null`, while the whole
|
|
402
|
+
// point of the media is that it has `currentTime`, `volume` and `play`. An id that
|
|
403
|
+
// names something else is no media at all, which is the same answer `null` gives.
|
|
404
|
+
const byId = id ? this.ownerDocument.getElementById(id) : null;
|
|
405
|
+
const media =
|
|
406
|
+
(byId instanceof HTMLVideoElement ? byId : null) ??
|
|
407
|
+
this.closest('gtk-video')?.querySelector<HTMLVideoElement>('video') ??
|
|
408
|
+
null;
|
|
409
|
+
if (media === this._media) {
|
|
410
|
+
this._render();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (this._media) this._detachMedia();
|
|
414
|
+
this._media = media;
|
|
415
|
+
if (media) {
|
|
416
|
+
this._mediaListeners = [
|
|
417
|
+
['timeupdate', () => this._updateTimestamp()],
|
|
418
|
+
['durationchange', () => this._updateDuration()],
|
|
419
|
+
['play', () => this._updatePlaying(true)],
|
|
420
|
+
['pause', () => this._updatePlaying(false)],
|
|
421
|
+
['seeked', () => this._updateTimestamp()],
|
|
422
|
+
];
|
|
423
|
+
for (const [type, handler] of this._mediaListeners) media.addEventListener(type, handler);
|
|
424
|
+
// The adjustment's `value` is 1 (ui/gtkmediacontrols.ui) and the C applies the
|
|
425
|
+
// adjustment's own value to the stream when a stream is attached, so full volume
|
|
426
|
+
// is the state a freshly attached bar starts in.
|
|
427
|
+
this._setVolume(media.volume);
|
|
428
|
+
}
|
|
429
|
+
this._render();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** The listeners bound on `_media`, so `_detachMedia` removes the SAME ones. */
|
|
433
|
+
private _mediaListeners: [string, EventListener][] = [];
|
|
434
|
+
|
|
435
|
+
private _detachMedia(): void {
|
|
436
|
+
if (this._media) {
|
|
437
|
+
for (const [type, handler] of this._mediaListeners) this._media.removeEventListener(type, handler);
|
|
438
|
+
}
|
|
439
|
+
this._mediaListeners = [];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* `update_timestamp` (gtkmediacontrols.c:352-366). TWO cases and they are not
|
|
444
|
+
* symmetric: with no duration, the POSITION label shows the elapsed time and the
|
|
445
|
+
* DURATION label is hidden (`:358-359`); with a duration, the DURATION label shows the
|
|
446
|
+
* NEGATIVE remaining time and is visible only when the duration is greater than zero
|
|
447
|
+
* (`:389-391`).
|
|
448
|
+
*/
|
|
449
|
+
private _updateTimestamp(): void {
|
|
450
|
+
if (!this._initialized) return;
|
|
451
|
+
const timestamp = this.timestamp;
|
|
452
|
+
const duration = this.duration;
|
|
453
|
+
// The scale follows the stream unless the user has hold of it.
|
|
454
|
+
if (!this._seeking && duration > 0) this._seekScale.value = String(timestamp);
|
|
455
|
+
this._timeLabel.textContent = mediaTimeToString(timestamp);
|
|
456
|
+
this._durationLabel.textContent = mediaTimeToString(duration > timestamp ? duration - timestamp : 0, true);
|
|
457
|
+
this._durationLabel.hidden = duration <= 0;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** `gtk_media_controls_set_duration` (gtkmediacontrols.c:380-392): the scale's range is
|
|
461
|
+
* the media's own duration, and the `upper` 10 of the template is only the pre-stream
|
|
462
|
+
* placeholder. */
|
|
463
|
+
private _updateDuration(): void {
|
|
464
|
+
if (!this._initialized) return;
|
|
465
|
+
this._seekScale.max = String(this.duration > 0 ? this.duration : 10);
|
|
466
|
+
this._updateTimestamp();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** The play button's icon, `play_button_clicked`'s counterpart in `ui/gtkvideo.ui`'s
|
|
470
|
+
* `gtk_video_update_playing`: `media-playback-start-symbolic` stopped,
|
|
471
|
+
* `media-playback-pause-symbolic` playing. */
|
|
472
|
+
private _updatePlaying(playing: boolean): void {
|
|
473
|
+
if (!this._initialized) return;
|
|
474
|
+
const image = document.createElement('gtk-image');
|
|
475
|
+
// `ui/gtkvideo.ui`'s `play_button` carries `media-playback-start-symbolic`, and the
|
|
476
|
+
// pause glyph is its counterpart — the same two names the video's overlay icon uses,
|
|
477
|
+
// which is why the two elements share the glyphs rather than inventing their own.
|
|
478
|
+
image.setAttribute('icon-name', playing ? PAUSE_ICON : PLAY_ICON);
|
|
479
|
+
this._playButton.replaceChildren(image);
|
|
480
|
+
this._playButton.title = playing ? 'Pause' : 'Play';
|
|
481
|
+
this._playButton.setAttribute('aria-label', playing ? 'Pause' : 'Play');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* The whole bar's sensitivity. The template ships `sensitive` 0
|
|
486
|
+
* (ui/gtkmediacontrols.ui), so an unattached bar is inert — which is what `insensitive`
|
|
487
|
+
* carries here, and why the spec can assert it before any media is adopted.
|
|
488
|
+
*/
|
|
489
|
+
private _render(): void {
|
|
490
|
+
if (!this._initialized) return;
|
|
491
|
+
// One truth, one expression. The live test was spelled out a second time at the
|
|
492
|
+
// `!this._media.paused` call below, where an `_media` of `undefined` — which is what
|
|
493
|
+
// an optional chain hands back when there is no enclosing `<gtk-video>` — passes
|
|
494
|
+
// `!== null` and then throws on the property read. Deriving the playing state from the
|
|
495
|
+
// same narrowing makes that unrepresentable.
|
|
496
|
+
const media = this._media ?? null;
|
|
497
|
+
const live = media !== null;
|
|
498
|
+
this.classList.toggle('insensitive', !live);
|
|
499
|
+
this._playButton.disabled = !live;
|
|
500
|
+
this._seekScale.disabled = !live;
|
|
501
|
+
this._volumeButton.disabled = !live;
|
|
502
|
+
this._volumeScale.disabled = !live;
|
|
503
|
+
this._updateDuration();
|
|
504
|
+
this._updateTimestamp();
|
|
505
|
+
this._updatePlaying(live && !media.paused);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
customElements.define('gtk-media-controls', GtkMediaControls);
|