@gjsify/adwaita-web 0.64.2-next.252 → 0.64.2-next.255
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 +10 -80
- package/dist/adwaita-web.css +52 -3
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/accessible.d.ts +8 -0
- package/lib/types/compute-expand.d.ts +5 -0
- package/lib/types/compute-expand.spec.d.ts +1 -0
- package/lib/types/elements/adw-about-dialog.d.ts +28 -1
- package/lib/types/elements/adw-action-row.d.ts +2 -1
- package/lib/types/elements/adw-alert-dialog.d.ts +3 -2
- package/lib/types/elements/adw-avatar.d.ts +2 -1
- package/lib/types/elements/adw-banner.d.ts +2 -1
- package/lib/types/elements/adw-bin.d.ts +2 -1
- package/lib/types/elements/adw-bottom-sheet.d.ts +5 -4
- package/lib/types/elements/adw-breakpoint-bin.d.ts +2 -1
- package/lib/types/elements/adw-button-content.d.ts +2 -1
- package/lib/types/elements/adw-button-row.d.ts +2 -1
- package/lib/types/elements/adw-card.d.ts +2 -1
- package/lib/types/elements/adw-carousel.d.ts +20 -3
- package/lib/types/elements/adw-clamp-scrollable.d.ts +2 -1
- package/lib/types/elements/adw-clamp.d.ts +2 -1
- package/lib/types/elements/adw-combo-row.d.ts +6 -1
- package/lib/types/elements/adw-data-grid.d.ts +2 -1
- package/lib/types/elements/adw-dialog.d.ts +2 -1
- package/lib/types/elements/adw-entry-row.d.ts +2 -1
- package/lib/types/elements/adw-expander-row.d.ts +2 -1
- package/lib/types/elements/adw-header-bar.d.ts +2 -1
- package/lib/types/elements/adw-inline-view-switcher.d.ts +3 -2
- package/lib/types/elements/adw-layout-slot.d.ts +2 -1
- package/lib/types/elements/adw-multi-layout-view.d.ts +2 -1
- package/lib/types/elements/adw-navigation-split-view.d.ts +2 -1
- package/lib/types/elements/adw-navigation-view.d.ts +3 -2
- package/lib/types/elements/adw-overlay-split-view.d.ts +2 -1
- package/lib/types/elements/adw-preferences-dialog.d.ts +5 -4
- package/lib/types/elements/adw-preferences-group.d.ts +2 -1
- package/lib/types/elements/adw-preferences-row.d.ts +2 -1
- package/lib/types/elements/adw-shortcut-label.d.ts +2 -1
- package/lib/types/elements/adw-shortcuts-item.d.ts +2 -1
- package/lib/types/elements/adw-shortcuts-section.d.ts +2 -1
- package/lib/types/elements/adw-sidebar.d.ts +4 -3
- package/lib/types/elements/adw-spin-row.d.ts +2 -1
- package/lib/types/elements/adw-spinner.d.ts +2 -1
- package/lib/types/elements/adw-split-button.d.ts +2 -1
- package/lib/types/elements/adw-status-page.d.ts +2 -1
- package/lib/types/elements/adw-switch-row.d.ts +4 -1
- package/lib/types/elements/adw-tab-bar.d.ts +2 -1
- package/lib/types/elements/adw-tab-button.d.ts +2 -1
- package/lib/types/elements/adw-tab-overview.d.ts +2 -1
- package/lib/types/elements/adw-tab-view.d.ts +3 -2
- package/lib/types/elements/adw-toast-overlay.d.ts +2 -1
- package/lib/types/elements/adw-toggle-group.d.ts +3 -2
- package/lib/types/elements/adw-toolbar-view.d.ts +2 -1
- package/lib/types/elements/adw-view-stack.d.ts +3 -1
- package/lib/types/elements/adw-view-switcher-bar.d.ts +2 -1
- package/lib/types/elements/adw-view-switcher-sidebar.d.ts +2 -1
- package/lib/types/elements/adw-view-switcher.d.ts +3 -2
- package/lib/types/elements/adw-window-title.d.ts +2 -1
- package/lib/types/elements/adw-wrap-box.d.ts +2 -1
- package/lib/types/elements/checks.d.ts +16 -1
- package/lib/types/elements/gtk-action-bar.d.ts +2 -1
- package/lib/types/elements/gtk-aspect-frame.d.ts +2 -1
- package/lib/types/elements/gtk-box.d.ts +5 -1
- package/lib/types/elements/gtk-button.d.ts +5 -7
- package/lib/types/elements/gtk-calendar.d.ts +2 -1
- package/lib/types/elements/gtk-center-box.d.ts +2 -1
- package/lib/types/elements/gtk-color-dialog-button.d.ts +2 -1
- package/lib/types/elements/gtk-drag-icon.d.ts +2 -1
- package/lib/types/elements/gtk-drawing-area.d.ts +2 -1
- package/lib/types/elements/gtk-drop-down.d.ts +2 -1
- package/lib/types/elements/gtk-editable-label.d.ts +2 -1
- package/lib/types/elements/gtk-entry.d.ts +2 -1
- package/lib/types/elements/gtk-expander.d.ts +2 -1
- package/lib/types/elements/gtk-fixed.d.ts +2 -1
- package/lib/types/elements/gtk-flow-box-child.d.ts +2 -1
- package/lib/types/elements/gtk-flow-box.d.ts +2 -1
- package/lib/types/elements/gtk-font-dialog-button.d.ts +2 -1
- package/lib/types/elements/gtk-frame.d.ts +2 -1
- package/lib/types/elements/gtk-gl-area.d.ts +2 -1
- package/lib/types/elements/gtk-graphics-offload.d.ts +2 -1
- package/lib/types/elements/gtk-grid.d.ts +2 -1
- package/lib/types/elements/gtk-header-bar.d.ts +2 -1
- package/lib/types/elements/gtk-image.d.ts +5 -6
- package/lib/types/elements/gtk-inscription.d.ts +2 -1
- package/lib/types/elements/gtk-label.d.ts +10 -3
- package/lib/types/elements/gtk-level-bar.d.ts +2 -1
- package/lib/types/elements/gtk-list-box-row.d.ts +2 -1
- package/lib/types/elements/gtk-list-box.d.ts +2 -1
- package/lib/types/elements/gtk-list-view.d.ts +2 -1
- package/lib/types/elements/gtk-media-controls.d.ts +2 -1
- package/lib/types/elements/gtk-menu-button.d.ts +2 -1
- package/lib/types/elements/gtk-notebook.d.ts +2 -1
- package/lib/types/elements/gtk-overlay.d.ts +2 -1
- package/lib/types/elements/gtk-paned.d.ts +2 -1
- package/lib/types/elements/gtk-picture.d.ts +2 -1
- package/lib/types/elements/gtk-popover-bin.d.ts +2 -1
- package/lib/types/elements/gtk-popover-menu-bar.d.ts +2 -1
- package/lib/types/elements/gtk-popover.d.ts +2 -1
- package/lib/types/elements/gtk-progress-bar.d.ts +2 -1
- package/lib/types/elements/gtk-revealer.d.ts +2 -1
- package/lib/types/elements/gtk-scale-button.d.ts +2 -1
- package/lib/types/elements/gtk-scale.d.ts +2 -1
- package/lib/types/elements/gtk-scrollbar.d.ts +2 -1
- package/lib/types/elements/gtk-scrolled-window.d.ts +2 -1
- package/lib/types/elements/gtk-search-bar.d.ts +2 -1
- package/lib/types/elements/gtk-separator.d.ts +2 -1
- package/lib/types/elements/gtk-spin-button.d.ts +2 -1
- package/lib/types/elements/gtk-spinner.d.ts +2 -1
- package/lib/types/elements/gtk-stack-page.d.ts +2 -1
- package/lib/types/elements/gtk-stack-sidebar.d.ts +2 -1
- package/lib/types/elements/gtk-stack-switcher.d.ts +2 -1
- package/lib/types/elements/gtk-stack.d.ts +2 -1
- package/lib/types/elements/gtk-switch.d.ts +2 -1
- package/lib/types/elements/gtk-text-view.d.ts +2 -1
- package/lib/types/elements/gtk-text.d.ts +2 -1
- package/lib/types/elements/gtk-tree-expander.d.ts +2 -1
- package/lib/types/elements/gtk-video.d.ts +2 -1
- package/lib/types/elements/gtk-viewport.d.ts +2 -1
- package/lib/types/elements/gtk-window-controls.d.ts +2 -1
- package/lib/types/elements/gtk-window-handle.d.ts +2 -1
- package/lib/types/elements/gtk-window.d.ts +2 -1
- package/lib/types/gobject-elements.d.ts +7 -1
- package/lib/types/gtksource/gtk-source-view.d.ts +2 -1
- package/lib/types/{icon-registry.spec.d.ts → icon-masks.spec.d.ts} +1 -1
- package/lib/types/icon-theme.d.ts +15 -35
- package/lib/types/index.d.ts +1 -2
- package/lib/types/namespace/gdk.d.ts +1 -0
- package/lib/types/namespace/gio.d.ts +1 -0
- package/lib/types/namespace/glib.d.ts +2 -1
- package/lib/types/signals.d.ts +5 -1
- package/lib/types/source-view/adw-source-view.d.ts +2 -1
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/widget-element.d.ts +25 -0
- package/lib/types/widget-map.spec.d.ts +1 -0
- package/lib/types/widget-mapping.d.ts +7 -0
- package/package.json +12 -12
- package/scss/_about_dialog.scss +46 -1
- package/scss/_checks.scss +5 -2
- package/scss/_icon.scss +3 -4
- package/scss/_labels.scss +2 -2
- package/scss/_overlay_split_view.scss +10 -0
- package/scss/_scrolled_window.scss +15 -0
- package/scss/_widget.scss +5 -1
- package/src/accessible.ts +29 -0
- package/src/adw-about-dialog.spec.ts +81 -12
- package/src/adw-action-rows.spec.ts +2 -2
- package/src/adw-button-content.spec.ts +1 -1
- package/src/adw-carousel.spec.ts +49 -1
- package/src/capabilities.mjs +1 -0
- package/src/checks.spec.ts +51 -1
- package/src/compute-expand.spec.ts +186 -0
- package/src/compute-expand.ts +134 -0
- package/src/construct-vectors.spec.ts +46 -1
- package/src/elements/adw-about-dialog.ts +152 -9
- package/src/elements/adw-action-row.ts +2 -1
- package/src/elements/adw-alert-dialog.ts +3 -2
- package/src/elements/adw-avatar.ts +2 -1
- package/src/elements/adw-banner.ts +2 -1
- package/src/elements/adw-bin.ts +2 -1
- package/src/elements/adw-bottom-sheet.ts +5 -4
- package/src/elements/adw-breakpoint-bin.ts +2 -1
- package/src/elements/adw-button-content.ts +2 -1
- package/src/elements/adw-button-row.ts +2 -1
- package/src/elements/adw-card.ts +2 -1
- package/src/elements/adw-carousel.ts +62 -3
- package/src/elements/adw-clamp-scrollable.ts +2 -1
- package/src/elements/adw-clamp.ts +2 -1
- package/src/elements/adw-combo-row.ts +15 -1
- package/src/elements/adw-data-grid.ts +2 -1
- package/src/elements/adw-dialog.ts +2 -1
- package/src/elements/adw-entry-row.ts +2 -1
- package/src/elements/adw-expander-row.ts +2 -1
- package/src/elements/adw-header-bar.ts +2 -1
- package/src/elements/adw-inline-view-switcher.ts +3 -2
- package/src/elements/adw-layout-slot.ts +2 -1
- package/src/elements/adw-multi-layout-view.ts +2 -1
- package/src/elements/adw-navigation-split-view.ts +2 -1
- package/src/elements/adw-navigation-view.ts +3 -2
- package/src/elements/adw-overlay-split-view.ts +2 -1
- package/src/elements/adw-preferences-dialog.ts +6 -4
- package/src/elements/adw-preferences-group.ts +2 -1
- package/src/elements/adw-preferences-row.ts +2 -1
- package/src/elements/adw-shortcut-label.ts +2 -1
- package/src/elements/adw-shortcuts-item.ts +2 -1
- package/src/elements/adw-shortcuts-section.ts +2 -1
- package/src/elements/adw-sidebar.ts +4 -3
- package/src/elements/adw-spin-row.ts +2 -1
- package/src/elements/adw-spinner.ts +2 -1
- package/src/elements/adw-split-button.ts +2 -1
- package/src/elements/adw-status-page.ts +2 -1
- package/src/elements/adw-switch-row.ts +10 -1
- package/src/elements/adw-tab-bar.ts +2 -1
- package/src/elements/adw-tab-button.ts +2 -1
- package/src/elements/adw-tab-overview.ts +2 -1
- package/src/elements/adw-tab-view.ts +3 -2
- package/src/elements/adw-toast-overlay.ts +2 -1
- package/src/elements/adw-toggle-group.ts +3 -2
- package/src/elements/adw-toolbar-view.ts +2 -1
- package/src/elements/adw-view-stack.ts +39 -1
- package/src/elements/adw-view-switcher-bar.ts +2 -1
- package/src/elements/adw-view-switcher-sidebar.ts +2 -1
- package/src/elements/adw-view-switcher.ts +3 -2
- package/src/elements/adw-window-title.ts +2 -1
- package/src/elements/adw-wrap-box.ts +2 -1
- package/src/elements/checks.ts +85 -6
- package/src/elements/gtk-action-bar.ts +2 -1
- package/src/elements/gtk-aspect-frame.ts +2 -1
- package/src/elements/gtk-box.ts +56 -1
- package/src/elements/gtk-button.ts +10 -22
- package/src/elements/gtk-calendar.ts +2 -1
- package/src/elements/gtk-center-box.ts +2 -1
- package/src/elements/gtk-color-dialog-button.ts +2 -1
- package/src/elements/gtk-drag-icon.ts +2 -1
- package/src/elements/gtk-drawing-area.ts +2 -1
- package/src/elements/gtk-drop-down.ts +2 -1
- package/src/elements/gtk-editable-label.ts +2 -1
- package/src/elements/gtk-entry.ts +2 -1
- package/src/elements/gtk-expander.ts +2 -1
- package/src/elements/gtk-fixed.ts +2 -1
- package/src/elements/gtk-flow-box-child.ts +2 -1
- package/src/elements/gtk-flow-box.ts +2 -1
- package/src/elements/gtk-font-dialog-button.ts +2 -1
- package/src/elements/gtk-frame.ts +2 -1
- package/src/elements/gtk-gl-area.ts +2 -1
- package/src/elements/gtk-graphics-offload.ts +2 -1
- package/src/elements/gtk-grid.ts +2 -1
- package/src/elements/gtk-header-bar.ts +2 -1
- package/src/elements/gtk-image.ts +14 -15
- package/src/elements/gtk-inscription.ts +2 -1
- package/src/elements/gtk-label.ts +12 -5
- package/src/elements/gtk-level-bar.ts +2 -1
- package/src/elements/gtk-list-box-row.ts +2 -1
- package/src/elements/gtk-list-box.ts +2 -1
- package/src/elements/gtk-list-view.ts +2 -1
- package/src/elements/gtk-media-controls.ts +2 -1
- package/src/elements/gtk-menu-button.ts +2 -1
- package/src/elements/gtk-notebook.ts +2 -1
- package/src/elements/gtk-overlay.ts +2 -1
- package/src/elements/gtk-paned.ts +2 -1
- package/src/elements/gtk-picture.ts +2 -1
- package/src/elements/gtk-popover-bin.ts +2 -1
- package/src/elements/gtk-popover-menu-bar.ts +2 -1
- package/src/elements/gtk-popover.ts +2 -1
- package/src/elements/gtk-progress-bar.ts +2 -1
- package/src/elements/gtk-revealer.ts +2 -1
- package/src/elements/gtk-scale-button.ts +2 -1
- package/src/elements/gtk-scale.ts +2 -1
- package/src/elements/gtk-scrollbar.ts +2 -1
- package/src/elements/gtk-scrolled-window.ts +8 -5
- package/src/elements/gtk-search-bar.ts +2 -1
- package/src/elements/gtk-separator.ts +2 -1
- package/src/elements/gtk-spin-button.ts +2 -1
- package/src/elements/gtk-spinner.ts +2 -1
- package/src/elements/gtk-stack-page.ts +2 -1
- package/src/elements/gtk-stack-sidebar.ts +2 -1
- package/src/elements/gtk-stack-switcher.ts +2 -1
- package/src/elements/gtk-stack.ts +2 -1
- package/src/elements/gtk-switch.ts +2 -1
- package/src/elements/gtk-text-view.ts +2 -1
- package/src/elements/gtk-text.ts +2 -1
- package/src/elements/gtk-tree-expander.ts +2 -1
- package/src/elements/gtk-video.ts +2 -1
- package/src/elements/gtk-viewport.ts +2 -1
- package/src/elements/gtk-window-controls.ts +2 -1
- package/src/elements/gtk-window-handle.ts +2 -1
- package/src/elements/gtk-window.ts +2 -1
- package/src/gio.spec.ts +13 -3
- package/src/glib.spec.ts +9 -0
- package/src/gobject-elements.ts +18 -4
- package/src/gtk-label.spec.ts +39 -0
- package/src/gtk-scrolled-window.spec.ts +6 -6
- package/src/gtk-value-doors.spec.ts +19 -0
- package/src/gtk-window.spec.ts +2 -2
- package/src/gtksource/gtk-source-view.ts +2 -1
- package/src/{icon-registry.spec.ts → icon-masks.spec.ts} +4 -100
- package/src/icon-theme.spec.ts +69 -124
- package/src/icon-theme.ts +29 -119
- package/src/index.ts +3 -7
- package/src/namespace/gdk.ts +1 -0
- package/src/namespace/gio.ts +8 -0
- package/src/namespace/glib.ts +4 -0
- package/src/shared-tree-builder.ts +26 -3
- package/src/signals.ts +11 -2
- package/src/source-view/adw-source-view.ts +2 -1
- package/src/split-button.spec.ts +1 -1
- package/src/style-manager.spec.ts +13 -0
- package/src/style-manager.ts +6 -0
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +6 -2
- package/src/widget-element.ts +75 -0
- package/src/widget-map.spec.ts +116 -0
- package/src/widget-mapping.ts +100 -0
- package/src/widget-visible.spec.ts +44 -0
- package/lib/types/icon-registry.d.ts +0 -37
- package/src/icon-registry.ts +0 -163
package/src/icon-theme.spec.ts
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
// The colour half of the icon theme, measured through `getComputedStyle` like
|
|
2
|
-
// `icon-
|
|
2
|
+
// `icon-masks.spec.ts` measures the symbolic half: a class on the node proves nothing
|
|
3
3
|
// about what it paints.
|
|
4
4
|
//
|
|
5
5
|
// The order under test is GLib's (`g_themed_icon_update_names`, which `GtkImage`'s lookup
|
|
6
6
|
// goes through): `X` tries [X, X-symbolic], `X-symbolic` tries [X-symbolic, X], and a name
|
|
7
7
|
// neither answers draws `image-missing`. Over the names the order is gtkicontheme.c's: the
|
|
8
|
-
// current theme (the stylesheet's glyphs) before hicolor, where
|
|
9
|
-
//
|
|
10
|
-
//
|
|
8
|
+
// current theme (the stylesheet's glyphs) before hicolor, where an app's resource files sit.
|
|
9
|
+
// Colour icons come from the shared GResource fixture, on the theme's resource path as
|
|
10
|
+
// `GtkApplication` adds `<resource-base-path>/icons/` (ADR 0116 § 4).
|
|
11
|
+
import { ICON_FIXTURE_PATH, gioResourceFixtures } from '@gjsify/adwaita-core/conformance';
|
|
11
12
|
import { describe, expect, it } from '@gjsify/unit';
|
|
12
13
|
|
|
13
14
|
import { COLOR_ICON_CLASS, type GtkImage } from './elements/gtk-image.js';
|
|
14
|
-
import { fallbackMask, maskOf } from './icon-
|
|
15
|
-
import { isIconAvailable
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
import { fallbackMask, maskOf } from './icon-masks.spec.js';
|
|
16
|
+
import { isIconAvailable } from './icon-theme.js';
|
|
17
|
+
import { Gio, Gtk } from './index.js';
|
|
18
|
+
|
|
19
|
+
const theme = Gtk.IconTheme.get_for_display(null);
|
|
20
|
+
|
|
21
|
+
/** Runs `body` with the fixture's icons on the theme's resource path. */
|
|
22
|
+
async function withIcons(body: () => void): Promise<void> {
|
|
23
|
+
const resource = Gio.Resource.new_from_data(gioResourceFixtures().icons);
|
|
24
|
+
Gio.resources_register(resource);
|
|
25
|
+
const before = theme.get_resource_path();
|
|
26
|
+
theme.add_resource_path(ICON_FIXTURE_PATH);
|
|
27
|
+
// `changed` is deferred: let it drop the previous scan's URLs first.
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
29
|
+
try {
|
|
30
|
+
body();
|
|
31
|
+
} finally {
|
|
32
|
+
theme.set_resource_path(before);
|
|
33
|
+
Gio.resources_unregister(resource);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
28
36
|
|
|
29
37
|
function mount(iconName: string): { el: GtkImage; host: HTMLElement } {
|
|
30
38
|
const host = document.createElement('div');
|
|
@@ -40,7 +48,7 @@ function maskClasses(el: HTMLElement): string[] {
|
|
|
40
48
|
return [...el.classList].filter((c) => c.startsWith('adw-icon--'));
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
/** True when `el` paints
|
|
51
|
+
/** True when `el` paints a resource image as-is and nothing masked. */
|
|
44
52
|
function drawsInColor(el: HTMLElement): boolean {
|
|
45
53
|
const style = getComputedStyle(el);
|
|
46
54
|
return (
|
|
@@ -48,66 +56,41 @@ function drawsInColor(el: HTMLElement): boolean {
|
|
|
48
56
|
maskClasses(el).length === 0 &&
|
|
49
57
|
style.maskImage === 'none' &&
|
|
50
58
|
style.backgroundColor === 'rgba(0, 0, 0, 0)' &&
|
|
51
|
-
|
|
52
|
-
style.backgroundImage.includes('%23e01b24')
|
|
59
|
+
style.backgroundImage.startsWith('url("blob:')
|
|
53
60
|
);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
export const AdwIconThemeTest = async () => {
|
|
57
|
-
await describe('
|
|
58
|
-
await it('
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
host.remove();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
await it('an already-mounted element redraws when the registration lands', () => {
|
|
69
|
-
const APP_ID = 'org.gjsify.LateColorProbe';
|
|
70
|
-
const { el, host } = mount(APP_ID);
|
|
71
|
-
// Before: what GTK draws for a name its theme does not have.
|
|
72
|
-
expect(maskOf(el)).toBe(fallbackMask());
|
|
73
|
-
|
|
74
|
-
registerColorIcon(APP_ID, COLOR_SVG);
|
|
75
|
-
expect(drawsInColor(el)).toBe(true);
|
|
76
|
-
host.remove();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
await it("AdwAboutDialog's application-icon draws in colour", () => {
|
|
80
|
-
const APP_ID = 'org.gjsify.AboutColorProbe';
|
|
81
|
-
registerColorIcon(APP_ID, COLOR_SVG);
|
|
82
|
-
|
|
83
|
-
const host = document.createElement('div');
|
|
84
|
-
document.body.appendChild(host);
|
|
85
|
-
const dialog = document.createElement('adw-about-dialog');
|
|
86
|
-
dialog.setAttribute('application-icon', APP_ID);
|
|
87
|
-
host.appendChild(dialog);
|
|
88
|
-
|
|
89
|
-
const icon = dialog.querySelector('.adw-about-dialog-icon') as HTMLElement;
|
|
90
|
-
expect(drawsInColor(icon)).toBe(true);
|
|
91
|
-
// The 96px box, and NOT a solid square under the image: the about dialog used to
|
|
92
|
-
// force `background-color: currentColor` on it.
|
|
93
|
-
expect(getComputedStyle(icon).width).toBe('96px');
|
|
94
|
-
host.remove();
|
|
64
|
+
await describe('a full-colour resource icon draws as-is', async () => {
|
|
65
|
+
await it('draws its image, unmasked and unfilled', async () => {
|
|
66
|
+
await withIcons(() => {
|
|
67
|
+
const { el, host } = mount('flat');
|
|
68
|
+
expect(drawsInColor(el)).toBe(true);
|
|
69
|
+
expect(isIconAvailable('flat')).toBe(true);
|
|
70
|
+
host.remove();
|
|
71
|
+
});
|
|
95
72
|
});
|
|
96
73
|
|
|
97
|
-
await it('
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
74
|
+
await it("AdwAboutDialog's application-icon draws in colour", async () => {
|
|
75
|
+
await withIcons(() => {
|
|
76
|
+
const host = document.createElement('div');
|
|
77
|
+
document.body.appendChild(host);
|
|
78
|
+
const dialog = document.createElement('adw-about-dialog');
|
|
79
|
+
dialog.setAttribute('application-icon', 'flat');
|
|
80
|
+
host.appendChild(dialog);
|
|
81
|
+
|
|
82
|
+
const icon = dialog.querySelector('.adw-about-dialog-icon') as HTMLElement;
|
|
83
|
+
expect(drawsInColor(icon)).toBe(true);
|
|
84
|
+
// The 96px box, and NOT a solid square under the image: the about dialog used to
|
|
85
|
+
// force `background-color: currentColor` on it.
|
|
86
|
+
expect(getComputedStyle(icon).width).toBe('96px');
|
|
87
|
+
host.remove();
|
|
88
|
+
});
|
|
106
89
|
});
|
|
107
90
|
});
|
|
108
91
|
|
|
109
92
|
await describe('the lookup order is GThemedIcon’s', async () => {
|
|
110
|
-
await it('a symbolic icon still masks — a compiled one with no
|
|
93
|
+
await it('a symbolic icon still masks — a compiled one with no resource', () => {
|
|
111
94
|
const { el, host } = mount('go-next-symbolic');
|
|
112
95
|
expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
|
|
113
96
|
expect(maskClasses(el)).toStrictEqual(['adw-icon--go-next']);
|
|
@@ -116,41 +99,28 @@ export const AdwIconThemeTest = async () => {
|
|
|
116
99
|
host.remove();
|
|
117
100
|
});
|
|
118
101
|
|
|
119
|
-
await it("the theme's X-symbolic answers X
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
expect(maskOf(plain.el).includes('m%203%203')).toBe(true);
|
|
129
|
-
plain.host.remove();
|
|
130
|
-
|
|
131
|
-
const symbolic = mount(`${NAME}-symbolic`);
|
|
132
|
-
expect(symbolic.el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
|
|
133
|
-
expect(maskOf(symbolic.el).includes('m%203%203')).toBe(true);
|
|
134
|
-
expect(getComputedStyle(symbolic.el).backgroundImage).toBe('none');
|
|
135
|
-
symbolic.host.remove();
|
|
102
|
+
await it("the theme's X-symbolic answers X before a resource's colour X, as on GTK", async () => {
|
|
103
|
+
// The fixture ships a colour `16x16/actions/go-next.png`; gtkicontheme.c
|
|
104
|
+
// `real_choose_icon` goes theme outer, name inner, and the file is hicolor's.
|
|
105
|
+
await withIcons(() => {
|
|
106
|
+
const { el, host } = mount('go-next');
|
|
107
|
+
expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
|
|
108
|
+
expect(maskClasses(el)).toStrictEqual(['adw-icon--go-next']);
|
|
109
|
+
host.remove();
|
|
110
|
+
});
|
|
136
111
|
});
|
|
137
112
|
|
|
138
|
-
await it('X-symbolic falls back to the colour X,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
registerIcon(NAME, SYMBOLIC_SVG);
|
|
147
|
-
expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
|
|
148
|
-
expect(maskOf(el).includes('m%203%203')).toBe(true);
|
|
149
|
-
host.remove();
|
|
113
|
+
await it('X-symbolic falls back to the colour X', async () => {
|
|
114
|
+
await withIcons(() => {
|
|
115
|
+
const { el, host } = mount('flat-symbolic');
|
|
116
|
+
expect(drawsInColor(el)).toBe(true);
|
|
117
|
+
expect(isIconAvailable('flat-symbolic')).toBe(true);
|
|
118
|
+
host.remove();
|
|
119
|
+
});
|
|
150
120
|
});
|
|
151
121
|
|
|
152
122
|
await it('an unknown name draws image-missing', () => {
|
|
153
|
-
for (const unknown of ['org.gjsify.
|
|
123
|
+
for (const unknown of ['org.gjsify.NeverShipped', 'gjsify-theme-never-shipped', 'flat']) {
|
|
154
124
|
const { el, host } = mount(unknown);
|
|
155
125
|
expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
|
|
156
126
|
expect(maskOf(el)).toBe(fallbackMask());
|
|
@@ -159,29 +129,4 @@ export const AdwIconThemeTest = async () => {
|
|
|
159
129
|
}
|
|
160
130
|
});
|
|
161
131
|
});
|
|
162
|
-
|
|
163
|
-
await describe('registerColorIcon rejects what it cannot draw faithfully', async () => {
|
|
164
|
-
await it('throws on a name that is empty, spaced, or symbolic', () => {
|
|
165
|
-
expect(() => registerColorIcon('', COLOR_SVG)).toThrow();
|
|
166
|
-
expect(() => registerColorIcon('a b', COLOR_SVG)).toThrow();
|
|
167
|
-
// The symbolic half has its own door; GTK's theme treats all three suffixes as
|
|
168
|
-
// symbolic files.
|
|
169
|
-
expect(() => registerColorIcon('gjsify-probe-symbolic', COLOR_SVG)).toThrow();
|
|
170
|
-
expect(() => registerColorIcon('gjsify-probe-symbolic-ltr', COLOR_SVG)).toThrow();
|
|
171
|
-
expect(() => registerColorIcon('gjsify-probe-symbolic-rtl', COLOR_SVG)).toThrow();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
await it('throws on a source that is not SVG or an image URL, and registers nothing', () => {
|
|
175
|
-
const REJECTED = 'org.gjsify.RejectedColorProbe';
|
|
176
|
-
expect(() => registerColorIcon(REJECTED, '')).toThrow();
|
|
177
|
-
expect(() => registerColorIcon(REJECTED, '<p>not svg</p>')).toThrow();
|
|
178
|
-
expect(() => registerColorIcon(REJECTED, 'javascript:alert(1)')).toThrow();
|
|
179
|
-
expect(() => registerColorIcon(REJECTED, 'data:text/html,<svg></svg>')).toThrow();
|
|
180
|
-
expect(isIconAvailable(REJECTED)).toBe(false);
|
|
181
|
-
|
|
182
|
-
const { el, host } = mount(REJECTED);
|
|
183
|
-
expect(maskOf(el)).toBe(fallbackMask());
|
|
184
|
-
host.remove();
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
132
|
};
|
package/src/icon-theme.ts
CHANGED
|
@@ -13,25 +13,18 @@
|
|
|
13
13
|
// and `gtk_icon_theme_lookup_icon` returns the first candidate the theme has, else
|
|
14
14
|
// `image-missing` — it never returns NULL (refs/gtk/gtk/gtkicontheme.c, `choose_icon`).
|
|
15
15
|
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// ends where it ended before this module existed, so nothing that only uses symbolic icons
|
|
20
|
-
// changes.
|
|
21
|
-
//
|
|
22
|
-
// The colour map is keyed by the raw name, not by `normalizeIconName`: an application icon
|
|
23
|
-
// is named after its application ID (`org.gnome.Builder`, adw-about-dialog.c:1215), which is
|
|
24
|
-
// not a CSS token and never could be a mask class, but is a perfectly good Map key.
|
|
16
|
+
// The stylesheet compiles a SUBSET of Adwaita's symbolic glyphs (the `.adw-icon--<name>`
|
|
17
|
+
// masks); that subset is the port's current theme. Everything else — an application's own
|
|
18
|
+
// symbolic glyphs and its full-colour icons alike — comes from GResource files.
|
|
25
19
|
//
|
|
26
20
|
// RESOURCE PATHS (ADR 0116 § 4). The names go through the `Gtk.IconTheme` resource paths an
|
|
27
21
|
// application added (`GtkApplication` adds `<resource-base-path>/icons/` at startup);
|
|
28
22
|
// `@gjsify/adwaita-core`'s `lookupResourceIcon` decides which file answers, as gtkicontheme.c
|
|
29
|
-
// does, and asks this module only whether the stylesheet holds a symbolic glyph
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// with a warning.
|
|
23
|
+
// does, and asks this module only whether the stylesheet holds a symbolic glyph: the
|
|
24
|
+
// stylesheet's `X-symbolic` answers `X` before a resource's colour `X`, as Adwaita's does on
|
|
25
|
+
// GNOME. A file answers as a blob URL of its bytes: a symbolic one is drawn as a mask in
|
|
26
|
+
// `currentColor`, the rest as-is. A browser draws SVG and PNG; an XPM or a compressed entry
|
|
27
|
+
// draws `image-missing` with a warning.
|
|
35
28
|
//
|
|
36
29
|
// Original implementation.
|
|
37
30
|
|
|
@@ -42,14 +35,11 @@ import {
|
|
|
42
35
|
resources_lookup_data,
|
|
43
36
|
type IconDirection,
|
|
44
37
|
} from '@gjsify/adwaita-core';
|
|
45
|
-
import { toDataUri } from '@gjsify/adwaita-icons/utils';
|
|
46
38
|
|
|
47
39
|
import { IconTheme } from './namespace/gtk-platform.js';
|
|
48
40
|
|
|
49
41
|
/** What one icon name resolves to, in the order GTK's theme would try it. */
|
|
50
42
|
export type IconLookup =
|
|
51
|
-
/** A registered full-colour image, drawn as-is. `url` is ready for `url("…")`. */
|
|
52
|
-
| { kind: 'color'; name: string; url: string }
|
|
53
43
|
/**
|
|
54
44
|
* A file under a `Gtk.IconTheme` resource path; `symbolic` files are masks in `currentColor`.
|
|
55
45
|
* `url` is `null` when the browser cannot draw the file (XPM, compressed): draw `image-missing`.
|
|
@@ -61,21 +51,9 @@ export type IconLookup =
|
|
|
61
51
|
| { kind: 'missing' };
|
|
62
52
|
|
|
63
53
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
const SYMBOLIC_FILE_NAME = /-symbolic(?:-ltr|-rtl)?$/;
|
|
68
|
-
|
|
69
|
-
/** Schemes a colour icon may be fetched from. Anything else can run script or is unknown. */
|
|
70
|
-
const ALLOWED_PROTOCOLS = new Set(['http:', 'https:', 'blob:', 'file:']);
|
|
71
|
-
|
|
72
|
-
const colorIcons = new Map<string, string>();
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Elements that drew a lookup result and have to redraw when the theme changes. A symbolic
|
|
76
|
-
* registration is live through CSS and needs no redraw, but a colour one is an inline
|
|
77
|
-
* `background-image`, and GTK's own images re-run their lookup on the theme's `changed`
|
|
78
|
-
* signal for the same reason.
|
|
54
|
+
* Elements that drew a lookup result and have to redraw when the theme changes. A resource
|
|
55
|
+
* file is an inline `background-image` or mask, and GTK's own images re-run their lookup on
|
|
56
|
+
* the theme's `changed` signal for the same reason.
|
|
79
57
|
*/
|
|
80
58
|
const listeners = new Set<() => void>();
|
|
81
59
|
|
|
@@ -87,11 +65,6 @@ export function onIconThemeChanged(listener: () => void): () => void {
|
|
|
87
65
|
};
|
|
88
66
|
}
|
|
89
67
|
|
|
90
|
-
/** Run every subscriber — after any registration that can change a lookup's answer. */
|
|
91
|
-
export function notifyIconThemeChanged(): void {
|
|
92
|
-
for (const listener of listeners) listener();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
68
|
/** Whether a symbolic glyph for an already-normalized name is in the cascade. */
|
|
96
69
|
function hasSymbolic(normalized: string): boolean {
|
|
97
70
|
if (normalized === '') return false;
|
|
@@ -102,11 +75,9 @@ function hasSymbolic(normalized: string): boolean {
|
|
|
102
75
|
* Resolve `iconName` as `gtk_icon_theme_lookup_by_gicon` would for a `GtkImage`. `null` when
|
|
103
76
|
* no name was given — the state where GTK draws nothing, not `image-missing`.
|
|
104
77
|
*
|
|
105
|
-
* When
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* `<gtk-image>` behaved before this module. The cascade is read only to choose between a
|
|
109
|
-
* symbolic glyph and a colour entry or file.
|
|
78
|
+
* When no resource holds a name, the request is returned as a symbolic one WITHOUT checking
|
|
79
|
+
* the cascade: the mask class falls back to `image-missing` through CSS by itself. The cascade
|
|
80
|
+
* is read only to choose between a symbolic glyph and a resource file.
|
|
110
81
|
*/
|
|
111
82
|
export function lookupIcon(iconName: string | null | undefined, request: IconRequest = {}): IconLookup | null {
|
|
112
83
|
if (iconName === null || iconName === undefined || iconName === '') return null;
|
|
@@ -133,7 +104,7 @@ const resourceUrls = new Map<string, string | null>();
|
|
|
133
104
|
theme.connect('changed', () => {
|
|
134
105
|
for (const url of resourceUrls.values()) if (url !== null) URL.revokeObjectURL(url);
|
|
135
106
|
resourceUrls.clear();
|
|
136
|
-
|
|
107
|
+
for (const listener of listeners) listener();
|
|
137
108
|
});
|
|
138
109
|
|
|
139
110
|
const RESOURCE_TYPES: Record<string, string> = { '.svg': 'image/svg+xml', '.png': 'image/png' };
|
|
@@ -159,8 +130,8 @@ function resourceUrl(path: string): string | null {
|
|
|
159
130
|
}
|
|
160
131
|
|
|
161
132
|
/**
|
|
162
|
-
* The lookup over the
|
|
163
|
-
*
|
|
133
|
+
* The lookup over the theme's resource paths; `null` when no resource answers any name, a
|
|
134
|
+
* `symbolic` entry when the current theme's glyph comes first.
|
|
164
135
|
*/
|
|
165
136
|
export function resourceIcon(iconName: string, request: IconRequest): IconLookup | null {
|
|
166
137
|
const match = lookupResourceIcon(theme, {
|
|
@@ -170,11 +141,9 @@ export function resourceIcon(iconName: string, request: IconRequest): IconLookup
|
|
|
170
141
|
},
|
|
171
142
|
scale: request.scale ?? 1,
|
|
172
143
|
themeHas: (name) => hasSymbolic(normalizeIconName(name)),
|
|
173
|
-
hicolorHas: (name) => colorIcons.has(name),
|
|
174
144
|
});
|
|
175
145
|
if (match === null) return null;
|
|
176
146
|
if (match.kind === 'theme') return { kind: 'symbolic', name: normalizeIconName(match.name) };
|
|
177
|
-
if (match.kind === 'hicolor') return { kind: 'color', name: match.name, url: colorIcons.get(match.name)! };
|
|
178
147
|
return {
|
|
179
148
|
kind: 'resource',
|
|
180
149
|
name: match.name,
|
|
@@ -185,78 +154,19 @@ export function resourceIcon(iconName: string, request: IconRequest): IconLookup
|
|
|
185
154
|
}
|
|
186
155
|
|
|
187
156
|
/**
|
|
188
|
-
* Whether `
|
|
189
|
-
*
|
|
190
|
-
*
|
|
157
|
+
* Whether `name` draws something other than `image-missing` — a file under a `Gtk.IconTheme`
|
|
158
|
+
* resource path, or a symbolic glyph compiled into the stylesheet. The name is looked up
|
|
159
|
+
* exactly as `<gtk-image icon-name>` looks it up.
|
|
160
|
+
*
|
|
161
|
+
* Reads the live cascade rather than a generated list, so it answers for a glyph that arrived
|
|
162
|
+
* by any route, including a consumer's own `--icon-<name>` declaration. It is deliberately the
|
|
163
|
+
* CUSTOM PROPERTY that is read and not the rendered `mask-image` of a probe element: since
|
|
164
|
+
* `_icon.scss` gives every icon box the `image-missing` fallback, a rendered mask is never
|
|
165
|
+
* `none`, and comparing against the fallback would answer `false` for `image-missing` itself.
|
|
191
166
|
*/
|
|
192
|
-
export function
|
|
193
|
-
const found = lookupIcon(
|
|
167
|
+
export function isIconAvailable(name: string): boolean {
|
|
168
|
+
const found = lookupIcon(name);
|
|
194
169
|
if (found === null || found.kind === 'missing') return false;
|
|
195
170
|
if (found.kind === 'resource') return found.url !== null;
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** `source` as a URL `background-image` can load, or a thrown reason why not. */
|
|
200
|
-
function colorIconUrl(name: string, source: string): string {
|
|
201
|
-
if (source.trimStart().startsWith('<')) {
|
|
202
|
-
const root = new DOMParser().parseFromString(source, 'image/svg+xml').documentElement;
|
|
203
|
-
if (root.tagName !== 'svg') {
|
|
204
|
-
throw new TypeError(
|
|
205
|
-
`registerColorIcon(${JSON.stringify(name)}): the source starts with '<' but parsed ` +
|
|
206
|
-
`to <${root.tagName}>, not an <svg> root.`,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
return toDataUri(source);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
let url: URL;
|
|
213
|
-
try {
|
|
214
|
-
url = new URL(source, document.baseURI);
|
|
215
|
-
} catch {
|
|
216
|
-
throw new TypeError(`registerColorIcon(${JSON.stringify(name)}): the source is neither SVG source nor a URL.`);
|
|
217
|
-
}
|
|
218
|
-
const isImageData = url.protocol === 'data:' && /^data:image\//i.test(url.href);
|
|
219
|
-
if (!isImageData && !ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
220
|
-
throw new TypeError(
|
|
221
|
-
`registerColorIcon(${JSON.stringify(name)}): ${JSON.stringify(url.protocol)} URLs are not ` +
|
|
222
|
-
'accepted — use http(s):, blob:, file: or a data:image/ URI.',
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
return url.href;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Register a FULL-COLOUR icon under `name` — what an application ships in
|
|
230
|
-
* `hicolor/scalable/apps/<app-id>.svg` and GTK finds through its icon theme. Every
|
|
231
|
-
* `<gtk-image>` asking for the name draws the image unrecoloured, the way `AdwAboutDialog`'s
|
|
232
|
-
* `application-icon` does on GNOME.
|
|
233
|
-
*
|
|
234
|
-
* `name` is the EXACT icon name, reverse-DNS application IDs included. A name with a
|
|
235
|
-
* `-symbolic` suffix THROWS: that is the other half of the theme, and {@link registerIcon}
|
|
236
|
-
* is its door. `source` is SVG source, or a URL (resolved against the document) to any
|
|
237
|
-
* image the browser can draw.
|
|
238
|
-
*
|
|
239
|
-
* Re-registering a name replaces the image, and every mounted `<gtk-image>` redraws.
|
|
240
|
-
*
|
|
241
|
-
* CLIENT-SIDE ONLY, like `registerIcon`: it reads `document`.
|
|
242
|
-
*/
|
|
243
|
-
export function registerColorIcon(name: string, source: string): void {
|
|
244
|
-
if (typeof name !== 'string' || name === '' || /\s/.test(name)) {
|
|
245
|
-
throw new TypeError(
|
|
246
|
-
`registerColorIcon: ${JSON.stringify(name)} is not an icon name — it must be non-empty ` +
|
|
247
|
-
'and contain no whitespace.',
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
if (SYMBOLIC_FILE_NAME.test(name)) {
|
|
251
|
-
throw new TypeError(
|
|
252
|
-
`registerColorIcon: ${JSON.stringify(name)} names a SYMBOLIC icon, which is recoloured ` +
|
|
253
|
-
'rather than drawn as-is — register it with registerIcon() instead.',
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
if (typeof source !== 'string' || source.trim() === '') {
|
|
257
|
-
throw new TypeError(`registerColorIcon(${JSON.stringify(name)}): the source is empty.`);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
colorIcons.set(name, colorIconUrl(name, source));
|
|
261
|
-
notifyIconThemeChanged();
|
|
171
|
+
return hasSymbolic(found.name);
|
|
262
172
|
}
|
package/src/index.ts
CHANGED
|
@@ -85,13 +85,9 @@ export type { BuildOptions, BuiltTree, MountOptions, MountedSharedTree, SizeSour
|
|
|
85
85
|
export { registerTemplateClass } from './template-classes.js';
|
|
86
86
|
|
|
87
87
|
// The stylesheet compiles a chosen SUBSET of `@gjsify/adwaita-icons` (the whole set is
|
|
88
|
-
// ~1.07 MB of data-URI)
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
export { isIconAvailable, registerIcon } from './icon-registry.js';
|
|
92
|
-
// The full-colour half of the icon theme — an application icon drawn as-is, the web stand-in
|
|
93
|
-
// for an app's `hicolor/…/apps/<app-id>.svg`. See `icon-theme.ts` for the lookup order.
|
|
94
|
-
export { registerColorIcon } from './icon-theme.js';
|
|
88
|
+
// ~1.07 MB of data-URI). An app's own icons, symbolic and full-colour, come from its
|
|
89
|
+
// GResource through `Gtk.IconTheme`'s resource paths (ADR 0116); see `icon-theme.ts`.
|
|
90
|
+
export { isIconAvailable } from './icon-theme.js';
|
|
95
91
|
|
|
96
92
|
// THE WIDGET CLASSES ARE REACHABLE ONLY AS `Adw.<Name>` / `Gtk.<Name>` (ADR 0034
|
|
97
93
|
// clause 2 + § Amendment 6). The namespace used to be the LAST line in the file, under a
|
package/src/namespace/gdk.ts
CHANGED
|
@@ -15,6 +15,7 @@ export const Display = gdk.Display;
|
|
|
15
15
|
export const Clipboard = gdk.Clipboard;
|
|
16
16
|
export const ContentProvider = gdk.ContentProvider;
|
|
17
17
|
export const ContentFormats = gdk.ContentFormats;
|
|
18
|
+
export const AppLaunchContext = gdk.AppLaunchContext;
|
|
18
19
|
|
|
19
20
|
export const CURRENT_TIME = GDK_PLATFORM_CURRENT_TIME;
|
|
20
21
|
export const {
|
package/src/namespace/gio.ts
CHANGED
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
|
|
25
25
|
import '../file-chooser-host.js';
|
|
26
26
|
|
|
27
|
+
import { createAppInfo } from '@gjsify/adwaita-core';
|
|
28
|
+
|
|
29
|
+
import { webPlatformHost } from '../platform-host.js';
|
|
30
|
+
|
|
27
31
|
export { GioMenu as Menu, GioMenuItem as MenuItem } from '@gjsify/adwaita-core';
|
|
28
32
|
export {
|
|
29
33
|
GioFile as File,
|
|
@@ -47,3 +51,7 @@ export {
|
|
|
47
51
|
} from '@gjsify/adwaita-core';
|
|
48
52
|
export { GioApplication as Application } from '../application.js';
|
|
49
53
|
export { Settings } from '@gjsify/app-settings/browser';
|
|
54
|
+
|
|
55
|
+
// `Gio.AppInfo.launch_default_for_uri` over the host `Gtk.show_uri` uses (ADR 0101): the core's
|
|
56
|
+
// `gio-app-info.ts`.
|
|
57
|
+
export const AppInfo = createAppInfo(webPlatformHost);
|
package/src/namespace/glib.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
// in `glib-paths.ts` of the core; the host has no filesystem, so `get_current_dir()` is `"/"` and
|
|
8
8
|
// `get_system_data_dirs()` is `[]`. A port that knows better replaces the member in its own file.
|
|
9
9
|
//
|
|
10
|
+
// `uri_escape_string` is the core's `glib-uri.ts`, a pure function over UTF-8 bytes.
|
|
11
|
+
//
|
|
10
12
|
// `Bytes` and `Error` are the ADR 0100 subset a `Gio.File` reads and fails with (`get_data`,
|
|
11
13
|
// `matches`, `code`, `message`), re-exported from the core's `glib-bytes.ts` / `glib-error.ts`.
|
|
12
14
|
//
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
getCurrentDir,
|
|
22
24
|
getSystemDataDirs,
|
|
23
25
|
MAXUINT32 as GLIB_MAXUINT32,
|
|
26
|
+
uriEscapeString,
|
|
24
27
|
} from '@gjsify/adwaita-core';
|
|
25
28
|
|
|
26
29
|
const glib = createGLibTimers();
|
|
@@ -40,3 +43,4 @@ export const MAXUINT32 = GLIB_MAXUINT32;
|
|
|
40
43
|
export const build_filenamev = buildFilenamev;
|
|
41
44
|
export const get_current_dir = getCurrentDir;
|
|
42
45
|
export const get_system_data_dirs = getSystemDataDirs;
|
|
46
|
+
export const uri_escape_string = uriEscapeString;
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
// into a host `<div>` it was never given.
|
|
29
29
|
//
|
|
30
30
|
// Placed flat under `src/`, at parity with this package's other single-purpose modules
|
|
31
|
-
// (`accent.ts`, `breakpoints.ts`, `icon-
|
|
31
|
+
// (`accent.ts`, `breakpoints.ts`, `icon-theme.ts`) rather than a new `conformance/` or
|
|
32
32
|
// `builder/` directory for one file: unlike `gtk-host`, this package has no existing
|
|
33
33
|
// `conformance/` home to add to. Exported through the existing barrel, `src/index.ts` — the
|
|
34
34
|
// package's `exports` map ships only `.`, and a new subpath would buy nothing for a module
|
|
@@ -233,7 +233,7 @@ function refuseSiblings(node: SharedTreeNode, door: string): void {
|
|
|
233
233
|
function buildTree(node: SharedTreeNode, record: BuildRecord): BuiltTree {
|
|
234
234
|
// ADR 0093 § 2: the whole tree is checked against the capability table before an element is
|
|
235
235
|
// created. A refused `layout` is named with its node and the reason the table gives.
|
|
236
|
-
assertTreeConstructs('adwaita-web', capabilities, node);
|
|
236
|
+
assertTreeConstructs('adwaita-web', capabilities, node, Object.keys(ARIA_OF));
|
|
237
237
|
if (node.menus !== undefined) record.menus = node.menus;
|
|
238
238
|
const root = buildNode(node, record);
|
|
239
239
|
const siblings = new Map<string, HTMLElement>();
|
|
@@ -512,6 +512,29 @@ function writeLook(el: HTMLElement, node: SharedTreeNode, menus?: SharedTreeNode
|
|
|
512
512
|
// `.blp`'s `styles ["title-1"]` reached the tree and never the page.
|
|
513
513
|
if (node.styleClasses !== undefined && node.styleClasses.length > 0) el.classList.add(...node.styleClasses);
|
|
514
514
|
writeExtensions(el, node);
|
|
515
|
+
writeAccessibility(el, node);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* The `accessibility { }` names this renderer has a door for, and the ARIA attribute each is.
|
|
520
|
+
* The rest (states, relations, `row-index`, …) are refused by name before the build, in
|
|
521
|
+
* {@link assertTreeConstructs}: no element here reads them, so they would be dropped.
|
|
522
|
+
*/
|
|
523
|
+
const ARIA_OF: Readonly<Record<string, string>> = {
|
|
524
|
+
label: 'aria-label',
|
|
525
|
+
description: 'aria-description',
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* `accessibility { label; description; }` as the host's `aria-label` / `aria-description`.
|
|
530
|
+
* Written after the props, so an element that derives a name from its tooltip sees the
|
|
531
|
+
* authored one first; an element whose role lives on an inner control forwards both
|
|
532
|
+
* (`accessible.ts`). A translatable value is written as its source text, as a prop's is.
|
|
533
|
+
*/
|
|
534
|
+
function writeAccessibility(el: HTMLElement, node: SharedTreeNode): void {
|
|
535
|
+
for (const [name, entry] of Object.entries(node.accessibility ?? {})) {
|
|
536
|
+
el.setAttribute(ARIA_OF[name], String(entry.value));
|
|
537
|
+
}
|
|
515
538
|
}
|
|
516
539
|
|
|
517
540
|
/** The `GtkGridLayoutChild` properties `layout { }` may carry, and the CSS Grid line each one is. */
|
|
@@ -586,7 +609,7 @@ export interface BuiltTemplateTree {
|
|
|
586
609
|
*/
|
|
587
610
|
export function buildTemplateTree(tree: SharedTreeNode, scope: TemplateScope, host: HTMLElement): BuiltTemplateTree {
|
|
588
611
|
refuseSiblings(tree, 'buildTemplateTree');
|
|
589
|
-
assertTreeConstructs('adwaita-web', capabilities, tree);
|
|
612
|
+
assertTreeConstructs('adwaita-web', capabilities, tree, Object.keys(ARIA_OF));
|
|
590
613
|
const record = newRecord(undefined, undefined, scope);
|
|
591
614
|
record.menus = tree.menus;
|
|
592
615
|
if (tree.id !== undefined) record.ids.set(tree.id, host);
|
package/src/signals.ts
CHANGED
|
@@ -9,7 +9,16 @@
|
|
|
9
9
|
/** GTK signal name → the DOM event type the element dispatches it as. */
|
|
10
10
|
export type DispatchedSignals = Readonly<Record<string, string>>;
|
|
11
11
|
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The signals `el`'s class and every class above it declare, or none. A class inherits its
|
|
14
|
+
* base's signals as a GObject class does (`map` / `unmap` are declared once, on
|
|
15
|
+
* `WidgetElement`); where two declare the same name, the more derived class wins.
|
|
16
|
+
*/
|
|
13
17
|
export function dispatchedSignalsOf(el: Element): DispatchedSignals {
|
|
14
|
-
|
|
18
|
+
const chain: DispatchedSignals[] = [];
|
|
19
|
+
for (let klass: unknown = el.constructor; typeof klass === 'function'; klass = Object.getPrototypeOf(klass)) {
|
|
20
|
+
const own = Object.getOwnPropertyDescriptor(klass, 'signals')?.value as DispatchedSignals | undefined;
|
|
21
|
+
if (own !== undefined) chain.unshift(own);
|
|
22
|
+
}
|
|
23
|
+
return Object.assign({}, ...chain) as DispatchedSignals;
|
|
15
24
|
}
|
|
@@ -36,13 +36,14 @@ import { adwaitaEditorTheme, adwaitaHighlightStyle, ensureSourceViewStyleInjecte
|
|
|
36
36
|
import { formatHexAddress, formatLineNumber, stripSourceWhitespace } from '@gjsify/adwaita-core';
|
|
37
37
|
import { createGtkImage } from '../elements/gtk-image.js';
|
|
38
38
|
import { createCopyGlyph } from './copy-glyph.js';
|
|
39
|
+
import { WidgetElement } from '../widget-element.js';
|
|
39
40
|
|
|
40
41
|
/** Parse a boolean attribute: present (any value but "false") ⇒ true. */
|
|
41
42
|
function boolAttr(value: string | null): boolean {
|
|
42
43
|
return value !== null && value !== 'false';
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
export class AdwSourceView extends
|
|
46
|
+
export class AdwSourceView extends WidgetElement {
|
|
46
47
|
private _view?: EditorView;
|
|
47
48
|
private _editorHost!: HTMLDivElement;
|
|
48
49
|
private _copyButton!: HTMLButtonElement;
|