@gtkx/testing 1.0.0-rc.1 → 1.0.0-rc.3
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 +5 -5
- 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 +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +99 -35
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +314 -63
- package/dist/matchers.js.map +1 -1
- package/dist/normalize.d.ts +10 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +20 -0
- package/dist/normalize.js.map +1 -0
- 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 +129 -90
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +235 -164
- 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 +25 -19
- 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 +65 -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 +7 -5
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +16 -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 +36 -25
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +215 -68
- 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 +6 -6
- 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 +13 -3
- package/src/matchers.ts +568 -114
- package/src/normalize.ts +28 -0
- package/src/pretty-widget.ts +114 -42
- package/src/queries.ts +418 -256
- package/src/register-test-hooks.ts +4 -1
- package/src/render-hook.tsx +10 -4
- package/src/render.tsx +59 -34
- 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 +111 -32
- package/src/user-event/adjustment.ts +23 -10
- package/src/user-event/click.ts +49 -20
- package/src/user-event/controller.ts +41 -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 +385 -79
- package/src/within.ts +8 -5
|
@@ -3,136 +3,346 @@ import { getAccessibleMetadata } from "@gtkx/react/internal";
|
|
|
3
3
|
import { EDITABLE_ROLES, isEditable, readEditableText } from "./editable.js";
|
|
4
4
|
import { descendants } from "./traversal.js";
|
|
5
5
|
|
|
6
|
+
type WidgetValue = {
|
|
7
|
+
now: number | null;
|
|
8
|
+
min: number | null;
|
|
9
|
+
max: number | null;
|
|
10
|
+
text: string | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type ValueTriplet = { now: number | null; min: number | null; max: number | null };
|
|
14
|
+
/** A widget's checked state: on, off, or the mixed state of an inconsistent check button. */
|
|
15
|
+
type CheckedState = "checked" | "unchecked" | "mixed";
|
|
16
|
+
|
|
17
|
+
const DEFAULT_TEXT_GETTERS = ["getLabel", "getText", "getTitle"] as const;
|
|
18
|
+
|
|
19
|
+
const SELECTABLE_ROLES: Set<Gtk.AccessibleRole> = new Set<Gtk.AccessibleRole>([
|
|
20
|
+
Gtk.AccessibleRole.ROW,
|
|
21
|
+
Gtk.AccessibleRole.LIST_ITEM,
|
|
22
|
+
Gtk.AccessibleRole.GRID_CELL,
|
|
23
|
+
Gtk.AccessibleRole.OPTION,
|
|
24
|
+
Gtk.AccessibleRole.TREE_ITEM,
|
|
25
|
+
]);
|
|
26
|
+
|
|
6
27
|
const callStringGetter = (widget: Gtk.Widget, method: string): string | null => {
|
|
7
28
|
const fn: unknown = Reflect.get(widget, method);
|
|
8
|
-
|
|
29
|
+
|
|
30
|
+
if (typeof fn !== "function") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
9
34
|
const value = (fn as () => string | null).call(widget);
|
|
35
|
+
|
|
10
36
|
return value ?? null;
|
|
11
37
|
};
|
|
12
38
|
|
|
13
39
|
const readAccessibleString = (widget: Gtk.Widget, key: string): string | null => {
|
|
14
|
-
const value = getAccessibleMetadata
|
|
40
|
+
const value = getAccessibleMetadata(widget, key);
|
|
41
|
+
|
|
15
42
|
return typeof value === "string" ? value : null;
|
|
16
43
|
};
|
|
17
44
|
|
|
18
45
|
const readAccessibleNumber = (widget: Gtk.Widget, key: string): number | null => {
|
|
19
|
-
const value = getAccessibleMetadata
|
|
46
|
+
const value = getAccessibleMetadata(widget, key);
|
|
47
|
+
|
|
20
48
|
return typeof value === "number" ? value : null;
|
|
21
49
|
};
|
|
22
50
|
|
|
51
|
+
const readAccessibleWidgets = (widget: Gtk.Widget, key: string): Gtk.Widget[] | null => {
|
|
52
|
+
const value = getAccessibleMetadata(widget, key);
|
|
53
|
+
|
|
54
|
+
if (!Array.isArray(value)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const widgets = value.filter((item): item is Gtk.Widget => item instanceof Gtk.Widget);
|
|
59
|
+
|
|
60
|
+
return widgets.length > 0 ? widgets : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
23
63
|
const readAccessibleBoolean = (widget: Gtk.Widget, key: string): boolean | null => {
|
|
24
|
-
const value = getAccessibleMetadata
|
|
64
|
+
const value = getAccessibleMetadata(widget, key);
|
|
65
|
+
|
|
25
66
|
return typeof value === "boolean" ? value : null;
|
|
26
67
|
};
|
|
27
68
|
|
|
28
69
|
const getLabelText = (widget: Gtk.Widget): string | null => {
|
|
29
|
-
if (widget instanceof Gtk.Label)
|
|
30
|
-
|
|
70
|
+
if (widget instanceof Gtk.Label) {
|
|
71
|
+
return widget.getLabel();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (widget instanceof Gtk.Inscription) {
|
|
75
|
+
return widget.getText() ?? null;
|
|
76
|
+
}
|
|
77
|
+
|
|
31
78
|
return null;
|
|
32
79
|
};
|
|
33
80
|
|
|
34
|
-
const DEFAULT_TEXT_GETTERS = ["getLabel", "getText", "getTitle"] as const;
|
|
35
|
-
|
|
36
81
|
/**
|
|
37
82
|
* Returns a widget's own text by trying its label, text, and title getters in
|
|
38
83
|
* order, or null when none produce a value.
|
|
39
84
|
*
|
|
40
85
|
* @param widget The widget to read text from.
|
|
41
86
|
*/
|
|
42
|
-
|
|
87
|
+
const getWidgetNodeText = (widget: Gtk.Widget): string | null => {
|
|
43
88
|
for (const getter of DEFAULT_TEXT_GETTERS) {
|
|
44
89
|
const value = callStringGetter(widget, getter);
|
|
45
|
-
|
|
90
|
+
|
|
91
|
+
if (value) {
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
46
94
|
}
|
|
95
|
+
|
|
47
96
|
return null;
|
|
48
97
|
};
|
|
49
98
|
|
|
50
|
-
const stripMnemonic = (text: string): string => text.
|
|
99
|
+
const stripMnemonic = (text: string): string => text.replaceAll(/_(.)/g, "$1");
|
|
51
100
|
|
|
52
101
|
const namingLabelText = (widget: Gtk.Widget): string | null => {
|
|
53
102
|
const text = getLabelText(widget);
|
|
54
|
-
|
|
103
|
+
|
|
104
|
+
if (text === null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
55
108
|
return widget instanceof Gtk.Label && widget.getUseUnderline() ? stripMnemonic(text) : text;
|
|
56
109
|
};
|
|
57
110
|
|
|
58
|
-
const isNamingLabelRole = (role: Gtk.AccessibleRole,
|
|
59
|
-
role === Gtk.AccessibleRole.LABEL ||
|
|
111
|
+
const isNamingLabelRole = (role: Gtk.AccessibleRole, shouldIncludePresentation: boolean): boolean =>
|
|
112
|
+
role === Gtk.AccessibleRole.LABEL ||
|
|
113
|
+
(shouldIncludePresentation && role === Gtk.AccessibleRole.PRESENTATION);
|
|
60
114
|
|
|
61
115
|
const collectLabels = (widget: Gtk.Widget): string[] => {
|
|
62
|
-
const
|
|
116
|
+
const isIncludePresentation = widget.getAccessibleRole() === Gtk.AccessibleRole.MENU_ITEM;
|
|
63
117
|
const labels: string[] = [];
|
|
118
|
+
|
|
64
119
|
for (const descendant of descendants(widget)) {
|
|
65
|
-
if (!isNamingLabelRole(descendant.getAccessibleRole(),
|
|
120
|
+
if (!isNamingLabelRole(descendant.getAccessibleRole(), isIncludePresentation)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
66
124
|
const labelText = namingLabelText(descendant);
|
|
67
|
-
|
|
125
|
+
|
|
126
|
+
if (labelText) {
|
|
127
|
+
labels.push(labelText);
|
|
128
|
+
}
|
|
68
129
|
}
|
|
130
|
+
|
|
69
131
|
return labels;
|
|
70
132
|
};
|
|
71
133
|
|
|
72
|
-
|
|
73
|
-
if (widget.getAccessibleRole() !== Gtk.AccessibleRole.LABEL)
|
|
134
|
+
const getWidgetLabelText = (widget: Gtk.Widget): string | null => {
|
|
135
|
+
if (widget.getAccessibleRole() !== Gtk.AccessibleRole.LABEL) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
74
139
|
return getLabelText(widget);
|
|
75
140
|
};
|
|
76
141
|
|
|
77
|
-
|
|
142
|
+
const getChildren = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {
|
|
143
|
+
let child = widget.getFirstChild();
|
|
144
|
+
|
|
145
|
+
while (child) {
|
|
146
|
+
yield child;
|
|
147
|
+
child = child.getNextSibling();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const textContentParts = (widget: Gtk.Widget): string[] => {
|
|
152
|
+
const own = getWidgetNodeText(widget);
|
|
153
|
+
|
|
154
|
+
if (own !== null) {
|
|
155
|
+
return [own];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return [...getChildren(widget)].flatMap((child) => textContentParts(child));
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const getWidgetTextContent = (widget: Gtk.Widget): string | null => {
|
|
162
|
+
const parts = textContentParts(widget);
|
|
163
|
+
|
|
164
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const isComboBoxFaceCandidate = (widget: Gtk.Widget): boolean =>
|
|
168
|
+
!(widget instanceof Gtk.Popover) && widget.getChildVisible();
|
|
169
|
+
|
|
170
|
+
const comboBoxChildFaceText = (child: Gtk.Widget): string | null =>
|
|
171
|
+
isComboBoxFaceCandidate(child) ? getWidgetLabelText(child) ?? comboBoxFaceText(child) : null;
|
|
172
|
+
|
|
173
|
+
const comboBoxFaceText = (widget: Gtk.Widget): string | null => {
|
|
174
|
+
for (const child of getChildren(widget)) {
|
|
175
|
+
const text = comboBoxChildFaceText(child);
|
|
176
|
+
|
|
177
|
+
if (text !== null) {
|
|
178
|
+
return text;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return null;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const tabPanelTitle = (widget: Gtk.Widget): string | null => {
|
|
186
|
+
const parent = widget.getParent();
|
|
187
|
+
|
|
188
|
+
if (parent instanceof Gtk.Stack) {
|
|
189
|
+
return parent.getPage(widget).getTitle() ?? null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return null;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const getWidgetAccessibleName = (widget: Gtk.Widget): string | null => {
|
|
78
196
|
const role = widget.getAccessibleRole();
|
|
79
197
|
|
|
80
198
|
if (role === Gtk.AccessibleRole.TAB_PANEL) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
199
|
+
return tabPanelTitle(widget);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const labelledByText = getWidgetLabelledByText(widget);
|
|
203
|
+
|
|
204
|
+
if (labelledByText) {
|
|
205
|
+
return labelledByText;
|
|
87
206
|
}
|
|
88
207
|
|
|
89
|
-
const accessibleLabel =
|
|
90
|
-
|
|
208
|
+
const accessibleLabel = readAccessibleString(widget, "accessibleLabel");
|
|
209
|
+
|
|
210
|
+
if (accessibleLabel) {
|
|
211
|
+
return accessibleLabel;
|
|
212
|
+
}
|
|
91
213
|
|
|
92
214
|
const ownText = getWidgetNodeText(widget);
|
|
93
|
-
|
|
215
|
+
|
|
216
|
+
if (ownText) {
|
|
217
|
+
return ownText;
|
|
218
|
+
}
|
|
94
219
|
|
|
95
220
|
const childLabels = collectLabels(widget);
|
|
96
|
-
|
|
221
|
+
|
|
222
|
+
if (childLabels.length > 0) {
|
|
223
|
+
return childLabels.join(" ");
|
|
224
|
+
}
|
|
97
225
|
|
|
98
226
|
return callStringGetter(widget, "getTooltipText");
|
|
99
227
|
};
|
|
100
228
|
|
|
101
|
-
|
|
229
|
+
const getWidgetName = (widget: Gtk.Widget): string | null => {
|
|
102
230
|
return widget.getName();
|
|
103
231
|
};
|
|
104
232
|
|
|
105
|
-
|
|
233
|
+
const getWidgetPlaceholderText = (widget: Gtk.Widget): string | null => {
|
|
106
234
|
if (!EDITABLE_ROLES.has(widget.getAccessibleRole()) || !(widget instanceof Gtk.Editable)) {
|
|
107
235
|
return null;
|
|
108
236
|
}
|
|
109
237
|
|
|
110
|
-
|
|
238
|
+
const fromGetter = callStringGetter(widget, "getPlaceholderText");
|
|
239
|
+
|
|
240
|
+
if (fromGetter !== null) {
|
|
241
|
+
return fromGetter;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const fromAccessor: unknown = Reflect.get(widget, "placeholderText");
|
|
245
|
+
|
|
246
|
+
return typeof fromAccessor === "string" && fromAccessor !== "" ? fromAccessor : null;
|
|
111
247
|
};
|
|
112
248
|
|
|
113
|
-
|
|
249
|
+
const getWidgetDisplayValue = (widget: Gtk.Widget): string | null => {
|
|
114
250
|
if (EDITABLE_ROLES.has(widget.getAccessibleRole()) && isEditable(widget)) {
|
|
115
251
|
return readEditableText(widget);
|
|
116
252
|
}
|
|
117
253
|
|
|
254
|
+
if (widget.getAccessibleRole() === Gtk.AccessibleRole.COMBO_BOX) {
|
|
255
|
+
return comboBoxFaceText(widget);
|
|
256
|
+
}
|
|
257
|
+
|
|
118
258
|
return null;
|
|
119
259
|
};
|
|
120
260
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
261
|
+
const hasDisplayValue = (widget: Gtk.Widget): boolean => {
|
|
262
|
+
const role = widget.getAccessibleRole();
|
|
263
|
+
|
|
264
|
+
return (EDITABLE_ROLES.has(role) && isEditable(widget)) || role === Gtk.AccessibleRole.COMBO_BOX;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const isWidgetDisabled = (widget: Gtk.Widget): boolean => !widget.isSensitive();
|
|
268
|
+
|
|
269
|
+
const hasZeroOpacityAncestor = (widget: Gtk.Widget): boolean => {
|
|
270
|
+
let current: Gtk.Widget | null = widget;
|
|
271
|
+
|
|
272
|
+
while (current) {
|
|
273
|
+
if (current.getOpacity() === 0) {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
current = current.getParent();
|
|
126
278
|
}
|
|
279
|
+
|
|
280
|
+
return false;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const isWidgetVisible = (widget: Gtk.Widget): boolean => widget.isVisible() && !hasZeroOpacityAncestor(widget);
|
|
284
|
+
|
|
285
|
+
const getWidgetRequiredState = (widget: Gtk.Widget): boolean | null =>
|
|
286
|
+
readAccessibleBoolean(widget, "accessibleRequired");
|
|
287
|
+
|
|
288
|
+
const readTextViewSelection = (view: Gtk.TextView): string | null => {
|
|
289
|
+
const buffer = view.getBuffer();
|
|
290
|
+
const [hasSelection, start, end] = buffer.getSelectionBounds();
|
|
291
|
+
|
|
292
|
+
return hasSelection ? buffer.getText(start, end, true) : null;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const readEditableSelection = (editable: Gtk.Editable): string | null => {
|
|
296
|
+
const [hasSelection, start, end] = editable.getSelectionBounds();
|
|
297
|
+
|
|
298
|
+
return hasSelection ? editable.getChars(start, end) : null;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const getWidgetSelection = (widget: Gtk.Widget): string | null => {
|
|
302
|
+
if (widget instanceof Gtk.TextView) {
|
|
303
|
+
return readTextViewSelection(widget);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (widget instanceof Gtk.Editable) {
|
|
307
|
+
return readEditableSelection(widget);
|
|
308
|
+
}
|
|
309
|
+
|
|
127
310
|
return null;
|
|
128
311
|
};
|
|
129
312
|
|
|
130
|
-
|
|
131
|
-
|
|
313
|
+
const checkedFromActive = (isActive: boolean): CheckedState => (isActive ? "checked" : "unchecked");
|
|
314
|
+
|
|
315
|
+
const isCheckableToggle = (widget: Gtk.Widget): boolean =>
|
|
316
|
+
widget instanceof Gtk.Switch ||
|
|
317
|
+
(widget instanceof Gtk.ToggleButton && widget.getAccessibleRole() === Gtk.AccessibleRole.RADIO);
|
|
318
|
+
|
|
319
|
+
const getWidgetCheckedState = (widget: Gtk.Widget): CheckedState | null => {
|
|
320
|
+
if (widget instanceof Gtk.CheckButton) {
|
|
321
|
+
return widget.getInconsistent() ? "mixed" : checkedFromActive(widget.getActive());
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (isCheckableToggle(widget)) {
|
|
325
|
+
return checkedFromActive(Reflect.get(widget, "active") === true);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return null;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const isWidgetChecked = (widget: Gtk.Widget): boolean | null => {
|
|
332
|
+
const state = getWidgetCheckedState(widget);
|
|
333
|
+
|
|
334
|
+
return state === null || state === "mixed" ? null : state === "checked";
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const getWidgetPressedState = (widget: Gtk.Widget): boolean | null => {
|
|
338
|
+
if (widget instanceof Gtk.ToggleButton) {
|
|
339
|
+
return widget.getActive();
|
|
340
|
+
}
|
|
341
|
+
|
|
132
342
|
return null;
|
|
133
343
|
};
|
|
134
344
|
|
|
135
|
-
|
|
345
|
+
const getWidgetExpandedState = (widget: Gtk.Widget): boolean | null => {
|
|
136
346
|
if (widget instanceof Gtk.Expander) {
|
|
137
347
|
return widget.getExpanded();
|
|
138
348
|
}
|
|
@@ -144,70 +354,91 @@ export const getWidgetExpandedState = (widget: Gtk.Widget): boolean | null => {
|
|
|
144
354
|
return null;
|
|
145
355
|
};
|
|
146
356
|
|
|
147
|
-
|
|
148
|
-
if (widget instanceof Gtk.ListBoxRow) {
|
|
357
|
+
const getWidgetSelectedState = (widget: Gtk.Widget): boolean | null => {
|
|
358
|
+
if (widget instanceof Gtk.ListBoxRow || widget instanceof Gtk.FlowBoxChild) {
|
|
149
359
|
return widget.isSelected();
|
|
150
360
|
}
|
|
151
361
|
|
|
152
|
-
if (widget.getAccessibleRole()
|
|
362
|
+
if (SELECTABLE_ROLES.has(widget.getAccessibleRole())) {
|
|
153
363
|
return (widget.getStateFlags() & Gtk.StateFlags.SELECTED) !== 0;
|
|
154
364
|
}
|
|
155
365
|
|
|
156
366
|
return null;
|
|
157
367
|
};
|
|
158
368
|
|
|
159
|
-
|
|
369
|
+
const getWidgetLevel = (widget: Gtk.Widget): number | null => {
|
|
160
370
|
return readAccessibleNumber(widget, "accessibleLevel");
|
|
161
371
|
};
|
|
162
372
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
};
|
|
373
|
+
const getWidgetInvalidState = (widget: Gtk.Widget): Gtk.AccessibleInvalidState | null => {
|
|
374
|
+
const value = readAccessibleNumber(widget, "accessibleInvalid");
|
|
166
375
|
|
|
167
|
-
|
|
168
|
-
const targets = getAccessibleMetadata<Gtk.Widget[]>(widget, "accessibleErrorMessage");
|
|
169
|
-
return Array.isArray(targets) && targets.length > 0 ? targets : null;
|
|
376
|
+
return value ?? null;
|
|
170
377
|
};
|
|
171
378
|
|
|
172
|
-
|
|
379
|
+
const getWidgetErrorMessage = (widget: Gtk.Widget): Gtk.Widget[] | null =>
|
|
380
|
+
readAccessibleWidgets(widget, "accessibleErrorMessage");
|
|
381
|
+
|
|
382
|
+
const getWidgetBusyState = (widget: Gtk.Widget): boolean | null => {
|
|
173
383
|
return readAccessibleBoolean(widget, "accessibleBusy");
|
|
174
384
|
};
|
|
175
385
|
|
|
176
|
-
|
|
386
|
+
const getWidgetDescription = (widget: Gtk.Widget): string | null => {
|
|
177
387
|
return readAccessibleString(widget, "accessibleDescription");
|
|
178
388
|
};
|
|
179
389
|
|
|
180
|
-
export type WidgetValue = {
|
|
181
|
-
now: number | null;
|
|
182
|
-
min: number | null;
|
|
183
|
-
max: number | null;
|
|
184
|
-
text: string | null;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
type ValueTriplet = { now: number | null; min: number | null; max: number | null };
|
|
188
|
-
|
|
189
390
|
const adjustmentValue = (adjustment: Gtk.Adjustment): ValueTriplet => ({
|
|
190
391
|
now: adjustment.getValue(),
|
|
191
392
|
min: adjustment.getLower(),
|
|
192
393
|
max: adjustment.getUpper(),
|
|
193
394
|
});
|
|
194
395
|
|
|
396
|
+
const adjustmentWidgetValue = (widget: Gtk.Widget): ValueTriplet | null => {
|
|
397
|
+
if (widget instanceof Gtk.Range) {
|
|
398
|
+
return adjustmentValue(widget.getAdjustment());
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (widget instanceof Gtk.Scrollbar) {
|
|
402
|
+
return adjustmentValue(widget.getAdjustment());
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (widget instanceof Gtk.SpinButton) {
|
|
406
|
+
return adjustmentValue(widget.getAdjustment());
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (widget instanceof Gtk.ScaleButton) {
|
|
410
|
+
return adjustmentValue(widget.getAdjustment());
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return null;
|
|
414
|
+
};
|
|
415
|
+
|
|
195
416
|
const getWidgetLiveValue = (widget: Gtk.Widget): ValueTriplet | null => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
|
|
417
|
+
const adjustmentBased = adjustmentWidgetValue(widget);
|
|
418
|
+
|
|
419
|
+
if (adjustmentBased) {
|
|
420
|
+
return adjustmentBased;
|
|
421
|
+
}
|
|
422
|
+
|
|
200
423
|
if (widget instanceof Gtk.LevelBar) {
|
|
201
424
|
return { now: widget.getValue(), min: widget.getMinValue(), max: widget.getMaxValue() };
|
|
202
425
|
}
|
|
203
|
-
|
|
426
|
+
|
|
427
|
+
if (widget instanceof Gtk.ProgressBar) {
|
|
428
|
+
return { now: widget.getFraction(), min: 0, max: 1 };
|
|
429
|
+
}
|
|
430
|
+
|
|
204
431
|
return null;
|
|
205
432
|
};
|
|
206
433
|
|
|
207
|
-
|
|
434
|
+
const getWidgetValue = (widget: Gtk.Widget): WidgetValue => {
|
|
208
435
|
const text = readAccessibleString(widget, "accessibleValueText");
|
|
209
436
|
const live = getWidgetLiveValue(widget);
|
|
210
|
-
|
|
437
|
+
|
|
438
|
+
if (live) {
|
|
439
|
+
return { ...live, text };
|
|
440
|
+
}
|
|
441
|
+
|
|
211
442
|
return {
|
|
212
443
|
now: readAccessibleNumber(widget, "accessibleValueNow"),
|
|
213
444
|
min: readAccessibleNumber(widget, "accessibleValueMin"),
|
|
@@ -216,29 +447,104 @@ export const getWidgetValue = (widget: Gtk.Widget): WidgetValue => {
|
|
|
216
447
|
};
|
|
217
448
|
};
|
|
218
449
|
|
|
219
|
-
|
|
450
|
+
const getWidgetOwnLabel = (widget: Gtk.Widget): string | null => {
|
|
220
451
|
return readAccessibleString(widget, "accessibleLabel");
|
|
221
452
|
};
|
|
222
453
|
|
|
223
|
-
|
|
224
|
-
const targets = getAccessibleMetadata<Gtk.Widget[]>(widget, "accessibleLabelledBy");
|
|
225
|
-
if (!Array.isArray(targets) || targets.length === 0) return null;
|
|
226
|
-
|
|
454
|
+
const collectAccessibleNames = (targets: Gtk.Widget[]): string[] => {
|
|
227
455
|
const texts: string[] = [];
|
|
456
|
+
|
|
228
457
|
for (const target of targets) {
|
|
229
458
|
const text = getWidgetAccessibleName(target);
|
|
230
|
-
|
|
459
|
+
|
|
460
|
+
if (text !== null) {
|
|
461
|
+
texts.push(text);
|
|
462
|
+
}
|
|
231
463
|
}
|
|
232
464
|
|
|
465
|
+
return texts;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const getWidgetLabelledByText = (widget: Gtk.Widget): string | null => {
|
|
469
|
+
const targets = readAccessibleWidgets(widget, "accessibleLabelledBy");
|
|
470
|
+
|
|
471
|
+
if (targets === null) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const texts = collectAccessibleNames(targets);
|
|
476
|
+
|
|
233
477
|
return texts.length > 0 ? texts.join(" ") : null;
|
|
234
478
|
};
|
|
235
479
|
|
|
236
|
-
|
|
480
|
+
const isTooltipUsedAsName = (widget: Gtk.Widget): boolean =>
|
|
481
|
+
readAccessibleString(widget, "accessibleLabel") === null &&
|
|
482
|
+
getWidgetNodeText(widget) === null &&
|
|
483
|
+
collectLabels(widget).length === 0;
|
|
484
|
+
|
|
485
|
+
const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
|
|
486
|
+
const targets = readAccessibleWidgets(widget, "accessibleDescribedBy");
|
|
487
|
+
const texts = targets === null ? [] : collectAccessibleNames(targets);
|
|
488
|
+
|
|
489
|
+
if (texts.length > 0) {
|
|
490
|
+
return texts.join(" ");
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const description = readAccessibleString(widget, "accessibleDescription");
|
|
494
|
+
|
|
495
|
+
if (description !== null) {
|
|
496
|
+
return description;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return isTooltipUsedAsName(widget) ? null : callStringGetter(widget, "getTooltipText");
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const isInaccessible = (widget: Gtk.Widget): boolean => {
|
|
237
503
|
let current: Gtk.Widget | null = widget;
|
|
504
|
+
|
|
238
505
|
while (current) {
|
|
239
|
-
if (!current.getVisible())
|
|
240
|
-
|
|
506
|
+
if (!current.getVisible()) {
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (readAccessibleBoolean(current, "accessibleHidden") === true) {
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
|
|
241
514
|
current = current.getParent();
|
|
242
515
|
}
|
|
516
|
+
|
|
243
517
|
return false;
|
|
244
518
|
};
|
|
519
|
+
|
|
520
|
+
export {
|
|
521
|
+
getWidgetNodeText,
|
|
522
|
+
getWidgetTextContent,
|
|
523
|
+
getWidgetLabelText,
|
|
524
|
+
getWidgetAccessibleName,
|
|
525
|
+
getWidgetName,
|
|
526
|
+
getWidgetPlaceholderText,
|
|
527
|
+
getWidgetDisplayValue,
|
|
528
|
+
hasDisplayValue,
|
|
529
|
+
isWidgetDisabled,
|
|
530
|
+
isWidgetVisible,
|
|
531
|
+
getWidgetRequiredState,
|
|
532
|
+
getWidgetSelection,
|
|
533
|
+
getWidgetDescribedByText,
|
|
534
|
+
getWidgetCheckedState,
|
|
535
|
+
isWidgetChecked,
|
|
536
|
+
getWidgetPressedState,
|
|
537
|
+
getWidgetExpandedState,
|
|
538
|
+
getWidgetSelectedState,
|
|
539
|
+
getWidgetLevel,
|
|
540
|
+
getWidgetInvalidState,
|
|
541
|
+
getWidgetErrorMessage,
|
|
542
|
+
getWidgetBusyState,
|
|
543
|
+
getWidgetDescription,
|
|
544
|
+
getWidgetValue,
|
|
545
|
+
getWidgetOwnLabel,
|
|
546
|
+
getWidgetLabelledByText,
|
|
547
|
+
isInaccessible,
|
|
548
|
+
type CheckedState,
|
|
549
|
+
type WidgetValue,
|
|
550
|
+
};
|
package/src/within.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { BoundQueries } from "./bound-queries.js";
|
|
2
|
-
import { builtinQueries } from "./queries.js";
|
|
3
2
|
import type { Container } from "./traversal.js";
|
|
4
3
|
import type { BoundCustomQueries, QueryMap } from "./types.js";
|
|
4
|
+
import { builtinQueries } from "./queries.js";
|
|
5
5
|
|
|
6
6
|
const bindQuery =
|
|
7
7
|
<Args extends unknown[], Result>(query: (container: Container, ...args: Args) => Result, container: Container) =>
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
(...args: Args): Result =>
|
|
9
|
+
query(container, ...args);
|
|
10
10
|
|
|
11
11
|
const bindCustomQueries = <Q extends QueryMap>(customQueries: Q, container: Container): BoundCustomQueries<Q> => {
|
|
12
12
|
const entries = Object.entries(customQueries).map(([key, query]) => [key, bindQuery(query, container)] as const);
|
|
13
|
+
|
|
13
14
|
return Object.fromEntries(entries) as BoundCustomQueries<Q>;
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -21,10 +22,12 @@ const bindCustomQueries = <Q extends QueryMap>(customQueries: Q, container: Cont
|
|
|
21
22
|
* @param queries Optional custom queries to bind alongside the built-in ones.
|
|
22
23
|
* @returns The queries bound to the container.
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
+
const within = <Q extends QueryMap = Record<never, never>>(
|
|
25
26
|
container: Container,
|
|
26
27
|
queries?: Q,
|
|
27
28
|
): BoundQueries & BoundCustomQueries<Q> => ({
|
|
28
|
-
...bindCustomQueries(builtinQueries, container),
|
|
29
|
+
...(bindCustomQueries(builtinQueries, container) as BoundQueries),
|
|
29
30
|
...(queries ? bindCustomQueries(queries, container) : ({} as BoundCustomQueries<Q>)),
|
|
30
31
|
});
|
|
32
|
+
|
|
33
|
+
export { within };
|