@gjsify/adwaita-web 0.54.0 → 0.56.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/README.md +8 -0
- package/dist/adwaita-web.css +8146 -3011
- 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-status-page.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/attributes.d.ts +19 -0
- package/lib/types/breakpoints.d.ts +14 -3
- package/lib/types/construct-vectors.spec.d.ts +1 -0
- package/lib/types/elements/adw-action-row.d.ts +4 -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-combo-row.d.ts +4 -0
- package/lib/types/elements/adw-dialog.d.ts +10 -0
- package/lib/types/elements/adw-entry-row.d.ts +6 -0
- package/lib/types/elements/adw-expander-row.d.ts +2 -0
- package/lib/types/elements/adw-header-bar.d.ts +6 -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-overlay-split-view.d.ts +13 -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 +8 -2
- package/lib/types/elements/adw-spin-row.d.ts +6 -0
- package/lib/types/elements/adw-switch-row.d.ts +7 -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-toggle-group.d.ts +17 -0
- 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 +129 -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 +103 -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/row-line-clamp.d.ts +20 -0
- package/lib/types/row-tooltip.d.ts +8 -0
- package/lib/types/row-tooltip.spec.d.ts +1 -0
- package/lib/types/scroll-shading.d.ts +15 -1
- package/lib/types/shared-tree-builder.d.ts +75 -17
- package/lib/types/shared-tree-mount.spec.d.ts +1 -0
- 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/lib/types/widget-size.spec.d.ts +1 -0
- package/package.json +12 -8
- package/scss/_action_row.scss +41 -17
- 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/_button_row.scss +5 -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/_expander_row.scss +36 -3
- 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 +134 -1
- 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 +60 -0
- package/scss/_print_dialogs.scss +328 -0
- package/scss/_reset.scss +21 -19
- package/scss/_revealer.scss +37 -0
- package/scss/_row.scss +36 -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/_status_page.scss +1 -1
- 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/_toggle_group.scss +28 -0
- 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/_widget.scss +14 -3
- package/scss/_window.scss +131 -2
- package/scss/_window_handle.scss +20 -0
- package/scss/adwaita-skin.scss +109 -0
- package/src/adw-action-rows.spec.ts +331 -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-header-bar.spec.ts +18 -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-status-page.spec.ts +124 -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/attributes.ts +52 -0
- package/src/breakpoints.ts +25 -8
- package/src/capabilities.d.mts +3 -0
- package/src/capabilities.mjs +21 -0
- package/src/connect-lifecycle.spec.ts +12 -0
- package/src/construct-vectors.spec.ts +175 -0
- package/src/elements/adw-action-row.ts +31 -2
- package/src/elements/adw-alert-dialog.ts +4 -1
- 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-button-row.ts +7 -1
- package/src/elements/adw-clamp-scrollable.ts +207 -0
- package/src/elements/adw-combo-row.ts +26 -1
- package/src/elements/adw-dialog.ts +19 -0
- package/src/elements/adw-entry-row.ts +22 -12
- package/src/elements/adw-expander-row.ts +34 -1
- package/src/elements/adw-header-bar.ts +18 -4
- package/src/elements/adw-layout-slot.ts +95 -0
- package/src/elements/adw-multi-layout-view.ts +326 -0
- package/src/elements/adw-overlay-split-view.ts +29 -6
- package/src/elements/adw-preferences-row.ts +153 -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 +74 -7
- package/src/elements/adw-spin-row.ts +37 -2
- package/src/elements/adw-status-page.ts +10 -2
- package/src/elements/adw-switch-row.ts +53 -2
- 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 +138 -14
- 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 +190 -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 +653 -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 +372 -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 +202 -0
- package/src/index.ts +9 -1
- package/src/keyboard-operable.spec.ts +77 -9
- 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/row-line-clamp.ts +74 -0
- package/src/row-tooltip.spec.ts +313 -0
- package/src/row-tooltip.ts +52 -0
- package/src/scroll-shading.ts +18 -1
- package/src/shared-tree-builder.ts +336 -41
- package/src/shared-tree-mount.spec.ts +130 -0
- package/src/shared-trees.spec.ts +15 -0
- 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 +160 -0
- package/src/widget-size.spec.ts +117 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
// <gtk-about-dialog> — GTK's own "About" window, the browser counterpart of
|
|
2
|
+
// `GtkAboutDialog`, and NOT the libadwaita one `<adw-about-dialog>` ports: this widget
|
|
3
|
+
// is a `GtkWindow` whose headerbar holds a `GtkStackSwitcher` over a four-page
|
|
4
|
+
// `GtkStack` — main, Credits, License, System — and whose main page is a column of
|
|
5
|
+
// centred labels under a dialog-size logo (gtkaboutdialog.ui).
|
|
6
|
+
//
|
|
7
|
+
// The DIALOG CHROME IS `<adw-dialog>`'s, not a copy of it: this element extends
|
|
8
|
+
// `AdwDialog`, so the full-cover scrim, the floating sheet, `present()`/`close()`,
|
|
9
|
+
// `can-close`, Escape, the Tab trap, the initial focus and the return-focus are the ones
|
|
10
|
+
// `adw-dialog.ts` already implements, and the only thing added here is the header hook
|
|
11
|
+
// (`decorateHeader`) that puts the switcher where GTK puts it — in the title position,
|
|
12
|
+
// with NO title text, exactly as the `.ui` declares. That is also why there is no
|
|
13
|
+
// `aria-modal` anywhere below: `elements/modal-surface.ts` owns the declaration.
|
|
14
|
+
//
|
|
15
|
+
// WHAT IS PORTED FROM `gtkaboutdialog.c`, AND WHERE IT SHOWS
|
|
16
|
+
//
|
|
17
|
+
// · `update_name_version` (:1066-1082): the window title is `About <program-name>`
|
|
18
|
+
// and the name label is BOLD; the version label is visible only when a version was
|
|
19
|
+
// set. An UNSET `program-name` falls back to `g_get_application_name()`, which in a
|
|
20
|
+
// document is `document.title` — the same "the application calls itself whatever
|
|
21
|
+
// its own name is" rule, and the reason the element shows something for a dialog
|
|
22
|
+
// that named nothing.
|
|
23
|
+
// · the three page-visibility rules: License exists only for
|
|
24
|
+
// `license-type == custom && license != NULL && license[0] != 0`
|
|
25
|
+
// (`update_license_button_visibility`, :686-696), System only for a non-empty
|
|
26
|
+
// `system-information` (:700-710), and Credits for any of authors / documenters /
|
|
27
|
+
// artists / credit sections / a `translator-credits` that is not an untranslated
|
|
28
|
+
// gettext sentinel (:700-717). The STACK SWITCHER is visible when any of the three
|
|
29
|
+
// is (`update_stack_switcher_visibility`, :657-670), which is why a dialog with
|
|
30
|
+
// none shows an EMPTY headerbar — GTK's own answer.
|
|
31
|
+
// · `set_license_type` (:2259-2313): any type other than `custom` OVERWRITES `license`
|
|
32
|
+
// with the warranty preamble, links it to that licence's URL (falling back to
|
|
33
|
+
// `website` for a licence whose row has no URL, which is what `unknown` does), and
|
|
34
|
+
// forces `wrap-license` TRUE. `gtk_license_info` (:126-146) is the table, held once
|
|
35
|
+
// in `@gjsify/adwaita-core` as `ADW_LICENSES`; the preamble is GTK's own string,
|
|
36
|
+
// which differs from libadwaita's in its first words and in the newline.
|
|
37
|
+
// · `stack_visible_child_notify` (:297-333): the Credits, License and System pages are
|
|
38
|
+
// populated LAZILY, the first time the stack shows them. A credits grid for 300
|
|
39
|
+
// authors is not built by a dialog that is never opened to it.
|
|
40
|
+
// · `populate_credits_page` (:2029-2080) order and headings — "Created by",
|
|
41
|
+
// "Documented by", "Translated by", "Design by", then the app's own sections — with
|
|
42
|
+
// the translator list split on newlines and the untranslated sentinel suppressed
|
|
43
|
+
// (`translatorCreditsPeople`, `creditsSections`).
|
|
44
|
+
// · `add_credits_section` (:1903-2027): a two-column grid, the heading in column 0
|
|
45
|
+
// right-aligned, each name in column 1, and a blank row after every section ("skip
|
|
46
|
+
// one at the end").
|
|
47
|
+
// · `text_buffer_new` (:1903-1990 in `gtkaboutdialog.c`; the License and System text
|
|
48
|
+
// views): `<http://…>` and `<mail@host>` angle-bracket links and bare `http(s)` URLs
|
|
49
|
+
// become clickable spans, everything else stays literal.
|
|
50
|
+
// · `emit_activate_link` (:646-655) + the `activate-link` accumulator: the signal is
|
|
51
|
+
// `BOOLEAN__STRING` with `_gtk_boolean_handled_accumulator`, so a handler returning
|
|
52
|
+
// TRUE suppresses the default navigation — which is what `preventDefault()` on the
|
|
53
|
+
// cancelable CustomEvent spells.
|
|
54
|
+
// · `gtk_widget_class_add_binding_action (…, GDK_KEY_Escape, …, "window.close")`
|
|
55
|
+
// (:725): the base class's Escape handling, through the shared modal surface.
|
|
56
|
+
//
|
|
57
|
+
// KNOWN_GAPS — one property, and it is a TYPE the attribute could have carried:
|
|
58
|
+
// · `logo` (`Gdk.Paintable`) is taken as a URL string and drawn as an `<img>`, because
|
|
59
|
+
// the browser has no paintable object and an attribute can hold a URL. `logo-icon-name`
|
|
60
|
+
// overrides it, as in the C (`gtk_about_dialog_set_logo_icon_name` overwrites the
|
|
61
|
+
// paintable and notifies `logo` back to NULL).
|
|
62
|
+
// · The `<a href="…">` markup form a credit line may carry is NOT recognised as a link
|
|
63
|
+
// (`add_credits_section` leaves it to the label's Pango parser, :2160-2164): this
|
|
64
|
+
// renderer builds DOM nodes, not markup, so such a line shows its markup literally.
|
|
65
|
+
// The link syntax that DOES work is the same one everywhere else — `Ada <ada@x.org>`
|
|
66
|
+
// or `Ada https://example.org`.
|
|
67
|
+
// · The visited-link colour (`update_links_cb`, :720-747) is not tracked: a clicked
|
|
68
|
+
// link does not stay a second colour, which is the one state GTK keeps across a
|
|
69
|
+
// dialog's whole life.
|
|
70
|
+
//
|
|
71
|
+
// Attributes (the GObject property names, hyphenated):
|
|
72
|
+
// program-name, version, copyright, comments, license, license-type,
|
|
73
|
+
// system-information, website, website-label, translator-credits, logo-icon-name,
|
|
74
|
+
// wrap-license — plus `open`/`can-close`/`title`/`content-width`/`content-height`/
|
|
75
|
+
// `presentation-mode`/`show-header` from the `<adw-dialog>` chrome it extends.
|
|
76
|
+
//
|
|
77
|
+
// Properties with no attribute form, exactly as in the C (a `GStrv` is not a string and
|
|
78
|
+
// `authors` is `char **`): `authors`, `documenters`, `artists`, and
|
|
79
|
+
// `addCreditSection(name, people)` for the app's own sections. `logo` takes a URL.
|
|
80
|
+
//
|
|
81
|
+
// Events (bubbling CustomEvents): `notify::open` (`{ open }`), `closed` and
|
|
82
|
+
// `activate-link` (`{ uri }`, CANCELABLE) from the base class, plus `notify::<prop>` for
|
|
83
|
+
// every property this element reflects.
|
|
84
|
+
//
|
|
85
|
+
// A11y: role `dialog` on the sheet, from `AdwModalSurface`. The switcher is a row of
|
|
86
|
+
// toggle buttons naming the three pages, and the sheet's `aria-label` is the window
|
|
87
|
+
// title, since the headerbar carries no text of its own.
|
|
88
|
+
//
|
|
89
|
+
// Reference: refs/gtk/gtk/gtkaboutdialog.c (every derivation cited above by line)
|
|
90
|
+
// Reference: refs/gtk/gtk/ui/gtkaboutdialog.ui (the page tree and the headerbar)
|
|
91
|
+
// Reference: refs/libadwaita/src/stylesheet/widgets/_dialogs.scss:46-56
|
|
92
|
+
// (window.aboutdialog: a FLAT titlebar and `image.large-icons` at 128px)
|
|
93
|
+
// Reference: packages/web/adwaita-web/src/elements/adw-dialog.ts (the chrome reused)
|
|
94
|
+
// Copyright (c) 2008-2024 Red Hat, Inc. / The GTK authors. LGPLv2.1+.
|
|
95
|
+
// Modifications: Implemented as a Web Component for @gjsify/adwaita-web; the icon nodes
|
|
96
|
+
// are <gtk-image>; the licence table and the credit parsing come from
|
|
97
|
+
// @gjsify/adwaita-core (ADR 0004).
|
|
98
|
+
|
|
99
|
+
import { ADW_LICENSES, GTK_LICENSE, parseCreditPerson, translatorCreditsPeople } from '@gjsify/adwaita-core';
|
|
100
|
+
|
|
101
|
+
import { AdwDialog } from './adw-dialog.js';
|
|
102
|
+
import { createGtkImage } from './gtk-image.js';
|
|
103
|
+
|
|
104
|
+
/** The four `GtkStackPage` titles of the `.ui`, mnemonics absent (they carry none). */
|
|
105
|
+
const PAGE_TITLES = {
|
|
106
|
+
main: 'About',
|
|
107
|
+
credits: 'Credits',
|
|
108
|
+
license: 'License',
|
|
109
|
+
system: 'System',
|
|
110
|
+
} as const;
|
|
111
|
+
|
|
112
|
+
/** The three pages behind the switcher, in stack order. */
|
|
113
|
+
const EXTRA_PAGES = ['credits', 'license', 'system'] as const;
|
|
114
|
+
type ExtraPage = (typeof EXTRA_PAGES)[number];
|
|
115
|
+
|
|
116
|
+
/** The `GtkLicense` nicks `packages/framework/gtk-host` generates, in enum order. */
|
|
117
|
+
const LICENSE_NICKS = [
|
|
118
|
+
'unknown',
|
|
119
|
+
'custom',
|
|
120
|
+
'gpl-2-0',
|
|
121
|
+
'gpl-3-0',
|
|
122
|
+
'lgpl-2-1',
|
|
123
|
+
'lgpl-3-0',
|
|
124
|
+
'bsd',
|
|
125
|
+
'mit-x11',
|
|
126
|
+
'artistic',
|
|
127
|
+
'gpl-2-0-only',
|
|
128
|
+
'gpl-3-0-only',
|
|
129
|
+
'lgpl-2-1-only',
|
|
130
|
+
'lgpl-3-0-only',
|
|
131
|
+
'agpl-3-0',
|
|
132
|
+
'agpl-3-0-only',
|
|
133
|
+
'bsd-3',
|
|
134
|
+
'apache-2-0',
|
|
135
|
+
'mpl-2-0',
|
|
136
|
+
'0bsd',
|
|
137
|
+
] as const;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* GTK's licence preamble (`gtk_about_dialog_set_license_type`, :2301) — NOT
|
|
141
|
+
* libadwaita's `ADW_LICENSE_WARRANTY_TEMPLATE`, which says "This application" and has no
|
|
142
|
+
* newline. Both strings are translatable and they are not translations of each other.
|
|
143
|
+
* The C template is `'This program comes with absolutely no warranty.\nSee the
|
|
144
|
+
* <a href="%s">%s</a> for details.'`; `_licenseMarkup` composes the same three runs —
|
|
145
|
+
* text, link, text — as DOM, because the scan it uses for plain licence text would read
|
|
146
|
+
* the `<a href>` as an email address.
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
export class GtkAboutDialog extends AdwDialog {
|
|
150
|
+
private _built = false;
|
|
151
|
+
private _logo = '';
|
|
152
|
+
private _switcherEl!: HTMLDivElement;
|
|
153
|
+
private _stackEl!: HTMLDivElement;
|
|
154
|
+
private _pages = new Map<string, HTMLElement>();
|
|
155
|
+
private _populated = new Set<string>();
|
|
156
|
+
private _authors: string[] = [];
|
|
157
|
+
private _documenters: string[] = [];
|
|
158
|
+
private _artists: string[] = [];
|
|
159
|
+
private _creditSections: Array<{ name: string; people: string[] }> = [];
|
|
160
|
+
|
|
161
|
+
static get observedAttributes(): string[] {
|
|
162
|
+
return [
|
|
163
|
+
...AdwDialog.observedAttributes,
|
|
164
|
+
'program-name',
|
|
165
|
+
'version',
|
|
166
|
+
'copyright',
|
|
167
|
+
'comments',
|
|
168
|
+
'license',
|
|
169
|
+
'license-type',
|
|
170
|
+
'system-information',
|
|
171
|
+
'website',
|
|
172
|
+
'website-label',
|
|
173
|
+
'translator-credits',
|
|
174
|
+
'logo-icon-name',
|
|
175
|
+
'wrap-license',
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ------------------------------------------------------------ properties
|
|
180
|
+
|
|
181
|
+
/** The program name; unset falls back to the document's own title (`g_get_application_name`). */
|
|
182
|
+
get programName(): string {
|
|
183
|
+
return this.getAttribute('program-name') ?? '';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
set programName(value: string) {
|
|
187
|
+
this.setAttribute('program-name', value);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
get version(): string {
|
|
191
|
+
return this.getAttribute('version') ?? '';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
set version(value: string) {
|
|
195
|
+
this.setAttribute('version', value);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
get copyright(): string {
|
|
199
|
+
return this.getAttribute('copyright') ?? '';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
set copyright(value: string) {
|
|
203
|
+
this.setAttribute('copyright', value);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get comments(): string {
|
|
207
|
+
return this.getAttribute('comments') ?? '';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
set comments(value: string) {
|
|
211
|
+
this.setAttribute('comments', value);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
get license(): string {
|
|
215
|
+
return this.getAttribute('license') ?? '';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
set license(value: string) {
|
|
219
|
+
// `set_license` (:1279) stores the text AND forces the type to CUSTOM, in
|
|
220
|
+
// that order — a MIT-typed dialog that is given licence text becomes custom.
|
|
221
|
+
this.setAttribute('license', value);
|
|
222
|
+
this.setAttribute('license-type', 'custom');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The `GtkLicense` nick — `unknown`, `custom`, `mit-x11`, … */
|
|
226
|
+
get licenseType(): string {
|
|
227
|
+
const value = this.getAttribute('license-type');
|
|
228
|
+
return value !== null && LICENSE_NICKS.includes(value as (typeof LICENSE_NICKS)[number]) ? value : 'unknown';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
set licenseType(value: string) {
|
|
232
|
+
if (LICENSE_NICKS.includes(value as (typeof LICENSE_NICKS)[number])) this.setAttribute('license-type', value);
|
|
233
|
+
else this.removeAttribute('license-type');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
get systemInformation(): string {
|
|
237
|
+
return this.getAttribute('system-information') ?? '';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
set systemInformation(value: string) {
|
|
241
|
+
this.setAttribute('system-information', value);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get website(): string {
|
|
245
|
+
return this.getAttribute('website') ?? '';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
set website(value: string) {
|
|
249
|
+
this.setAttribute('website', value);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
get websiteLabel(): string {
|
|
253
|
+
return this.getAttribute('website-label') ?? '';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
set websiteLabel(value: string) {
|
|
257
|
+
this.setAttribute('website-label', value);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
get translatorCredits(): string {
|
|
261
|
+
return this.getAttribute('translator-credits') ?? '';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
set translatorCredits(value: string) {
|
|
265
|
+
this.setAttribute('translator-credits', value);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** The named-icon logo; overrides {@link logo}, as in the C. */
|
|
269
|
+
get logoIconName(): string {
|
|
270
|
+
return this.getAttribute('logo-icon-name') ?? '';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
set logoIconName(value: string) {
|
|
274
|
+
this.setAttribute('logo-icon-name', value);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Whether the License page's text view WRAPS (`populate_license_page`, :2157). */
|
|
278
|
+
get wrapLicense(): boolean {
|
|
279
|
+
return this.hasAttribute('wrap-license');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
set wrapLicense(value: boolean) {
|
|
283
|
+
this.toggleAttribute('wrap-license', value);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* A logo URL, drawn as an `<img>`.
|
|
288
|
+
*
|
|
289
|
+
* `GtkAboutDialog:logo` is a `GdkPaintable`, which is the one GObject property here
|
|
290
|
+
* an attribute could have carried and cannot: there is no paintable in a document.
|
|
291
|
+
* A URL is the honest browser stand-in and the override order is the C's.
|
|
292
|
+
*/
|
|
293
|
+
get logo(): string {
|
|
294
|
+
return this._logo;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
set logo(value: string) {
|
|
298
|
+
this._logo = value;
|
|
299
|
+
if (this._built) this._renderLogo();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** The people whose names the Credits page shows — `GtkAboutDialog:authors`. */
|
|
303
|
+
get authors(): string[] {
|
|
304
|
+
return [...this._authors];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
set authors(value: readonly string[]) {
|
|
308
|
+
this._authors = [...value];
|
|
309
|
+
this._creditsChanged();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
get documenters(): string[] {
|
|
313
|
+
return [...this._documenters];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
set documenters(value: readonly string[]) {
|
|
317
|
+
this._documenters = [...value];
|
|
318
|
+
this._creditsChanged();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
get artists(): string[] {
|
|
322
|
+
return [...this._artists];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
set artists(value: readonly string[]) {
|
|
326
|
+
this._artists = [...value];
|
|
327
|
+
this._creditsChanged();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** `gtk_about_dialog_add_credit_section` — an extra titled group on the Credits page. */
|
|
331
|
+
addCreditSection(name: string, people: readonly string[]): void {
|
|
332
|
+
this._creditSections.push({ name, people: [...people] });
|
|
333
|
+
this._creditsChanged();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private _creditsChanged(): void {
|
|
337
|
+
// The C latches the page visible the moment a credit is set and never clears it;
|
|
338
|
+
// recomputing from the inputs is the same rule without the stale flag, and it is
|
|
339
|
+
// what `creditsSections` exists to be read by.
|
|
340
|
+
this._populated.delete('credits');
|
|
341
|
+
if (this._built) this._renderAbout();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ------------------------------------------------------------ lifecycle
|
|
345
|
+
|
|
346
|
+
connectedCallback(): void {
|
|
347
|
+
if (this._built) {
|
|
348
|
+
// A re-attach (AdwDialog.present on a dialog already in the document) must
|
|
349
|
+
// not rebuild the subtree — the DOM already exists.
|
|
350
|
+
super.connectedCallback();
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// BEFORE `super.connectedCallback()`, which renders and therefore calls
|
|
354
|
+
// `decorateHeader` — the switcher is what that hook installs.
|
|
355
|
+
this._switcherEl = document.createElement('div');
|
|
356
|
+
this._switcherEl.className = 'adw-gtk-about-dialog-switcher';
|
|
357
|
+
this._switcherEl.setAttribute('role', 'tablist');
|
|
358
|
+
this._switcherEl.setAttribute('aria-label', 'Pages');
|
|
359
|
+
|
|
360
|
+
super.connectedCallback();
|
|
361
|
+
// `_stackEl` before `_build`, which appends the pages to it — and `_built`
|
|
362
|
+
// goes last: `setAttribute('title')` below re-enters through
|
|
363
|
+
// `attributeChangedCallback`, and rendering on an empty `_pages` map throws.
|
|
364
|
+
this._stackEl = document.createElement('div');
|
|
365
|
+
this._stackEl.className = 'adw-gtk-about-dialog-stack';
|
|
366
|
+
this._stackEl.dataset.transitionType = 'crossfade';
|
|
367
|
+
|
|
368
|
+
// GTK's window title is `About <name>` whatever the headerbar shows, and the
|
|
369
|
+
// headerbar shows no text of its own — so the title is what assistive tech reads.
|
|
370
|
+
this.setAttribute('title', `About ${this.programName || document.title}`);
|
|
371
|
+
// The base adopts the author's light-DOM children into `contentArea` on
|
|
372
|
+
// connect; rebuilding around them must keep them, AFTER the dialog's own UI —
|
|
373
|
+
// which is also where children appended later land (`slotted-children.spec.ts`
|
|
374
|
+
// pins the equality, path and index).
|
|
375
|
+
const adopted = [...this.contentArea.childNodes];
|
|
376
|
+
this.contentArea.replaceChildren(this._build());
|
|
377
|
+
for (const node of adopted) this.contentArea.appendChild(node);
|
|
378
|
+
// `_build` hides every page but `main`, which is `gtk_stack_set_visible_child_name
|
|
379
|
+
// (…, "main")`; the three visibility rules then decide which of the others exist.
|
|
380
|
+
this._renderAbout();
|
|
381
|
+
this._built = true;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
|
|
385
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
386
|
+
if (!this._built) return;
|
|
387
|
+
if (oldValue === newValue) return;
|
|
388
|
+
if (name === 'license' || name === 'license-type' || name === 'website') {
|
|
389
|
+
// `set_license` sets the type to CUSTOM and `set_license_type` overwrites
|
|
390
|
+
// the text, so either one can invalidate the other's rendering.
|
|
391
|
+
this._populated.delete('license');
|
|
392
|
+
}
|
|
393
|
+
if (name === 'system-information') this._populated.delete('system');
|
|
394
|
+
if (name === 'website' || name === 'website-label' || name === 'license' || name === 'license-type') {
|
|
395
|
+
this._renderMain();
|
|
396
|
+
this._populateLicense();
|
|
397
|
+
}
|
|
398
|
+
if (name === 'wrap-license') this._populateLicense();
|
|
399
|
+
if (name === 'system-information') this._populateSystem();
|
|
400
|
+
this._renderAbout();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The headerbar carries the stack switcher and nothing else, exactly as
|
|
405
|
+
* `gtkaboutdialog.ui` declares it (`title-widget` → the switcher, no title).
|
|
406
|
+
*/
|
|
407
|
+
protected override decorateHeader(header: HTMLElement): void {
|
|
408
|
+
const title = header.querySelector('.adw-dialog-title');
|
|
409
|
+
// The switcher takes the title's place; the title carries no text of its own here.
|
|
410
|
+
if (title !== null) {
|
|
411
|
+
title.replaceWith(this._switcherEl);
|
|
412
|
+
} else {
|
|
413
|
+
header.prepend(this._switcherEl);
|
|
414
|
+
}
|
|
415
|
+
this._renderSwitcher();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Which stack page is showing — `gtk_stack_get_visible_child_name`. */
|
|
419
|
+
get visiblePage(): string {
|
|
420
|
+
return this._stackEl.dataset.visible ?? 'main';
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Show a page of the stack — `gtk_stack_set_visible_child_name`. The switcher buttons
|
|
425
|
+
* call it, and it is the one way the stack's lazy population (`stack_visible_child_notify`)
|
|
426
|
+
* runs: a page is built the first time it is shown, not before.
|
|
427
|
+
*/
|
|
428
|
+
showPage(name: string): void {
|
|
429
|
+
this._populatePage(name);
|
|
430
|
+
for (const [key, page] of this._pages) page.hidden = key !== name;
|
|
431
|
+
this._stackEl.dataset.visible = name;
|
|
432
|
+
this._renderSwitcher();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ------------------------------------------------------------ rendering
|
|
436
|
+
|
|
437
|
+
private _build(): HTMLElement {
|
|
438
|
+
const root = document.createElement('div');
|
|
439
|
+
root.className = 'adw-gtk-about-dialog-box';
|
|
440
|
+
|
|
441
|
+
const head = document.createElement('div');
|
|
442
|
+
head.className = 'adw-gtk-about-dialog-head';
|
|
443
|
+
|
|
444
|
+
const logoSlot = document.createElement('div');
|
|
445
|
+
logoSlot.className = 'adw-gtk-about-dialog-logo-slot';
|
|
446
|
+
head.appendChild(logoSlot);
|
|
447
|
+
this._renderLogo();
|
|
448
|
+
|
|
449
|
+
const name = document.createElement('div');
|
|
450
|
+
name.className = 'adw-gtk-about-dialog-name';
|
|
451
|
+
name.textContent = this.programName || document.title;
|
|
452
|
+
head.appendChild(name);
|
|
453
|
+
|
|
454
|
+
for (const page of ['main', ...EXTRA_PAGES]) {
|
|
455
|
+
const element = document.createElement('div');
|
|
456
|
+
element.className = 'adw-gtk-about-dialog-page';
|
|
457
|
+
element.dataset.page = page;
|
|
458
|
+
element.hidden = page !== 'main';
|
|
459
|
+
this._pages.set(page, element);
|
|
460
|
+
this._stackEl.appendChild(element);
|
|
461
|
+
}
|
|
462
|
+
this._renderMain();
|
|
463
|
+
head.appendChild(this._stackEl);
|
|
464
|
+
root.appendChild(head);
|
|
465
|
+
return root;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private _renderAbout(): void {
|
|
469
|
+
this.setAttribute('title', `About ${this.programName || document.title}`);
|
|
470
|
+
for (const page of EXTRA_PAGES) {
|
|
471
|
+
const element = this._pages.get(page);
|
|
472
|
+
if (element === undefined) continue;
|
|
473
|
+
// `gtk_stack_page_set_visible` — and a page that stops being visible is also
|
|
474
|
+
// dropped from the switcher, because the switcher lists the pages the stack
|
|
475
|
+
// holds and no more.
|
|
476
|
+
const visible = this._pageVisible(page);
|
|
477
|
+
if (element.hidden === !visible) continue;
|
|
478
|
+
element.hidden = !visible;
|
|
479
|
+
if (!visible) this._populated.delete(page);
|
|
480
|
+
}
|
|
481
|
+
if (this._pages.get(this._stackEl.dataset.visible ?? 'main')?.hidden !== false) {
|
|
482
|
+
this._stackEl.dataset.visible = 'main';
|
|
483
|
+
this._pages.get('main')!.hidden = false;
|
|
484
|
+
}
|
|
485
|
+
this._renderMain();
|
|
486
|
+
this._renderSwitcher();
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** The three visibility rules, one predicate each, as in the C. */
|
|
490
|
+
private _pageVisible(page: ExtraPage): boolean {
|
|
491
|
+
if (page === 'credits') return this._creditsPeople().length > 0;
|
|
492
|
+
if (page === 'license') return this.licenseType === 'custom' && this.license.length > 0;
|
|
493
|
+
return this.systemInformation.length > 0;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** `add_credits_section`'s input, in `populate_credits_page`'s order. */
|
|
497
|
+
private _creditsPeople(): Array<{ title: string; people: readonly string[] }> {
|
|
498
|
+
const sections: Array<{ title: string; people: readonly string[] }> = [];
|
|
499
|
+
if (this._authors.length > 0) sections.push({ title: 'Created by', people: this._authors });
|
|
500
|
+
if (this._documenters.length > 0) sections.push({ title: 'Documented by', people: this._documenters });
|
|
501
|
+
const translators = translatorCreditsPeople(this.translatorCredits);
|
|
502
|
+
if (translators.length > 0) sections.push({ title: 'Translated by', people: translators });
|
|
503
|
+
if (this._artists.length > 0) sections.push({ title: 'Design by', people: this._artists });
|
|
504
|
+
for (const section of this._creditSections) sections.push({ title: section.name, people: section.people });
|
|
505
|
+
return sections;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** Whether the stack switcher has anything to show (`update_stack_switcher_visibility`). */
|
|
509
|
+
private _visiblePages(): ExtraPage[] {
|
|
510
|
+
return EXTRA_PAGES.filter((page) => this._pages.get(page)?.hidden === false);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private _renderSwitcher(): void {
|
|
514
|
+
if (this._switcherEl === undefined) return;
|
|
515
|
+
const pages = this._visiblePages();
|
|
516
|
+
// GTK hides the whole switcher (`gtk_widget_set_visible (about->stack_switcher,
|
|
517
|
+
// any_visible)`), which is why an about dialog with no credits, licence or system
|
|
518
|
+
// information shows an EMPTY headerbar.
|
|
519
|
+
this._switcherEl.hidden = pages.length === 0;
|
|
520
|
+
this._switcherEl.replaceChildren(
|
|
521
|
+
...pages.map((page) => {
|
|
522
|
+
const button = document.createElement('button');
|
|
523
|
+
button.type = 'button';
|
|
524
|
+
button.className = 'adw-gtk-about-dialog-switcher-item';
|
|
525
|
+
button.setAttribute('role', 'tab');
|
|
526
|
+
button.textContent = PAGE_TITLES[page];
|
|
527
|
+
button.classList.toggle('checked', this._stackEl.dataset.visible === page);
|
|
528
|
+
button.setAttribute('aria-selected', String(this._stackEl.dataset.visible === page));
|
|
529
|
+
button.addEventListener('click', () => this.showPage(page));
|
|
530
|
+
return button;
|
|
531
|
+
}),
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private _renderLogo(): void {
|
|
536
|
+
const slot = this.contentArea.querySelector('.adw-gtk-about-dialog-logo-slot');
|
|
537
|
+
if (slot === null) return;
|
|
538
|
+
// `set_logo_icon_name` overwrites the paintable, and notifies `logo` back to NULL:
|
|
539
|
+
// the icon name wins whenever it is set, which is the only precedence there is.
|
|
540
|
+
if (this.logoIconName.length > 0) {
|
|
541
|
+
slot.replaceChildren(createGtkImage(this.logoIconName, 'adw-gtk-about-dialog-logo'));
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (this._logo.length > 0) {
|
|
545
|
+
const image = document.createElement('img');
|
|
546
|
+
image.className = 'adw-gtk-about-dialog-logo';
|
|
547
|
+
image.src = this._logo;
|
|
548
|
+
image.alt = '';
|
|
549
|
+
slot.replaceChildren(image);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
slot.replaceChildren();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/** The main page: version, comments, website, copyright, licence — in `.ui` order. */
|
|
556
|
+
private _renderMain(): void {
|
|
557
|
+
const page = this._pages.get('main');
|
|
558
|
+
if (page === undefined) return;
|
|
559
|
+
const rows: HTMLElement[] = [];
|
|
560
|
+
|
|
561
|
+
const label = (text: string, className: string): HTMLElement => {
|
|
562
|
+
const element = document.createElement('div');
|
|
563
|
+
element.className = className;
|
|
564
|
+
element.textContent = text;
|
|
565
|
+
return element;
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
if (this.version.length > 0) rows.push(label(this.version, 'adw-gtk-about-dialog-version'));
|
|
569
|
+
if (this.comments.length > 0) rows.push(label(this.comments, 'adw-gtk-about-dialog-comments'));
|
|
570
|
+
// `update_website` (:1009-1041): a URL with no label shows the word "Website", a
|
|
571
|
+
// label with no URL shows the label alone and is NOT a link, and neither shows
|
|
572
|
+
// anything when both are unset.
|
|
573
|
+
if (this.website.length > 0) {
|
|
574
|
+
rows.push(
|
|
575
|
+
this.websiteLabel.length > 0
|
|
576
|
+
? this._link(this.websiteLabel, this.website, 'adw-gtk-about-dialog-website')
|
|
577
|
+
: this._link('Website', this.website, 'adw-gtk-about-dialog-website'),
|
|
578
|
+
);
|
|
579
|
+
} else if (this.websiteLabel.length > 0) {
|
|
580
|
+
rows.push(label(this.websiteLabel, 'adw-gtk-about-dialog-website'));
|
|
581
|
+
}
|
|
582
|
+
if (this.copyright.length > 0) rows.push(label(this.copyright, 'adw-gtk-about-dialog-copyright'));
|
|
583
|
+
const licence = this._licenseMarkup();
|
|
584
|
+
if (licence !== null) rows.push(licence);
|
|
585
|
+
|
|
586
|
+
page.replaceChildren(...rows);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* The licence label: the text `license` holds, with its `<…>` and bare-URL links
|
|
591
|
+
* turned into spans — or, for a known type, the warranty preamble `set_license_type`
|
|
592
|
+
* composes. The preamble keeps its `<a href>` markup and is read with
|
|
593
|
+
* `gtk_label_set_markup` (:2354), a REAL link: it never goes through the
|
|
594
|
+
* `text_buffer_new` scan below, which would read `<a href="…">` as an email
|
|
595
|
+
* address. Same split here.
|
|
596
|
+
*/
|
|
597
|
+
private _licenseMarkup(): HTMLElement | null {
|
|
598
|
+
const type = this.licenseType;
|
|
599
|
+
const index = LICENSE_NICKS.indexOf(type as (typeof LICENSE_NICKS)[number]);
|
|
600
|
+
const licenseType =
|
|
601
|
+
index >= 0 ? (index as (typeof GTK_LICENSE)[keyof typeof GTK_LICENSE]) : GTK_LICENSE.UNKNOWN;
|
|
602
|
+
const element = document.createElement('div');
|
|
603
|
+
element.className = 'adw-gtk-about-dialog-license';
|
|
604
|
+
if (licenseType !== GTK_LICENSE.CUSTOM && licenseType !== GTK_LICENSE.UNKNOWN) {
|
|
605
|
+
const info = ADW_LICENSES[licenseType]!;
|
|
606
|
+
// A licence row with no URL of its own links to the WEBSITE, which is what
|
|
607
|
+
// `set_license_type` does for `unknown` and for a row whose `url` is NULL.
|
|
608
|
+
const url = info.url ?? this.website;
|
|
609
|
+
element.append(
|
|
610
|
+
document.createTextNode('This program comes with absolutely no warranty.\nSee the '),
|
|
611
|
+
this._link(info.name ?? '', url, 'adw-gtk-about-dialog-license-link'),
|
|
612
|
+
document.createTextNode(' for details.'),
|
|
613
|
+
);
|
|
614
|
+
return element;
|
|
615
|
+
}
|
|
616
|
+
if (licenseType === GTK_LICENSE.UNKNOWN || this.license.length === 0) return null;
|
|
617
|
+
this._fillWithLinks(element, this.license);
|
|
618
|
+
return element;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/** The License page — a text view whose wrap mode is `wrap-license`. */
|
|
622
|
+
private _populateLicense(): void {
|
|
623
|
+
const page = this._pages.get('license');
|
|
624
|
+
if (page === undefined) return;
|
|
625
|
+
const view = document.createElement('div');
|
|
626
|
+
view.className = 'adw-gtk-about-dialog-text-view';
|
|
627
|
+
// `populate_license_page` (:2157-2166): WRAP_WORD when `wrap-license`, else NONE.
|
|
628
|
+
if (this.wrapLicense) view.classList.add('wrap');
|
|
629
|
+
const markup = this._licenseMarkup();
|
|
630
|
+
if (markup !== null) {
|
|
631
|
+
const body = document.createElement('div');
|
|
632
|
+
body.className = 'adw-gtk-about-dialog-text';
|
|
633
|
+
// The main label's runs, cloned — including the preamble's real link.
|
|
634
|
+
body.replaceChildren(...[...markup.childNodes].map((node) => node.cloneNode(true)));
|
|
635
|
+
view.appendChild(body);
|
|
636
|
+
}
|
|
637
|
+
page.replaceChildren(this._frame(view));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** The System page — a text view with no wrap mode of its own. */
|
|
641
|
+
private _populateSystem(): void {
|
|
642
|
+
const page = this._pages.get('system');
|
|
643
|
+
if (page === undefined) return;
|
|
644
|
+
const view = document.createElement('div');
|
|
645
|
+
view.className = 'adw-gtk-about-dialog-text-view';
|
|
646
|
+
const body = document.createElement('div');
|
|
647
|
+
body.className = 'adw-gtk-about-dialog-text';
|
|
648
|
+
this._fillWithLinks(body, this.systemInformation);
|
|
649
|
+
view.appendChild(body);
|
|
650
|
+
page.replaceChildren(this._frame(view));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/** The Credits page — the scrolled, framed grid `populate_credits_page` fills. */
|
|
654
|
+
private _populateCredits(): void {
|
|
655
|
+
const page = this._pages.get('credits');
|
|
656
|
+
if (page === undefined) return;
|
|
657
|
+
|
|
658
|
+
const grid = document.createElement('div');
|
|
659
|
+
grid.className = 'adw-gtk-about-dialog-credits-grid';
|
|
660
|
+
grid.setAttribute('role', 'group');
|
|
661
|
+
grid.setAttribute('aria-label', PAGE_TITLES.credits);
|
|
662
|
+
|
|
663
|
+
for (const section of this._creditsPeople()) {
|
|
664
|
+
const heading = document.createElement('div');
|
|
665
|
+
heading.className = 'adw-gtk-about-dialog-credits-heading';
|
|
666
|
+
heading.textContent = section.title;
|
|
667
|
+
grid.appendChild(heading);
|
|
668
|
+
for (const person of section.people) {
|
|
669
|
+
const parsed = parseCreditPerson(person);
|
|
670
|
+
const row = document.createElement('div');
|
|
671
|
+
row.className = 'adw-gtk-about-dialog-credits-person';
|
|
672
|
+
if (parsed.link !== null) {
|
|
673
|
+
row.appendChild(this._link(parsed.name, parsed.uri ?? '', 'adw-gtk-about-dialog-credits-link'));
|
|
674
|
+
} else {
|
|
675
|
+
row.textContent = parsed.name;
|
|
676
|
+
}
|
|
677
|
+
grid.appendChild(row);
|
|
678
|
+
}
|
|
679
|
+
// "skip one at the end" — an empty row so consecutive sections do not touch.
|
|
680
|
+
grid.appendChild(document.createElement('div'));
|
|
681
|
+
}
|
|
682
|
+
page.replaceChildren(this._frame(grid));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* `stack_visible_child_notify` (:297-333): each of the three pages is built the first
|
|
687
|
+
* time the stack SHOWS it, and never rebuilt after. A credits grid for three hundred
|
|
688
|
+
* authors is not work a dialog nobody opened it for.
|
|
689
|
+
*/
|
|
690
|
+
private _populatePage(name: string): void {
|
|
691
|
+
if (this._populated.has(name)) return;
|
|
692
|
+
this._populated.add(name);
|
|
693
|
+
if (name === 'credits') this._populateCredits();
|
|
694
|
+
else if (name === 'license') this._populateLicense();
|
|
695
|
+
else if (name === 'system') this._populateSystem();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/** The framed scrolled window the three extra pages sit in (`has-frame`). */
|
|
699
|
+
private _frame(content: HTMLElement): HTMLElement {
|
|
700
|
+
const scrolled = document.createElement('div');
|
|
701
|
+
scrolled.className = 'adw-gtk-about-dialog-scrolled';
|
|
702
|
+
const viewport = document.createElement('div');
|
|
703
|
+
viewport.className = 'adw-view';
|
|
704
|
+
viewport.appendChild(content);
|
|
705
|
+
scrolled.appendChild(viewport);
|
|
706
|
+
return scrolled;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
private _link(text: string, uri: string, className: string): HTMLElement {
|
|
710
|
+
const link = document.createElement('a');
|
|
711
|
+
link.className = className;
|
|
712
|
+
link.href = uri;
|
|
713
|
+
link.rel = 'noopener noreferrer';
|
|
714
|
+
link.textContent = text;
|
|
715
|
+
link.addEventListener('click', (event) => {
|
|
716
|
+
event.preventDefault();
|
|
717
|
+
this._activateLink(uri);
|
|
718
|
+
});
|
|
719
|
+
return link;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* `text_buffer_new` (:1903-2028 of `gtkaboutdialog.c`) for the plain-DOM case: split
|
|
724
|
+
* the string into literal runs and links. A `<…>` run is an email when it carries no
|
|
725
|
+
* scheme and a URL when it does; a bare `http://` or `https://` run ends at the first
|
|
726
|
+
* of space, newline, tab or `>`, which is `strpbrk (r1, " \n\t>")`.
|
|
727
|
+
*/
|
|
728
|
+
private _fillWithLinks(target: HTMLElement, text: string): void {
|
|
729
|
+
for (const part of splitLinks(text)) {
|
|
730
|
+
if (part.uri === null) {
|
|
731
|
+
target.appendChild(document.createTextNode(part.text));
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const link = document.createElement('a');
|
|
735
|
+
link.href = part.uri;
|
|
736
|
+
link.rel = 'noopener noreferrer';
|
|
737
|
+
link.textContent = part.text;
|
|
738
|
+
link.addEventListener('click', (event) => {
|
|
739
|
+
event.preventDefault();
|
|
740
|
+
this._activateLink(part.uri!);
|
|
741
|
+
});
|
|
742
|
+
target.appendChild(link);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* `emit_activate_link` (:646-655): the `BOOLEAN__STRING` accumulator. A handler that
|
|
748
|
+
* returns TRUE says it took over, which is `preventDefault()` here — and only then
|
|
749
|
+
* does the default `Gtk.UriLauncher` navigation not happen.
|
|
750
|
+
*/
|
|
751
|
+
private _activateLink(uri: string): void {
|
|
752
|
+
const event = new CustomEvent('activate-link', {
|
|
753
|
+
bubbles: true,
|
|
754
|
+
cancelable: true,
|
|
755
|
+
detail: { uri },
|
|
756
|
+
});
|
|
757
|
+
if (this.dispatchEvent(event)) window.open(uri, '_blank', 'noopener,noreferrer');
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** One run of `text_buffer_new`'s output: literal text, or a link with its URI. */
|
|
762
|
+
interface LinkPart {
|
|
763
|
+
readonly text: string;
|
|
764
|
+
readonly uri: string | null;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** The terminator set a bare URL ends at (`strpbrk (r1, " \n\t>")`). */
|
|
768
|
+
const URL_TERMINATORS = ' \n\t>';
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* `text_buffer_new`'s scan, in order: the first `<` and the first `>` after it, the
|
|
772
|
+
* earliest bare `http://` or `https://` and where it ends, and the C's own precedence —
|
|
773
|
+
* a URL that starts AT or just after the `<` wins, which leaves the bracket in the text.
|
|
774
|
+
*/
|
|
775
|
+
export function splitLinks(text: string): LinkPart[] {
|
|
776
|
+
const parts: LinkPart[] = [];
|
|
777
|
+
let rest = text;
|
|
778
|
+
while (rest.length > 0) {
|
|
779
|
+
let bracket = rest.indexOf('<');
|
|
780
|
+
let bracketEnd = bracket < 0 ? -1 : rest.indexOf('>', bracket);
|
|
781
|
+
const httpAt = rest.indexOf('http://');
|
|
782
|
+
const httpsAt = rest.indexOf('https://');
|
|
783
|
+
let urlAt = httpAt;
|
|
784
|
+
if (httpAt < 0 || (httpsAt >= 0 && httpsAt < httpAt)) urlAt = httpsAt;
|
|
785
|
+
let urlEnd = -1;
|
|
786
|
+
if (urlAt >= 0) {
|
|
787
|
+
urlEnd = URL_TERMINATORS.split('').reduce((found, character) => {
|
|
788
|
+
if (found >= 0) return found;
|
|
789
|
+
const at = rest.indexOf(character, urlAt);
|
|
790
|
+
return at < 0 ? -1 : at;
|
|
791
|
+
}, -1);
|
|
792
|
+
if (urlEnd < 0) urlEnd = rest.length;
|
|
793
|
+
}
|
|
794
|
+
if (urlAt >= 0 && (bracket < 0 || bracketEnd < 0 || urlAt <= bracket + 1)) {
|
|
795
|
+
bracket = urlAt;
|
|
796
|
+
bracketEnd = urlEnd;
|
|
797
|
+
}
|
|
798
|
+
if (bracket < 0 || bracketEnd < 0) {
|
|
799
|
+
parts.push({ text: rest, uri: null });
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
if (bracket > 0) parts.push({ text: rest.slice(0, bracket), uri: null });
|
|
803
|
+
const isEmail = rest[bracket] === '<';
|
|
804
|
+
const body = isEmail ? rest.slice(bracket + 1, bracketEnd) : rest.slice(bracket, bracketEnd);
|
|
805
|
+
parts.push({ text: body, uri: isEmail ? `mailto:${body}` : body });
|
|
806
|
+
rest = rest.slice(bracketEnd + (isEmail ? 1 : 0));
|
|
807
|
+
}
|
|
808
|
+
return parts;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
customElements.define('gtk-about-dialog', GtkAboutDialog);
|