@gtkx/testing 1.0.0-rc.1 → 1.0.0-rc.2
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/dist/act.d.ts +6 -6
- package/dist/act.d.ts.map +1 -1
- package/dist/act.js +34 -26
- package/dist/act.js.map +1 -1
- package/dist/bound-queries.d.ts +4 -3
- package/dist/bound-queries.d.ts.map +1 -1
- package/dist/bound-queries.js.map +1 -1
- package/dist/build-queries.d.ts +5 -5
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +21 -9
- package/dist/build-queries.js.map +1 -1
- package/dist/cleanup-registry.d.ts +4 -3
- package/dist/cleanup-registry.d.ts.map +1 -1
- package/dist/cleanup-registry.js +13 -3
- package/dist/cleanup-registry.js.map +1 -1
- package/dist/config.d.ts +5 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -9
- package/dist/config.js.map +1 -1
- package/dist/editable.d.ts +6 -5
- package/dist/editable.d.ts.map +1 -1
- package/dist/editable.js +7 -5
- package/dist/editable.js.map +1 -1
- package/dist/errors.d.ts +7 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +64 -48
- package/dist/errors.js.map +1 -1
- package/dist/fire-event.d.ts +2 -1
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +2 -1
- package/dist/fire-event.js.map +1 -1
- package/dist/frame-sync.d.ts +2 -1
- package/dist/frame-sync.d.ts.map +1 -1
- package/dist/frame-sync.js +28 -21
- package/dist/frame-sync.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +38 -34
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +72 -58
- package/dist/matchers.js.map +1 -1
- package/dist/pretty-widget.d.ts +6 -4
- package/dist/pretty-widget.d.ts.map +1 -1
- package/dist/pretty-widget.js +64 -24
- package/dist/pretty-widget.js.map +1 -1
- package/dist/queries.d.ts +143 -86
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +231 -147
- package/dist/queries.js.map +1 -1
- package/dist/register-test-hooks.js +2 -1
- package/dist/register-test-hooks.js.map +1 -1
- package/dist/render-hook.d.ts +3 -2
- package/dist/render-hook.d.ts.map +1 -1
- package/dist/render-hook.js +2 -1
- package/dist/render-hook.js.map +1 -1
- package/dist/render.d.ts +3 -2
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +24 -18
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +6 -5
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +16 -12
- package/dist/role-helpers.js.map +1 -1
- package/dist/screen.d.ts +4 -3
- package/dist/screen.d.ts.map +1 -1
- package/dist/screen.js +13 -11
- package/dist/screen.js.map +1 -1
- package/dist/screenshot.d.ts +4 -3
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +43 -36
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +11 -6
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js +10 -5
- package/dist/suggestions.js.map +1 -1
- package/dist/traversal.d.ts +7 -7
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +17 -11
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +39 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/adjustment.d.ts +5 -3
- package/dist/user-event/adjustment.d.ts.map +1 -1
- package/dist/user-event/adjustment.js +6 -4
- package/dist/user-event/adjustment.js.map +1 -1
- package/dist/user-event/click.d.ts +6 -5
- package/dist/user-event/click.d.ts.map +1 -1
- package/dist/user-event/click.js +29 -21
- package/dist/user-event/click.js.map +1 -1
- package/dist/user-event/controller.d.ts +6 -5
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +9 -6
- package/dist/user-event/controller.js.map +1 -1
- package/dist/user-event/dispatch.d.ts +3 -3
- package/dist/user-event/dispatch.d.ts.map +1 -1
- package/dist/user-event/dispatch.js +3 -2
- package/dist/user-event/dispatch.js.map +1 -1
- package/dist/user-event/event-wrapper.d.ts +2 -1
- package/dist/user-event/event-wrapper.d.ts.map +1 -1
- package/dist/user-event/event-wrapper.js +27 -16
- package/dist/user-event/event-wrapper.js.map +1 -1
- package/dist/user-event/gesture.d.ts +18 -14
- package/dist/user-event/gesture.d.ts.map +1 -1
- package/dist/user-event/gesture.js +70 -37
- package/dist/user-event/gesture.js.map +1 -1
- package/dist/user-event/index.d.ts +17 -10
- package/dist/user-event/index.d.ts.map +1 -1
- package/dist/user-event/index.js +7 -3
- package/dist/user-event/index.js.map +1 -1
- package/dist/user-event/keyboard.d.ts +4 -3
- package/dist/user-event/keyboard.d.ts.map +1 -1
- package/dist/user-event/keyboard.js +89 -58
- package/dist/user-event/keyboard.js.map +1 -1
- package/dist/user-event/pointer.d.ts +7 -3
- package/dist/user-event/pointer.d.ts.map +1 -1
- package/dist/user-event/pointer.js +4 -1
- package/dist/user-event/pointer.js.map +1 -1
- package/dist/user-event/selection.d.ts +3 -2
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +26 -27
- package/dist/user-event/selection.js.map +1 -1
- package/dist/user-event/state.d.ts +3 -2
- package/dist/user-event/state.d.ts.map +1 -1
- package/dist/user-event/state.js +2 -1
- package/dist/user-event/state.js.map +1 -1
- package/dist/user-event/text.d.ts +8 -7
- package/dist/user-event/text.d.ts.map +1 -1
- package/dist/user-event/text.js +39 -33
- package/dist/user-event/text.js.map +1 -1
- package/dist/wait-for.d.ts +5 -5
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js +45 -30
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +26 -25
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +106 -61
- package/dist/widget-accessible-properties.js.map +1 -1
- package/dist/within.d.ts +2 -1
- package/dist/within.d.ts.map +1 -1
- package/dist/within.js +2 -1
- package/dist/within.js.map +1 -1
- package/package.json +11 -9
- package/src/act.ts +44 -35
- package/src/bound-queries.ts +5 -4
- package/src/build-queries.ts +84 -43
- package/src/cleanup-registry.ts +18 -5
- package/src/config.ts +16 -12
- package/src/editable.ts +13 -6
- package/src/errors.ts +106 -45
- package/src/fire-event.ts +3 -1
- package/src/frame-sync.ts +38 -21
- package/src/index.ts +2 -1
- package/src/matchers.ts +158 -107
- package/src/pretty-widget.ts +114 -42
- package/src/queries.ts +422 -233
- package/src/register-test-hooks.ts +4 -1
- package/src/render-hook.tsx +10 -4
- package/src/render.tsx +58 -33
- package/src/role-helpers.ts +29 -16
- package/src/screen.ts +17 -12
- package/src/screenshot.ts +61 -38
- package/src/suggestions.ts +29 -10
- package/src/traversal.ts +35 -15
- package/src/types.ts +65 -32
- package/src/user-event/adjustment.ts +23 -10
- package/src/user-event/click.ts +49 -20
- package/src/user-event/controller.ts +21 -7
- package/src/user-event/dispatch.ts +4 -2
- package/src/user-event/event-wrapper.ts +42 -16
- package/src/user-event/gesture.ts +135 -50
- package/src/user-event/index.ts +19 -12
- package/src/user-event/keyboard.ts +147 -61
- package/src/user-event/pointer.ts +20 -8
- package/src/user-event/selection.ts +69 -32
- package/src/user-event/state.ts +4 -2
- package/src/user-event/text.ts +73 -38
- package/src/wait-for.ts +77 -36
- package/src/widget-accessible-properties.ts +218 -74
- package/src/within.ts +7 -4
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
import * as Gdk from "@gtkx/gi/gdk";
|
|
2
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
|
+
import type { UserEventState } from "./state.js";
|
|
3
4
|
import { getEditableDelegate } from "../editable.js";
|
|
4
5
|
import { fireEvent } from "../fire-event.js";
|
|
5
6
|
import { getOrCreateControllers } from "./controller.js";
|
|
6
7
|
import { wrapEvent } from "./event-wrapper.js";
|
|
7
|
-
import type { UserEventState } from "./state.js";
|
|
8
8
|
|
|
9
9
|
/** Options for {@link tab}: when `shift` is set, move focus backward instead of forward. */
|
|
10
|
-
|
|
10
|
+
type TabOptions = {
|
|
11
11
|
shift?: boolean;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const direction = options?.shift ? Gtk.DirectionType.TAB_BACKWARD : Gtk.DirectionType.TAB_FORWARD;
|
|
17
|
-
const root = widget.getRoot();
|
|
18
|
-
|
|
19
|
-
if (root) {
|
|
20
|
-
root.childFocus(direction);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
14
|
+
type KeyAction = { keyval: number; press: boolean };
|
|
15
|
+
type ParseStep = { actions: KeyAction[]; next: number };
|
|
23
16
|
|
|
24
17
|
const KEY_MAP: Record<string, number> = {
|
|
25
18
|
Enter: Gdk.KEY_Return,
|
|
@@ -54,67 +47,110 @@ const KEY_MAP: Record<string, number> = {
|
|
|
54
47
|
F12: Gdk.KEY_F12,
|
|
55
48
|
};
|
|
56
49
|
|
|
57
|
-
|
|
50
|
+
const MODIFIER_KEYVAL_TO_MASK: Record<number, number> = {
|
|
51
|
+
[Gdk.KEY_Shift_L]: Gdk.ModifierType.SHIFT_MASK,
|
|
52
|
+
[Gdk.KEY_Shift_R]: Gdk.ModifierType.SHIFT_MASK,
|
|
53
|
+
[Gdk.KEY_Control_L]: Gdk.ModifierType.CONTROL_MASK,
|
|
54
|
+
[Gdk.KEY_Control_R]: Gdk.ModifierType.CONTROL_MASK,
|
|
55
|
+
[Gdk.KEY_Alt_L]: Gdk.ModifierType.ALT_MASK,
|
|
56
|
+
[Gdk.KEY_Alt_R]: Gdk.ModifierType.ALT_MASK,
|
|
57
|
+
[Gdk.KEY_Meta_L]: Gdk.ModifierType.META_MASK,
|
|
58
|
+
[Gdk.KEY_Meta_R]: Gdk.ModifierType.META_MASK,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const tab = (widget: Gtk.Widget, options?: TabOptions): Promise<void> =>
|
|
62
|
+
wrapEvent(widget, () => {
|
|
63
|
+
const direction = options?.shift ? Gtk.DirectionType.TAB_BACKWARD : Gtk.DirectionType.TAB_FORWARD;
|
|
64
|
+
const root = widget.getRoot();
|
|
65
|
+
|
|
66
|
+
if (root) {
|
|
67
|
+
root.childFocus(direction);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
58
70
|
|
|
59
71
|
const parseKeyToken = (token: string): { keyval: number; press: boolean; release: boolean } => {
|
|
60
72
|
let keyName = token;
|
|
61
|
-
let
|
|
62
|
-
let
|
|
73
|
+
let isPress = true;
|
|
74
|
+
let isRelease = true;
|
|
63
75
|
|
|
64
76
|
if (keyName.startsWith("/")) {
|
|
65
77
|
keyName = keyName.slice(1);
|
|
66
|
-
|
|
78
|
+
isPress = false;
|
|
67
79
|
} else if (keyName.endsWith(">")) {
|
|
68
80
|
keyName = keyName.slice(0, -1);
|
|
69
|
-
|
|
81
|
+
isRelease = false;
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
const keyval = KEY_MAP[keyName];
|
|
85
|
+
|
|
73
86
|
if (keyval === undefined) {
|
|
74
87
|
throw new Error(`Unknown key: {${keyName}}`);
|
|
75
88
|
}
|
|
76
|
-
|
|
89
|
+
|
|
90
|
+
return { keyval, press: isPress, release: isRelease };
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const parseCharAt = (input: string, i: number): ParseStep => {
|
|
94
|
+
const keyval = input.codePointAt(i) ?? 0;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
actions: [
|
|
98
|
+
{ keyval, press: true },
|
|
99
|
+
{ keyval, press: false },
|
|
100
|
+
],
|
|
101
|
+
next: i + 1,
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const parseBraceAt = (input: string, i: number): ParseStep | null => {
|
|
106
|
+
const endBrace = input.indexOf("}", i);
|
|
107
|
+
|
|
108
|
+
if (endBrace === -1) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const { keyval, press, release } = parseKeyToken(input.slice(i + 1, endBrace));
|
|
113
|
+
const actions: KeyAction[] = [];
|
|
114
|
+
|
|
115
|
+
if (press) {
|
|
116
|
+
actions.push({ keyval, press: true });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (release) {
|
|
120
|
+
actions.push({ keyval, press: false });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { actions, next: endBrace + 1 };
|
|
77
124
|
};
|
|
78
125
|
|
|
126
|
+
const parseStepAt = (input: string, i: number): ParseStep | null =>
|
|
127
|
+
input[i] === "{" ? parseBraceAt(input, i) : parseCharAt(input, i);
|
|
128
|
+
|
|
79
129
|
const parseKeyboardInput = (input: string): KeyAction[] => {
|
|
80
130
|
const actions: KeyAction[] = [];
|
|
81
131
|
let i = 0;
|
|
82
132
|
|
|
83
133
|
while (i < input.length) {
|
|
84
|
-
|
|
85
|
-
const keyval = input.codePointAt(i) ?? 0;
|
|
86
|
-
actions.push({ keyval, press: true }, { keyval, press: false });
|
|
87
|
-
i++;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
134
|
+
const step = parseStepAt(input, i);
|
|
90
135
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const { keyval, press, release } = parseKeyToken(input.slice(i + 1, endBrace));
|
|
95
|
-
if (press) actions.push({ keyval, press: true });
|
|
96
|
-
if (release) actions.push({ keyval, press: false });
|
|
136
|
+
if (step === null) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
97
139
|
|
|
98
|
-
|
|
140
|
+
actions.push(...step.actions);
|
|
141
|
+
i = step.next;
|
|
99
142
|
}
|
|
100
143
|
|
|
101
144
|
return actions;
|
|
102
145
|
};
|
|
103
146
|
|
|
104
|
-
const MODIFIER_KEYVAL_TO_MASK: Record<number, number> = {
|
|
105
|
-
[Gdk.KEY_Shift_L]: Gdk.ModifierType.SHIFT_MASK,
|
|
106
|
-
[Gdk.KEY_Shift_R]: Gdk.ModifierType.SHIFT_MASK,
|
|
107
|
-
[Gdk.KEY_Control_L]: Gdk.ModifierType.CONTROL_MASK,
|
|
108
|
-
[Gdk.KEY_Control_R]: Gdk.ModifierType.CONTROL_MASK,
|
|
109
|
-
[Gdk.KEY_Alt_L]: Gdk.ModifierType.ALT_MASK,
|
|
110
|
-
[Gdk.KEY_Alt_R]: Gdk.ModifierType.ALT_MASK,
|
|
111
|
-
[Gdk.KEY_Meta_L]: Gdk.ModifierType.META_MASK,
|
|
112
|
-
[Gdk.KEY_Meta_R]: Gdk.ModifierType.META_MASK,
|
|
113
|
-
};
|
|
114
|
-
|
|
115
147
|
const updateModifierState = (state: UserEventState, action: KeyAction): void => {
|
|
116
148
|
const mask = MODIFIER_KEYVAL_TO_MASK[action.keyval];
|
|
117
|
-
|
|
149
|
+
|
|
150
|
+
if (!mask) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
118
154
|
if (action.press) {
|
|
119
155
|
state.modifierState |= mask;
|
|
120
156
|
} else {
|
|
@@ -122,60 +158,108 @@ const updateModifierState = (state: UserEventState, action: KeyAction): void =>
|
|
|
122
158
|
}
|
|
123
159
|
};
|
|
124
160
|
|
|
125
|
-
const
|
|
161
|
+
const isTriggerMatch = (
|
|
162
|
+
trigger: Gtk.ShortcutTrigger | null,
|
|
163
|
+
keyval: number,
|
|
164
|
+
modifiers: Gdk.ModifierType,
|
|
165
|
+
): boolean => {
|
|
126
166
|
if (trigger instanceof Gtk.KeyvalTrigger) {
|
|
127
167
|
return trigger.getKeyval() === keyval && trigger.getModifiers() === modifiers;
|
|
128
168
|
}
|
|
169
|
+
|
|
129
170
|
if (trigger instanceof Gtk.AlternativeTrigger) {
|
|
130
171
|
return (
|
|
131
|
-
|
|
132
|
-
|
|
172
|
+
isTriggerMatch(trigger.getFirst(), keyval, modifiers) ||
|
|
173
|
+
isTriggerMatch(trigger.getSecond(), keyval, modifiers)
|
|
133
174
|
);
|
|
134
175
|
}
|
|
176
|
+
|
|
135
177
|
return false;
|
|
136
178
|
};
|
|
137
179
|
|
|
138
|
-
const
|
|
180
|
+
const didActivateShortcut = (
|
|
181
|
+
shortcut: Gtk.Shortcut,
|
|
182
|
+
widget: Gtk.Widget,
|
|
183
|
+
keyval: number,
|
|
184
|
+
modifiers: Gdk.ModifierType,
|
|
185
|
+
): boolean => {
|
|
186
|
+
if (!isTriggerMatch(shortcut.getTrigger(), keyval, modifiers)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const action = shortcut.getAction();
|
|
191
|
+
|
|
192
|
+
if (action instanceof Gtk.SignalAction && action.getSignalName() === "move-focus") {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return action?.activate(0 as Gtk.ShortcutActionFlags, widget, shortcut.getArguments()) ?? false;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const didActivateMatchingShortcut = (
|
|
139
200
|
controller: Gtk.ShortcutController,
|
|
140
201
|
widget: Gtk.Widget,
|
|
141
202
|
keyval: number,
|
|
142
203
|
modifiers: number,
|
|
143
204
|
): boolean => {
|
|
144
205
|
const count = controller.getNItems();
|
|
206
|
+
|
|
145
207
|
for (let j = 0; j < count; j++) {
|
|
146
208
|
const shortcut = controller.getItem(j);
|
|
147
|
-
|
|
148
|
-
if (!
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
|
|
210
|
+
if (!(shortcut instanceof Gtk.Shortcut)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (didActivateShortcut(shortcut, widget, keyval, modifiers)) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
152
217
|
}
|
|
218
|
+
|
|
153
219
|
return false;
|
|
154
220
|
};
|
|
155
221
|
|
|
156
|
-
const
|
|
222
|
+
const didDispatchShortcutsOnWidget = (widget: Gtk.Widget, keyval: number, modifiers: number): boolean => {
|
|
157
223
|
const controllers = widget.observeControllers();
|
|
224
|
+
|
|
158
225
|
for (let i = 0; i < controllers.getNItems(); i++) {
|
|
159
226
|
const controller = controllers.getItem(i);
|
|
227
|
+
|
|
160
228
|
if (
|
|
161
229
|
controller instanceof Gtk.ShortcutController &&
|
|
162
|
-
|
|
230
|
+
didActivateMatchingShortcut(controller, widget, keyval, modifiers)
|
|
163
231
|
) {
|
|
164
232
|
return true;
|
|
165
233
|
}
|
|
166
234
|
}
|
|
235
|
+
|
|
167
236
|
return false;
|
|
168
237
|
};
|
|
169
238
|
|
|
170
|
-
const
|
|
239
|
+
const didDispatchShortcuts = (widget: Gtk.Widget, keyval: number, modifiers: number): boolean => {
|
|
171
240
|
const delegate = getEditableDelegate(widget);
|
|
172
|
-
|
|
241
|
+
|
|
242
|
+
if (delegate && didDispatchShortcutsOnWidget(delegate, keyval, modifiers)) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
173
246
|
for (let current: Gtk.Widget | null = widget; current; current = current.getParent()) {
|
|
174
|
-
if (
|
|
247
|
+
if (didDispatchShortcutsOnWidget(current, keyval, modifiers)) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
175
250
|
}
|
|
251
|
+
|
|
176
252
|
return false;
|
|
177
253
|
};
|
|
178
254
|
|
|
255
|
+
const handleKeyPress = async (widget: Gtk.Widget, keyval: number, modifiers: number): Promise<void> => {
|
|
256
|
+
const isHandled = didDispatchShortcuts(widget, keyval, modifiers);
|
|
257
|
+
|
|
258
|
+
if (!isHandled && keyval === Gdk.KEY_Return && widget instanceof Gtk.Editable) {
|
|
259
|
+
await fireEvent(widget, "activate");
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
179
263
|
const applyKeyAction = async (
|
|
180
264
|
widget: Gtk.Widget,
|
|
181
265
|
controllers: Gtk.EventControllerKey[],
|
|
@@ -184,21 +268,23 @@ const applyKeyAction = async (
|
|
|
184
268
|
): Promise<void> => {
|
|
185
269
|
updateModifierState(state, action);
|
|
186
270
|
const signalName = action.press ? "key-pressed" : "key-released";
|
|
271
|
+
|
|
187
272
|
for (const controller of controllers) {
|
|
188
273
|
controller.emit(signalName, action.keyval, 0, state.modifierState);
|
|
189
274
|
}
|
|
275
|
+
|
|
190
276
|
if (action.press) {
|
|
191
|
-
|
|
192
|
-
if (!handled && action.keyval === Gdk.KEY_Return && widget instanceof Gtk.Editable) {
|
|
193
|
-
await fireEvent(widget, "activate");
|
|
194
|
-
}
|
|
277
|
+
await handleKeyPress(widget, action.keyval, state.modifierState);
|
|
195
278
|
}
|
|
196
279
|
};
|
|
197
280
|
|
|
198
|
-
|
|
281
|
+
const keyboard = (state: UserEventState, widget: Gtk.Widget, input: string): Promise<void> =>
|
|
199
282
|
wrapEvent(widget, async () => {
|
|
200
283
|
const controllers = getOrCreateControllers(widget, Gtk.EventControllerKey);
|
|
284
|
+
|
|
201
285
|
for (const action of parseKeyboardInput(input)) {
|
|
202
286
|
await applyKeyAction(widget, controllers, state, action);
|
|
203
287
|
}
|
|
204
288
|
});
|
|
289
|
+
|
|
290
|
+
export { tab, keyboard, type TabOptions };
|
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { UserEventState } from "./state.js";
|
|
2
3
|
import { emitPress, emitRelease } from "./click.js";
|
|
3
4
|
import { getOrCreateControllers } from "./controller.js";
|
|
4
5
|
import { wrapEvent } from "./event-wrapper.js";
|
|
5
|
-
import type { UserEventState } from "./state.js";
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A pointer action token: a full click (`click`, `[MouseLeft]`), a button press (`down`,
|
|
9
|
+
* `[MouseLeft>]`), or a button release (`up`, `[/MouseLeft]`).
|
|
10
|
+
*/
|
|
11
|
+
type PointerInput = "click" | "down" | "up" | "[MouseLeft]" | "[MouseLeft>]" | "[/MouseLeft]";
|
|
9
12
|
|
|
10
|
-
const PRESS_INPUTS = new Set
|
|
11
|
-
const RELEASE_INPUTS = new Set
|
|
12
|
-
const CLICK_INPUTS = new Set
|
|
13
|
+
const PRESS_INPUTS: Set<PointerInput> = new Set(["[MouseLeft>]", "down"]);
|
|
14
|
+
const RELEASE_INPUTS: Set<PointerInput> = new Set(["[/MouseLeft]", "up"]);
|
|
15
|
+
const CLICK_INPUTS: Set<PointerInput> = new Set(["[MouseLeft]", "click"]);
|
|
13
16
|
|
|
14
17
|
const applyPointerInput = (widget: Gtk.Widget, state: UserEventState, input: PointerInput): void => {
|
|
15
18
|
const controllers = getOrCreateControllers(widget, Gtk.GestureClick);
|
|
19
|
+
|
|
16
20
|
if (CLICK_INPUTS.has(input)) {
|
|
17
21
|
emitPress(widget, controllers, 1);
|
|
18
22
|
emitRelease(widget, controllers, 1);
|
|
19
23
|
state.mouseLeftDown = false;
|
|
24
|
+
|
|
20
25
|
return;
|
|
21
26
|
}
|
|
27
|
+
|
|
22
28
|
if (PRESS_INPUTS.has(input) && !state.mouseLeftDown) {
|
|
23
29
|
emitPress(widget, controllers, 1);
|
|
24
30
|
state.mouseLeftDown = true;
|
|
31
|
+
|
|
25
32
|
return;
|
|
26
33
|
}
|
|
34
|
+
|
|
27
35
|
if (RELEASE_INPUTS.has(input) && state.mouseLeftDown) {
|
|
28
36
|
emitRelease(widget, controllers, 1);
|
|
29
37
|
state.mouseLeftDown = false;
|
|
30
38
|
}
|
|
31
39
|
};
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
wrapEvent(widget, () =>
|
|
41
|
+
const pointer = (state: UserEventState, widget: Gtk.Widget, input: PointerInput): Promise<void> =>
|
|
42
|
+
wrapEvent(widget, () => {
|
|
43
|
+
applyPointerInput(widget, state, input);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export { pointer, type PointerInput };
|
|
@@ -2,21 +2,31 @@ import * as Gtk from "@gtkx/gi/gtk";
|
|
|
2
2
|
import { formatRoleList } from "../role-helpers.js";
|
|
3
3
|
import { wrapEvent } from "./event-wrapper.js";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
type CollectionWidget = Gtk.ListView | Gtk.GridView | Gtk.ColumnView;
|
|
6
|
+
type ListBoxRowAction = (listBox: Gtk.ListBox, row: Gtk.ListBoxRow) => void;
|
|
7
|
+
|
|
8
|
+
const SELECTABLE_ROLES: Set<Gtk.AccessibleRole> = new Set([Gtk.AccessibleRole.COMBO_BOX, Gtk.AccessibleRole.LIST]);
|
|
6
9
|
|
|
7
10
|
const isSelectable = (widget: Gtk.Widget): boolean => {
|
|
8
11
|
return SELECTABLE_ROLES.has(widget.getAccessibleRole());
|
|
9
12
|
};
|
|
10
13
|
|
|
11
|
-
const selectListViewItems = (
|
|
14
|
+
const selectListViewItems = (
|
|
15
|
+
selectionModel: Gtk.SelectionModel,
|
|
16
|
+
positions: number[],
|
|
17
|
+
isExclusive: boolean,
|
|
18
|
+
): void => {
|
|
12
19
|
if (positions.length === 0) {
|
|
13
20
|
selectionModel.unselectRange(0, selectionModel.getNItems());
|
|
21
|
+
|
|
14
22
|
return;
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
const [first] = positions;
|
|
18
|
-
|
|
26
|
+
|
|
27
|
+
if (isExclusive && first !== undefined && positions.length === 1) {
|
|
19
28
|
selectionModel.selectItem(first, true);
|
|
29
|
+
|
|
20
30
|
return;
|
|
21
31
|
}
|
|
22
32
|
|
|
@@ -31,16 +41,20 @@ const selectListViewItems = (selectionModel: Gtk.SelectionModel, positions: numb
|
|
|
31
41
|
selectionModel.setSelection(selected, mask);
|
|
32
42
|
};
|
|
33
43
|
|
|
34
|
-
const isListView = (widget: Gtk.Widget): widget is
|
|
35
|
-
|
|
36
|
-
};
|
|
44
|
+
const isListView = (widget: Gtk.Widget): widget is CollectionWidget =>
|
|
45
|
+
widget instanceof Gtk.ListView || widget instanceof Gtk.GridView || widget instanceof Gtk.ColumnView;
|
|
37
46
|
|
|
38
47
|
const selectComboBoxOption = (widget: Gtk.Widget, valueArray: number[]): void => {
|
|
39
48
|
if (valueArray.length > 1) {
|
|
40
49
|
throw new Error("Cannot select multiple options: ComboBox only supports single selection");
|
|
41
50
|
}
|
|
51
|
+
|
|
42
52
|
const [selection] = valueArray;
|
|
43
|
-
|
|
53
|
+
|
|
54
|
+
if (selection === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
44
58
|
if (widget instanceof Gtk.DropDown) {
|
|
45
59
|
widget.setSelected(selection);
|
|
46
60
|
} else if (widget instanceof Gtk.ComboBox) {
|
|
@@ -48,31 +62,41 @@ const selectComboBoxOption = (widget: Gtk.Widget, valueArray: number[]): void =>
|
|
|
48
62
|
}
|
|
49
63
|
};
|
|
50
64
|
|
|
51
|
-
const
|
|
65
|
+
const selectListBoxRow = (listBox: Gtk.ListBox, row: Gtk.ListBoxRow): void => {
|
|
66
|
+
listBox.selectRow(row);
|
|
67
|
+
row.activate();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const unselectListBoxRow = (listBox: Gtk.ListBox, row: Gtk.ListBoxRow): void => {
|
|
71
|
+
listBox.unselectRow(row);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const applyListBoxRows = (listBox: Gtk.ListBox, valueArray: number[], apply: ListBoxRowAction): void => {
|
|
52
75
|
for (const value of valueArray) {
|
|
53
76
|
const row = listBox.getRowAtIndex(value);
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
row.activate();
|
|
58
|
-
} else {
|
|
59
|
-
listBox.unselectRow(row);
|
|
77
|
+
|
|
78
|
+
if (!row) {
|
|
79
|
+
continue;
|
|
60
80
|
}
|
|
81
|
+
|
|
82
|
+
apply(listBox, row);
|
|
61
83
|
}
|
|
62
84
|
};
|
|
63
85
|
|
|
64
86
|
const requireSelectionModel = (
|
|
65
|
-
widget:
|
|
87
|
+
widget: CollectionWidget,
|
|
66
88
|
verb: string,
|
|
67
89
|
): Gtk.SelectionModel => {
|
|
68
90
|
const selectionModel = widget.getModel();
|
|
91
|
+
|
|
69
92
|
if (selectionModel === null) {
|
|
70
93
|
throw new Error(`Cannot ${verb} options: list view has no selection model`);
|
|
71
94
|
}
|
|
95
|
+
|
|
72
96
|
return selectionModel;
|
|
73
97
|
};
|
|
74
98
|
|
|
75
|
-
const selectInListView = (widget:
|
|
99
|
+
const selectInListView = (widget: CollectionWidget, valueArray: number[]): void => {
|
|
76
100
|
const selectionModel = requireSelectionModel(widget, "select");
|
|
77
101
|
const isMultiSelection = selectionModel instanceof Gtk.MultiSelection;
|
|
78
102
|
selectListViewItems(selectionModel, valueArray, !isMultiSelection);
|
|
@@ -84,39 +108,52 @@ const selectByRole = (widget: Gtk.Widget, valueArray: number[]): void => {
|
|
|
84
108
|
}
|
|
85
109
|
|
|
86
110
|
const role = widget.getAccessibleRole();
|
|
111
|
+
|
|
87
112
|
if (role === Gtk.AccessibleRole.COMBO_BOX) {
|
|
88
113
|
selectComboBoxOption(widget, valueArray);
|
|
89
114
|
} else if (widget instanceof Gtk.ListBox) {
|
|
90
|
-
applyListBoxRows(widget, valueArray,
|
|
115
|
+
applyListBoxRows(widget, valueArray, selectListBoxRow);
|
|
91
116
|
}
|
|
92
117
|
};
|
|
93
118
|
|
|
94
|
-
|
|
119
|
+
const runSelectionEvent = (
|
|
120
|
+
widget: Gtk.Widget,
|
|
121
|
+
values: number | number[],
|
|
122
|
+
inListView: (view: Gtk.ListView | Gtk.GridView | Gtk.ColumnView, valueArray: number[]) => void,
|
|
123
|
+
byRole: (widget: Gtk.Widget, valueArray: number[]) => void,
|
|
124
|
+
): Promise<void> =>
|
|
95
125
|
wrapEvent(widget, () => {
|
|
96
126
|
const valueArray = Array.isArray(values) ? values : [values];
|
|
127
|
+
|
|
97
128
|
if (isListView(widget)) {
|
|
98
|
-
|
|
129
|
+
inListView(widget, valueArray);
|
|
130
|
+
|
|
99
131
|
return;
|
|
100
132
|
}
|
|
101
|
-
|
|
133
|
+
|
|
134
|
+
byRole(widget, valueArray);
|
|
102
135
|
});
|
|
103
136
|
|
|
137
|
+
const selectOptions = (widget: Gtk.Widget, values: number | number[]): Promise<void> =>
|
|
138
|
+
runSelectionEvent(widget, values, selectInListView, selectByRole);
|
|
139
|
+
|
|
104
140
|
const deselectInListView = (widget: Gtk.ListView | Gtk.GridView | Gtk.ColumnView, valueArray: number[]): void => {
|
|
105
141
|
const selectionModel = requireSelectionModel(widget, "deselect");
|
|
142
|
+
|
|
106
143
|
for (const pos of valueArray) {
|
|
107
144
|
selectionModel.unselectItem(pos);
|
|
108
145
|
}
|
|
109
146
|
};
|
|
110
147
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
148
|
+
const deselectByRole = (widget: Gtk.Widget, valueArray: number[]): void => {
|
|
149
|
+
if (!(widget instanceof Gtk.ListBox)) {
|
|
150
|
+
throw new TypeError("Cannot deselect options: only ListBox supports deselection");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
applyListBoxRows(widget, valueArray, unselectListBoxRow);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const deselectOptions = (widget: Gtk.Widget, values: number | number[]): Promise<void> =>
|
|
157
|
+
runSelectionEvent(widget, values, deselectInListView, deselectByRole);
|
|
158
|
+
|
|
159
|
+
export { selectOptions, deselectOptions };
|
package/src/user-event/state.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
type UserEventState = {
|
|
2
2
|
modifierState: number;
|
|
3
3
|
mouseLeftDown: boolean;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const createInitialState = (): UserEventState => ({ modifierState: 0, mouseLeftDown: false });
|
|
7
|
+
|
|
8
|
+
export { createInitialState, type UserEventState };
|