@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/user-event/index.ts
CHANGED
|
@@ -5,77 +5,167 @@ import { drag, dragAndDrop, drop, hover, longPress, rotate, swipe, unhover, zoom
|
|
|
5
5
|
import { keyboard, tab } from "./keyboard.js";
|
|
6
6
|
import { pointer, type PointerInput } from "./pointer.js";
|
|
7
7
|
import { deselectOptions, selectOptions } from "./selection.js";
|
|
8
|
-
import { createInitialState } from "./state.js";
|
|
8
|
+
import { createInitialState, type UserEventState } from "./state.js";
|
|
9
9
|
import { clear, copy, cut, paste, type } from "./text.js";
|
|
10
10
|
|
|
11
|
+
/** Options shared by every helper on a {@link UserEvent} instance. */
|
|
12
|
+
type UserEventOptions = {
|
|
13
|
+
/** Milliseconds to wait after each helper resolves; null, the default, waits not at all. */
|
|
14
|
+
delay?: number | null | undefined;
|
|
15
|
+
};
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* The set of user interaction helpers exposed by {@link userEvent}, covering clicks, typing,
|
|
13
19
|
* keyboard, pointer, gestures, selection, and scrolling.
|
|
14
20
|
*/
|
|
15
21
|
type UserEvent = {
|
|
22
|
+
/**
|
|
23
|
+
* Returns a fresh instance whose helpers share one keyboard and pointer state, so held modifiers
|
|
24
|
+
* and buttons carry across its calls but not across instances.
|
|
25
|
+
*/
|
|
26
|
+
setup: (options?: UserEventOptions) => UserEvent;
|
|
27
|
+
/**
|
|
28
|
+
* Activates a widget that neither claims the click nor carries a click gesture of its own, and
|
|
29
|
+
* otherwise presses and releases outwards from the clicked widget through every widget carrying
|
|
30
|
+
* a click gesture, stopping at the first button or indexed child. A list box row or flow box
|
|
31
|
+
* child receives its click gesture at the clicked position, passes one on to the gestures its
|
|
32
|
+
* container carries, and is then activated, or exclusively selected when its container does not
|
|
33
|
+
* activate on a single click. The press also stops at a widget whose click GTK4 implements
|
|
34
|
+
* itself, where the gestures that widget carries of its own still take the press and the same
|
|
35
|
+
* outcome is then applied through GTK's own public action: a list, grid, or column-view row is
|
|
36
|
+
* focused and selected, and activated as well when its view activates on a single click; an
|
|
37
|
+
* expandable tree expander that is itself the clicked widget toggles its expansion; a column
|
|
38
|
+
* header sorts by its column. A column-view cell and the row carrying the column headers stand
|
|
39
|
+
* in the way of the click rather than taking it, so a click on either reaches the row or the
|
|
40
|
+
* view behind it.
|
|
41
|
+
*/
|
|
16
42
|
click: typeof click;
|
|
43
|
+
/**
|
|
44
|
+
* Delivers a two-press click gesture along the same path a single click takes, without trying
|
|
45
|
+
* activation first, activating a list box row or flow box child on that path and replacing its
|
|
46
|
+
* container's selection whether or not that container activates on a single click. A widget
|
|
47
|
+
* whose click GTK4 implements itself receives that outcome once per press, so a row is selected
|
|
48
|
+
* and activated by the second press, a tree expander ends back where it started, and a column
|
|
49
|
+
* header inverts its order after sorting.
|
|
50
|
+
*/
|
|
17
51
|
dblClick: typeof dblClick;
|
|
52
|
+
/**
|
|
53
|
+
* Delivers a three-press click gesture the same way a double click is delivered, applying the
|
|
54
|
+
* same outcome to a list box row or flow box child, and applying the outcome GTK4 implements
|
|
55
|
+
* itself once per press to a row, tree expander, or column header.
|
|
56
|
+
*/
|
|
18
57
|
tripleClick: typeof tripleClick;
|
|
58
|
+
/** Moves focus within the widget's root, forward by default and backward with `isShiftHeld`. */
|
|
19
59
|
tab: typeof tab;
|
|
60
|
+
/** Focuses an editable widget, applies any initial selection, and inserts the text at the cursor. */
|
|
20
61
|
type: typeof type;
|
|
62
|
+
/** Focuses an editable widget and deletes its whole text, leaving nothing selected. */
|
|
21
63
|
clear: typeof clear;
|
|
64
|
+
/** Writes an editable widget's current selection to the clipboard. */
|
|
22
65
|
copy: typeof copy;
|
|
66
|
+
/** Writes an editable widget's current selection to the clipboard and deletes it. */
|
|
23
67
|
cut: typeof cut;
|
|
68
|
+
/** Inserts the given text, or the clipboard's text, at an editable widget's cursor. */
|
|
24
69
|
paste: typeof paste;
|
|
70
|
+
/**
|
|
71
|
+
* Selects the items at those positions in a view or drop-down, or selects the indexed children of
|
|
72
|
+
* a list box or flow box through their container, without activating them.
|
|
73
|
+
*/
|
|
25
74
|
selectOptions: typeof selectOptions;
|
|
75
|
+
/** Unselects the items at those positions, leaving list box and flow box children unactivated. */
|
|
26
76
|
deselectOptions: typeof deselectOptions;
|
|
77
|
+
/** Emits a motion `enter` on the widget, adding a motion controller when it has none. */
|
|
27
78
|
hover: typeof hover;
|
|
79
|
+
/** Emits a motion `leave` on the widget, adding a motion controller when it has none. */
|
|
28
80
|
unhover: typeof unhover;
|
|
81
|
+
/** Emits `angle-changed` on the widget's rotate gestures. */
|
|
29
82
|
rotate: typeof rotate;
|
|
83
|
+
/** Emits `scale-changed` on the widget's zoom gestures. */
|
|
30
84
|
zoom: typeof zoom;
|
|
85
|
+
/** Emits `swipe` with the given velocity on the widget's swipe gestures. */
|
|
31
86
|
swipe: typeof swipe;
|
|
87
|
+
/** Emits `pressed` at the given point on the widget's long-press gestures. */
|
|
32
88
|
longPress: typeof longPress;
|
|
89
|
+
/** Runs a begin, update, and end sequence on the widget's drag gestures, ending at the given offset. */
|
|
33
90
|
drag: typeof drag;
|
|
91
|
+
/** Emits `drop` with the given content on the widget's drop targets. */
|
|
34
92
|
drop: typeof drop;
|
|
93
|
+
/** Requires a drag source on the source widget, then emits `drop` with the content on the target's drop targets. */
|
|
35
94
|
dragAndDrop: typeof dragAndDrop;
|
|
95
|
+
/** Emits a jump `change-value` on a Gtk.Range so it moves to the given value. */
|
|
36
96
|
slide: typeof slide;
|
|
97
|
+
/** Adds the delta to the adjustments of the widget itself, or of its nearest scrollable ancestor. */
|
|
37
98
|
scroll: typeof scroll;
|
|
99
|
+
/** Sends a key sequence, dispatching matching shortcuts and tracking held modifiers across calls. */
|
|
38
100
|
keyboard: (widget: Gtk.Widget, input: string) => Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Applies a pointer token to the click gestures the widget already carries, tracking whether the
|
|
103
|
+
* left button is held across calls. On a widget whose click GTK4 implements itself, the release
|
|
104
|
+
* also applies GTK's own outcome, the way {@link UserEvent.click} does, and a column-view cell
|
|
105
|
+
* or the row carrying the column headers passes the token to the row or view behind it.
|
|
106
|
+
*/
|
|
39
107
|
pointer: (widget: Gtk.Widget, input: PointerInput) => Promise<void>;
|
|
40
108
|
};
|
|
41
109
|
|
|
42
|
-
const state = createInitialState();
|
|
43
|
-
|
|
44
110
|
/**
|
|
45
111
|
* High-level helpers that drive widgets by dispatching the same events and gestures GTK4 delivers
|
|
46
|
-
* when someone clicks, types, or drags.
|
|
112
|
+
* when someone clicks, types, or drags. Call `setup` for an instance with its own keyboard and
|
|
113
|
+
* pointer state.
|
|
47
114
|
*/
|
|
48
115
|
const userEvent: UserEvent = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
deselectOptions,
|
|
60
|
-
hover,
|
|
61
|
-
unhover,
|
|
62
|
-
rotate,
|
|
63
|
-
zoom,
|
|
64
|
-
swipe,
|
|
65
|
-
longPress,
|
|
66
|
-
drag,
|
|
67
|
-
drop,
|
|
68
|
-
dragAndDrop,
|
|
69
|
-
slide,
|
|
70
|
-
scroll,
|
|
71
|
-
keyboard: (widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input),
|
|
72
|
-
pointer: (widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input),
|
|
116
|
+
...createInstance(createInitialState(), {}),
|
|
117
|
+
setup: (options?: UserEventOptions): UserEvent => createInstance(createInitialState(), options ?? {}),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const settle = (delay: number | null | undefined): Promise<void> => {
|
|
121
|
+
if (delay === null || delay === undefined) {
|
|
122
|
+
return Promise.resolve();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
73
126
|
};
|
|
74
127
|
|
|
128
|
+
function createInstance(state: UserEventState, options: UserEventOptions): UserEvent {
|
|
129
|
+
const after =
|
|
130
|
+
<Args extends unknown[]>(helper: (...args: Args) => Promise<void>) =>
|
|
131
|
+
async (...args: Args): Promise<void> => {
|
|
132
|
+
await helper(...args);
|
|
133
|
+
await settle(options.delay);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
setup: (overrides?: UserEventOptions): UserEvent => createInstance(state, { ...options, ...overrides }),
|
|
138
|
+
click: after(click),
|
|
139
|
+
dblClick: after(dblClick),
|
|
140
|
+
tripleClick: after(tripleClick),
|
|
141
|
+
tab: after(tab),
|
|
142
|
+
type: after(type),
|
|
143
|
+
clear: after(clear),
|
|
144
|
+
copy: after(copy),
|
|
145
|
+
cut: after(cut),
|
|
146
|
+
paste: after(paste),
|
|
147
|
+
selectOptions: after(selectOptions),
|
|
148
|
+
deselectOptions: after(deselectOptions),
|
|
149
|
+
hover: after(hover),
|
|
150
|
+
unhover: after(unhover),
|
|
151
|
+
rotate: after(rotate),
|
|
152
|
+
zoom: after(zoom),
|
|
153
|
+
swipe: after(swipe),
|
|
154
|
+
longPress: after(longPress),
|
|
155
|
+
drag: after(drag),
|
|
156
|
+
drop: after(drop),
|
|
157
|
+
dragAndDrop: after(dragAndDrop),
|
|
158
|
+
slide: after(slide),
|
|
159
|
+
scroll: after(scroll),
|
|
160
|
+
keyboard: after((widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input)),
|
|
161
|
+
pointer: after((widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input)),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
75
165
|
export type { ScrollDelta } from "./adjustment.js";
|
|
76
166
|
export type { DragOffset, DragOptions, DropContent, DropOptions } from "./gesture.js";
|
|
77
167
|
export type { TabOptions } from "./keyboard.js";
|
|
78
168
|
export type { PointerInput } from "./pointer.js";
|
|
79
169
|
export type { TypeOptions } from "./text.js";
|
|
80
170
|
export { resetClipboard } from "./text.js";
|
|
81
|
-
export { userEvent, type UserEvent };
|
|
171
|
+
export { userEvent, type UserEvent, type UserEventOptions };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import { callBooleanGetter, getCallableMethod, hasWidgetMethod } from "../widget-getters.js";
|
|
3
|
+
|
|
4
|
+
const CHILD_AT_INDEX_GETTERS = ["getRowAtIndex", "getChildAtIndex"];
|
|
5
|
+
const CHILD_SELECTORS = ["selectRow", "selectChild"];
|
|
6
|
+
const CHILD_DESELECTORS = ["unselectRow", "unselectChild"];
|
|
7
|
+
const SELECTED_CHILD_GETTERS = ["getSelectedRows", "getSelectedChildren"];
|
|
8
|
+
const SELECTED_PROBE = "isSelected";
|
|
9
|
+
|
|
10
|
+
const hasIndexedChildren = (widget: Gtk.Widget): boolean =>
|
|
11
|
+
CHILD_AT_INDEX_GETTERS.some((getter) => hasWidgetMethod(widget, getter));
|
|
12
|
+
|
|
13
|
+
const getChildAtIndex = (widget: Gtk.Widget, index: number): Gtk.Widget | null => {
|
|
14
|
+
for (const getter of CHILD_AT_INDEX_GETTERS) {
|
|
15
|
+
const fn = getCallableMethod<[number], Gtk.Widget | null>(widget, getter);
|
|
16
|
+
|
|
17
|
+
if (fn) {
|
|
18
|
+
return fn(index) ?? null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const applyChildMethod = (container: Gtk.Widget, names: string[], child: Gtk.Widget): void => {
|
|
26
|
+
for (const name of names) {
|
|
27
|
+
const fn = getCallableMethod<[Gtk.Widget], unknown>(container, name);
|
|
28
|
+
|
|
29
|
+
if (fn) {
|
|
30
|
+
fn(child);
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const selectContainerChild = (container: Gtk.Widget, child: Gtk.Widget): void => {
|
|
38
|
+
applyChildMethod(container, CHILD_SELECTORS, child);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const unselectContainerChild = (container: Gtk.Widget, child: Gtk.Widget): void => {
|
|
42
|
+
applyChildMethod(container, CHILD_DESELECTORS, child);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const isChildSelected = (child: Gtk.Widget): boolean => callBooleanGetter(child, SELECTED_PROBE) ?? false;
|
|
46
|
+
|
|
47
|
+
const getSelectedChildren = (container: Gtk.Widget): Gtk.Widget[] => {
|
|
48
|
+
for (const getter of SELECTED_CHILD_GETTERS) {
|
|
49
|
+
const fn = getCallableMethod<[], Gtk.Widget[]>(container, getter);
|
|
50
|
+
|
|
51
|
+
if (fn) {
|
|
52
|
+
return fn();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return [];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const unselectOtherChildren = (container: Gtk.Widget, child: Gtk.Widget): void => {
|
|
60
|
+
for (const selected of getSelectedChildren(container)) {
|
|
61
|
+
if (selected !== child) {
|
|
62
|
+
unselectContainerChild(container, selected);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
getChildAtIndex,
|
|
69
|
+
hasIndexedChildren,
|
|
70
|
+
isChildSelected,
|
|
71
|
+
SELECTED_PROBE,
|
|
72
|
+
selectContainerChild,
|
|
73
|
+
unselectContainerChild,
|
|
74
|
+
unselectOtherChildren,
|
|
75
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Gdk from "@gtkx/gi/gdk";
|
|
2
|
+
import * as Gio from "@gtkx/gi/gio";
|
|
2
3
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
3
4
|
import type { UserEventState } from "./state.js";
|
|
4
5
|
import { getEditableDelegate } from "../editable.js";
|
|
@@ -6,12 +7,13 @@ import { fireEvent } from "../fire-event.js";
|
|
|
6
7
|
import { getOrCreateControllers } from "./controller.js";
|
|
7
8
|
import { wrapEvent } from "./event-wrapper.js";
|
|
8
9
|
|
|
9
|
-
/** Options for
|
|
10
|
+
/** Options for `userEvent.tab`. */
|
|
10
11
|
type TabOptions = {
|
|
11
|
-
|
|
12
|
+
/** Move focus backward instead of forward. */
|
|
13
|
+
isShiftHeld?: boolean;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
type KeyAction = { keyval: number;
|
|
16
|
+
type KeyAction = { keyval: number; isPress: boolean };
|
|
15
17
|
type ParseStep = { actions: KeyAction[]; next: number };
|
|
16
18
|
|
|
17
19
|
const KEY_MAP: Record<string, number> = {
|
|
@@ -58,9 +60,10 @@ const MODIFIER_KEYVAL_TO_MASK: Record<number, number> = {
|
|
|
58
60
|
[Gdk.KEY_Meta_R]: Gdk.ModifierType.META_MASK,
|
|
59
61
|
};
|
|
60
62
|
|
|
63
|
+
/** Moves focus within the widget's root, forward by default and backward when `isShiftHeld` is set. */
|
|
61
64
|
const tab = (widget: Gtk.Widget, options?: TabOptions): Promise<void> =>
|
|
62
65
|
wrapEvent(widget, () => {
|
|
63
|
-
const direction = options?.
|
|
66
|
+
const direction = options?.isShiftHeld ? Gtk.DirectionType.TAB_BACKWARD : Gtk.DirectionType.TAB_FORWARD;
|
|
64
67
|
const root = widget.getRoot();
|
|
65
68
|
|
|
66
69
|
if (root) {
|
|
@@ -68,7 +71,7 @@ const tab = (widget: Gtk.Widget, options?: TabOptions): Promise<void> =>
|
|
|
68
71
|
}
|
|
69
72
|
});
|
|
70
73
|
|
|
71
|
-
const parseKeyToken = (token: string): { keyval: number;
|
|
74
|
+
const parseKeyToken = (token: string): { keyval: number; isPress: boolean; isRelease: boolean } => {
|
|
72
75
|
let keyName = token;
|
|
73
76
|
let isPress = true;
|
|
74
77
|
let isRelease = true;
|
|
@@ -87,7 +90,7 @@ const parseKeyToken = (token: string): { keyval: number; press: boolean; release
|
|
|
87
90
|
throw new Error(`Unknown key: {${keyName}}`);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
return { keyval,
|
|
93
|
+
return { keyval, isPress, isRelease };
|
|
91
94
|
};
|
|
92
95
|
|
|
93
96
|
const parseCharAt = (input: string, i: number): ParseStep => {
|
|
@@ -95,8 +98,8 @@ const parseCharAt = (input: string, i: number): ParseStep => {
|
|
|
95
98
|
|
|
96
99
|
return {
|
|
97
100
|
actions: [
|
|
98
|
-
{ keyval,
|
|
99
|
-
{ keyval,
|
|
101
|
+
{ keyval, isPress: true },
|
|
102
|
+
{ keyval, isPress: false },
|
|
100
103
|
],
|
|
101
104
|
next: i + 1,
|
|
102
105
|
};
|
|
@@ -109,15 +112,15 @@ const parseBraceAt = (input: string, i: number): ParseStep | null => {
|
|
|
109
112
|
return null;
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
const { keyval,
|
|
115
|
+
const { keyval, isPress, isRelease } = parseKeyToken(input.slice(i + 1, endBrace));
|
|
113
116
|
const actions: KeyAction[] = [];
|
|
114
117
|
|
|
115
|
-
if (
|
|
116
|
-
actions.push({ keyval,
|
|
118
|
+
if (isPress) {
|
|
119
|
+
actions.push({ keyval, isPress: true });
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
if (
|
|
120
|
-
actions.push({ keyval,
|
|
122
|
+
if (isRelease) {
|
|
123
|
+
actions.push({ keyval, isPress: false });
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
return { actions, next: endBrace + 1 };
|
|
@@ -151,7 +154,7 @@ const updateModifierState = (state: UserEventState, action: KeyAction): void =>
|
|
|
151
154
|
return;
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
if (action.
|
|
157
|
+
if (action.isPress) {
|
|
155
158
|
state.modifierState |= mask;
|
|
156
159
|
} else {
|
|
157
160
|
state.modifierState &= ~mask;
|
|
@@ -236,13 +239,55 @@ const didDispatchShortcutsOnWidget = (widget: Gtk.Widget, keyval: number, modifi
|
|
|
236
239
|
return false;
|
|
237
240
|
};
|
|
238
241
|
|
|
239
|
-
const
|
|
240
|
-
const
|
|
242
|
+
const isAccelMatch = (accel: string, keyval: number, modifiers: Gdk.ModifierType): boolean => {
|
|
243
|
+
const [isParsed, accelKeyval, accelModifiers] = Gtk.acceleratorParse(accel);
|
|
244
|
+
|
|
245
|
+
return isParsed && accelKeyval === keyval && accelModifiers === modifiers;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const hasMatchingAccel = (
|
|
249
|
+
application: Gtk.Application,
|
|
250
|
+
detailedActionName: string,
|
|
251
|
+
keyval: number,
|
|
252
|
+
modifiers: number,
|
|
253
|
+
): boolean =>
|
|
254
|
+
application.getAccelsForAction(detailedActionName).some((accel) => isAccelMatch(accel, keyval, modifiers));
|
|
255
|
+
|
|
256
|
+
const didActivateDetailedAction = (window: Gtk.Window, detailedActionName: string): boolean => {
|
|
257
|
+
const [isParsed, actionName, target] = Gio.Action.parseDetailedName(detailedActionName);
|
|
241
258
|
|
|
242
|
-
|
|
243
|
-
|
|
259
|
+
return isParsed && window.activateAction(actionName, target);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const didDispatchAccelsOnWindow = (window: Gtk.Window, keyval: number, modifiers: number): boolean => {
|
|
263
|
+
const application = window.getApplication();
|
|
264
|
+
|
|
265
|
+
if (!application) {
|
|
266
|
+
return false;
|
|
244
267
|
}
|
|
245
268
|
|
|
269
|
+
for (const detailedActionName of application.listActionDescriptions()) {
|
|
270
|
+
if (hasMatchingAccel(application, detailedActionName, keyval, modifiers)) {
|
|
271
|
+
return didActivateDetailedAction(window, detailedActionName);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return false;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const didDispatchApplicationAccels = (widget: Gtk.Widget, keyval: number, modifiers: number): boolean => {
|
|
279
|
+
const root = widget.getRoot();
|
|
280
|
+
|
|
281
|
+
return root instanceof Gtk.Window && didDispatchAccelsOnWindow(root, keyval, modifiers);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const didDispatchDelegateShortcuts = (widget: Gtk.Widget, keyval: number, modifiers: number): boolean => {
|
|
285
|
+
const delegate = getEditableDelegate(widget);
|
|
286
|
+
|
|
287
|
+
return delegate !== null && didDispatchShortcutsOnWidget(delegate, keyval, modifiers);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const didDispatchShortcutsOnAncestors = (widget: Gtk.Widget, keyval: number, modifiers: number): boolean => {
|
|
246
291
|
for (let current: Gtk.Widget | null = widget; current; current = current.getParent()) {
|
|
247
292
|
if (didDispatchShortcutsOnWidget(current, keyval, modifiers)) {
|
|
248
293
|
return true;
|
|
@@ -252,6 +297,11 @@ const didDispatchShortcuts = (widget: Gtk.Widget, keyval: number, modifiers: num
|
|
|
252
297
|
return false;
|
|
253
298
|
};
|
|
254
299
|
|
|
300
|
+
const didDispatchShortcuts = (widget: Gtk.Widget, keyval: number, modifiers: number): boolean =>
|
|
301
|
+
didDispatchApplicationAccels(widget, keyval, modifiers) ||
|
|
302
|
+
didDispatchDelegateShortcuts(widget, keyval, modifiers) ||
|
|
303
|
+
didDispatchShortcutsOnAncestors(widget, keyval, modifiers);
|
|
304
|
+
|
|
255
305
|
const handleKeyPress = async (widget: Gtk.Widget, keyval: number, modifiers: number): Promise<void> => {
|
|
256
306
|
const isHandled = didDispatchShortcuts(widget, keyval, modifiers);
|
|
257
307
|
|
|
@@ -267,13 +317,13 @@ const applyKeyAction = async (
|
|
|
267
317
|
action: KeyAction,
|
|
268
318
|
): Promise<void> => {
|
|
269
319
|
updateModifierState(state, action);
|
|
270
|
-
const signalName = action.
|
|
320
|
+
const signalName = action.isPress ? "key-pressed" : "key-released";
|
|
271
321
|
|
|
272
322
|
for (const controller of controllers) {
|
|
273
323
|
controller.emit(signalName, action.keyval, 0, state.modifierState);
|
|
274
324
|
}
|
|
275
325
|
|
|
276
|
-
if (action.
|
|
326
|
+
if (action.isPress) {
|
|
277
327
|
await handleKeyPress(widget, action.keyval, state.modifierState);
|
|
278
328
|
}
|
|
279
329
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import * as GLib from "@gtkx/gi/glib";
|
|
3
|
+
import { callBooleanGetter, hasWidgetMethod } from "../widget-getters.js";
|
|
4
|
+
|
|
5
|
+
const SELECT_ACTION = "listitem.select";
|
|
6
|
+
const SINGLE_CLICK_ACTIVATE_GETTER = "getSingleClickActivate";
|
|
7
|
+
const DOUBLE_CLICK_PRESS = 2;
|
|
8
|
+
|
|
9
|
+
const viewFor = (row: Gtk.Widget): Gtk.Widget | null => {
|
|
10
|
+
let current: Gtk.Widget | null = row.getParent();
|
|
11
|
+
|
|
12
|
+
while (current !== null) {
|
|
13
|
+
if (hasWidgetMethod(current, SINGLE_CLICK_ACTIVATE_GETTER)) {
|
|
14
|
+
return current;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
current = current.getParent();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const isViewSingleClickActivating = (row: Gtk.Widget): boolean => {
|
|
24
|
+
const view = viewFor(row);
|
|
25
|
+
|
|
26
|
+
return view === null ? false : callBooleanGetter(view, SINGLE_CLICK_ACTIVATE_GETTER) ?? false;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const selectRow = (row: Gtk.Widget): void => {
|
|
30
|
+
row.activateAction(
|
|
31
|
+
SELECT_ACTION,
|
|
32
|
+
GLib.Variant.newTuple([GLib.Variant.newBoolean(false), GLib.Variant.newBoolean(false)]),
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const isRowActivatedByPress = (row: Gtk.Widget, press: number): boolean =>
|
|
37
|
+
press === DOUBLE_CLICK_PRESS || isViewSingleClickActivating(row);
|
|
38
|
+
|
|
39
|
+
const applyRowPress = (row: Gtk.Widget, press: number): void => {
|
|
40
|
+
if (row.getFocusOnClick()) {
|
|
41
|
+
row.grabFocus();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
selectRow(row);
|
|
45
|
+
|
|
46
|
+
if (isRowActivatedByPress(row, press)) {
|
|
47
|
+
row.activate();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const applyRowClick = (row: Gtk.Widget, nPress: number): void => {
|
|
52
|
+
for (let press = 1; press <= nPress; press++) {
|
|
53
|
+
applyRowPress(row, press);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export { applyRowClick };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import { getWidgetTypeName } from "../widget-getters.js";
|
|
3
|
+
import { applyHeaderClick } from "./column-header-click.js";
|
|
4
|
+
import { applyRowClick } from "./list-row-click.js";
|
|
5
|
+
|
|
6
|
+
type NativeClick = (widget: Gtk.Widget, nPress: number) => void;
|
|
7
|
+
|
|
8
|
+
const COLUMN_ROW_TYPE_NAME = "GtkColumnViewRowWidget";
|
|
9
|
+
const ROW_TYPE_NAMES: Set<string> = new Set([COLUMN_ROW_TYPE_NAME, "GtkListItemWidget"]);
|
|
10
|
+
const CELL_TYPE_NAME = "GtkColumnViewCellWidget";
|
|
11
|
+
const TITLE_TYPE_NAME = "GtkColumnViewTitle";
|
|
12
|
+
const TOGGLE_EXPAND_ACTION = "listitem.toggle-expand";
|
|
13
|
+
|
|
14
|
+
const isHeaderRow = (widget: Gtk.Widget): boolean =>
|
|
15
|
+
getWidgetTypeName(widget) === COLUMN_ROW_TYPE_NAME && widget.getParent() instanceof Gtk.ColumnView;
|
|
16
|
+
|
|
17
|
+
const isClickTransparent = (widget: Gtk.Widget): boolean =>
|
|
18
|
+
getWidgetTypeName(widget) === CELL_TYPE_NAME || isHeaderRow(widget);
|
|
19
|
+
|
|
20
|
+
const isExpanderToggling = (widget: Gtk.Widget): boolean =>
|
|
21
|
+
widget instanceof Gtk.TreeExpander &&
|
|
22
|
+
widget.getListRow()?.isExpandable() === true &&
|
|
23
|
+
!widget.getHideExpander();
|
|
24
|
+
|
|
25
|
+
const isColumnHeader = (widget: Gtk.Widget): boolean => getWidgetTypeName(widget) === TITLE_TYPE_NAME;
|
|
26
|
+
const isFactoryRow = (widget: Gtk.Widget): boolean => ROW_TYPE_NAMES.has(getWidgetTypeName(widget) ?? "");
|
|
27
|
+
|
|
28
|
+
const toggleExpander = (widget: Gtk.Widget, nPress: number): void => {
|
|
29
|
+
for (let press = 1; press <= nPress; press++) {
|
|
30
|
+
widget.activateAction(TOGGLE_EXPAND_ACTION, null);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const nativeClickFor = (widget: Gtk.Widget, isClicked: boolean): NativeClick | null => {
|
|
35
|
+
if (isClicked && isExpanderToggling(widget)) {
|
|
36
|
+
return toggleExpander;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (isColumnHeader(widget)) {
|
|
40
|
+
return applyHeaderClick;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return isFactoryRow(widget) ? applyRowClick : null;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export { isClickTransparent, type NativeClick, nativeClickFor };
|
|
@@ -1,40 +1,79 @@
|
|
|
1
|
-
import * as Gtk from "@gtkx/gi/gtk";
|
|
1
|
+
import type * as Gtk from "@gtkx/gi/gtk";
|
|
2
|
+
import type { NativeClick } from "./native-click.js";
|
|
2
3
|
import type { UserEventState } from "./state.js";
|
|
3
|
-
import {
|
|
4
|
-
import { getOrCreateControllers } from "./controller.js";
|
|
4
|
+
import { clickGestures, emitClickPhase, getAuthoredClickGestures } from "./click.js";
|
|
5
5
|
import { wrapEvent } from "./event-wrapper.js";
|
|
6
|
+
import { isClickTransparent, nativeClickFor } from "./native-click.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A pointer action token: a full click (`click`, `[MouseLeft]`), a button press (`down`,
|
|
9
10
|
* `[MouseLeft>]`), or a button release (`up`, `[/MouseLeft]`).
|
|
10
11
|
*/
|
|
11
12
|
type PointerInput = "click" | "down" | "up" | "[MouseLeft]" | "[MouseLeft>]" | "[/MouseLeft]";
|
|
13
|
+
type PointerTarget = { widget: Gtk.Widget; gestures: Gtk.GestureClick[]; native: NativeClick | null };
|
|
12
14
|
|
|
13
15
|
const PRESS_INPUTS: Set<PointerInput> = new Set(["[MouseLeft>]", "down"]);
|
|
14
16
|
const RELEASE_INPUTS: Set<PointerInput> = new Set(["[/MouseLeft]", "up"]);
|
|
15
17
|
const CLICK_INPUTS: Set<PointerInput> = new Set(["[MouseLeft]", "click"]);
|
|
16
18
|
|
|
19
|
+
const pointerWidgetFor = (widget: Gtk.Widget): Gtk.Widget => {
|
|
20
|
+
let current = widget;
|
|
21
|
+
|
|
22
|
+
while (isClickTransparent(current)) {
|
|
23
|
+
const parent = current.getParent();
|
|
24
|
+
|
|
25
|
+
if (parent === null) {
|
|
26
|
+
return current;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
current = parent;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return current;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const pointerGesturesFor = (widget: Gtk.Widget, native: NativeClick | null): Gtk.GestureClick[] =>
|
|
36
|
+
native === null ? clickGestures(widget) : getAuthoredClickGestures(widget);
|
|
37
|
+
|
|
38
|
+
const pointerTargetFor = (widget: Gtk.Widget): PointerTarget => {
|
|
39
|
+
const target = pointerWidgetFor(widget);
|
|
40
|
+
const native = nativeClickFor(target, true);
|
|
41
|
+
|
|
42
|
+
return { widget: target, gestures: pointerGesturesFor(target, native), native };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const pressPointer = ({ widget, gestures }: PointerTarget): void => {
|
|
46
|
+
emitClickPhase(widget, gestures, 1, "pressed");
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const releasePointer = ({ widget, gestures, native }: PointerTarget): void => {
|
|
50
|
+
emitClickPhase(widget, gestures, 1, "released");
|
|
51
|
+
native?.(widget, 1);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const isPointerPressable = (target: PointerTarget): boolean => target.native !== null || target.gestures.length > 0;
|
|
55
|
+
|
|
17
56
|
const applyPointerInput = (widget: Gtk.Widget, state: UserEventState, input: PointerInput): void => {
|
|
18
|
-
const
|
|
57
|
+
const target = pointerTargetFor(widget);
|
|
19
58
|
|
|
20
59
|
if (CLICK_INPUTS.has(input)) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
state.
|
|
60
|
+
pressPointer(target);
|
|
61
|
+
releasePointer(target);
|
|
62
|
+
state.isMouseLeftDown = false;
|
|
24
63
|
|
|
25
64
|
return;
|
|
26
65
|
}
|
|
27
66
|
|
|
28
|
-
if (PRESS_INPUTS.has(input) && !state.
|
|
29
|
-
|
|
30
|
-
state.
|
|
67
|
+
if (PRESS_INPUTS.has(input) && !state.isMouseLeftDown && isPointerPressable(target)) {
|
|
68
|
+
pressPointer(target);
|
|
69
|
+
state.isMouseLeftDown = true;
|
|
31
70
|
|
|
32
71
|
return;
|
|
33
72
|
}
|
|
34
73
|
|
|
35
|
-
if (RELEASE_INPUTS.has(input) && state.
|
|
36
|
-
|
|
37
|
-
state.
|
|
74
|
+
if (RELEASE_INPUTS.has(input) && state.isMouseLeftDown) {
|
|
75
|
+
releasePointer(target);
|
|
76
|
+
state.isMouseLeftDown = false;
|
|
38
77
|
}
|
|
39
78
|
};
|
|
40
79
|
|