@gtkx/testing 1.0.0-rc.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/dist/accessible-native.d.ts +16 -0
- package/dist/accessible-native.d.ts.map +1 -0
- package/dist/accessible-native.js +206 -0
- package/dist/accessible-native.js.map +1 -0
- package/dist/act.d.ts +2 -0
- package/dist/act.d.ts.map +1 -1
- package/dist/act.js.map +1 -1
- package/dist/build-queries.d.ts +27 -10
- package/dist/build-queries.d.ts.map +1 -1
- package/dist/build-queries.js +39 -30
- package/dist/build-queries.js.map +1 -1
- package/dist/cleanup-registry.d.ts +1 -1
- package/dist/cleanup-registry.d.ts.map +1 -1
- package/dist/cleanup-registry.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -3
- package/dist/errors.js.map +1 -1
- package/dist/fire-event.d.ts +35 -6
- package/dist/fire-event.d.ts.map +1 -1
- package/dist/fire-event.js +23 -7
- 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 +15 -5
- package/dist/frame-sync.js.map +1 -1
- package/dist/harness-window.d.ts +4 -0
- package/dist/harness-window.d.ts.map +1 -0
- package/dist/harness-window.js +6 -0
- package/dist/harness-window.js.map +1 -0
- package/dist/index.d.ts +14 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +4 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -0
- package/dist/matchers.d.ts +125 -39
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js +156 -18
- package/dist/matchers.js.map +1 -1
- package/dist/pretty-format.d.ts +2 -0
- package/dist/pretty-format.d.ts.map +1 -0
- package/dist/pretty-format.js +2 -0
- package/dist/pretty-format.js.map +1 -0
- package/dist/pretty-widget.d.ts +7 -3
- package/dist/pretty-widget.d.ts.map +1 -1
- package/dist/pretty-widget.js +74 -57
- package/dist/pretty-widget.js.map +1 -1
- package/dist/production.d.ts +8 -0
- package/dist/production.d.ts.map +1 -0
- package/dist/production.js +34 -0
- package/dist/production.js.map +1 -0
- package/dist/queries.d.ts +99 -56
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +123 -83
- package/dist/queries.js.map +1 -1
- package/dist/query-helpers.d.ts +44 -0
- package/dist/query-helpers.d.ts.map +1 -0
- package/dist/query-helpers.js +57 -0
- package/dist/query-helpers.js.map +1 -0
- package/dist/render-children.d.ts +9 -0
- package/dist/render-children.d.ts.map +1 -0
- package/dist/render-children.js +11 -0
- package/dist/render-children.js.map +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +13 -11
- package/dist/render.js.map +1 -1
- package/dist/role-helpers.d.ts +12 -3
- package/dist/role-helpers.d.ts.map +1 -1
- package/dist/role-helpers.js +13 -4
- package/dist/role-helpers.js.map +1 -1
- package/dist/role-naming.d.ts +5 -0
- package/dist/role-naming.d.ts.map +1 -0
- package/dist/role-naming.js +96 -0
- package/dist/role-naming.js.map +1 -0
- package/dist/screenshot.d.ts +12 -20
- package/dist/screenshot.d.ts.map +1 -1
- package/dist/screenshot.js +183 -91
- package/dist/screenshot.js.map +1 -1
- package/dist/suggestions.d.ts +3 -0
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/traversal.d.ts +9 -1
- package/dist/traversal.d.ts.map +1 -1
- package/dist/traversal.js +26 -6
- package/dist/traversal.js.map +1 -1
- package/dist/types.d.ts +67 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/user-event/adjustment.d.ts +16 -0
- package/dist/user-event/adjustment.d.ts.map +1 -1
- package/dist/user-event/adjustment.js +34 -2
- package/dist/user-event/adjustment.js.map +1 -1
- package/dist/user-event/click.d.ts +45 -3
- package/dist/user-event/click.d.ts.map +1 -1
- package/dist/user-event/click.js +177 -39
- package/dist/user-event/click.js.map +1 -1
- package/dist/user-event/column-header-click.d.ts +4 -0
- package/dist/user-event/column-header-click.d.ts.map +1 -0
- package/dist/user-event/column-header-click.js +65 -0
- package/dist/user-event/column-header-click.js.map +1 -0
- package/dist/user-event/controller.d.ts +29 -0
- package/dist/user-event/controller.d.ts.map +1 -1
- package/dist/user-event/controller.js +28 -0
- package/dist/user-event/controller.js.map +1 -1
- package/dist/user-event/event-wrapper.d.ts.map +1 -1
- package/dist/user-event/event-wrapper.js +2 -1
- package/dist/user-event/event-wrapper.js.map +1 -1
- package/dist/user-event/gesture.d.ts +51 -0
- package/dist/user-event/gesture.d.ts.map +1 -1
- package/dist/user-event/gesture.js +47 -4
- package/dist/user-event/gesture.js.map +1 -1
- package/dist/user-event/index.d.ts +70 -2
- package/dist/user-event/index.d.ts.map +1 -1
- package/dist/user-event/index.js +43 -26
- package/dist/user-event/index.js.map +1 -1
- package/dist/user-event/indexed-children.d.ts +10 -0
- package/dist/user-event/indexed-children.d.ts.map +1 -0
- package/dist/user-event/indexed-children.js +50 -0
- package/dist/user-event/indexed-children.js.map +1 -0
- package/dist/user-event/keyboard.d.ts +4 -2
- package/dist/user-event/keyboard.d.ts.map +1 -1
- package/dist/user-event/keyboard.js +46 -16
- package/dist/user-event/keyboard.js.map +1 -1
- package/dist/user-event/list-row-click.d.ts +4 -0
- package/dist/user-event/list-row-click.d.ts.map +1 -0
- package/dist/user-event/list-row-click.js +39 -0
- package/dist/user-event/list-row-click.js.map +1 -0
- package/dist/user-event/native-click.d.ts +6 -0
- package/dist/user-event/native-click.d.ts.map +1 -0
- package/dist/user-event/native-click.js +32 -0
- package/dist/user-event/native-click.js.map +1 -0
- package/dist/user-event/pointer.d.ts +1 -1
- package/dist/user-event/pointer.d.ts.map +1 -1
- package/dist/user-event/pointer.js +37 -13
- package/dist/user-event/pointer.js.map +1 -1
- package/dist/user-event/selection.d.ts +18 -0
- package/dist/user-event/selection.d.ts.map +1 -1
- package/dist/user-event/selection.js +56 -30
- package/dist/user-event/selection.js.map +1 -1
- package/dist/user-event/state.d.ts +1 -1
- package/dist/user-event/state.d.ts.map +1 -1
- package/dist/user-event/state.js +1 -1
- package/dist/user-event/state.js.map +1 -1
- package/dist/user-event/text.d.ts +34 -3
- package/dist/user-event/text.d.ts.map +1 -1
- package/dist/user-event/text.js +94 -17
- package/dist/user-event/text.js.map +1 -1
- package/dist/wait-for.d.ts +2 -0
- package/dist/wait-for.d.ts.map +1 -1
- package/dist/wait-for.js.map +1 -1
- package/dist/widget-accessible-properties.d.ts +14 -10
- package/dist/widget-accessible-properties.d.ts.map +1 -1
- package/dist/widget-accessible-properties.js +135 -173
- package/dist/widget-accessible-properties.js.map +1 -1
- package/dist/widget-getters.d.ts +8 -0
- package/dist/widget-getters.d.ts.map +1 -0
- package/dist/widget-getters.js +19 -0
- package/dist/widget-getters.js.map +1 -0
- package/dist/within.d.ts +3 -2
- package/dist/within.d.ts.map +1 -1
- package/dist/within.js +3 -2
- package/dist/within.js.map +1 -1
- package/package.json +13 -8
- package/src/accessible-native.ts +333 -0
- package/src/act.ts +2 -0
- package/src/build-queries.ts +66 -48
- package/src/cleanup-registry.ts +1 -1
- package/src/config.ts +4 -3
- package/src/errors.ts +14 -3
- package/src/fire-event.ts +61 -8
- package/src/frame-sync.ts +21 -5
- package/src/harness-window.ts +9 -0
- package/src/index.ts +18 -11
- package/src/internal.ts +11 -0
- package/src/matchers.ts +466 -64
- package/src/pretty-format.ts +1 -0
- package/src/pretty-widget.ts +100 -72
- package/src/production.ts +52 -0
- package/src/queries.ts +128 -89
- package/src/query-helpers.ts +97 -0
- package/src/render-children.ts +25 -0
- package/src/render.tsx +18 -13
- package/src/role-helpers.ts +14 -4
- package/src/role-naming.ts +99 -0
- package/src/screenshot.ts +272 -103
- package/src/suggestions.ts +3 -0
- package/src/traversal.ts +49 -6
- package/src/types.ts +69 -19
- package/src/user-event/adjustment.ts +42 -2
- package/src/user-event/click.ts +248 -46
- package/src/user-event/column-header-click.ts +90 -0
- package/src/user-event/controller.ts +29 -0
- package/src/user-event/event-wrapper.ts +3 -1
- package/src/user-event/gesture.ts +57 -6
- package/src/user-event/index.ts +119 -29
- package/src/user-event/indexed-children.ts +75 -0
- package/src/user-event/keyboard.ts +70 -20
- package/src/user-event/list-row-click.ts +57 -0
- package/src/user-event/native-click.ts +46 -0
- package/src/user-event/pointer.ts +52 -13
- package/src/user-event/selection.ts +74 -33
- package/src/user-event/state.ts +2 -2
- package/src/user-event/text.ts +121 -23
- package/src/wait-for.ts +2 -0
- package/src/widget-accessible-properties.ts +183 -214
- package/src/widget-getters.ts +32 -0
- package/src/within.ts +3 -2
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
isAccessibleNumberMatch,
|
|
4
|
+
readAccessibleBooleanProperty,
|
|
5
|
+
readAccessibleFlag,
|
|
6
|
+
readAccessibleInt,
|
|
7
|
+
readAccessibleRelation,
|
|
8
|
+
readAccessibleState,
|
|
9
|
+
readAccessibleNumber as readNativeNumber,
|
|
10
|
+
readAccessibleString as readNativeString,
|
|
11
|
+
} from "./accessible-native.js";
|
|
3
12
|
import { EDITABLE_ROLES, isEditable, readEditableText } from "./editable.js";
|
|
4
|
-
import {
|
|
13
|
+
import { isNameFromAuthor, isNameProhibited } from "./role-naming.js";
|
|
14
|
+
import { children, descendants, relationCandidates } from "./traversal.js";
|
|
15
|
+
import { callBooleanGetter, callStringGetter, getCallableMethod } from "./widget-getters.js";
|
|
5
16
|
|
|
6
|
-
type
|
|
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. */
|
|
17
|
+
type WidgetValueField = "now" | "min" | "max";
|
|
15
18
|
type CheckedState = "checked" | "unchecked" | "mixed";
|
|
16
19
|
|
|
17
|
-
const
|
|
20
|
+
const EDITABLE_TEXT_GETTER = "getText";
|
|
21
|
+
const DEFAULT_TEXT_GETTERS = ["getLabel", EDITABLE_TEXT_GETTER, "getTitle"];
|
|
18
22
|
|
|
19
23
|
const SELECTABLE_ROLES: Set<Gtk.AccessibleRole> = new Set<Gtk.AccessibleRole>([
|
|
20
24
|
Gtk.AccessibleRole.ROW,
|
|
@@ -24,55 +28,60 @@ const SELECTABLE_ROLES: Set<Gtk.AccessibleRole> = new Set<Gtk.AccessibleRole>([
|
|
|
24
28
|
Gtk.AccessibleRole.TREE_ITEM,
|
|
25
29
|
]);
|
|
26
30
|
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
31
|
+
const PRESSED_BY_TRISTATE: Map<number, boolean> = new Map<number, boolean>([
|
|
32
|
+
[Gtk.AccessibleTristate.FALSE, false],
|
|
33
|
+
[Gtk.AccessibleTristate.TRUE, true],
|
|
34
|
+
]);
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
const CHECKED_BY_TRISTATE: Map<number, CheckedState> = new Map<number, CheckedState>([
|
|
37
|
+
[Gtk.AccessibleTristate.FALSE, "unchecked"],
|
|
38
|
+
[Gtk.AccessibleTristate.TRUE, "checked"],
|
|
39
|
+
[Gtk.AccessibleTristate.MIXED, "mixed"],
|
|
40
|
+
]);
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
const VALUE_PROPERTIES: Record<WidgetValueField, Gtk.AccessibleProperty> = {
|
|
43
|
+
now: Gtk.AccessibleProperty.VALUE_NOW,
|
|
44
|
+
min: Gtk.AccessibleProperty.VALUE_MIN,
|
|
45
|
+
max: Gtk.AccessibleProperty.VALUE_MAX,
|
|
37
46
|
};
|
|
38
47
|
|
|
39
|
-
const readAccessibleString = (widget: Gtk.Widget,
|
|
40
|
-
|
|
48
|
+
const readAccessibleString = (widget: Gtk.Widget, property: Gtk.AccessibleProperty): string | null =>
|
|
49
|
+
readNativeString(widget, property);
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
const readAccessibleNumber = (widget: Gtk.Widget, property: Gtk.AccessibleProperty): number | null =>
|
|
52
|
+
readNativeNumber(widget, property);
|
|
44
53
|
|
|
45
|
-
const
|
|
46
|
-
const
|
|
54
|
+
const readAccessibleWidgets = (widget: Gtk.Widget, relation: Gtk.AccessibleRelation): Gtk.Widget[] | null => {
|
|
55
|
+
const targets = readAccessibleRelation(widget, relation, relationCandidates(widget));
|
|
56
|
+
const widgets = targets.filter((target): target is Gtk.Widget => target instanceof Gtk.Widget);
|
|
47
57
|
|
|
48
|
-
return
|
|
58
|
+
return widgets.length > 0 ? widgets : null;
|
|
49
59
|
};
|
|
50
60
|
|
|
51
|
-
const
|
|
52
|
-
|
|
61
|
+
const readAccessibleBoolean = (widget: Gtk.Widget, state: Gtk.AccessibleState): boolean | null =>
|
|
62
|
+
readAccessibleFlag(widget, state);
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
const getLabelText = (widget: Gtk.Widget): string | null => {
|
|
65
|
+
if (widget instanceof Gtk.Label) {
|
|
66
|
+
return widget.getLabel();
|
|
56
67
|
}
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return widgets.length > 0 ? widgets : null;
|
|
69
|
+
return readAccessibleString(widget, Gtk.AccessibleProperty.LABEL);
|
|
61
70
|
};
|
|
62
71
|
|
|
63
|
-
const
|
|
64
|
-
|
|
72
|
+
const stripMnemonic = (text: string): string => text.replaceAll(/_(.)/g, "$1");
|
|
73
|
+
const isUnderlineUsed = (widget: Gtk.Widget): boolean => callBooleanGetter(widget, "getUseUnderline") ?? false;
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
const readNamingText = (widget: Gtk.Widget, getter: string, value: string): string =>
|
|
76
|
+
getter !== EDITABLE_TEXT_GETTER && isUnderlineUsed(widget) ? stripMnemonic(value) : value;
|
|
68
77
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
78
|
+
const readFirstText = (widget: Gtk.Widget, getters: string[]): string | null => {
|
|
79
|
+
for (const getter of getters) {
|
|
80
|
+
const value = callStringGetter(widget, getter);
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
if (value) {
|
|
83
|
+
return readNamingText(widget, getter, value);
|
|
84
|
+
}
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
return null;
|
|
@@ -84,19 +93,7 @@ const getLabelText = (widget: Gtk.Widget): string | null => {
|
|
|
84
93
|
*
|
|
85
94
|
* @param widget The widget to read text from.
|
|
86
95
|
*/
|
|
87
|
-
const
|
|
88
|
-
for (const getter of DEFAULT_TEXT_GETTERS) {
|
|
89
|
-
const value = callStringGetter(widget, getter);
|
|
90
|
-
|
|
91
|
-
if (value) {
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return null;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const stripMnemonic = (text: string): string => text.replaceAll(/_(.)/g, "$1");
|
|
96
|
+
const getWidgetText = (widget: Gtk.Widget): string | null => readFirstText(widget, DEFAULT_TEXT_GETTERS);
|
|
100
97
|
|
|
101
98
|
const namingLabelText = (widget: Gtk.Widget): string | null => {
|
|
102
99
|
const text = getLabelText(widget);
|
|
@@ -105,7 +102,7 @@ const namingLabelText = (widget: Gtk.Widget): string | null => {
|
|
|
105
102
|
return null;
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
return widget
|
|
105
|
+
return isUnderlineUsed(widget) ? stripMnemonic(text) : text;
|
|
109
106
|
};
|
|
110
107
|
|
|
111
108
|
const isNamingLabelRole = (role: Gtk.AccessibleRole, shouldIncludePresentation: boolean): boolean =>
|
|
@@ -136,26 +133,17 @@ const getWidgetLabelText = (widget: Gtk.Widget): string | null => {
|
|
|
136
133
|
return null;
|
|
137
134
|
}
|
|
138
135
|
|
|
139
|
-
return
|
|
140
|
-
};
|
|
141
|
-
|
|
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
|
-
}
|
|
136
|
+
return namingLabelText(widget);
|
|
149
137
|
};
|
|
150
138
|
|
|
151
139
|
const textContentParts = (widget: Gtk.Widget): string[] => {
|
|
152
|
-
const own =
|
|
140
|
+
const own = getWidgetText(widget);
|
|
153
141
|
|
|
154
142
|
if (own !== null) {
|
|
155
143
|
return [own];
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
return [...
|
|
146
|
+
return [...children(widget)].flatMap((child) => textContentParts(child));
|
|
159
147
|
};
|
|
160
148
|
|
|
161
149
|
const getWidgetTextContent = (widget: Gtk.Widget): string | null => {
|
|
@@ -164,15 +152,15 @@ const getWidgetTextContent = (widget: Gtk.Widget): string | null => {
|
|
|
164
152
|
return parts.length > 0 ? parts.join(" ") : null;
|
|
165
153
|
};
|
|
166
154
|
|
|
167
|
-
const
|
|
155
|
+
const isDropDownFaceCandidate = (widget: Gtk.Widget): boolean =>
|
|
168
156
|
!(widget instanceof Gtk.Popover) && widget.getChildVisible();
|
|
169
157
|
|
|
170
|
-
const
|
|
171
|
-
|
|
158
|
+
const dropDownChildFaceText = (child: Gtk.Widget): string | null =>
|
|
159
|
+
isDropDownFaceCandidate(child) ? getWidgetLabelText(child) ?? dropDownFaceText(child) : null;
|
|
172
160
|
|
|
173
|
-
const
|
|
174
|
-
for (const child of
|
|
175
|
-
const text =
|
|
161
|
+
const dropDownFaceText = (widget: Gtk.Widget): string | null => {
|
|
162
|
+
for (const child of children(widget)) {
|
|
163
|
+
const text = dropDownChildFaceText(child);
|
|
176
164
|
|
|
177
165
|
if (text !== null) {
|
|
178
166
|
return text;
|
|
@@ -192,67 +180,77 @@ const tabPanelTitle = (widget: Gtk.Widget): string | null => {
|
|
|
192
180
|
return null;
|
|
193
181
|
};
|
|
194
182
|
|
|
195
|
-
const
|
|
196
|
-
|
|
183
|
+
const nameFromAuthor = (widget: Gtk.Widget): string | null =>
|
|
184
|
+
getWidgetLabelledByText(widget) ?? readAccessibleString(widget, Gtk.AccessibleProperty.LABEL);
|
|
197
185
|
|
|
198
|
-
|
|
199
|
-
|
|
186
|
+
const nameFromContent = (widget: Gtk.Widget): string | null => {
|
|
187
|
+
const ownText = getWidgetText(widget);
|
|
188
|
+
|
|
189
|
+
if (ownText) {
|
|
190
|
+
return ownText;
|
|
200
191
|
}
|
|
201
192
|
|
|
202
|
-
const
|
|
193
|
+
const childLabels = collectLabels(widget);
|
|
203
194
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
195
|
+
return childLabels.length > 0 ? childLabels.join(" ") : null;
|
|
196
|
+
};
|
|
207
197
|
|
|
208
|
-
|
|
198
|
+
const getWidgetAccessibleName = (widget: Gtk.Widget): string | null => {
|
|
199
|
+
const role = widget.getAccessibleRole();
|
|
209
200
|
|
|
210
|
-
if (
|
|
211
|
-
return
|
|
201
|
+
if (isNameProhibited(role)) {
|
|
202
|
+
return null;
|
|
212
203
|
}
|
|
213
204
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (ownText) {
|
|
217
|
-
return ownText;
|
|
205
|
+
if (role === Gtk.AccessibleRole.TAB_PANEL) {
|
|
206
|
+
return tabPanelTitle(widget);
|
|
218
207
|
}
|
|
219
208
|
|
|
220
|
-
const
|
|
209
|
+
const authored = isNameFromAuthor(role) ? nameFromAuthor(widget) : null;
|
|
221
210
|
|
|
222
|
-
if (
|
|
223
|
-
return
|
|
211
|
+
if (authored) {
|
|
212
|
+
return authored;
|
|
224
213
|
}
|
|
225
214
|
|
|
226
|
-
return callStringGetter(widget, "getTooltipText");
|
|
215
|
+
return nameFromContent(widget) ?? callStringGetter(widget, "getTooltipText");
|
|
227
216
|
};
|
|
228
217
|
|
|
229
218
|
const getWidgetName = (widget: Gtk.Widget): string | null => {
|
|
230
219
|
return widget.getName();
|
|
231
220
|
};
|
|
232
221
|
|
|
233
|
-
const
|
|
234
|
-
if (!EDITABLE_ROLES.has(widget.getAccessibleRole()) || !(widget instanceof Gtk.Editable)) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
222
|
+
const nonEmptyText = (text: string | null): string | null => (text === null || text === "" ? null : text);
|
|
237
223
|
|
|
238
|
-
|
|
224
|
+
const readPlaceholderProperty = (widget: Gtk.Widget): string | null => {
|
|
225
|
+
const value: unknown = Reflect.get(widget, "placeholderText");
|
|
239
226
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
227
|
+
return typeof value === "string" ? nonEmptyText(value) : null;
|
|
228
|
+
};
|
|
243
229
|
|
|
244
|
-
|
|
230
|
+
const getWidgetPlaceholderText = (widget: Gtk.Widget): string | null => {
|
|
231
|
+
if (!EDITABLE_ROLES.has(widget.getAccessibleRole())) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
245
234
|
|
|
246
|
-
return
|
|
235
|
+
return (
|
|
236
|
+
nonEmptyText(callStringGetter(widget, "getPlaceholderText")) ??
|
|
237
|
+
readPlaceholderProperty(widget) ??
|
|
238
|
+
nonEmptyText(readAccessibleString(widget, Gtk.AccessibleProperty.PLACEHOLDER))
|
|
239
|
+
);
|
|
247
240
|
};
|
|
248
241
|
|
|
242
|
+
const comboBoxDisplayValue = (widget: Gtk.Widget): string | null =>
|
|
243
|
+
widget instanceof Gtk.DropDown
|
|
244
|
+
? (dropDownFaceText(widget) ?? getWidgetValueText(widget))
|
|
245
|
+
: (getWidgetValueText(widget) ?? dropDownFaceText(widget));
|
|
246
|
+
|
|
249
247
|
const getWidgetDisplayValue = (widget: Gtk.Widget): string | null => {
|
|
250
248
|
if (EDITABLE_ROLES.has(widget.getAccessibleRole()) && isEditable(widget)) {
|
|
251
249
|
return readEditableText(widget);
|
|
252
250
|
}
|
|
253
251
|
|
|
254
252
|
if (widget.getAccessibleRole() === Gtk.AccessibleRole.COMBO_BOX) {
|
|
255
|
-
return
|
|
253
|
+
return comboBoxDisplayValue(widget);
|
|
256
254
|
}
|
|
257
255
|
|
|
258
256
|
return null;
|
|
@@ -283,7 +281,7 @@ const hasZeroOpacityAncestor = (widget: Gtk.Widget): boolean => {
|
|
|
283
281
|
const isWidgetVisible = (widget: Gtk.Widget): boolean => widget.isVisible() && !hasZeroOpacityAncestor(widget);
|
|
284
282
|
|
|
285
283
|
const getWidgetRequiredState = (widget: Gtk.Widget): boolean | null =>
|
|
286
|
-
|
|
284
|
+
readAccessibleBooleanProperty(widget, Gtk.AccessibleProperty.REQUIRED);
|
|
287
285
|
|
|
288
286
|
const readTextViewSelection = (view: Gtk.TextView): string | null => {
|
|
289
287
|
const buffer = view.getBuffer();
|
|
@@ -292,40 +290,48 @@ const readTextViewSelection = (view: Gtk.TextView): string | null => {
|
|
|
292
290
|
return hasSelection ? buffer.getText(start, end, true) : null;
|
|
293
291
|
};
|
|
294
292
|
|
|
295
|
-
const
|
|
296
|
-
const [
|
|
293
|
+
const sliceSelectedText = (widget: Gtk.Widget, start: number, end: number): string | null => {
|
|
294
|
+
const chars = getCallableMethod<[number, number], string | null>(widget, "getChars");
|
|
297
295
|
|
|
298
|
-
|
|
296
|
+
if (chars) {
|
|
297
|
+
return chars(start, end) ?? null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const text = callStringGetter(widget, "getText");
|
|
301
|
+
|
|
302
|
+
/* eslint-disable-next-line @typescript-eslint/no-misused-spread -- bounds are code point offsets */
|
|
303
|
+
return text === null ? null : [...text].slice(start, end).join("");
|
|
299
304
|
};
|
|
300
305
|
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
return readTextViewSelection(widget);
|
|
304
|
-
}
|
|
306
|
+
const readBoundedSelection = (widget: Gtk.Widget): string | null => {
|
|
307
|
+
const bounds = getCallableMethod<[], [boolean, number, number]>(widget, "getSelectionBounds");
|
|
305
308
|
|
|
306
|
-
if (
|
|
307
|
-
return
|
|
309
|
+
if (!bounds) {
|
|
310
|
+
return null;
|
|
308
311
|
}
|
|
309
312
|
|
|
310
|
-
|
|
313
|
+
const [hasSelection, start, end] = bounds();
|
|
314
|
+
|
|
315
|
+
return hasSelection ? sliceSelectedText(widget, start, end) : null;
|
|
311
316
|
};
|
|
312
317
|
|
|
318
|
+
const getWidgetSelection = (widget: Gtk.Widget): string | null =>
|
|
319
|
+
widget instanceof Gtk.TextView ? readTextViewSelection(widget) : readBoundedSelection(widget);
|
|
320
|
+
|
|
313
321
|
const checkedFromActive = (isActive: boolean): CheckedState => (isActive ? "checked" : "unchecked");
|
|
314
322
|
|
|
315
|
-
const
|
|
316
|
-
widget instanceof Gtk.
|
|
317
|
-
(widget instanceof Gtk.ToggleButton && widget.getAccessibleRole() === Gtk.AccessibleRole.RADIO);
|
|
323
|
+
const isRadioToggle = (widget: Gtk.Widget): widget is Gtk.ToggleButton =>
|
|
324
|
+
widget instanceof Gtk.ToggleButton && widget.getAccessibleRole() === Gtk.AccessibleRole.RADIO;
|
|
318
325
|
|
|
319
326
|
const getWidgetCheckedState = (widget: Gtk.Widget): CheckedState | null => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
327
|
+
const tristate = readAccessibleState(widget, Gtk.AccessibleState.CHECKED);
|
|
328
|
+
const state = tristate === null ? undefined : CHECKED_BY_TRISTATE.get(tristate);
|
|
323
329
|
|
|
324
|
-
if (
|
|
325
|
-
return
|
|
330
|
+
if (state !== undefined) {
|
|
331
|
+
return state;
|
|
326
332
|
}
|
|
327
333
|
|
|
328
|
-
return null;
|
|
334
|
+
return isRadioToggle(widget) ? checkedFromActive(widget.getActive()) : null;
|
|
329
335
|
};
|
|
330
336
|
|
|
331
337
|
const isWidgetChecked = (widget: Gtk.Widget): boolean | null => {
|
|
@@ -335,28 +341,24 @@ const isWidgetChecked = (widget: Gtk.Widget): boolean | null => {
|
|
|
335
341
|
};
|
|
336
342
|
|
|
337
343
|
const getWidgetPressedState = (widget: Gtk.Widget): boolean | null => {
|
|
338
|
-
|
|
339
|
-
return widget.getActive();
|
|
340
|
-
}
|
|
344
|
+
const tristate = readAccessibleState(widget, Gtk.AccessibleState.PRESSED);
|
|
341
345
|
|
|
342
|
-
return null;
|
|
346
|
+
return tristate === null ? null : PRESSED_BY_TRISTATE.get(tristate) ?? null;
|
|
343
347
|
};
|
|
344
348
|
|
|
345
349
|
const getWidgetExpandedState = (widget: Gtk.Widget): boolean | null => {
|
|
346
|
-
if (widget instanceof Gtk.Expander) {
|
|
347
|
-
return widget.getExpanded();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
350
|
if (widget instanceof Gtk.TreeExpander) {
|
|
351
351
|
return widget.getListRow()?.getExpanded() ?? null;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
return
|
|
354
|
+
return readAccessibleBoolean(widget, Gtk.AccessibleState.EXPANDED);
|
|
355
355
|
};
|
|
356
356
|
|
|
357
357
|
const getWidgetSelectedState = (widget: Gtk.Widget): boolean | null => {
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
const selected = readAccessibleBoolean(widget, Gtk.AccessibleState.SELECTED);
|
|
359
|
+
|
|
360
|
+
if (selected !== null) {
|
|
361
|
+
return selected;
|
|
360
362
|
}
|
|
361
363
|
|
|
362
364
|
if (SELECTABLE_ROLES.has(widget.getAccessibleRole())) {
|
|
@@ -367,88 +369,37 @@ const getWidgetSelectedState = (widget: Gtk.Widget): boolean | null => {
|
|
|
367
369
|
};
|
|
368
370
|
|
|
369
371
|
const getWidgetLevel = (widget: Gtk.Widget): number | null => {
|
|
370
|
-
return
|
|
372
|
+
return readAccessibleInt(widget, Gtk.AccessibleProperty.LEVEL);
|
|
371
373
|
};
|
|
372
374
|
|
|
373
375
|
const getWidgetInvalidState = (widget: Gtk.Widget): Gtk.AccessibleInvalidState | null => {
|
|
374
|
-
const value =
|
|
376
|
+
const value = readAccessibleState(widget, Gtk.AccessibleState.INVALID);
|
|
375
377
|
|
|
376
378
|
return value ?? null;
|
|
377
379
|
};
|
|
378
380
|
|
|
379
381
|
const getWidgetErrorMessage = (widget: Gtk.Widget): Gtk.Widget[] | null =>
|
|
380
|
-
readAccessibleWidgets(widget,
|
|
382
|
+
readAccessibleWidgets(widget, Gtk.AccessibleRelation.ERROR_MESSAGE);
|
|
381
383
|
|
|
382
384
|
const getWidgetBusyState = (widget: Gtk.Widget): boolean | null => {
|
|
383
|
-
return readAccessibleBoolean(widget,
|
|
385
|
+
return readAccessibleBoolean(widget, Gtk.AccessibleState.BUSY);
|
|
384
386
|
};
|
|
385
387
|
|
|
386
388
|
const getWidgetDescription = (widget: Gtk.Widget): string | null => {
|
|
387
|
-
return readAccessibleString(widget,
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
const adjustmentValue = (adjustment: Gtk.Adjustment): ValueTriplet => ({
|
|
391
|
-
now: adjustment.getValue(),
|
|
392
|
-
min: adjustment.getLower(),
|
|
393
|
-
max: adjustment.getUpper(),
|
|
394
|
-
});
|
|
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
|
-
|
|
416
|
-
const getWidgetLiveValue = (widget: Gtk.Widget): ValueTriplet | null => {
|
|
417
|
-
const adjustmentBased = adjustmentWidgetValue(widget);
|
|
418
|
-
|
|
419
|
-
if (adjustmentBased) {
|
|
420
|
-
return adjustmentBased;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (widget instanceof Gtk.LevelBar) {
|
|
424
|
-
return { now: widget.getValue(), min: widget.getMinValue(), max: widget.getMaxValue() };
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (widget instanceof Gtk.ProgressBar) {
|
|
428
|
-
return { now: widget.getFraction(), min: 0, max: 1 };
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return null;
|
|
389
|
+
return readAccessibleString(widget, Gtk.AccessibleProperty.DESCRIPTION);
|
|
432
390
|
};
|
|
433
391
|
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
const live = getWidgetLiveValue(widget);
|
|
392
|
+
const getWidgetValueNow = (widget: Gtk.Widget): number | null =>
|
|
393
|
+
readAccessibleNumber(widget, Gtk.AccessibleProperty.VALUE_NOW);
|
|
437
394
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
395
|
+
const getWidgetValueText = (widget: Gtk.Widget): string | null =>
|
|
396
|
+
readAccessibleString(widget, Gtk.AccessibleProperty.VALUE_TEXT);
|
|
441
397
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
min: readAccessibleNumber(widget, "accessibleValueMin"),
|
|
445
|
-
max: readAccessibleNumber(widget, "accessibleValueMax"),
|
|
446
|
-
text,
|
|
447
|
-
};
|
|
448
|
-
};
|
|
398
|
+
const isWidgetValueMatch = (widget: Gtk.Widget, field: WidgetValueField, expected: number): boolean =>
|
|
399
|
+
isAccessibleNumberMatch(widget, VALUE_PROPERTIES[field], expected);
|
|
449
400
|
|
|
450
401
|
const getWidgetOwnLabel = (widget: Gtk.Widget): string | null => {
|
|
451
|
-
return readAccessibleString(widget,
|
|
402
|
+
return readAccessibleString(widget, Gtk.AccessibleProperty.LABEL);
|
|
452
403
|
};
|
|
453
404
|
|
|
454
405
|
const collectAccessibleNames = (targets: Gtk.Widget[]): string[] => {
|
|
@@ -457,7 +408,7 @@ const collectAccessibleNames = (targets: Gtk.Widget[]): string[] => {
|
|
|
457
408
|
for (const target of targets) {
|
|
458
409
|
const text = getWidgetAccessibleName(target);
|
|
459
410
|
|
|
460
|
-
if (text
|
|
411
|
+
if (text) {
|
|
461
412
|
texts.push(text);
|
|
462
413
|
}
|
|
463
414
|
}
|
|
@@ -465,8 +416,14 @@ const collectAccessibleNames = (targets: Gtk.Widget[]): string[] => {
|
|
|
465
416
|
return texts;
|
|
466
417
|
};
|
|
467
418
|
|
|
419
|
+
const isSelfLabelling = (widget: Gtk.Widget, targets: Gtk.Widget[]): boolean => {
|
|
420
|
+
const own = new Set(descendants(widget));
|
|
421
|
+
|
|
422
|
+
return targets.every((target) => own.has(target));
|
|
423
|
+
};
|
|
424
|
+
|
|
468
425
|
const getWidgetLabelledByText = (widget: Gtk.Widget): string | null => {
|
|
469
|
-
const targets = readAccessibleWidgets(widget,
|
|
426
|
+
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
470
427
|
|
|
471
428
|
if (targets === null) {
|
|
472
429
|
return null;
|
|
@@ -477,20 +434,26 @@ const getWidgetLabelledByText = (widget: Gtk.Widget): string | null => {
|
|
|
477
434
|
return texts.length > 0 ? texts.join(" ") : null;
|
|
478
435
|
};
|
|
479
436
|
|
|
437
|
+
const getWidgetExternalLabelText = (widget: Gtk.Widget): string | null => {
|
|
438
|
+
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.LABELLED_BY);
|
|
439
|
+
|
|
440
|
+
return targets === null || isSelfLabelling(widget, targets) ? null : getWidgetLabelledByText(widget);
|
|
441
|
+
};
|
|
442
|
+
|
|
480
443
|
const isTooltipUsedAsName = (widget: Gtk.Widget): boolean =>
|
|
481
|
-
readAccessibleString(widget,
|
|
482
|
-
|
|
444
|
+
readAccessibleString(widget, Gtk.AccessibleProperty.LABEL) === null &&
|
|
445
|
+
getWidgetText(widget) === null &&
|
|
483
446
|
collectLabels(widget).length === 0;
|
|
484
447
|
|
|
485
448
|
const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
|
|
486
|
-
const targets = readAccessibleWidgets(widget,
|
|
449
|
+
const targets = readAccessibleWidgets(widget, Gtk.AccessibleRelation.DESCRIBED_BY);
|
|
487
450
|
const texts = targets === null ? [] : collectAccessibleNames(targets);
|
|
488
451
|
|
|
489
452
|
if (texts.length > 0) {
|
|
490
453
|
return texts.join(" ");
|
|
491
454
|
}
|
|
492
455
|
|
|
493
|
-
const description = readAccessibleString(widget,
|
|
456
|
+
const description = readAccessibleString(widget, Gtk.AccessibleProperty.DESCRIPTION);
|
|
494
457
|
|
|
495
458
|
if (description !== null) {
|
|
496
459
|
return description;
|
|
@@ -499,15 +462,17 @@ const getWidgetDescribedByText = (widget: Gtk.Widget): string | null => {
|
|
|
499
462
|
return isTooltipUsedAsName(widget) ? null : callStringGetter(widget, "getTooltipText");
|
|
500
463
|
};
|
|
501
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Returns whether a widget is excluded from the accessibility tree, because it or one of its
|
|
467
|
+
* ancestors is marked hidden.
|
|
468
|
+
*
|
|
469
|
+
* @param widget The widget to test.
|
|
470
|
+
*/
|
|
502
471
|
const isInaccessible = (widget: Gtk.Widget): boolean => {
|
|
503
472
|
let current: Gtk.Widget | null = widget;
|
|
504
473
|
|
|
505
474
|
while (current) {
|
|
506
|
-
if (
|
|
507
|
-
return true;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (readAccessibleBoolean(current, "accessibleHidden") === true) {
|
|
475
|
+
if (readAccessibleBoolean(current, Gtk.AccessibleState.HIDDEN) === true) {
|
|
511
476
|
return true;
|
|
512
477
|
}
|
|
513
478
|
|
|
@@ -518,7 +483,8 @@ const isInaccessible = (widget: Gtk.Widget): boolean => {
|
|
|
518
483
|
};
|
|
519
484
|
|
|
520
485
|
export {
|
|
521
|
-
|
|
486
|
+
namingLabelText,
|
|
487
|
+
getWidgetText,
|
|
522
488
|
getWidgetTextContent,
|
|
523
489
|
getWidgetLabelText,
|
|
524
490
|
getWidgetAccessibleName,
|
|
@@ -541,10 +507,13 @@ export {
|
|
|
541
507
|
getWidgetErrorMessage,
|
|
542
508
|
getWidgetBusyState,
|
|
543
509
|
getWidgetDescription,
|
|
544
|
-
|
|
510
|
+
getWidgetValueNow,
|
|
511
|
+
getWidgetValueText,
|
|
545
512
|
getWidgetOwnLabel,
|
|
513
|
+
getWidgetExternalLabelText,
|
|
546
514
|
getWidgetLabelledByText,
|
|
547
515
|
isInaccessible,
|
|
516
|
+
isWidgetValueMatch,
|
|
548
517
|
type CheckedState,
|
|
549
|
-
type
|
|
518
|
+
type WidgetValueField,
|
|
550
519
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import { getInstanceType, typeName } from "@gtkx/runtime";
|
|
3
|
+
|
|
4
|
+
const getWidgetMethod = (widget: Gtk.Widget, name: string): unknown => Reflect.get(widget, name);
|
|
5
|
+
const getWidgetTypeName = (widget: Gtk.Widget): string | null => typeName(getInstanceType(widget));
|
|
6
|
+
|
|
7
|
+
const hasWidgetMethod = (widget: Gtk.Widget, name: string): boolean =>
|
|
8
|
+
typeof getWidgetMethod(widget, name) === "function";
|
|
9
|
+
|
|
10
|
+
/* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- callers name the signature */
|
|
11
|
+
const getCallableMethod = <Args extends unknown[], Result>(
|
|
12
|
+
widget: Gtk.Widget,
|
|
13
|
+
name: string,
|
|
14
|
+
): ((...args: Args) => Result) | null => {
|
|
15
|
+
const fn = getWidgetMethod(widget, name);
|
|
16
|
+
|
|
17
|
+
return typeof fn === "function" ? (fn as (...args: Args) => Result).bind(widget) : null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const callBooleanGetter = (widget: Gtk.Widget, method: string): boolean | null => {
|
|
21
|
+
const value = getCallableMethod<[], unknown>(widget, method)?.();
|
|
22
|
+
|
|
23
|
+
return typeof value === "boolean" ? value : null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const callStringGetter = (widget: Gtk.Widget, method: string): string | null => {
|
|
27
|
+
const value = getCallableMethod<[], unknown>(widget, method)?.();
|
|
28
|
+
|
|
29
|
+
return typeof value === "string" ? value : null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { callBooleanGetter, callStringGetter, getCallableMethod, getWidgetTypeName, hasWidgetMethod };
|