@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
package/src/traversal.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A scope that resolves to a single root widget: the widget itself, the widget a controller or
|
|
5
|
+
* layout manager is attached to, or a list item's or header's child.
|
|
6
|
+
*/
|
|
3
7
|
type QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk.ListItem | Gtk.ListHeader;
|
|
4
8
|
/**
|
|
5
9
|
* A scope that queries and traversal can run against: a widget, an event
|
|
@@ -8,26 +12,55 @@ type QueryContainer = Gtk.Widget | Gtk.EventController | Gtk.LayoutManager | Gtk
|
|
|
8
12
|
*/
|
|
9
13
|
type Container = QueryContainer | Gtk.Application | typeof TOPLEVELS;
|
|
10
14
|
|
|
15
|
+
/** Container sentinel that widens a query to every toplevel window currently open. */
|
|
11
16
|
const TOPLEVELS: unique symbol = Symbol("gtkx.toplevels");
|
|
12
17
|
|
|
13
18
|
const isApplication = (container: Container): container is Gtk.Application => container instanceof Gtk.Application;
|
|
19
|
+
const isAnyWidget = (): boolean => true;
|
|
20
|
+
const isOnScreen = (widget: Gtk.Widget): boolean => widget.getMapped();
|
|
21
|
+
|
|
22
|
+
const traverseWidgetTree = function* (
|
|
23
|
+
root: Gtk.Widget,
|
|
24
|
+
isIncluded: (widget: Gtk.Widget) => boolean,
|
|
25
|
+
): Generator<Gtk.Widget> {
|
|
26
|
+
if (isIncluded(root)) {
|
|
27
|
+
yield root;
|
|
28
|
+
}
|
|
14
29
|
|
|
15
|
-
const traverseWidgetTree = function* (root: Gtk.Widget): Generator<Gtk.Widget> {
|
|
16
|
-
yield root;
|
|
17
30
|
let child = root.getFirstChild();
|
|
18
31
|
|
|
19
32
|
while (child) {
|
|
20
|
-
yield* traverseWidgetTree(child);
|
|
33
|
+
yield* traverseWidgetTree(child, isIncluded);
|
|
34
|
+
child = child.getNextSibling();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const children = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {
|
|
39
|
+
let child = widget.getFirstChild();
|
|
40
|
+
|
|
41
|
+
while (child) {
|
|
42
|
+
yield child;
|
|
21
43
|
child = child.getNextSibling();
|
|
22
44
|
}
|
|
23
45
|
};
|
|
24
46
|
|
|
25
47
|
const descendants = function* (widget: Gtk.Widget): Generator<Gtk.Widget> {
|
|
26
|
-
const tree = traverseWidgetTree(widget);
|
|
48
|
+
const tree = traverseWidgetTree(widget, isAnyWidget);
|
|
27
49
|
tree.next();
|
|
28
50
|
yield* tree;
|
|
29
51
|
};
|
|
30
52
|
|
|
53
|
+
const relationCandidates = (widget: Gtk.Widget): Gtk.Accessible[] => {
|
|
54
|
+
const pool: Gtk.Accessible[] = [...widget.listMnemonicLabels(), ...descendants(widget)];
|
|
55
|
+
const root = widget.getRoot();
|
|
56
|
+
|
|
57
|
+
if (root instanceof Gtk.Widget) {
|
|
58
|
+
pool.push(...traverseWidgetTree(root, isAnyWidget));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return [...new Set(pool)];
|
|
62
|
+
};
|
|
63
|
+
|
|
31
64
|
const resolveRoot = (container: QueryContainer): Gtk.Widget | null => {
|
|
32
65
|
if (container instanceof Gtk.Widget) {
|
|
33
66
|
return container;
|
|
@@ -64,7 +97,7 @@ const roots = function* (container: Container): Generator<Gtk.Widget> {
|
|
|
64
97
|
|
|
65
98
|
const traverse = function* (container: Container): Generator<Gtk.Widget> {
|
|
66
99
|
for (const root of roots(container)) {
|
|
67
|
-
yield* traverseWidgetTree(root);
|
|
100
|
+
yield* traverseWidgetTree(root, isOnScreen);
|
|
68
101
|
}
|
|
69
102
|
};
|
|
70
103
|
|
|
@@ -80,4 +113,14 @@ const findAll = (container: Container, isMatch: (node: Gtk.Widget) => boolean):
|
|
|
80
113
|
return results;
|
|
81
114
|
};
|
|
82
115
|
|
|
83
|
-
export {
|
|
116
|
+
export {
|
|
117
|
+
TOPLEVELS,
|
|
118
|
+
children,
|
|
119
|
+
descendants,
|
|
120
|
+
findAll,
|
|
121
|
+
isOnScreen,
|
|
122
|
+
relationCandidates,
|
|
123
|
+
roots,
|
|
124
|
+
traverse,
|
|
125
|
+
type Container,
|
|
126
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -55,11 +55,19 @@ type MatcherOptions<T extends Gtk.Widget = Gtk.Widget> = {
|
|
|
55
55
|
as?: WidgetType<T> | undefined;
|
|
56
56
|
} & WaitForOptions;
|
|
57
57
|
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Constraints on a widget's numeric range value used by role queries. The numbers come from the
|
|
60
|
+
* accessibility tree and match within 0.001, the resolution GTK keeps them to.
|
|
61
|
+
*/
|
|
59
62
|
type ByRoleValue = {
|
|
60
63
|
/** The current value. */
|
|
61
64
|
now?: number | undefined;
|
|
65
|
+
/** The lower bound of the value's range. */
|
|
62
66
|
min?: number | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* The highest value the widget can reach, which on a paged widget such as a scrollbar is its
|
|
69
|
+
* adjustment's upper bound less one page.
|
|
70
|
+
*/
|
|
63
71
|
max?: number | undefined;
|
|
64
72
|
/** Matcher for the value's textual representation. */
|
|
65
73
|
text?: Matcher | undefined;
|
|
@@ -67,17 +75,25 @@ type ByRoleValue = {
|
|
|
67
75
|
|
|
68
76
|
/** Options for role queries: an accessible name matcher plus accessible state and value constraints. */
|
|
69
77
|
type ByRoleOptions<T extends Gtk.Widget = Gtk.Widget> = MatcherOptions<T> & {
|
|
78
|
+
/** Matcher for the widget's accessible name. */
|
|
70
79
|
name?: Matcher | undefined;
|
|
80
|
+
/** Required checked state; a mixed check button reads as neither, so it matches neither value. */
|
|
71
81
|
checked?: boolean | undefined;
|
|
82
|
+
/** Required active state of a toggle button; any other widget matches neither value. */
|
|
72
83
|
pressed?: boolean | undefined;
|
|
84
|
+
/** Required selected state of a row, list item, grid cell, option or tree item. */
|
|
73
85
|
selected?: boolean | undefined;
|
|
86
|
+
/** Required expanded state of an expander or tree expander. */
|
|
74
87
|
expanded?: boolean | undefined;
|
|
75
88
|
/** Heading or hierarchy level. */
|
|
76
89
|
level?: number | undefined;
|
|
90
|
+
/** Required busy state, which an unset state satisfies as false. */
|
|
77
91
|
busy?: boolean | undefined;
|
|
92
|
+
/** Matcher for the widget's own accessible description, ignoring its `described-by` targets. */
|
|
78
93
|
description?: Matcher | undefined;
|
|
94
|
+
/** Constraints on the widget's range value, each checked only when given. */
|
|
79
95
|
value?: ByRoleValue | undefined;
|
|
80
|
-
/** When true, include widgets excluded from the accessibility tree. */
|
|
96
|
+
/** When true, include widgets excluded from the accessibility tree. Widgets that are not mapped stay excluded. */
|
|
81
97
|
hidden?: boolean | undefined;
|
|
82
98
|
};
|
|
83
99
|
|
|
@@ -86,26 +102,39 @@ type WrapperComponent = ComponentType<{
|
|
|
86
102
|
children: ReactNode;
|
|
87
103
|
}>;
|
|
88
104
|
|
|
105
|
+
/** A custom query, taking the scope to search as its first argument and its matcher arguments after it. */
|
|
89
106
|
type Query = (container: Container, ...args: never[]) => unknown;
|
|
107
|
+
/** Custom queries keyed by the name each is bound under. */
|
|
90
108
|
type QueryMap = Record<string, Query>;
|
|
91
109
|
|
|
110
|
+
/** A query with its container argument already applied. */
|
|
92
111
|
type BoundQuery<Q extends Query> = Q extends (container: Container, ...args: infer A) => infer R
|
|
93
112
|
? (...args: A) => R
|
|
94
113
|
: never;
|
|
95
114
|
|
|
115
|
+
/** Custom queries with their container argument already applied, keyed as they were passed in. */
|
|
96
116
|
type BoundCustomQueries<Q extends QueryMap> = { [K in keyof Q]: BoundQuery<Q[K]> };
|
|
97
117
|
|
|
118
|
+
/** What each variant of a query family yields for the widget type it matched. */
|
|
98
119
|
type QueryFamilyReturns<T extends Gtk.Widget> = {
|
|
120
|
+
/** The single match, or null when nothing matched; throws when more than one matched. */
|
|
99
121
|
queryBy: T | null;
|
|
122
|
+
/** Every match, empty when nothing matched. */
|
|
100
123
|
queryAllBy: T[];
|
|
124
|
+
/** The single match; throws when nothing or more than one matched. */
|
|
101
125
|
getBy: T;
|
|
126
|
+
/** Every match; throws when nothing matched. */
|
|
102
127
|
getAllBy: T[];
|
|
128
|
+
/** The single match, retried until it appears or the timeout elapses. */
|
|
103
129
|
findBy: Promise<T>;
|
|
130
|
+
/** Every match, retried until at least one appears or the timeout elapses. */
|
|
104
131
|
findAllBy: Promise<T[]>;
|
|
105
132
|
};
|
|
106
133
|
|
|
134
|
+
/** What a query family matches against, which fixes the arguments it takes. */
|
|
107
135
|
type QueryKind = "role" | "text" | "name" | "value";
|
|
108
136
|
|
|
137
|
+
/** The arguments a query of the given kind takes after the family's leading ones. */
|
|
109
138
|
type QueryArgs<Kind extends QueryKind, T extends Gtk.Widget> = Kind extends "role"
|
|
110
139
|
? [role: Gtk.AccessibleRole, options?: ByRoleOptions<T>]
|
|
111
140
|
: Kind extends "name"
|
|
@@ -115,9 +144,8 @@ type QueryArgs<Kind extends QueryKind, T extends Gtk.Widget> = Kind extends "rol
|
|
|
115
144
|
: [text: Matcher, options?: MatcherOptions<T>];
|
|
116
145
|
|
|
117
146
|
/**
|
|
118
|
-
* One query
|
|
119
|
-
*
|
|
120
|
-
* from an `as` option.
|
|
147
|
+
* One family's query variants, each named for its variant followed by `Suffix` and taking `Head`
|
|
148
|
+
* ahead of the family's own arguments.
|
|
121
149
|
*/
|
|
122
150
|
type QueryFamily<Suffix extends string, Kind extends QueryKind, Head extends unknown[]> = {
|
|
123
151
|
[K in keyof QueryFamilyReturns<Gtk.Widget> as `${K & string}${Suffix}`]: <T extends Gtk.Widget = Gtk.Widget>(
|
|
@@ -125,6 +153,10 @@ type QueryFamily<Suffix extends string, Kind extends QueryKind, Head extends unk
|
|
|
125
153
|
) => QueryFamilyReturns<T>[K];
|
|
126
154
|
};
|
|
127
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Every built-in query, spanning the Role, LabelText, Text, Name, PlaceholderText, and DisplayValue
|
|
158
|
+
* families, with `Head` prepended to each signature.
|
|
159
|
+
*/
|
|
128
160
|
type QueryFamilies<Head extends unknown[]> = QueryFamily<"Role", "role", Head> &
|
|
129
161
|
QueryFamily<"LabelText", "text", Head> &
|
|
130
162
|
QueryFamily<"Text", "text", Head> &
|
|
@@ -137,14 +169,16 @@ type QueryFamilies<Head extends unknown[]> = QueryFamily<"Role", "role", Head> &
|
|
|
137
169
|
* React behavior toggles, error callbacks, and custom queries to bind.
|
|
138
170
|
*/
|
|
139
171
|
type RenderOptions<Q extends QueryMap = Record<never, never>> = {
|
|
172
|
+
/** Widget or root element to mount into; an undecorated harness window is created when omitted. */
|
|
140
173
|
container?: Gtk.Widget | RootElement | undefined;
|
|
141
174
|
/** Root of the subtree that bound queries search. */
|
|
142
175
|
baseElement?: Container | undefined;
|
|
176
|
+
/** Component wrapped around the rendered element, such as a context provider. */
|
|
143
177
|
wrapper?: WrapperComponent | undefined;
|
|
144
178
|
/** Render inside React StrictMode. */
|
|
145
|
-
|
|
179
|
+
isReactStrictMode?: boolean | undefined;
|
|
146
180
|
/** Enable widget animations during the test. */
|
|
147
|
-
|
|
181
|
+
areAnimationsEnabled?: boolean | undefined;
|
|
148
182
|
/** Called for errors caught by React error boundaries. */
|
|
149
183
|
onCaughtError?: ((error: unknown, errorInfo: ErrorInfo) => void) | undefined;
|
|
150
184
|
/** Called for errors React recovered from automatically. */
|
|
@@ -153,40 +187,52 @@ type RenderOptions<Q extends QueryMap = Record<never, never>> = {
|
|
|
153
187
|
queries?: Q | undefined;
|
|
154
188
|
};
|
|
155
189
|
|
|
190
|
+
/** Console helpers bundled with the bound queries on {@link screen} and on a render result. */
|
|
156
191
|
type DebugUtilities = {
|
|
192
|
+
/** Prints the widget tree of the given containers, or of the whole scope, to the console. */
|
|
157
193
|
debug: (element?: Container | Container[], options?: PrettyWidgetOptions) => void;
|
|
194
|
+
/** Prints the accessible roles found in the scope, with the widgets carrying each one. */
|
|
158
195
|
logRoles: () => void;
|
|
159
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Captures what is on screen, which is the active toplevel window: GTK4 exposes no position for
|
|
198
|
+
* a toplevel, so windows cannot be composited into a single image.
|
|
199
|
+
*/
|
|
200
|
+
screenshot: (options?: ScreenshotOptions) => Promise<ScreenshotResult>;
|
|
160
201
|
};
|
|
161
202
|
|
|
162
203
|
/** A captured screenshot: base64-encoded image data, its MIME type, and pixel dimensions. */
|
|
163
204
|
type ScreenshotResult = {
|
|
205
|
+
/** Base64-encoded image bytes. */
|
|
164
206
|
data: string;
|
|
207
|
+
/** MIME type of the encoded image, always `image/png`. */
|
|
165
208
|
mimeType: string;
|
|
209
|
+
/** Image width in pixels, after the scale factor is applied. */
|
|
166
210
|
width: number;
|
|
211
|
+
/** Image height in pixels, after the scale factor is applied. */
|
|
167
212
|
height: number;
|
|
168
213
|
};
|
|
169
214
|
|
|
170
|
-
/**
|
|
215
|
+
/**
|
|
216
|
+
* Options for capturing a screenshot: the poll timeout and interval, a rendering scale factor, and
|
|
217
|
+
* a file to write the PNG to.
|
|
218
|
+
*/
|
|
171
219
|
type ScreenshotOptions = Pick<WaitForOptions, "timeout" | "interval"> & {
|
|
172
220
|
/** Device scale factor applied when rendering. */
|
|
173
|
-
scale?: number;
|
|
221
|
+
scale?: number | undefined;
|
|
222
|
+
/** File the PNG is written to, with its parent directories created as needed. */
|
|
223
|
+
path?: string | undefined;
|
|
174
224
|
};
|
|
175
225
|
|
|
176
|
-
/**
|
|
177
|
-
* Selects the window to screenshot by index, or by title (exact string or regular expression);
|
|
178
|
-
* undefined targets the default window.
|
|
179
|
-
*/
|
|
180
|
-
type WindowSelector = number | string | RegExp | undefined;
|
|
181
|
-
|
|
182
226
|
/**
|
|
183
227
|
* Options for {@link renderHook}: an optional wrapper and the initial props (required unless the
|
|
184
228
|
* props type permits undefined).
|
|
185
229
|
*/
|
|
186
230
|
type RenderHookOptions<Props> = {
|
|
231
|
+
/** Component wrapped around the one calling the hook, such as a context provider. */
|
|
187
232
|
wrapper?: WrapperComponent;
|
|
188
233
|
} & (undefined extends Props
|
|
189
234
|
? {
|
|
235
|
+
/** Props the hook is invoked with until `rerender` is given new ones. */
|
|
190
236
|
initialProps?: Props;
|
|
191
237
|
}
|
|
192
238
|
: {
|
|
@@ -198,9 +244,14 @@ type RenderHookOptions<Props> = {
|
|
|
198
244
|
* props and to unmount.
|
|
199
245
|
*/
|
|
200
246
|
type RenderHookResult<Result, Props> = {
|
|
201
|
-
/**
|
|
202
|
-
result: {
|
|
247
|
+
/** Stable object the hook's return value is written to on every render. */
|
|
248
|
+
result: {
|
|
249
|
+
/** The hook's latest return value. */
|
|
250
|
+
current: Result;
|
|
251
|
+
};
|
|
252
|
+
/** Re-invokes the hook, keeping the previous props when none are given. */
|
|
203
253
|
rerender: (newProps?: Props) => Promise<void>;
|
|
254
|
+
/** Unmounts the component that calls the hook. */
|
|
204
255
|
unmount: () => Promise<void>;
|
|
205
256
|
};
|
|
206
257
|
|
|
@@ -222,7 +273,6 @@ export {
|
|
|
222
273
|
type DebugUtilities,
|
|
223
274
|
type ScreenshotResult,
|
|
224
275
|
type ScreenshotOptions,
|
|
225
|
-
type WindowSelector,
|
|
226
276
|
type RenderHookOptions,
|
|
227
277
|
type RenderHookResult,
|
|
228
278
|
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import { scheduleNextFrame } from "../frame-sync.js";
|
|
2
3
|
import { wrapEvent } from "./event-wrapper.js";
|
|
3
4
|
|
|
4
5
|
/** A scroll distance in pixels along each axis. */
|
|
5
6
|
type ScrollDelta = {
|
|
7
|
+
/** Distance added to the horizontal adjustment. */
|
|
6
8
|
x?: number;
|
|
9
|
+
/** Distance added to the vertical adjustment. */
|
|
7
10
|
y?: number;
|
|
8
11
|
};
|
|
9
12
|
|
|
@@ -12,6 +15,11 @@ type ScrollAdjustments = {
|
|
|
12
15
|
vertical: Gtk.Adjustment | null;
|
|
13
16
|
};
|
|
14
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Emits a jump `change-value` so a Gtk.Range moves to the given value.
|
|
20
|
+
*
|
|
21
|
+
* @throws When the widget is not a Gtk.Range.
|
|
22
|
+
*/
|
|
15
23
|
const slide = (widget: Gtk.Widget, value: number): Promise<void> =>
|
|
16
24
|
wrapEvent(widget, () => {
|
|
17
25
|
if (!(widget instanceof Gtk.Range)) {
|
|
@@ -37,16 +45,47 @@ const resolveScrollAdjustments = (widget: Gtk.Widget): ScrollAdjustments | null
|
|
|
37
45
|
return null;
|
|
38
46
|
};
|
|
39
47
|
|
|
48
|
+
const stepToward = (adjustment: Gtk.Adjustment, target: number, step: number): number => {
|
|
49
|
+
const value = adjustment.getValue();
|
|
50
|
+
adjustment.setValue(value < target ? Math.min(value + step, target) : Math.max(value - step, target));
|
|
51
|
+
|
|
52
|
+
return adjustment.getValue();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const rampTo = (adjustment: Gtk.Adjustment, target: number, step: number): void => {
|
|
56
|
+
let value = adjustment.getValue();
|
|
57
|
+
|
|
58
|
+
while (value !== target) {
|
|
59
|
+
const moved = stepToward(adjustment, target, step);
|
|
60
|
+
|
|
61
|
+
if (moved === value) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
value = moved;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
40
69
|
const applyScrollDelta = (adjustment: Gtk.Adjustment | null, delta: number): void => {
|
|
41
70
|
if (!adjustment || delta === 0) {
|
|
42
71
|
return;
|
|
43
72
|
}
|
|
44
73
|
|
|
45
|
-
adjustment.
|
|
74
|
+
const pageSize = adjustment.getPageSize();
|
|
75
|
+
rampTo(adjustment, adjustment.getValue() + delta, pageSize > 0 ? pageSize : Math.abs(delta));
|
|
46
76
|
};
|
|
47
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Adds the delta to the adjustments of the widget itself, or of its nearest Gtk.ScrolledWindow or
|
|
80
|
+
* Gtk.Scrollable ancestor.
|
|
81
|
+
*
|
|
82
|
+
* Each adjustment advances in viewport-sized steps rather than one jump, so virtualized views such
|
|
83
|
+
* as Gtk.ListView, Gtk.GridView and Gtk.ColumnView re-anchor onto the rows the new offset shows.
|
|
84
|
+
*
|
|
85
|
+
* @throws When neither the widget nor any of its ancestors is scrollable.
|
|
86
|
+
*/
|
|
48
87
|
const scroll = (widget: Gtk.Widget, delta: ScrollDelta): Promise<void> =>
|
|
49
|
-
wrapEvent(widget, () => {
|
|
88
|
+
wrapEvent(widget, async () => {
|
|
50
89
|
const adjustments = resolveScrollAdjustments(widget);
|
|
51
90
|
|
|
52
91
|
if (!adjustments) {
|
|
@@ -57,6 +96,7 @@ const scroll = (widget: Gtk.Widget, delta: ScrollDelta): Promise<void> =>
|
|
|
57
96
|
|
|
58
97
|
applyScrollDelta(adjustments.horizontal, delta.x ?? 0);
|
|
59
98
|
applyScrollDelta(adjustments.vertical, delta.y ?? 0);
|
|
99
|
+
await scheduleNextFrame(widget);
|
|
60
100
|
});
|
|
61
101
|
|
|
62
102
|
export { slide, scroll, type ScrollDelta };
|