@gtkx/testing 2.0.0-beta.1 → 2.0.0-beta.10
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 +43 -40
- package/dist/accessible-native.d.ts +1 -1
- package/dist/accessible-native.d.ts.map +1 -1
- package/dist/accessible-native.js +29 -8
- package/dist/accessible-native.js.map +1 -1
- package/dist/build-queries.d.ts +9 -9
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +2 -1
- package/dist/build-queries.js.map +1 -1
- package/dist/fire-event.d.ts +3 -3
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +14 -4
- package/dist/fire-event.js.map +1 -1
- package/dist/matchers.d.ts +52 -56
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +4 -16
- package/dist/matchers.js.map +1 -1
- package/dist/queries.d.ts +7 -7
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +18 -10
- package/dist/queries.js.map +1 -1
- package/dist/query-helpers.d.ts +2 -2
- package/dist/query-helpers.d.ts.map +1 -1
- package/dist/query-helpers.js +10 -3
- package/dist/query-helpers.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +4 -0
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +1 -1
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +2 -1
- package/dist/role-helpers.js.map +1 -1
- package/dist/screenshot.d.ts +1 -1
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +25 -5
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +1 -1
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js +3 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/traversal.d.ts +4 -4
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +25 -7
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +10 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/click.d.ts +17 -4
- package/dist/user-event/click.d.ts.map +1 -1
- package/dist/user-event/click.js +28 -9
- package/dist/user-event/click.js.map +1 -1
- package/dist/user-event/controller.d.ts +5 -5
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +5 -2
- package/dist/user-event/controller.js.map +1 -1
- package/dist/user-event/index.d.ts +22 -22
- package/dist/user-event/index.d.ts.map +1 -1
- package/dist/user-event/index.js +28 -25
- package/dist/user-event/index.js.map +1 -1
- package/dist/user-event/keyboard.d.ts.map +1 -1
- package/dist/user-event/keyboard.js +23 -2
- package/dist/user-event/keyboard.js.map +1 -1
- package/dist/user-event/list-row-click.d.ts +1 -1
- package/dist/user-event/list-row-click.d.ts.map +1 -1
- package/dist/user-event/list-row-click.js +2 -1
- package/dist/user-event/list-row-click.js.map +1 -1
- package/dist/user-event/pointer.d.ts.map +1 -1
- package/dist/user-event/pointer.js +9 -11
- package/dist/user-event/pointer.js.map +1 -1
- package/dist/user-event/selection.d.ts +2 -2
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +8 -2
- package/dist/user-event/selection.js.map +1 -1
- package/dist/wait-for.d.ts +2 -2
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js +6 -4
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +4 -3
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +24 -17
- package/dist/widget-accessible-properties.js.map +1 -1
- package/dist/widget-getters.d.ts +7 -9
- package/dist/widget-getters.d.ts.map +1 -1
- package/dist/widget-getters.js.map +1 -1
- package/dist/widget-target.d.ts +4 -0
- package/dist/widget-target.d.ts.map +1 -0
- package/dist/widget-target.js +9 -0
- package/dist/widget-target.js.map +1 -0
- package/package.json +13 -10
- package/src/accessible-native.ts +45 -9
- package/src/build-queries.ts +60 -30
- package/src/fire-event.ts +32 -8
- package/src/matchers.ts +63 -85
- package/src/queries.ts +64 -30
- package/src/query-helpers.ts +17 -9
- package/src/render.tsx +7 -2
- package/src/role-helpers.ts +3 -1
- package/src/screenshot.ts +48 -5
- package/src/suggestions.ts +3 -1
- package/src/traversal.ts +35 -11
- package/src/types.ts +14 -10
- package/src/user-event/click.ts +37 -17
- package/src/user-event/controller.ts +8 -5
- package/src/user-event/index.ts +133 -47
- package/src/user-event/keyboard.ts +32 -3
- package/src/user-event/list-row-click.ts +2 -2
- package/src/user-event/pointer.ts +11 -15
- package/src/user-event/selection.ts +12 -4
- package/src/wait-for.ts +9 -6
- package/src/widget-accessible-properties.ts +39 -25
- package/src/widget-getters.ts +11 -13
- package/src/widget-target.ts +11 -0
package/src/user-event/index.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import { delay } from "../timers.js";
|
|
3
|
+
import { requireWidget } from "../widget-target.js";
|
|
3
4
|
import { scroll, slide } from "./adjustment.js";
|
|
4
5
|
import { click, dblClick, tripleClick } from "./click.js";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
drag,
|
|
8
|
+
dragAndDrop,
|
|
9
|
+
drop,
|
|
10
|
+
hover,
|
|
11
|
+
longPress,
|
|
12
|
+
rotate,
|
|
13
|
+
swipe,
|
|
14
|
+
unhover,
|
|
15
|
+
zoom,
|
|
16
|
+
} from "./gesture.js";
|
|
6
17
|
import { keyboard, tab } from "./keyboard.js";
|
|
7
18
|
import { pointer, type PointerInput } from "./pointer.js";
|
|
8
19
|
import { deselectOptions, selectOptions } from "./selection.js";
|
|
@@ -20,55 +31,126 @@ type UserEvent = {
|
|
|
20
31
|
/** Creates an instance with shared keyboard and pointer state. */
|
|
21
32
|
setup: (options?: UserEventOptions) => UserEvent;
|
|
22
33
|
/** Activates or presses and releases a widget. */
|
|
23
|
-
click:
|
|
34
|
+
click: (
|
|
35
|
+
widget: Gtk.Accessible,
|
|
36
|
+
...args: Parameters<typeof click> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
37
|
+
) => Promise<void>;
|
|
24
38
|
/** Delivers a double-click gesture. */
|
|
25
|
-
dblClick:
|
|
39
|
+
dblClick: (
|
|
40
|
+
widget: Gtk.Accessible,
|
|
41
|
+
...args: Parameters<typeof dblClick> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
42
|
+
) => Promise<void>;
|
|
26
43
|
/** Delivers a triple-click gesture. */
|
|
27
|
-
tripleClick:
|
|
44
|
+
tripleClick: (
|
|
45
|
+
widget: Gtk.Accessible,
|
|
46
|
+
...args: Parameters<typeof tripleClick> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
47
|
+
) => Promise<void>;
|
|
28
48
|
/** Moves focus forward or backward within the root. */
|
|
29
|
-
tab:
|
|
49
|
+
tab: (
|
|
50
|
+
widget: Gtk.Accessible,
|
|
51
|
+
...args: Parameters<typeof tab> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
52
|
+
) => Promise<void>;
|
|
30
53
|
/** Types into an editable widget. */
|
|
31
|
-
type:
|
|
54
|
+
type: (
|
|
55
|
+
widget: Gtk.Accessible,
|
|
56
|
+
...args: Parameters<typeof type> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
57
|
+
) => Promise<void>;
|
|
32
58
|
/** Deletes all text from an editable widget. */
|
|
33
|
-
clear:
|
|
59
|
+
clear: (
|
|
60
|
+
widget: Gtk.Accessible,
|
|
61
|
+
...args: Parameters<typeof clear> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
62
|
+
) => Promise<void>;
|
|
34
63
|
/** Copies the current selection. */
|
|
35
|
-
copy:
|
|
64
|
+
copy: (
|
|
65
|
+
widget: Gtk.Accessible,
|
|
66
|
+
...args: Parameters<typeof copy> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
67
|
+
) => Promise<void>;
|
|
36
68
|
/** Cuts the current selection. */
|
|
37
|
-
cut:
|
|
69
|
+
cut: (
|
|
70
|
+
widget: Gtk.Accessible,
|
|
71
|
+
...args: Parameters<typeof cut> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
72
|
+
) => Promise<void>;
|
|
38
73
|
/** Pastes text or clipboard contents. */
|
|
39
|
-
paste:
|
|
74
|
+
paste: (
|
|
75
|
+
widget: Gtk.Accessible,
|
|
76
|
+
...args: Parameters<typeof paste> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
77
|
+
) => Promise<void>;
|
|
40
78
|
/** Selects positions without activating them. */
|
|
41
79
|
selectOptions: typeof selectOptions;
|
|
42
80
|
/** Unselects positions without activating them. */
|
|
43
81
|
deselectOptions: typeof deselectOptions;
|
|
44
82
|
/** Emits a motion `enter` on the widget, adding a motion controller when it has none. */
|
|
45
|
-
hover:
|
|
83
|
+
hover: (
|
|
84
|
+
widget: Gtk.Accessible,
|
|
85
|
+
...args: Parameters<typeof hover> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
86
|
+
) => Promise<void>;
|
|
46
87
|
/** Emits a motion `leave` on the widget, adding a motion controller when it has none. */
|
|
47
|
-
unhover:
|
|
88
|
+
unhover: (
|
|
89
|
+
widget: Gtk.Accessible,
|
|
90
|
+
...args: Parameters<typeof unhover> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
91
|
+
) => Promise<void>;
|
|
48
92
|
/** Emits `angle-changed` on the widget's rotate gestures. */
|
|
49
|
-
rotate:
|
|
93
|
+
rotate: (
|
|
94
|
+
widget: Gtk.Accessible,
|
|
95
|
+
...args: Parameters<typeof rotate> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
96
|
+
) => Promise<void>;
|
|
50
97
|
/** Emits `scale-changed` on the widget's zoom gestures. */
|
|
51
|
-
zoom:
|
|
98
|
+
zoom: (
|
|
99
|
+
widget: Gtk.Accessible,
|
|
100
|
+
...args: Parameters<typeof zoom> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
101
|
+
) => Promise<void>;
|
|
52
102
|
/** Emits `swipe` with the given velocity on the widget's swipe gestures. */
|
|
53
|
-
swipe:
|
|
103
|
+
swipe: (
|
|
104
|
+
widget: Gtk.Accessible,
|
|
105
|
+
...args: Parameters<typeof swipe> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
106
|
+
) => Promise<void>;
|
|
54
107
|
/** Emits `pressed` at the given point on the widget's long-press gestures. */
|
|
55
|
-
longPress:
|
|
108
|
+
longPress: (
|
|
109
|
+
widget: Gtk.Accessible,
|
|
110
|
+
...args: Parameters<typeof longPress> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
111
|
+
) => Promise<void>;
|
|
56
112
|
/** Runs a begin, update, and end sequence on the widget's drag gestures, ending at the given offset. */
|
|
57
|
-
drag:
|
|
113
|
+
drag: (
|
|
114
|
+
widget: Gtk.Accessible,
|
|
115
|
+
...args: Parameters<typeof drag> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
116
|
+
) => Promise<void>;
|
|
58
117
|
/** Emits `drop` with the given content on the widget's drop targets. */
|
|
59
|
-
drop:
|
|
118
|
+
drop: (
|
|
119
|
+
widget: Gtk.Accessible,
|
|
120
|
+
...args: Parameters<typeof drop> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
121
|
+
) => Promise<void>;
|
|
60
122
|
/** Requires a drag source on the source widget, then emits `drop` with the content on the target's drop targets. */
|
|
61
|
-
dragAndDrop:
|
|
123
|
+
dragAndDrop: (
|
|
124
|
+
source: Gtk.Accessible,
|
|
125
|
+
target: Gtk.Accessible,
|
|
126
|
+
...args: Parameters<typeof dragAndDrop> extends [Gtk.Widget, Gtk.Widget, ...infer Args] ? Args : never
|
|
127
|
+
) => Promise<void>;
|
|
62
128
|
/** Emits a jump `change-value` on a Gtk.Range so it moves to the given value. */
|
|
63
|
-
slide:
|
|
129
|
+
slide: (
|
|
130
|
+
widget: Gtk.Accessible,
|
|
131
|
+
...args: Parameters<typeof slide> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
132
|
+
) => Promise<void>;
|
|
64
133
|
/** Adds the delta to the adjustments of the widget itself, or of its nearest scrollable ancestor. */
|
|
65
|
-
scroll:
|
|
134
|
+
scroll: (
|
|
135
|
+
widget: Gtk.Accessible,
|
|
136
|
+
...args: Parameters<typeof scroll> extends [Gtk.Widget, ...infer Args] ? Args : never
|
|
137
|
+
) => Promise<void>;
|
|
66
138
|
/** Sends a key sequence through GTK's propagation chain. */
|
|
67
|
-
keyboard: (widget: Gtk.
|
|
139
|
+
keyboard: (widget: Gtk.Accessible, input: string) => Promise<void>;
|
|
68
140
|
/** Applies a pointer token while preserving held-button state. */
|
|
69
|
-
pointer: (widget: Gtk.
|
|
141
|
+
pointer: (widget: Gtk.Accessible, input: PointerInput) => Promise<void>;
|
|
70
142
|
};
|
|
71
143
|
|
|
144
|
+
const onWidget =
|
|
145
|
+
<Args extends unknown[]>(helper: (widget: Gtk.Widget, ...args: Args) => Promise<void>) =>
|
|
146
|
+
(target: Gtk.Accessible, ...args: Args): Promise<void> =>
|
|
147
|
+
helper(requireWidget(target), ...args);
|
|
148
|
+
|
|
149
|
+
const onWidgetPair =
|
|
150
|
+
<Args extends unknown[]>(helper: (source: Gtk.Widget, target: Gtk.Widget, ...args: Args) => Promise<void>) =>
|
|
151
|
+
(source: Gtk.Accessible, target: Gtk.Accessible, ...args: Args): Promise<void> =>
|
|
152
|
+
helper(requireWidget(source), requireWidget(target), ...args);
|
|
153
|
+
|
|
72
154
|
/** User interactions that dispatch GTK events and gestures. */
|
|
73
155
|
const userEvent: UserEvent = {
|
|
74
156
|
...createInstance(createInitialState(), {}),
|
|
@@ -93,30 +175,34 @@ function createInstance(state: UserEventState, options: UserEventOptions): UserE
|
|
|
93
175
|
|
|
94
176
|
return {
|
|
95
177
|
setup: (overrides?: UserEventOptions): UserEvent => createInstance(state, { ...options, ...overrides }),
|
|
96
|
-
click: after(click),
|
|
97
|
-
dblClick: after(dblClick),
|
|
98
|
-
tripleClick: after(tripleClick),
|
|
99
|
-
tab: after(tab),
|
|
100
|
-
type: after(type),
|
|
101
|
-
clear: after(clear),
|
|
102
|
-
copy: after(copy),
|
|
103
|
-
cut: after(cut),
|
|
104
|
-
paste: after(paste),
|
|
105
|
-
selectOptions: after(selectOptions),
|
|
106
|
-
deselectOptions: after(deselectOptions),
|
|
107
|
-
hover: after(hover),
|
|
108
|
-
unhover: after(unhover),
|
|
109
|
-
rotate: after(rotate),
|
|
110
|
-
zoom: after(zoom),
|
|
111
|
-
swipe: after(swipe),
|
|
112
|
-
longPress: after(longPress),
|
|
113
|
-
drag: after(drag),
|
|
114
|
-
drop: after(drop),
|
|
115
|
-
dragAndDrop: after(dragAndDrop),
|
|
116
|
-
slide: after(slide),
|
|
117
|
-
scroll: after(scroll),
|
|
118
|
-
keyboard: after(
|
|
119
|
-
|
|
178
|
+
click: after(onWidget(click)),
|
|
179
|
+
dblClick: after(onWidget(dblClick)),
|
|
180
|
+
tripleClick: after(onWidget(tripleClick)),
|
|
181
|
+
tab: after(onWidget(tab)),
|
|
182
|
+
type: after(onWidget(type)),
|
|
183
|
+
clear: after(onWidget(clear)),
|
|
184
|
+
copy: after(onWidget(copy)),
|
|
185
|
+
cut: after(onWidget(cut)),
|
|
186
|
+
paste: after(onWidget(paste)),
|
|
187
|
+
selectOptions: after(onWidget(selectOptions)),
|
|
188
|
+
deselectOptions: after(onWidget(deselectOptions)),
|
|
189
|
+
hover: after(onWidget(hover)),
|
|
190
|
+
unhover: after(onWidget(unhover)),
|
|
191
|
+
rotate: after(onWidget(rotate)),
|
|
192
|
+
zoom: after(onWidget(zoom)),
|
|
193
|
+
swipe: after(onWidget(swipe)),
|
|
194
|
+
longPress: after(onWidget(longPress)),
|
|
195
|
+
drag: after(onWidget(drag)),
|
|
196
|
+
drop: after(onWidget(drop)),
|
|
197
|
+
dragAndDrop: after(onWidgetPair(dragAndDrop)),
|
|
198
|
+
slide: after(onWidget(slide)),
|
|
199
|
+
scroll: after(onWidget(scroll)),
|
|
200
|
+
keyboard: after(
|
|
201
|
+
onWidget((widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input)),
|
|
202
|
+
),
|
|
203
|
+
pointer: after(
|
|
204
|
+
onWidget((widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input)),
|
|
205
|
+
),
|
|
120
206
|
};
|
|
121
207
|
}
|
|
122
208
|
|
|
@@ -18,6 +18,7 @@ type KeyAction = { keyval: number; isPress: boolean };
|
|
|
18
18
|
type ParseStep = { actions: KeyAction[]; next: number };
|
|
19
19
|
type KeyStopController = Gtk.EventControllerKey | Gtk.ShortcutController;
|
|
20
20
|
type KeyStop = { widget: Gtk.Widget; controller: KeyStopController };
|
|
21
|
+
type KeyTarget = { root: Gtk.Root | null; focus: Gtk.Widget | null };
|
|
21
22
|
|
|
22
23
|
const KEY_MAP: Record<string, number> = {
|
|
23
24
|
Enter: Gdk.KEY_Return,
|
|
@@ -366,9 +367,37 @@ const didStopHandlePress = (stop: KeyStop, keyval: number, modifiers: Gdk.Modifi
|
|
|
366
367
|
return controller.emit("key-pressed", keyval, 0, modifiers);
|
|
367
368
|
};
|
|
368
369
|
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
370
|
+
const keyTargetFor = (widget: Gtk.Widget): KeyTarget => {
|
|
371
|
+
const root = widget.getRoot();
|
|
372
|
+
|
|
373
|
+
return { root, focus: root instanceof Gtk.Window ? root.getFocus() : null };
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const isKeyTargetInvalidated = (widget: Gtk.Widget, target: KeyTarget): boolean => {
|
|
377
|
+
const current = keyTargetFor(widget);
|
|
378
|
+
|
|
379
|
+
return current.root !== target.root || current.focus !== target.focus || !widget.getMapped();
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const didHandlePress = (widget: Gtk.Widget, keyval: number, modifiers: Gdk.ModifierType): boolean => {
|
|
383
|
+
const target = keyTargetFor(widget);
|
|
384
|
+
|
|
385
|
+
if (didDispatchApplicationAccels(widget, keyval, modifiers)) {
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (isKeyTargetInvalidated(widget, target)) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
for (const stop of keyPropagationChain(widget)) {
|
|
394
|
+
if (didStopHandlePress(stop, keyval, modifiers) || isKeyTargetInvalidated(widget, target)) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return false;
|
|
400
|
+
};
|
|
372
401
|
|
|
373
402
|
const emitKeyReleased = (widget: Gtk.Widget, keyval: number, modifiers: Gdk.ModifierType): void => {
|
|
374
403
|
for (const stop of keyPropagationChain(widget)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
1
|
import * as GLib from "@gtkx/gi/glib";
|
|
2
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
3
|
import { ancestors } from "../traversal.js";
|
|
4
4
|
import { callBooleanGetter, hasWidgetMethod } from "../widget-getters.js";
|
|
5
5
|
|
|
@@ -41,7 +41,7 @@ const applyRowPress = (row: Gtk.Widget, press: number): void => {
|
|
|
41
41
|
selectRow(row);
|
|
42
42
|
|
|
43
43
|
if (isRowActivatedByPress(row, press)) {
|
|
44
|
-
|
|
44
|
+
Gtk.Widget.prototype.activate.call(row);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
-
import type { NativeClick } from "./native-click.js";
|
|
3
2
|
import type { UserEventState } from "./state.js";
|
|
4
|
-
import {
|
|
3
|
+
import { type DirectClick, directClickFor, emitSitePhase } from "./click.js";
|
|
5
4
|
import { wrapEvent } from "./event-wrapper.js";
|
|
6
|
-
import { isClickTransparent
|
|
5
|
+
import { isClickTransparent } from "./native-click.js";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* A pointer action token: a full click (`click`, `[MouseLeft]`), a button press (`down`,
|
|
10
9
|
* `[MouseLeft>]`), or a button release (`up`, `[/MouseLeft]`).
|
|
11
10
|
*/
|
|
12
11
|
type PointerInput = "click" | "down" | "up" | "[MouseLeft]" | "[MouseLeft>]" | "[/MouseLeft]";
|
|
13
|
-
type PointerTarget = { widget: Gtk.Widget;
|
|
12
|
+
type PointerTarget = { widget: Gtk.Widget; click: DirectClick };
|
|
14
13
|
|
|
15
14
|
const PRESS_INPUTS: Set<PointerInput> = new Set(["[MouseLeft>]", "down"]);
|
|
16
15
|
const RELEASE_INPUTS: Set<PointerInput> = new Set(["[/MouseLeft]", "up"]);
|
|
@@ -32,26 +31,23 @@ const pointerWidgetFor = (widget: Gtk.Widget): Gtk.Widget => {
|
|
|
32
31
|
return current;
|
|
33
32
|
};
|
|
34
33
|
|
|
35
|
-
const pointerGesturesFor = (widget: Gtk.Widget, native: NativeClick | null): Gtk.GestureClick[] =>
|
|
36
|
-
native === null ? clickGestures(widget) : getAuthoredClickGestures(widget);
|
|
37
|
-
|
|
38
34
|
const pointerTargetFor = (widget: Gtk.Widget): PointerTarget => {
|
|
39
35
|
const target = pointerWidgetFor(widget);
|
|
40
|
-
const native = nativeClickFor(target, true);
|
|
41
36
|
|
|
42
|
-
return { widget: target,
|
|
37
|
+
return { widget: target, click: directClickFor(target) };
|
|
43
38
|
};
|
|
44
39
|
|
|
45
|
-
const pressPointer = ({
|
|
46
|
-
|
|
40
|
+
const pressPointer = ({ click }: PointerTarget): void => {
|
|
41
|
+
emitSitePhase(click.sites, 1, "pressed");
|
|
47
42
|
};
|
|
48
43
|
|
|
49
|
-
const releasePointer = ({ widget,
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
const releasePointer = ({ widget, click }: PointerTarget): void => {
|
|
45
|
+
emitSitePhase(click.sites, 1, "released");
|
|
46
|
+
click.outcome?.(widget, 1);
|
|
52
47
|
};
|
|
53
48
|
|
|
54
|
-
const isPointerPressable = (target: PointerTarget): boolean =>
|
|
49
|
+
const isPointerPressable = (target: PointerTarget): boolean =>
|
|
50
|
+
target.click.outcome !== null || target.click.sites.some((site) => site.gestures.length > 0);
|
|
55
51
|
|
|
56
52
|
const applyPointerInput = (widget: Gtk.Widget, state: UserEventState, input: PointerInput): void => {
|
|
57
53
|
const target = pointerTargetFor(widget);
|
|
@@ -153,9 +153,17 @@ const runSelectionEvent = (
|
|
|
153
153
|
byRole(widget, valueArray);
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
+
const requireWidget = (target: Gtk.Accessible): Gtk.Widget => {
|
|
157
|
+
if (!(target instanceof Gtk.Widget)) {
|
|
158
|
+
throw new TypeError("Cannot select options on a non-widget accessible");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return target;
|
|
162
|
+
};
|
|
163
|
+
|
|
156
164
|
/** Selects positions without activating them. */
|
|
157
|
-
const selectOptions = (widget: Gtk.
|
|
158
|
-
runSelectionEvent(widget, values, selectInListView, selectByRole);
|
|
165
|
+
const selectOptions = (widget: Gtk.Accessible, values: number | number[]): Promise<void> =>
|
|
166
|
+
runSelectionEvent(requireWidget(widget), values, selectInListView, selectByRole);
|
|
159
167
|
|
|
160
168
|
const deselectInListView = (widget: Gtk.ListView | Gtk.GridView | Gtk.ColumnView, valueArray: number[]): void => {
|
|
161
169
|
const selectionModel = requireSelectionModel(widget, "deselect");
|
|
@@ -176,7 +184,7 @@ const deselectByRole = (widget: Gtk.Widget, valueArray: number[]): void => {
|
|
|
176
184
|
};
|
|
177
185
|
|
|
178
186
|
/** Unselects positions without activating them. */
|
|
179
|
-
const deselectOptions = (widget: Gtk.
|
|
180
|
-
runSelectionEvent(widget, values, deselectInListView, deselectByRole);
|
|
187
|
+
const deselectOptions = (widget: Gtk.Accessible, values: number | number[]): Promise<void> =>
|
|
188
|
+
runSelectionEvent(requireWidget(widget), values, deselectInListView, deselectByRole);
|
|
181
189
|
|
|
182
190
|
export { selectOptions, deselectOptions };
|
package/src/wait-for.ts
CHANGED
|
@@ -4,12 +4,13 @@ import { runWithActEnvironment } from "./act.js";
|
|
|
4
4
|
import { getConfig } from "./config.js";
|
|
5
5
|
import { timeoutError } from "./errors.js";
|
|
6
6
|
import { advanceFakeClock, delay, now } from "./timers.js";
|
|
7
|
+
import { requireWidget } from "./widget-target.js";
|
|
7
8
|
|
|
8
9
|
type PollResult<T> = { status: "resolved"; value: T } | { status: "timedout"; lastError: Error | null };
|
|
9
10
|
/** Widgets watched for removal; null counts as already removed. */
|
|
10
|
-
type RemovalTarget = Gtk.
|
|
11
|
+
type RemovalTarget = Gtk.Accessible | Gtk.Accessible[] | null;
|
|
11
12
|
/** The widgets to watch for removal, or a function re-read on every poll to locate them. */
|
|
12
|
-
type ElementOrCallback = Gtk.
|
|
13
|
+
type ElementOrCallback = Gtk.Accessible | Gtk.Accessible[] | (() => RemovalTarget);
|
|
13
14
|
|
|
14
15
|
const DEFAULT_INTERVAL = 50;
|
|
15
16
|
const ELEMENT_NOT_REMOVED = new Error("Element not yet removed");
|
|
@@ -102,10 +103,8 @@ const waitFor = <T>(callback: () => T | Promise<T>, options?: WaitForOptions): P
|
|
|
102
103
|
const getTarget = (elementOrCallback: ElementOrCallback): RemovalTarget =>
|
|
103
104
|
typeof elementOrCallback === "function" ? elementOrCallback() : elementOrCallback;
|
|
104
105
|
|
|
105
|
-
const isWidgetRemoved = (
|
|
106
|
-
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
106
|
+
const isWidgetRemoved = (target: Gtk.Accessible): boolean => {
|
|
107
|
+
const widget = requireWidget(target);
|
|
109
108
|
|
|
110
109
|
try {
|
|
111
110
|
return widget.getRoot() === null;
|
|
@@ -115,6 +114,10 @@ const isWidgetRemoved = (widget: Gtk.Widget | null): boolean => {
|
|
|
115
114
|
};
|
|
116
115
|
|
|
117
116
|
const isTargetRemoved = (target: RemovalTarget): boolean => {
|
|
117
|
+
if (target === null) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
118
121
|
if (Array.isArray(target)) {
|
|
119
122
|
return target.every((widget) => isWidgetRemoved(widget));
|
|
120
123
|
}
|
|
@@ -13,6 +13,7 @@ import { EDITABLE_ROLES, isEditable, readEditableText } from "./editable.js";
|
|
|
13
13
|
import { isNameFromAuthor, isNameProhibited } from "./role-naming.js";
|
|
14
14
|
import { children, descendants, relationCandidates } from "./traversal.js";
|
|
15
15
|
import { callBooleanGetter, callStringGetter, getCallableMethod } from "./widget-getters.js";
|
|
16
|
+
import { requireWidget } from "./widget-target.js";
|
|
16
17
|
|
|
17
18
|
type WidgetValueField = "now" | "min" | "max";
|
|
18
19
|
type CheckedState = "checked" | "unchecked" | "mixed";
|
|
@@ -64,12 +65,12 @@ const getLabelText = (widget: Gtk.Widget): string | null => {
|
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
const stripMnemonic = (text: string): string => text.replaceAll(/_(.)/g, "$1");
|
|
67
|
-
const isUnderlineUsed = (widget:
|
|
68
|
+
const isUnderlineUsed = (widget: object): boolean => callBooleanGetter(widget, "getUseUnderline") ?? false;
|
|
68
69
|
|
|
69
|
-
const readNamingText = (widget:
|
|
70
|
+
const readNamingText = (widget: object, getter: string, value: string): string =>
|
|
70
71
|
getter !== EDITABLE_TEXT_GETTER && isUnderlineUsed(widget) ? stripMnemonic(value) : value;
|
|
71
72
|
|
|
72
|
-
const readFirstText = (widget:
|
|
73
|
+
const readFirstText = (widget: object, getters: string[]): string | null => {
|
|
73
74
|
for (const getter of getters) {
|
|
74
75
|
const value = callStringGetter(widget, getter);
|
|
75
76
|
|
|
@@ -87,7 +88,7 @@ const readFirstText = (widget: Gtk.Widget, getters: string[]): string | null =>
|
|
|
87
88
|
*
|
|
88
89
|
* @param widget The widget to read text from.
|
|
89
90
|
*/
|
|
90
|
-
const getWidgetText = (widget: Gtk.
|
|
91
|
+
const getWidgetText = (widget: Gtk.Accessible): string | null => readFirstText(widget, DEFAULT_TEXT_GETTERS);
|
|
91
92
|
|
|
92
93
|
const namingLabelText = (widget: Gtk.Widget): string | null => {
|
|
93
94
|
const text = getLabelText(widget);
|
|
@@ -400,22 +401,22 @@ const isSelfLabelling = (widget: Gtk.Widget, targets: Gtk.Widget[]): boolean =>
|
|
|
400
401
|
return targets.every((target) => own.has(target));
|
|
401
402
|
};
|
|
402
403
|
|
|
403
|
-
const
|
|
404
|
-
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
405
|
-
|
|
406
|
-
if (targets === null) {
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
|
|
404
|
+
const labelledByText = (targets: Gtk.Widget[]): string | null => {
|
|
410
405
|
const texts = collectAccessibleNames(targets);
|
|
411
406
|
|
|
412
407
|
return texts.length > 0 ? texts.join(" ") : null;
|
|
413
408
|
};
|
|
414
409
|
|
|
410
|
+
const getWidgetLabelledByText = (widget: Gtk.Widget): string | null => {
|
|
411
|
+
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
412
|
+
|
|
413
|
+
return targets === null ? null : labelledByText(targets);
|
|
414
|
+
};
|
|
415
|
+
|
|
415
416
|
const getWidgetExternalLabelText = (widget: Gtk.Widget): string | null => {
|
|
416
417
|
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
417
418
|
|
|
418
|
-
return targets === null || isSelfLabelling(widget, targets) ? null :
|
|
419
|
+
return targets === null || isSelfLabelling(widget, targets) ? null : labelledByText(targets);
|
|
419
420
|
};
|
|
420
421
|
|
|
421
422
|
const isTooltipUsedAsName = (widget: Gtk.Widget): boolean =>
|
|
@@ -440,27 +441,40 @@ const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
|
|
|
440
441
|
return isTooltipUsedAsName(widget) ? null : callStringGetter(widget, "getTooltipText");
|
|
441
442
|
};
|
|
442
443
|
|
|
444
|
+
const isHiddenWithin = (widget: Gtk.Widget, hiddenWidgets: Map<Gtk.Widget, boolean>): boolean => {
|
|
445
|
+
const cached = hiddenWidgets.get(widget);
|
|
446
|
+
|
|
447
|
+
if (cached !== undefined) {
|
|
448
|
+
return cached;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const parent = widget.getParent();
|
|
452
|
+
|
|
453
|
+
const isHidden =
|
|
454
|
+
readAccessibleBoolean(widget, Gtk.AccessibleState.HIDDEN) === true ||
|
|
455
|
+
(parent !== null && isHiddenWithin(parent, hiddenWidgets));
|
|
456
|
+
|
|
457
|
+
hiddenWidgets.set(widget, isHidden);
|
|
458
|
+
|
|
459
|
+
return isHidden;
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const buildInaccessibilityCheck = (): ((target: Gtk.Accessible) => boolean) => {
|
|
463
|
+
const hiddenWidgets: Map<Gtk.Widget, boolean> = new Map();
|
|
464
|
+
|
|
465
|
+
return (target) => isHiddenWithin(requireWidget(target), hiddenWidgets);
|
|
466
|
+
};
|
|
467
|
+
|
|
443
468
|
/**
|
|
444
469
|
* Returns whether a widget is excluded from the accessibility tree, because it or one of its
|
|
445
470
|
* ancestors is marked hidden.
|
|
446
471
|
*
|
|
447
472
|
* @param widget The widget to test.
|
|
448
473
|
*/
|
|
449
|
-
const isInaccessible = (
|
|
450
|
-
let current: Gtk.Widget | null = widget;
|
|
451
|
-
|
|
452
|
-
while (current) {
|
|
453
|
-
if (readAccessibleBoolean(current, Gtk.AccessibleState.HIDDEN) === true) {
|
|
454
|
-
return true;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
current = current.getParent();
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
return false;
|
|
461
|
-
};
|
|
474
|
+
const isInaccessible = (target: Gtk.Accessible): boolean => buildInaccessibilityCheck()(target);
|
|
462
475
|
|
|
463
476
|
export {
|
|
477
|
+
buildInaccessibilityCheck,
|
|
464
478
|
namingLabelText,
|
|
465
479
|
getWidgetText,
|
|
466
480
|
getWidgetTextContent,
|
package/src/widget-getters.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import type * as GObject from "@gtkx/gi/gobject";
|
|
2
|
-
import type * as Gtk from "@gtkx/gi/gtk";
|
|
3
1
|
import { getClassType, getInstanceType, TYPE_INVALID, typeName } from "@gtkx/runtime";
|
|
4
2
|
import { resolveWrapperClass } from "@gtkx/runtime/internal";
|
|
5
3
|
import { type AnyClass, walkClassChain } from "@gtkx/utils";
|
|
6
4
|
|
|
7
5
|
const UNNAMED_TYPE_TAG = "Object";
|
|
8
6
|
|
|
9
|
-
const getWidgetMethod = (widget:
|
|
10
|
-
const getWidgetTypeName = (widget:
|
|
7
|
+
const getWidgetMethod = (widget: object, name: string): unknown => Reflect.get(widget, name);
|
|
8
|
+
const getWidgetTypeName = (widget: object): string | null => typeName(getInstanceType(widget));
|
|
11
9
|
const isClass = (value: unknown): value is AnyClass => typeof value === "function";
|
|
12
10
|
|
|
13
11
|
const nonEmpty = (value: unknown): string | undefined =>
|
|
14
12
|
typeof value === "string" && value.length > 0 ? value : undefined;
|
|
15
13
|
|
|
16
|
-
const getObjectClass = (object:
|
|
14
|
+
const getObjectClass = (object: object): AnyClass | null => {
|
|
17
15
|
const cls: unknown = object.constructor;
|
|
18
16
|
|
|
19
17
|
return isClass(cls) ? cls : null;
|
|
20
18
|
};
|
|
21
19
|
|
|
22
|
-
const getNearestClassName = (object:
|
|
20
|
+
const getNearestClassName = (object: object): string | undefined =>
|
|
23
21
|
walkClassChain(getObjectClass(object), (ancestor) => nonEmpty(ancestor.name));
|
|
24
22
|
|
|
25
|
-
const getUntypedClassName = (object:
|
|
23
|
+
const getUntypedClassName = (object: object): string | undefined => {
|
|
26
24
|
const cls = getObjectClass(object);
|
|
27
25
|
|
|
28
26
|
return cls !== null && getClassType(cls) === TYPE_INVALID ? nonEmpty(cls.name) : undefined;
|
|
@@ -34,7 +32,7 @@ const getExactWrapperName = (type: bigint): string | undefined => {
|
|
|
34
32
|
return wrapper !== null && getClassType(wrapper) === type ? nonEmpty(wrapper.name) : undefined;
|
|
35
33
|
};
|
|
36
34
|
|
|
37
|
-
const getTypeTag = (object:
|
|
35
|
+
const getTypeTag = (object: object): string => {
|
|
38
36
|
const type = getInstanceType(object);
|
|
39
37
|
|
|
40
38
|
return (
|
|
@@ -46,15 +44,15 @@ const getTypeTag = (object: GObject.Object): string => {
|
|
|
46
44
|
);
|
|
47
45
|
};
|
|
48
46
|
|
|
49
|
-
const isDefaultWidgetName = (widget:
|
|
47
|
+
const isDefaultWidgetName = (widget: object, name: string): boolean =>
|
|
50
48
|
name.length === 0 || name === getWidgetTypeName(widget);
|
|
51
49
|
|
|
52
|
-
const hasWidgetMethod = (widget:
|
|
50
|
+
const hasWidgetMethod = (widget: object, name: string): boolean =>
|
|
53
51
|
typeof getWidgetMethod(widget, name) === "function";
|
|
54
52
|
|
|
55
53
|
/* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the signature */
|
|
56
54
|
const getCallableMethod = <Args extends unknown[], Result>(
|
|
57
|
-
widget:
|
|
55
|
+
widget: object,
|
|
58
56
|
name: string,
|
|
59
57
|
): ((...args: Args) => Result) | null => {
|
|
60
58
|
const fn = getWidgetMethod(widget, name);
|
|
@@ -62,13 +60,13 @@ const getCallableMethod = <Args extends unknown[], Result>(
|
|
|
62
60
|
return typeof fn === "function" ? (fn as (...args: Args) => Result).bind(widget) : null;
|
|
63
61
|
};
|
|
64
62
|
|
|
65
|
-
const callBooleanGetter = (widget:
|
|
63
|
+
const callBooleanGetter = (widget: object, method: string): boolean | null => {
|
|
66
64
|
const value = getCallableMethod<[], unknown>(widget, method)?.();
|
|
67
65
|
|
|
68
66
|
return typeof value === "boolean" ? value : null;
|
|
69
67
|
};
|
|
70
68
|
|
|
71
|
-
const callStringGetter = (widget:
|
|
69
|
+
const callStringGetter = (widget: object, method: string): string | null => {
|
|
72
70
|
const value = getCallableMethod<[], unknown>(widget, method)?.();
|
|
73
71
|
|
|
74
72
|
return typeof value === "string" ? value : null;
|