@gtkx/testing 2.0.0-beta.5 → 2.0.0-beta.7
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/queries.d.ts.map +1 -1
- package/dist/queries.js +4 -3
- package/dist/queries.js.map +1 -1
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +22 -4
- package/dist/screenshot.js.map +1 -1
- package/dist/traversal.d.ts +1 -1
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +24 -6
- package/dist/traversal.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 +26 -7
- package/dist/user-event/click.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/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/widget-accessible-properties.d.ts +2 -1
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +23 -18
- package/dist/widget-accessible-properties.js.map +1 -1
- package/package.json +8 -6
- package/src/accessible-native.ts +45 -9
- package/src/queries.ts +5 -3
- package/src/screenshot.ts +45 -4
- package/src/traversal.ts +31 -7
- package/src/user-event/click.ts +35 -15
- package/src/user-event/keyboard.ts +32 -3
- package/src/user-event/pointer.ts +11 -15
- package/src/widget-accessible-properties.ts +34 -22
package/src/user-event/click.ts
CHANGED
|
@@ -11,6 +11,7 @@ type PressPoint = { x: number; y: number };
|
|
|
11
11
|
type ClickPhase = "pressed" | "released";
|
|
12
12
|
type ClickSite = { gestures: Gtk.GestureClick[]; point: PressPoint };
|
|
13
13
|
type ClickScope = { isClicked: boolean; isInternalAllowed: boolean };
|
|
14
|
+
type DirectClick = { sites: ClickSite[]; outcome: NativeClick | null };
|
|
14
15
|
|
|
15
16
|
type ClickTarget = {
|
|
16
17
|
widget: Gtk.Widget;
|
|
@@ -59,15 +60,6 @@ const emitGesture = (
|
|
|
59
60
|
}
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
const emitClickPhase = (
|
|
63
|
-
widget: Gtk.Widget,
|
|
64
|
-
controllers: Gtk.GestureClick[],
|
|
65
|
-
nPress: number,
|
|
66
|
-
phase: ClickPhase,
|
|
67
|
-
): void => {
|
|
68
|
-
emitGesture(getCenterPoint(widget), controllers, nPress, phase);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
63
|
const emitSitePhase = (sites: ClickSite[], nPress: number, phase: ClickPhase): void => {
|
|
72
64
|
for (const site of sites) {
|
|
73
65
|
emitGesture(site.point, site.gestures, nPress, phase);
|
|
@@ -97,14 +89,23 @@ const clickGestures = (widget: Gtk.Widget): Gtk.GestureClick[] =>
|
|
|
97
89
|
const getAuthoredClickGestures = (widget: Gtk.Widget): Gtk.GestureClick[] =>
|
|
98
90
|
clickGestures(widget).filter((gesture) => hasClickHandlers(gesture));
|
|
99
91
|
|
|
100
|
-
const isClaimingTarget = (widget: Gtk.Widget): boolean =>
|
|
101
|
-
widget instanceof Gtk.Button || containerFor(widget) !== null;
|
|
102
|
-
|
|
103
92
|
const gestureTargetFor = (widget: Gtk.Widget, isInternalAllowed: boolean): ClickTarget | null => {
|
|
104
93
|
const gestures = clickGestures(widget);
|
|
105
94
|
|
|
106
|
-
if (
|
|
107
|
-
return { widget, container:
|
|
95
|
+
if (widget instanceof Gtk.Button) {
|
|
96
|
+
return { widget, container: null, gestures, isClaiming: true, native: null };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const container = containerFor(widget);
|
|
100
|
+
|
|
101
|
+
if (container !== null) {
|
|
102
|
+
return {
|
|
103
|
+
widget,
|
|
104
|
+
container,
|
|
105
|
+
gestures: gestures.filter((gesture) => hasClickHandlers(gesture)),
|
|
106
|
+
isClaiming: true,
|
|
107
|
+
native: null,
|
|
108
|
+
};
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
const authored = gestures.filter((gesture) => hasClickHandlers(gesture));
|
|
@@ -223,6 +224,24 @@ const applyClickOutcome = (target: ClickTarget, nPress: number): void => {
|
|
|
223
224
|
target.native(target.widget, nPress);
|
|
224
225
|
};
|
|
225
226
|
|
|
227
|
+
const applyContainerClick: NativeClick = (widget, nPress) => {
|
|
228
|
+
const container = containerFor(widget);
|
|
229
|
+
|
|
230
|
+
if (container !== null) {
|
|
231
|
+
applyContainerOutcome({ widget, container, gestures: [], isClaiming: true, native: null }, nPress);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const directClickFor = (widget: Gtk.Widget): DirectClick => {
|
|
236
|
+
const native = nativeClickFor(widget, true);
|
|
237
|
+
const container = native === null ? containerFor(widget) : null;
|
|
238
|
+
const gestures =
|
|
239
|
+
native !== null || container !== null ? getAuthoredClickGestures(widget) : clickGestures(widget);
|
|
240
|
+
const target: ClickTarget = { widget, container, gestures, isClaiming: true, native };
|
|
241
|
+
|
|
242
|
+
return { sites: targetSites(widget, target), outcome: native ?? (container === null ? null : applyContainerClick) };
|
|
243
|
+
};
|
|
244
|
+
|
|
226
245
|
const applyClickOutcomes = (targets: ClickTarget[], nPress: number): void => {
|
|
227
246
|
for (const target of targets) {
|
|
228
247
|
applyClickOutcome(target, nPress);
|
|
@@ -266,4 +285,5 @@ const dblClick = (widget: Gtk.Widget): Promise<void> => deliverClick(widget, 2);
|
|
|
266
285
|
/** Delivers a triple-click gesture. */
|
|
267
286
|
const tripleClick = (widget: Gtk.Widget): Promise<void> => deliverClick(widget, 3);
|
|
268
287
|
|
|
269
|
-
export {
|
|
288
|
+
export { directClickFor, emitSitePhase, click, dblClick, tripleClick };
|
|
289
|
+
export type { DirectClick };
|
|
@@ -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,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);
|
|
@@ -401,22 +401,22 @@ const isSelfLabelling = (widget: Gtk.Widget, targets: Gtk.Widget[]): boolean =>
|
|
|
401
401
|
return targets.every((target) => own.has(target));
|
|
402
402
|
};
|
|
403
403
|
|
|
404
|
-
const
|
|
405
|
-
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
406
|
-
|
|
407
|
-
if (targets === null) {
|
|
408
|
-
return null;
|
|
409
|
-
}
|
|
410
|
-
|
|
404
|
+
const labelledByText = (targets: Gtk.Widget[]): string | null => {
|
|
411
405
|
const texts = collectAccessibleNames(targets);
|
|
412
406
|
|
|
413
407
|
return texts.length > 0 ? texts.join(" ") : null;
|
|
414
408
|
};
|
|
415
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
|
+
|
|
416
416
|
const getWidgetExternalLabelText = (widget: Gtk.Widget): string | null => {
|
|
417
417
|
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
418
418
|
|
|
419
|
-
return targets === null || isSelfLabelling(widget, targets) ? null :
|
|
419
|
+
return targets === null || isSelfLabelling(widget, targets) ? null : labelledByText(targets);
|
|
420
420
|
};
|
|
421
421
|
|
|
422
422
|
const isTooltipUsedAsName = (widget: Gtk.Widget): boolean =>
|
|
@@ -441,28 +441,40 @@ const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
|
|
|
441
441
|
return isTooltipUsedAsName(widget) ? null : callStringGetter(widget, "getTooltipText");
|
|
442
442
|
};
|
|
443
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
|
+
|
|
444
468
|
/**
|
|
445
469
|
* Returns whether a widget is excluded from the accessibility tree, because it or one of its
|
|
446
470
|
* ancestors is marked hidden.
|
|
447
471
|
*
|
|
448
472
|
* @param widget The widget to test.
|
|
449
473
|
*/
|
|
450
|
-
const isInaccessible = (target: Gtk.Accessible): boolean =>
|
|
451
|
-
const widget = requireWidget(target);
|
|
452
|
-
let current: Gtk.Widget | null = widget;
|
|
453
|
-
|
|
454
|
-
while (current) {
|
|
455
|
-
if (readAccessibleBoolean(current, Gtk.AccessibleState.HIDDEN) === true) {
|
|
456
|
-
return true;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
current = current.getParent();
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
return false;
|
|
463
|
-
};
|
|
474
|
+
const isInaccessible = (target: Gtk.Accessible): boolean => buildInaccessibilityCheck()(target);
|
|
464
475
|
|
|
465
476
|
export {
|
|
477
|
+
buildInaccessibilityCheck,
|
|
466
478
|
namingLabelText,
|
|
467
479
|
getWidgetText,
|
|
468
480
|
getWidgetTextContent,
|