@gjsify/adwaita-web 0.57.0 → 0.59.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/lib/types/clipboard-host.d.ts +7 -0
- package/lib/types/gdk.spec.d.ts +1 -0
- package/lib/types/gio.spec.d.ts +1 -0
- package/lib/types/gtksource/gtk-source-view.d.ts +11 -4
- package/lib/types/gtksource/web-editor-driver.d.ts +18 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/namespace/adw.d.ts +1 -0
- package/lib/types/namespace/gdk.d.ts +7 -0
- package/lib/types/namespace/gio.d.ts +1 -1
- package/lib/types/namespace/glib.d.ts +5 -0
- package/lib/types/namespace/gobject.d.ts +2 -0
- package/lib/types/namespace/gtk-enums.d.ts +5 -0
- package/lib/types/namespace/gtk.d.ts +1 -1
- package/lib/types/namespace/gtksource.d.ts +1 -1
- package/lib/types/style-manager.d.ts +5 -0
- package/lib/types/style-manager.spec.d.ts +1 -0
- package/package.json +9 -9
- package/src/application.spec.ts +1 -1
- package/src/clipboard-host.ts +47 -0
- package/src/gdk.spec.ts +122 -0
- package/src/gio.spec.ts +23 -0
- package/src/glib.spec.ts +17 -2
- package/src/gobject-door.ts +7 -0
- package/src/gtksource/gtk-source-view.spec.ts +154 -5
- package/src/gtksource/gtk-source-view.ts +35 -12
- package/src/gtksource/web-editor-driver.ts +144 -15
- package/src/index.ts +2 -0
- package/src/namespace/adw.ts +1 -0
- package/src/namespace/gdk.ts +17 -0
- package/src/namespace/gio.ts +2 -2
- package/src/namespace/glib.ts +17 -2
- package/src/namespace/gobject.ts +2 -0
- package/src/namespace/gtk-enums.ts +24 -5
- package/src/namespace/gtk.ts +1 -1
- package/src/namespace/gtksource.ts +14 -3
- package/src/style-manager.spec.ts +36 -0
- package/src/style-manager.ts +27 -0
- package/src/test.browser.mts +6 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ClipboardHost } from '@gjsify/adwaita-core';
|
|
2
|
+
/**
|
|
3
|
+
* Runs `run` as the handler of a DOM `copy` event: `Clipboard.set_content` calls made meanwhile
|
|
4
|
+
* land in `data`. Returns whether any did, so the caller knows to `preventDefault()`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function interceptCopy(data: DataTransfer | null, run: () => void): boolean;
|
|
7
|
+
export declare const webClipboardHost: ClipboardHost;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const GdkTest: () => Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const GioActionsTest: () => Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GtkAdjustment } from '@gjsify/adwaita-core';
|
|
2
|
-
import type { Buffer } from '@gjsify/gtksource-core';
|
|
2
|
+
import type { Buffer, Gutter } from '@gjsify/gtksource-core';
|
|
3
|
+
import { STOP_EMISSION } from '@gjsify/gtksource-core';
|
|
3
4
|
export declare class GtkSourceView extends HTMLElement {
|
|
4
5
|
static get observedAttributes(): string[];
|
|
5
6
|
private readonly session;
|
|
@@ -7,11 +8,13 @@ export declare class GtkSourceView extends HTMLElement {
|
|
|
7
8
|
private unsubscribe;
|
|
8
9
|
private built;
|
|
9
10
|
private readonly gutter;
|
|
11
|
+
private readonly rightGutter;
|
|
10
12
|
private readonly backdrop;
|
|
11
13
|
private readonly area;
|
|
12
14
|
private readonly vadj;
|
|
13
15
|
private readonly hadj;
|
|
14
16
|
private direction;
|
|
17
|
+
private readonly gutters;
|
|
15
18
|
constructor();
|
|
16
19
|
private syncAdjustments;
|
|
17
20
|
private readonly onAreaScroll;
|
|
@@ -59,8 +62,8 @@ export declare class GtkSourceView extends HTMLElement {
|
|
|
59
62
|
get_first_child(): null;
|
|
60
63
|
get_next_sibling(): Element | null;
|
|
61
64
|
get_parent(): Element | null;
|
|
62
|
-
/**
|
|
63
|
-
get_gutter(
|
|
65
|
+
/** `null` for any side but LEFT and RIGHT, as in GtkSourceView (ADR 0103). */
|
|
66
|
+
get_gutter(window_type: number): Gutter | null;
|
|
64
67
|
get leftMargin(): number;
|
|
65
68
|
set leftMargin(value: number | string);
|
|
66
69
|
get rightMargin(): number;
|
|
@@ -70,7 +73,11 @@ export declare class GtkSourceView extends HTMLElement {
|
|
|
70
73
|
get bottomMargin(): number;
|
|
71
74
|
set bottomMargin(value: number | string);
|
|
72
75
|
/** GJS `connect(name, cb) → id`; the handler's first argument is this view. */
|
|
73
|
-
connect(name: string, callback: (self: GtkSourceView, ...args: never[]) =>
|
|
76
|
+
connect(name: string, callback: (self: GtkSourceView, ...args: never[]) => unknown): number;
|
|
77
|
+
/** `g_signal_connect_after`: runs once the plain handlers and the default of the emission have run. */
|
|
78
|
+
connect_after(name: string, callback: (self: GtkSourceView, ...args: never[]) => unknown): number;
|
|
79
|
+
/** `GObject.signal_stop_emission_by_name(view, …)`: the session owns the emissions. */
|
|
80
|
+
[STOP_EMISSION](detailedSignal: string): void;
|
|
74
81
|
disconnect(id: number): void;
|
|
75
82
|
/** The inner textarea, for focus and tests. */
|
|
76
83
|
get textarea(): HTMLTextAreaElement;
|
|
@@ -4,9 +4,12 @@ export interface EditorParts {
|
|
|
4
4
|
readonly area: HTMLTextAreaElement;
|
|
5
5
|
readonly backdrop: HTMLElement;
|
|
6
6
|
readonly gutter: HTMLElement;
|
|
7
|
+
/** The RIGHT gutter's element, right of the text. */
|
|
8
|
+
readonly rightGutter: HTMLElement;
|
|
7
9
|
}
|
|
8
10
|
export declare class WebEditorDriver implements EditorDriver {
|
|
9
11
|
private readonly parts;
|
|
12
|
+
readonly gutterSides: number[];
|
|
10
13
|
private host;
|
|
11
14
|
private text;
|
|
12
15
|
private caret;
|
|
@@ -14,6 +17,7 @@ export declare class WebEditorDriver implements EditorDriver {
|
|
|
14
17
|
private layout;
|
|
15
18
|
private readonly runs;
|
|
16
19
|
private scheduled;
|
|
20
|
+
private numbers;
|
|
17
21
|
private listening;
|
|
18
22
|
private palette;
|
|
19
23
|
private attached;
|
|
@@ -24,6 +28,11 @@ export declare class WebEditorDriver implements EditorDriver {
|
|
|
24
28
|
detach(): void;
|
|
25
29
|
private onInput;
|
|
26
30
|
private onSelection;
|
|
31
|
+
/**
|
|
32
|
+
* The second and third press of a multi-click: GTK asks `extend-selection` before it selects the word
|
|
33
|
+
* or line. The first press has already placed the caret, which is the location asked about.
|
|
34
|
+
*/
|
|
35
|
+
private onMouseDown;
|
|
27
36
|
/** `accepts-tab` is GTK's default; a textarea would move focus, so Tab inserts a tab. */
|
|
28
37
|
private onKey;
|
|
29
38
|
setText(text: string): void;
|
|
@@ -35,10 +44,19 @@ export declare class WebEditorDriver implements EditorDriver {
|
|
|
35
44
|
private applyLayout;
|
|
36
45
|
setPalette(palette: EditorPalette): void;
|
|
37
46
|
private applyPalette;
|
|
47
|
+
invalidateGutter(): void;
|
|
38
48
|
spliceLines(first: number, removed: number, inserted: number): void;
|
|
39
49
|
paintLine(line: number, _lineStart: number, runs: readonly StyledRun[]): void;
|
|
40
50
|
clearAll(): void;
|
|
41
51
|
/** One repaint per task: a burst of `paintLine` calls from one edit costs one DOM rebuild. */
|
|
42
52
|
private scheduleRender;
|
|
53
|
+
private hasColumns;
|
|
54
|
+
private hasRightColumns;
|
|
43
55
|
private render;
|
|
56
|
+
/** The lines in view, or all of them where nothing is laid out (a detached element, a test DOM). */
|
|
57
|
+
private visibleLines;
|
|
58
|
+
private cellElement;
|
|
59
|
+
private columnElement;
|
|
60
|
+
/** The gutter: the renderers left of the line numbers, the numbers, the renderers right of them. */
|
|
61
|
+
private renderGutter;
|
|
44
62
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { isIconAvailable, registerIcon } from './icon-registry.js';
|
|
|
12
12
|
export * as Adw from './namespace/adw.js';
|
|
13
13
|
export * as Gtk from './namespace/gtk.js';
|
|
14
14
|
export * as Gio from './namespace/gio.js';
|
|
15
|
+
export * as Gdk from './namespace/gdk.js';
|
|
15
16
|
export * as GLib from './namespace/glib.js';
|
|
16
17
|
export * as GObject from './namespace/gobject.js';
|
|
17
18
|
export * as GtkSource from './namespace/gtksource.js';
|
|
@@ -55,3 +55,4 @@ export { AdwWindow as Window } from '../elements/adw-window.js';
|
|
|
55
55
|
export { AdwWindowTitle as WindowTitle } from '../elements/adw-window-title.js';
|
|
56
56
|
export { AdwWrapBox as WrapBox } from '../elements/adw-wrap-box.js';
|
|
57
57
|
export { AdwApplication as Application } from '../application.js';
|
|
58
|
+
export { AdwStyleManager as StyleManager } from '../style-manager.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const gdk: import("@gjsify/adwaita-core").GdkClipboard;
|
|
2
|
+
export declare const Display: {
|
|
3
|
+
get_default(): import("@gjsify/adwaita-core").Display;
|
|
4
|
+
};
|
|
5
|
+
export declare const Clipboard: typeof import("@gjsify/adwaita-core").Clipboard;
|
|
6
|
+
export declare const ContentProvider: Pick<typeof import("@gjsify/adwaita-core").ContentProvider, "new_for_value">;
|
|
7
|
+
export declare const ContentFormats: typeof import("@gjsify/adwaita-core").ContentFormats;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { GioMenu as Menu, GioMenuItem as MenuItem } from '@gjsify/adwaita-core';
|
|
2
|
-
export { SimpleAction, ApplicationFlags } from '@gjsify/adwaita-core';
|
|
2
|
+
export { SimpleAction, SimpleActionGroup, ApplicationFlags } from '@gjsify/adwaita-core';
|
|
3
3
|
export { GioApplication as Application } from '../application.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildFilenamev, getCurrentDir, getSystemDataDirs } from '@gjsify/adwaita-core';
|
|
1
2
|
export declare const PRIORITY_HIGH: number;
|
|
2
3
|
export declare const PRIORITY_DEFAULT: number;
|
|
3
4
|
export declare const PRIORITY_HIGH_IDLE: number;
|
|
@@ -8,3 +9,7 @@ export declare const SOURCE_CONTINUE: boolean;
|
|
|
8
9
|
export declare const idle_add: (priority: number, run: import("@gjsify/adwaita-core").SourceFunc) => number;
|
|
9
10
|
export declare const timeout_add: (priority: number, milliseconds: number, run: import("@gjsify/adwaita-core").SourceFunc) => number;
|
|
10
11
|
export declare const source_remove: (sourceId: number) => boolean;
|
|
12
|
+
export declare const MAXUINT32 = 4294967295;
|
|
13
|
+
export declare const build_filenamev: typeof buildFilenamev;
|
|
14
|
+
export declare const get_current_dir: typeof getCurrentDir;
|
|
15
|
+
export declare const get_system_data_dirs: typeof getSystemDataDirs;
|
|
@@ -2,6 +2,7 @@ declare const GObjectObject: import("@gjsify/adwaita-core").GObjectConstructor;
|
|
|
2
2
|
export { GObjectObject as Object };
|
|
3
3
|
export declare const registerClass: import("@gjsify/adwaita-core").RegisterClass;
|
|
4
4
|
export declare const type_ensure: (gtype: import("@gjsify/adwaita-core").GType) => void;
|
|
5
|
+
export declare const signal_stop_emission_by_name: (instance: import("@gjsify/adwaita-core").GObjectInstance, detailedSignal: string) => void;
|
|
5
6
|
export declare const ParamSpec: typeof import("@gjsify/adwaita-core").ParamSpec;
|
|
6
7
|
export declare const ParamFlags: {
|
|
7
8
|
readonly READABLE: 1;
|
|
@@ -10,6 +11,7 @@ export declare const ParamFlags: {
|
|
|
10
11
|
readonly CONSTRUCT: 4;
|
|
11
12
|
readonly CONSTRUCT_ONLY: 8;
|
|
12
13
|
};
|
|
14
|
+
export declare const Value: typeof import("@gjsify/adwaita-core").Value;
|
|
13
15
|
export declare const TYPE_STRING: import("@gjsify/adwaita-core").GType;
|
|
14
16
|
export declare const TYPE_BOOLEAN: import("@gjsify/adwaita-core").GType;
|
|
15
17
|
export declare const TYPE_INT: import("@gjsify/adwaita-core").GType;
|
|
@@ -20,3 +20,8 @@ export declare const TextWindowType: Readonly<{
|
|
|
20
20
|
readonly TOP: 5;
|
|
21
21
|
readonly BOTTOM: 6;
|
|
22
22
|
}>;
|
|
23
|
+
/** `Gtk.TextExtendSelection`: `WORD` 0 (a double click), `LINE` 1 (a triple click). */
|
|
24
|
+
export declare const TextExtendSelection: Readonly<{
|
|
25
|
+
readonly WORD: 0;
|
|
26
|
+
readonly LINE: 1;
|
|
27
|
+
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { GtkAdjustment as Adjustment } from '@gjsify/adwaita-core';
|
|
2
|
-
export { PolicyType, TextDirection, TextWindowType } from './gtk-enums.js';
|
|
2
|
+
export { PolicyType, TextDirection, TextExtendSelection, TextWindowType } from './gtk-enums.js';
|
|
3
3
|
export { GtkAboutDialog as AboutDialog } from '../elements/gtk-about-dialog.js';
|
|
4
4
|
export { GtkActionBar as ActionBar } from '../elements/gtk-action-bar.js';
|
|
5
5
|
export { GtkApplicationWindow as ApplicationWindow } from '../elements/gtk-application-window.js';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Buffer, Language, LanguageManager, StyleScheme, StyleSchemeManager, init } from '@gjsify/gtksource-core';
|
|
1
|
+
export { Buffer, Gutter, GutterLines, GutterRenderer, GutterRendererText, Language, LanguageManager, StyleScheme, StyleSchemeManager, init, } from '@gjsify/gtksource-core';
|
|
2
2
|
export { GtkSourceView as View } from '../gtksource/gtk-source-view.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const StyleManagerTest: () => Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/adwaita-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"description": "Adwaita/Libadwaita web components for browser targets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -50,17 +50,17 @@
|
|
|
50
50
|
"@codemirror/language": "^6.12.4",
|
|
51
51
|
"@codemirror/state": "^6.7.0",
|
|
52
52
|
"@codemirror/view": "^6.43.4",
|
|
53
|
-
"@gjsify/adwaita-core": "^0.
|
|
54
|
-
"@gjsify/adwaita-fonts": "^0.
|
|
55
|
-
"@gjsify/adwaita-icons": "^0.
|
|
56
|
-
"@gjsify/gtksource-core": "^0.
|
|
53
|
+
"@gjsify/adwaita-core": "^0.59.0",
|
|
54
|
+
"@gjsify/adwaita-fonts": "^0.59.0",
|
|
55
|
+
"@gjsify/adwaita-icons": "^0.59.0",
|
|
56
|
+
"@gjsify/gtksource-core": "^0.59.0",
|
|
57
57
|
"@lezer/highlight": "^1.2.3"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@gjsify/blueprint": "^0.
|
|
61
|
-
"@gjsify/cli": "^0.
|
|
62
|
-
"@gjsify/unit": "^0.
|
|
63
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
60
|
+
"@gjsify/blueprint": "^0.59.0",
|
|
61
|
+
"@gjsify/cli": "^0.59.0",
|
|
62
|
+
"@gjsify/unit": "^0.59.0",
|
|
63
|
+
"@gjsify/vite-plugin-blueprint": "^0.59.0",
|
|
64
64
|
"sass": "^1.101.0",
|
|
65
65
|
"typescript": "^6.0.3"
|
|
66
66
|
},
|
package/src/application.spec.ts
CHANGED
|
@@ -58,7 +58,7 @@ export const ApplicationTest = async () => {
|
|
|
58
58
|
|
|
59
59
|
await describe('adwaita-web: Application (ADR 0098 § 3)', async () => {
|
|
60
60
|
await it('has a vector for every unlocked vfunc', () => {
|
|
61
|
-
expect(unprovenVfuncs(APPLICATION_VECTORS).length).toBe(0);
|
|
61
|
+
expect(unprovenVfuncs(APPLICATION_VECTORS, 'Gio.').length).toBe(0);
|
|
62
62
|
});
|
|
63
63
|
await it('Adw.Application is a Gtk.Application', () => {
|
|
64
64
|
expect(Adw.Application.prototype instanceof Gtk.Application).toBe(true);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// How the web door publishes `Gdk.Clipboard` content (ADR 0096 Amendment 2).
|
|
2
|
+
//
|
|
3
|
+
// The browser has two paths and they are not interchangeable:
|
|
4
|
+
// - inside a DOM `copy` event, `event.clipboardData.setData` is synchronous and needs no
|
|
5
|
+
// permission, but only until the event returns; the handler must then `preventDefault()` so the
|
|
6
|
+
// browser does not replace the data with the selection. This is the path of a `copy-clipboard`
|
|
7
|
+
// handler that replaces the default copy.
|
|
8
|
+
// - anywhere else, `navigator.clipboard.writeText` is asynchronous and needs a secure context and
|
|
9
|
+
// a user activation (or the permission); a refusal rejects and the core logs it.
|
|
10
|
+
|
|
11
|
+
import type { ClipboardHost } from '@gjsify/adwaita-core';
|
|
12
|
+
|
|
13
|
+
let copying: { data: DataTransfer; wrote: boolean } | null = null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Runs `run` as the handler of a DOM `copy` event: `Clipboard.set_content` calls made meanwhile
|
|
17
|
+
* land in `data`. Returns whether any did, so the caller knows to `preventDefault()`.
|
|
18
|
+
*/
|
|
19
|
+
export function interceptCopy(data: DataTransfer | null, run: () => void): boolean {
|
|
20
|
+
if (!data) {
|
|
21
|
+
run();
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const outer = copying;
|
|
25
|
+
const frame = { data, wrote: false };
|
|
26
|
+
copying = frame;
|
|
27
|
+
try {
|
|
28
|
+
run();
|
|
29
|
+
} finally {
|
|
30
|
+
copying = outer;
|
|
31
|
+
}
|
|
32
|
+
return frame.wrote;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const webClipboardHost: ClipboardHost = {
|
|
36
|
+
writeText(text) {
|
|
37
|
+
if (copying) {
|
|
38
|
+
copying.data.setData('text/plain', text);
|
|
39
|
+
copying.wrote = true;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (typeof navigator === 'undefined' || !navigator.clipboard) {
|
|
43
|
+
return Promise.reject(new Error('navigator.clipboard is not available here'));
|
|
44
|
+
}
|
|
45
|
+
return navigator.clipboard.writeText(text);
|
|
46
|
+
},
|
|
47
|
+
};
|
package/src/gdk.spec.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// The Gdk clipboard door of adwaita-web, held to the shared vectors (real GJS is their oracle, in
|
|
2
|
+
// adwaita-core's `gdk.gjs.spec.ts`), plus what only a browser shows: the async Clipboard API for a
|
|
3
|
+
// write outside an event, and the `copy` event's `clipboardData` inside one. Neither needs the
|
|
4
|
+
// permission: `navigator.clipboard` is replaced by a recording fake, and the event carries a
|
|
5
|
+
// `DataTransfer` the test reads.
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
driveGdkClipboardVectors,
|
|
9
|
+
GDK_CLIPBOARD_VECTORS,
|
|
10
|
+
type GdkClipboardNamespaces,
|
|
11
|
+
} from '@gjsify/adwaita-core/conformance';
|
|
12
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
13
|
+
|
|
14
|
+
import { GtkSourceView } from './gtksource/gtk-source-view.js';
|
|
15
|
+
import * as Gdk from './namespace/gdk.js';
|
|
16
|
+
import * as GObject from './namespace/gobject.js';
|
|
17
|
+
|
|
18
|
+
async function withClipboardApi<T>(
|
|
19
|
+
api: { writeText(text: string): Promise<void> },
|
|
20
|
+
body: () => Promise<T>,
|
|
21
|
+
): Promise<T> {
|
|
22
|
+
const original = Object.getOwnPropertyDescriptor(navigator, 'clipboard');
|
|
23
|
+
Object.defineProperty(navigator, 'clipboard', { value: api, configurable: true });
|
|
24
|
+
try {
|
|
25
|
+
return await body();
|
|
26
|
+
} finally {
|
|
27
|
+
if (original) Object.defineProperty(navigator, 'clipboard', original);
|
|
28
|
+
else delete (navigator as unknown as Record<string, unknown>).clipboard;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0));
|
|
33
|
+
|
|
34
|
+
export const GdkTest = async () => {
|
|
35
|
+
const written: string[] = [];
|
|
36
|
+
await withClipboardApi({ writeText: async (text) => void written.push(text) }, () =>
|
|
37
|
+
driveGdkClipboardVectors(
|
|
38
|
+
{
|
|
39
|
+
name: 'adwaita-web',
|
|
40
|
+
isOracle: false,
|
|
41
|
+
display: true,
|
|
42
|
+
GObject,
|
|
43
|
+
Gdk,
|
|
44
|
+
readBack: async () => {
|
|
45
|
+
await tick();
|
|
46
|
+
return written[written.length - 1] ?? null;
|
|
47
|
+
},
|
|
48
|
+
} as unknown as GdkClipboardNamespaces & Parameters<typeof driveGdkClipboardVectors>[0],
|
|
49
|
+
{ describe, it, expect },
|
|
50
|
+
GDK_CLIPBOARD_VECTORS,
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
await describe('adwaita-web: Gdk clipboard door', async () => {
|
|
55
|
+
const mounted = () => {
|
|
56
|
+
const view = document.createElement('gtk-source-view') as GtkSourceView;
|
|
57
|
+
document.body.append(view);
|
|
58
|
+
return view;
|
|
59
|
+
};
|
|
60
|
+
const copyEvent = () =>
|
|
61
|
+
new ClipboardEvent('copy', { cancelable: true, bubbles: true, clipboardData: new DataTransfer() });
|
|
62
|
+
const setText = (text: string) => {
|
|
63
|
+
const value = new GObject.Value().init(GObject.TYPE_STRING);
|
|
64
|
+
value.set_string(text);
|
|
65
|
+
Gdk.Display.get_default().get_clipboard().set_content(Gdk.ContentProvider.new_for_value(value));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
await it('writes into the copy event when a copy-clipboard handler replaces the default copy', () => {
|
|
69
|
+
const view = mounted();
|
|
70
|
+
view.buffer.text = 'ff 00';
|
|
71
|
+
view.textarea.setSelectionRange(0, 2);
|
|
72
|
+
view.connect_after('copy-clipboard', () => setText('0xFF'));
|
|
73
|
+
const event = copyEvent();
|
|
74
|
+
view.textarea.dispatchEvent(event);
|
|
75
|
+
expect(event.clipboardData?.getData('text/plain')).toBe('0xFF');
|
|
76
|
+
expect(event.defaultPrevented).toBe(true);
|
|
77
|
+
view.remove();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await it('leaves the browser copy alone when no handler wrote anything', () => {
|
|
81
|
+
const view = mounted();
|
|
82
|
+
const event = copyEvent();
|
|
83
|
+
view.textarea.dispatchEvent(event);
|
|
84
|
+
expect(event.defaultPrevented).toBe(false);
|
|
85
|
+
view.remove();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await it('publishes through navigator.clipboard outside a copy event', async () => {
|
|
89
|
+
const seen: string[] = [];
|
|
90
|
+
await withClipboardApi({ writeText: async (text) => void seen.push(text) }, async () => {
|
|
91
|
+
setText('outside');
|
|
92
|
+
await tick();
|
|
93
|
+
});
|
|
94
|
+
expect(seen[0]).toBe('outside');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
await it('warns, and keeps the content, when the browser refuses the write', async () => {
|
|
98
|
+
const warnings: unknown[][] = [];
|
|
99
|
+
const original = console.warn;
|
|
100
|
+
console.warn = (...args: unknown[]) => void warnings.push(args);
|
|
101
|
+
try {
|
|
102
|
+
await withClipboardApi({ writeText: () => Promise.reject(new Error('NotAllowedError')) }, async () => {
|
|
103
|
+
setText('refused');
|
|
104
|
+
await tick();
|
|
105
|
+
});
|
|
106
|
+
} finally {
|
|
107
|
+
console.warn = original;
|
|
108
|
+
}
|
|
109
|
+
expect(warnings.length).toBe(1);
|
|
110
|
+
expect(Gdk.Display.get_default().get_clipboard().get_formats().to_string()).toBe(
|
|
111
|
+
'gchararray text/plain;charset=utf-8 text/plain',
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await it('puts get_display and get_clipboard on a registered class', () => {
|
|
116
|
+
const Widget = GObject.registerClass({ GTypeName: 'GdkSpecWidget' }, class extends HTMLElement {});
|
|
117
|
+
const widget = new (Widget as unknown as new () => { get_display(): unknown; get_clipboard(): unknown })();
|
|
118
|
+
expect(widget.get_display()).toBe(Gdk.Display.get_default());
|
|
119
|
+
expect(widget.get_clipboard()).toBe(Gdk.Display.get_default().get_clipboard());
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
};
|
package/src/gio.spec.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The `Gio.SimpleActionGroup` door of adwaita-web, held to the shared vectors (real GJS is their
|
|
2
|
+
// oracle, in adwaita-core's `simple-action-group.gjs.spec.ts`). The door is the namespace module itself.
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SIMPLE_ACTION_GROUP_VECTORS,
|
|
6
|
+
driveSimpleActionGroupVectors,
|
|
7
|
+
type GioActionsLike,
|
|
8
|
+
} from '@gjsify/adwaita-core/conformance';
|
|
9
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
10
|
+
|
|
11
|
+
import * as Gio from './namespace/gio.js';
|
|
12
|
+
|
|
13
|
+
export const GioActionsTest = async () => {
|
|
14
|
+
await driveSimpleActionGroupVectors(
|
|
15
|
+
{ name: 'adwaita-web', isOracle: false, Gio: Gio as unknown as GioActionsLike },
|
|
16
|
+
{ describe, it, expect },
|
|
17
|
+
);
|
|
18
|
+
await describe('adwaita-web: Gio door', async () => {
|
|
19
|
+
await it('has a vector row for every vector', () => {
|
|
20
|
+
expect(SIMPLE_ACTION_GROUP_VECTORS.length > 0).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
};
|
package/src/glib.spec.ts
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
// adwaita-core's `glib-timers.gjs.spec.ts`). The door is the namespace module itself, so a member
|
|
3
3
|
// that stops being exported fails here.
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
GLIB_PATH_VECTORS,
|
|
7
|
+
GLIB_TIMER_VECTORS,
|
|
8
|
+
driveGLibPathVectors,
|
|
9
|
+
driveGLibTimerVectors,
|
|
10
|
+
type GLibPathsLike,
|
|
11
|
+
type GLibTimersLike,
|
|
12
|
+
} from '@gjsify/adwaita-core/conformance';
|
|
6
13
|
import { describe, expect, it } from '@gjsify/unit';
|
|
7
14
|
|
|
8
15
|
import * as GLib from './namespace/glib.js';
|
|
@@ -26,9 +33,17 @@ export const GLibTest = async () => {
|
|
|
26
33
|
},
|
|
27
34
|
{ describe, it, expect },
|
|
28
35
|
);
|
|
36
|
+
await driveGLibPathVectors(
|
|
37
|
+
{ name: 'adwaita-web', isOracle: false, GLib: GLib as unknown as GLibPathsLike },
|
|
38
|
+
{ describe, it, expect },
|
|
39
|
+
);
|
|
29
40
|
await describe('adwaita-web: GLib door', async () => {
|
|
41
|
+
await it('answers the host-bound path questions with the root and no data dirs', () => {
|
|
42
|
+
expect(GLib.get_current_dir()).toBe('/');
|
|
43
|
+
expect(GLib.get_system_data_dirs().length).toBe(0);
|
|
44
|
+
});
|
|
30
45
|
await it('has a vector row for every vector', () => {
|
|
31
|
-
expect(GLIB_TIMER_VECTORS.length > 0).toBe(true);
|
|
46
|
+
expect(GLIB_TIMER_VECTORS.length > 0 && GLIB_PATH_VECTORS.length > 0).toBe(true);
|
|
32
47
|
});
|
|
33
48
|
});
|
|
34
49
|
};
|
package/src/gobject-door.ts
CHANGED
|
@@ -18,9 +18,11 @@ import {
|
|
|
18
18
|
type GObjectDoor,
|
|
19
19
|
type RegisteredClass,
|
|
20
20
|
type TemplateScope,
|
|
21
|
+
widgetDisplayMembers,
|
|
21
22
|
} from '@gjsify/adwaita-core';
|
|
22
23
|
import { propertyOf } from '@gjsify/adwaita-core/tags';
|
|
23
24
|
|
|
25
|
+
import { gdk } from './namespace/gdk.js';
|
|
24
26
|
import { dispatchElement, listenElement } from './gobject-elements.js';
|
|
25
27
|
import { buildTemplateTree, type BuiltTemplateTree } from './shared-tree-builder.js';
|
|
26
28
|
import { registerTemplateClass } from './template-classes.js';
|
|
@@ -119,6 +121,11 @@ export const webDoor: GObjectDoor = {
|
|
|
119
121
|
Object.setPrototypeOf((klass as { prototype: object }).prototype, Constructing.prototype);
|
|
120
122
|
|
|
121
123
|
const proto = (klass as { prototype: Record<string, unknown> }).prototype;
|
|
124
|
+
// `this.get_display()` / `this.get_clipboard()`, as on a realized Gtk.Widget; a class's own wins.
|
|
125
|
+
for (const [name, member] of Object.entries(widgetDisplayMembers(gdk))) {
|
|
126
|
+
if (!(name in proto))
|
|
127
|
+
Object.defineProperty(proto, name, { value: member, writable: true, configurable: true });
|
|
128
|
+
}
|
|
122
129
|
const inherited = proto.connectedCallback as (() => void) | undefined;
|
|
123
130
|
Object.defineProperty(proto, 'connectedCallback', {
|
|
124
131
|
configurable: true,
|