@gtkx/testing 1.0.0-rc.4 → 1.1.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.
Files changed (212) hide show
  1. package/README.md +5 -5
  2. package/dist/accessible-native.d.ts +16 -0
  3. package/dist/accessible-native.d.ts.map +1 -0
  4. package/dist/accessible-native.js +206 -0
  5. package/dist/accessible-native.js.map +1 -0
  6. package/dist/act.d.ts +1 -3
  7. package/dist/act.d.ts.map +1 -1
  8. package/dist/act.js +1 -1
  9. package/dist/act.js.map +1 -1
  10. package/dist/build-queries.d.ts +27 -10
  11. package/dist/build-queries.d.ts.map +1 -1
  12. package/dist/build-queries.js +22 -24
  13. package/dist/build-queries.js.map +1 -1
  14. package/dist/cleanup-registry.d.ts +1 -1
  15. package/dist/cleanup-registry.d.ts.map +1 -1
  16. package/dist/cleanup-registry.js.map +1 -1
  17. package/dist/config.d.ts +8 -1
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +1 -0
  20. package/dist/config.js.map +1 -1
  21. package/dist/errors.d.ts +10 -1
  22. package/dist/errors.d.ts.map +1 -1
  23. package/dist/errors.js +10 -1
  24. package/dist/errors.js.map +1 -1
  25. package/dist/fire-event.d.ts +35 -6
  26. package/dist/fire-event.d.ts.map +1 -1
  27. package/dist/fire-event.js +23 -7
  28. package/dist/fire-event.js.map +1 -1
  29. package/dist/frame-sync.d.ts +3 -2
  30. package/dist/frame-sync.d.ts.map +1 -1
  31. package/dist/frame-sync.js +19 -9
  32. package/dist/frame-sync.js.map +1 -1
  33. package/dist/harness-window.d.ts +5 -0
  34. package/dist/harness-window.d.ts.map +1 -0
  35. package/dist/harness-window.js +17 -0
  36. package/dist/harness-window.js.map +1 -0
  37. package/dist/index.d.ts +14 -7
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +9 -5
  40. package/dist/index.js.map +1 -1
  41. package/dist/internal.d.ts +5 -0
  42. package/dist/internal.d.ts.map +1 -0
  43. package/dist/internal.js +5 -0
  44. package/dist/internal.js.map +1 -0
  45. package/dist/matchers.d.ts +88 -46
  46. package/dist/matchers.d.ts.map +1 -1
  47. package/dist/matchers.js +178 -20
  48. package/dist/matchers.js.map +1 -1
  49. package/dist/pretty-format.d.ts +2 -0
  50. package/dist/pretty-format.d.ts.map +1 -0
  51. package/dist/pretty-format.js +2 -0
  52. package/dist/pretty-format.js.map +1 -0
  53. package/dist/pretty-widget.d.ts +5 -2
  54. package/dist/pretty-widget.d.ts.map +1 -1
  55. package/dist/pretty-widget.js +75 -57
  56. package/dist/pretty-widget.js.map +1 -1
  57. package/dist/production.d.ts +8 -0
  58. package/dist/production.d.ts.map +1 -0
  59. package/dist/production.js +34 -0
  60. package/dist/production.js.map +1 -0
  61. package/dist/queries.d.ts +98 -56
  62. package/dist/queries.d.ts.map +1 -1
  63. package/dist/queries.js +123 -83
  64. package/dist/queries.js.map +1 -1
  65. package/dist/query-helpers.d.ts +44 -0
  66. package/dist/query-helpers.d.ts.map +1 -0
  67. package/dist/query-helpers.js +57 -0
  68. package/dist/query-helpers.js.map +1 -0
  69. package/dist/render-children.d.ts +9 -0
  70. package/dist/render-children.d.ts.map +1 -0
  71. package/dist/render-children.js +11 -0
  72. package/dist/render-children.js.map +1 -0
  73. package/dist/render.d.ts +12 -1
  74. package/dist/render.d.ts.map +1 -1
  75. package/dist/render.js +52 -16
  76. package/dist/render.js.map +1 -1
  77. package/dist/role-helpers.d.ts +12 -3
  78. package/dist/role-helpers.d.ts.map +1 -1
  79. package/dist/role-helpers.js +15 -5
  80. package/dist/role-helpers.js.map +1 -1
  81. package/dist/role-naming.d.ts +5 -0
  82. package/dist/role-naming.d.ts.map +1 -0
  83. package/dist/role-naming.js +96 -0
  84. package/dist/role-naming.js.map +1 -0
  85. package/dist/screenshot.d.ts +12 -20
  86. package/dist/screenshot.d.ts.map +1 -1
  87. package/dist/screenshot.js +179 -93
  88. package/dist/screenshot.js.map +1 -1
  89. package/dist/timers.d.ts +9 -0
  90. package/dist/timers.d.ts.map +1 -0
  91. package/dist/timers.js +21 -0
  92. package/dist/timers.js.map +1 -0
  93. package/dist/traversal.d.ts +7 -1
  94. package/dist/traversal.d.ts.map +1 -1
  95. package/dist/traversal.js +40 -6
  96. package/dist/traversal.js.map +1 -1
  97. package/dist/types.d.ts +32 -15
  98. package/dist/types.d.ts.map +1 -1
  99. package/dist/types.js.map +1 -1
  100. package/dist/user-event/adjustment.d.ts +3 -0
  101. package/dist/user-event/adjustment.d.ts.map +1 -1
  102. package/dist/user-event/adjustment.js +25 -3
  103. package/dist/user-event/adjustment.js.map +1 -1
  104. package/dist/user-event/click.d.ts +44 -8
  105. package/dist/user-event/click.d.ts.map +1 -1
  106. package/dist/user-event/click.js +176 -44
  107. package/dist/user-event/click.js.map +1 -1
  108. package/dist/user-event/column-header-click.d.ts +4 -0
  109. package/dist/user-event/column-header-click.d.ts.map +1 -0
  110. package/dist/user-event/column-header-click.js +55 -0
  111. package/dist/user-event/column-header-click.js.map +1 -0
  112. package/dist/user-event/event-wrapper.d.ts.map +1 -1
  113. package/dist/user-event/event-wrapper.js +34 -20
  114. package/dist/user-event/event-wrapper.js.map +1 -1
  115. package/dist/user-event/index.d.ts +80 -13
  116. package/dist/user-event/index.d.ts.map +1 -1
  117. package/dist/user-event/index.js +44 -26
  118. package/dist/user-event/index.js.map +1 -1
  119. package/dist/user-event/indexed-children.d.ts +10 -0
  120. package/dist/user-event/indexed-children.d.ts.map +1 -0
  121. package/dist/user-event/indexed-children.js +50 -0
  122. package/dist/user-event/indexed-children.js.map +1 -0
  123. package/dist/user-event/keyboard.d.ts.map +1 -1
  124. package/dist/user-event/keyboard.js +115 -31
  125. package/dist/user-event/keyboard.js.map +1 -1
  126. package/dist/user-event/list-row-click.d.ts +4 -0
  127. package/dist/user-event/list-row-click.d.ts.map +1 -0
  128. package/dist/user-event/list-row-click.js +38 -0
  129. package/dist/user-event/list-row-click.js.map +1 -0
  130. package/dist/user-event/native-click.d.ts +6 -0
  131. package/dist/user-event/native-click.d.ts.map +1 -0
  132. package/dist/user-event/native-click.js +36 -0
  133. package/dist/user-event/native-click.js.map +1 -0
  134. package/dist/user-event/notebook-tab-click.d.ts +5 -0
  135. package/dist/user-event/notebook-tab-click.d.ts.map +1 -0
  136. package/dist/user-event/notebook-tab-click.js +39 -0
  137. package/dist/user-event/notebook-tab-click.js.map +1 -0
  138. package/dist/user-event/pointer.d.ts +1 -1
  139. package/dist/user-event/pointer.d.ts.map +1 -1
  140. package/dist/user-event/pointer.js +33 -9
  141. package/dist/user-event/pointer.js.map +1 -1
  142. package/dist/user-event/selection.d.ts +11 -8
  143. package/dist/user-event/selection.d.ts.map +1 -1
  144. package/dist/user-event/selection.js +49 -38
  145. package/dist/user-event/selection.js.map +1 -1
  146. package/dist/user-event/text.d.ts +12 -5
  147. package/dist/user-event/text.d.ts.map +1 -1
  148. package/dist/user-event/text.js +151 -46
  149. package/dist/user-event/text.js.map +1 -1
  150. package/dist/wait-for.d.ts.map +1 -1
  151. package/dist/wait-for.js +4 -3
  152. package/dist/wait-for.js.map +1 -1
  153. package/dist/widget-accessible-properties.d.ts +14 -26
  154. package/dist/widget-accessible-properties.d.ts.map +1 -1
  155. package/dist/widget-accessible-properties.js +135 -190
  156. package/dist/widget-accessible-properties.js.map +1 -1
  157. package/dist/widget-getters.d.ts +11 -0
  158. package/dist/widget-getters.d.ts.map +1 -0
  159. package/dist/widget-getters.js +46 -0
  160. package/dist/widget-getters.js.map +1 -0
  161. package/dist/window-state.d.ts +10 -0
  162. package/dist/window-state.d.ts.map +1 -0
  163. package/dist/window-state.js +41 -0
  164. package/dist/window-state.js.map +1 -0
  165. package/dist/within.d.ts +3 -2
  166. package/dist/within.d.ts.map +1 -1
  167. package/dist/within.js +3 -2
  168. package/dist/within.js.map +1 -1
  169. package/package.json +21 -8
  170. package/src/accessible-native.ts +333 -0
  171. package/src/act.ts +1 -1
  172. package/src/build-queries.ts +47 -41
  173. package/src/cleanup-registry.ts +1 -1
  174. package/src/config.ts +10 -1
  175. package/src/errors.ts +12 -1
  176. package/src/fire-event.ts +61 -8
  177. package/src/frame-sync.ts +30 -9
  178. package/src/harness-window.ts +23 -0
  179. package/src/index.ts +18 -11
  180. package/src/internal.ts +12 -0
  181. package/src/matchers.ts +457 -72
  182. package/src/pretty-format.ts +1 -0
  183. package/src/pretty-widget.ts +98 -70
  184. package/src/production.ts +51 -0
  185. package/src/queries.ts +127 -89
  186. package/src/query-helpers.ts +97 -0
  187. package/src/render-children.ts +25 -0
  188. package/src/render.tsx +77 -17
  189. package/src/role-helpers.ts +16 -5
  190. package/src/role-naming.ts +99 -0
  191. package/src/screenshot.ts +266 -104
  192. package/src/timers.ts +31 -0
  193. package/src/traversal.ts +66 -6
  194. package/src/types.ts +34 -17
  195. package/src/user-event/adjustment.ts +31 -3
  196. package/src/user-event/click.ts +248 -52
  197. package/src/user-event/column-header-click.ts +76 -0
  198. package/src/user-event/event-wrapper.ts +41 -23
  199. package/src/user-event/index.ts +130 -40
  200. package/src/user-event/indexed-children.ts +75 -0
  201. package/src/user-event/keyboard.ts +161 -43
  202. package/src/user-event/list-row-click.ts +54 -0
  203. package/src/user-event/native-click.ts +51 -0
  204. package/src/user-event/notebook-tab-click.ts +57 -0
  205. package/src/user-event/pointer.ts +48 -9
  206. package/src/user-event/selection.ts +67 -41
  207. package/src/user-event/text.ts +192 -46
  208. package/src/wait-for.ts +4 -4
  209. package/src/widget-accessible-properties.ts +183 -230
  210. package/src/widget-getters.ts +85 -0
  211. package/src/window-state.ts +70 -0
  212. package/src/within.ts +3 -2
@@ -6,35 +6,78 @@ import { tab } from "./keyboard.js";
6
6
  import { type PointerInput } from "./pointer.js";
7
7
  import { deselectOptions, selectOptions } from "./selection.js";
8
8
  import { clear, copy, cut, paste, type } from "./text.js";
9
+ /** Options shared by every helper on a {@link UserEvent} instance. */
10
+ type UserEventOptions = {
11
+ /** Milliseconds to wait after each helper resolves; null, the default, waits not at all. */
12
+ delay?: number | null | undefined;
13
+ };
9
14
  /**
10
15
  * The set of user interaction helpers exposed by {@link userEvent}, covering clicks, typing,
11
16
  * keyboard, pointer, gestures, selection, and scrolling.
12
17
  */
13
18
  type UserEvent = {
14
19
  /**
15
- * Clicks a button, toggles a switch, and otherwise activates the widget, falling back to a click
16
- * gesture on its nearest clickable ancestor when activation does nothing.
20
+ * Returns a fresh instance whose helpers share one keyboard and pointer state, so held modifiers
21
+ * and buttons carry across its calls but not across instances.
22
+ */
23
+ setup: (options?: UserEventOptions) => UserEvent;
24
+ /**
25
+ * Activates a widget that neither claims the click nor carries a click gesture of its own, and
26
+ * otherwise presses and releases outwards from the clicked widget through every widget carrying
27
+ * a click gesture, stopping at the first button or indexed child. A list box row or flow box
28
+ * child receives its click gesture at the clicked position, passes one on to the gestures its
29
+ * container carries, and is then activated, or exclusively selected when its container does not
30
+ * activate on a single click. The press also stops at a widget whose click GTK4 implements
31
+ * itself, where the gestures that widget carries of its own still take the press and the same
32
+ * outcome is then applied through GTK's own public action: a list, grid, or column-view row is
33
+ * focused and selected, and activated as well when its view activates on a single click; an
34
+ * expandable tree expander that is itself the clicked widget toggles its expansion; a notebook
35
+ * tab, clicked directly or through its label, focuses its notebook and switches to its page; a
36
+ * column header sorts by its column. A column-view cell and the row carrying the column headers
37
+ * stand in the way of the click rather than taking it, so a click on either reaches the row or
38
+ * the view behind it.
17
39
  */
18
40
  click: typeof click;
19
- /** Emits a two-press click gesture on the widget. */
41
+ /**
42
+ * Delivers a two-press click gesture along the same path a single click takes, without trying
43
+ * activation first, activating a list box row or flow box child on that path and replacing its
44
+ * container's selection whether or not that container activates on a single click. A widget
45
+ * whose click GTK4 implements itself receives that outcome once per press, so a row is selected
46
+ * and activated by the second press, a tree expander ends back where it started, a notebook tab
47
+ * stays on the page the first press opened, and a column header inverts its order after sorting.
48
+ */
20
49
  dblClick: typeof dblClick;
21
- /** Emits a three-press click gesture on the widget. */
50
+ /**
51
+ * Delivers a three-press click gesture the same way a double click is delivered, applying the
52
+ * same outcome to a list box row or flow box child, and applying the outcome GTK4 implements
53
+ * itself once per press to a row, tree expander, notebook tab, or column header.
54
+ */
22
55
  tripleClick: typeof tripleClick;
23
56
  /** Moves focus within the widget's root, forward by default and backward with `isShiftHeld`. */
24
57
  tab: typeof tab;
25
- /** Focuses an editable widget, applies any initial selection, and inserts the text at the cursor. */
58
+ /**
59
+ * Focuses an editable widget the way clicking into it does, leaving its text unselected, applies
60
+ * any initial selection, and inserts the text at the cursor, deleting the text the widget has
61
+ * selected first, the way GTK4 does.
62
+ */
26
63
  type: typeof type;
27
- /** Empties an editable widget's text. */
64
+ /** Focuses an editable widget and deletes its whole text, leaving nothing selected. */
28
65
  clear: typeof clear;
29
66
  /** Writes an editable widget's current selection to the clipboard. */
30
67
  copy: typeof copy;
31
68
  /** Writes an editable widget's current selection to the clipboard and deletes it. */
32
69
  cut: typeof cut;
33
- /** Inserts the given text, or the clipboard's text, at an editable widget's cursor. */
70
+ /**
71
+ * Inserts the given text, or the clipboard's text, at an editable widget's cursor, deleting the
72
+ * text the widget has selected first, the way GTK4 does.
73
+ */
34
74
  paste: typeof paste;
35
- /** Selects the items at the given positions in a list view, drop-down, combo box, or list box. */
75
+ /**
76
+ * Selects the items at those positions in a view or drop-down, or selects the indexed children of
77
+ * a list box or flow box through their container, without activating them.
78
+ */
36
79
  selectOptions: typeof selectOptions;
37
- /** Deselects the items at the given positions in a list view or list box. */
80
+ /** Unselects the items at those positions, leaving list box and flow box children unactivated. */
38
81
  deselectOptions: typeof deselectOptions;
39
82
  /** Emits a motion `enter` on the widget, adding a motion controller when it has none. */
40
83
  hover: typeof hover;
@@ -58,14 +101,38 @@ type UserEvent = {
58
101
  slide: typeof slide;
59
102
  /** Adds the delta to the adjustments of the widget itself, or of its nearest scrollable ancestor. */
60
103
  scroll: typeof scroll;
61
- /** Sends a key sequence, dispatching matching shortcuts and tracking held modifiers across calls. */
104
+ /**
105
+ * Sends a key sequence along the chain GTK4 propagates a key event through: the application's
106
+ * accelerators first, then the capture-phase controllers from the root down to the event widget,
107
+ * the event widget's target-phase controllers, and the bubble-phase controllers from it back up
108
+ * to the root, so a key controller or shortcut controller a container or window above the widget
109
+ * carries receives the key too, each in the phase it declares. The event widget is the editable
110
+ * delegate GTK4 focuses when the widget has one, the `Gtk.Text` inside a `Gtk.Entry` for
111
+ * instance, so the delegate stands ahead of the widget in the chain. A shortcut controller whose
112
+ * scope is not local runs where it registered instead, the root for a global one and the nearest
113
+ * `Gtk.ShortcutManager` for a managed one, so it fires wherever focus sits inside that subtree
114
+ * and never at its own widget, while a shortcut whose widget is insensitive or unmapped stays
115
+ * inert wherever it sits. The first controller that handles a press ends the chain, and a
116
+ * `Gdk.KEY_Return` press none of them handled reaches an editable widget's activate handler.
117
+ * Built-in key bindings take part, since GTK4 carries them on a shortcut controller: a
118
+ * `Gtk.Text`'s arrow keys or a `Gtk.TextView`'s undo can consume a press before an ancestor
119
+ * sees it. Key controllers GTK4 attaches itself are the exception, since they read a GDK event
120
+ * that off-screen synthesis cannot produce, so only the key controllers the tree connected a
121
+ * `key-pressed` or `key-released` handler to take part. Held modifiers carry across calls.
122
+ */
62
123
  keyboard: (widget: Gtk.Widget, input: string) => Promise<void>;
63
- /** Applies a pointer token, tracking whether the left button is held across calls. */
124
+ /**
125
+ * Applies a pointer token to the click gestures the widget already carries, tracking whether the
126
+ * left button is held across calls. On a widget whose click GTK4 implements itself, the release
127
+ * also applies GTK's own outcome, the way {@link UserEvent.click} does, and a column-view cell
128
+ * or the row carrying the column headers passes the token to the row or view behind it.
129
+ */
64
130
  pointer: (widget: Gtk.Widget, input: PointerInput) => Promise<void>;
65
131
  };
66
132
  /**
67
133
  * High-level helpers that drive widgets by dispatching the same events and gestures GTK4 delivers
68
- * when someone clicks, types, or drags.
134
+ * when someone clicks, types, or drags. Call `setup` for an instance with its own keyboard and
135
+ * pointer state.
69
136
  */
70
137
  declare const userEvent: UserEvent;
71
138
  export type { ScrollDelta } from "./adjustment.js";
@@ -74,5 +141,5 @@ export type { TabOptions } from "./keyboard.js";
74
141
  export type { PointerInput } from "./pointer.js";
75
142
  export type { TypeOptions } from "./text.js";
76
143
  export { resetClipboard } from "./text.js";
77
- export { userEvent, type UserEvent };
144
+ export { userEvent, type UserEvent, type UserEventOptions };
78
145
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/user-event/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/user-event/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACvG,OAAO,EAAY,GAAG,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1D,sEAAsE;AACtE,KAAK,gBAAgB,GAAG;IACpB,4FAA4F;IAC5F,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,KAAK,SAAS,GAAG;IACb;;;OAGG;IACH,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC;IACjD;;;;;;;;;;;;;;;OAeG;IACH,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B;;;;OAIG;IACH,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,gGAAgG;IAChG,GAAG,EAAE,OAAO,GAAG,CAAC;IAChB;;;;OAIG;IACH,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,uFAAuF;IACvF,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,sEAAsE;IACtE,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,qFAAqF;IACrF,GAAG,EAAE,OAAO,GAAG,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB;;;OAGG;IACH,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,kGAAkG;IAClG,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,yFAAyF;IACzF,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,yFAAyF;IACzF,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,6DAA6D;IAC7D,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,2DAA2D;IAC3D,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,8EAA8E;IAC9E,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,wGAAwG;IACxG,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,wEAAwE;IACxE,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,oHAAoH;IACpH,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,iFAAiF;IACjF,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,qGAAqG;IACrG,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D;;;;;OAKG;IACH,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,SAAS,EAAE,SAGhB,CAAC;AA+CF,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { delay } from "../timers.js";
1
2
  import { scroll, slide } from "./adjustment.js";
2
3
  import { click, dblClick, tripleClick } from "./click.js";
3
4
  import { drag, dragAndDrop, drop, hover, longPress, rotate, swipe, unhover, zoom } from "./gesture.js";
@@ -6,37 +7,54 @@ import { pointer } from "./pointer.js";
6
7
  import { deselectOptions, selectOptions } from "./selection.js";
7
8
  import { createInitialState } from "./state.js";
8
9
  import { clear, copy, cut, paste, type } from "./text.js";
9
- const state = createInitialState();
10
10
  /**
11
11
  * High-level helpers that drive widgets by dispatching the same events and gestures GTK4 delivers
12
- * when someone clicks, types, or drags.
12
+ * when someone clicks, types, or drags. Call `setup` for an instance with its own keyboard and
13
+ * pointer state.
13
14
  */
14
15
  const userEvent = {
15
- click,
16
- dblClick,
17
- tripleClick,
18
- tab,
19
- type,
20
- clear,
21
- copy,
22
- cut,
23
- paste,
24
- selectOptions,
25
- deselectOptions,
26
- hover,
27
- unhover,
28
- rotate,
29
- zoom,
30
- swipe,
31
- longPress,
32
- drag,
33
- drop,
34
- dragAndDrop,
35
- slide,
36
- scroll,
37
- keyboard: (widget, input) => keyboard(state, widget, input),
38
- pointer: (widget, input) => pointer(state, widget, input),
16
+ ...createInstance(createInitialState(), {}),
17
+ setup: (options) => createInstance(createInitialState(), options ?? {}),
39
18
  };
19
+ const settle = (ms) => {
20
+ if (ms === null || ms === undefined) {
21
+ return Promise.resolve();
22
+ }
23
+ return delay(ms);
24
+ };
25
+ function createInstance(state, options) {
26
+ const after = (helper) => async (...args) => {
27
+ await helper(...args);
28
+ await settle(options.delay);
29
+ };
30
+ return {
31
+ setup: (overrides) => createInstance(state, { ...options, ...overrides }),
32
+ click: after(click),
33
+ dblClick: after(dblClick),
34
+ tripleClick: after(tripleClick),
35
+ tab: after(tab),
36
+ type: after(type),
37
+ clear: after(clear),
38
+ copy: after(copy),
39
+ cut: after(cut),
40
+ paste: after(paste),
41
+ selectOptions: after(selectOptions),
42
+ deselectOptions: after(deselectOptions),
43
+ hover: after(hover),
44
+ unhover: after(unhover),
45
+ rotate: after(rotate),
46
+ zoom: after(zoom),
47
+ swipe: after(swipe),
48
+ longPress: after(longPress),
49
+ drag: after(drag),
50
+ drop: after(drop),
51
+ dragAndDrop: after(dragAndDrop),
52
+ slide: after(slide),
53
+ scroll: after(scroll),
54
+ keyboard: after((widget, input) => keyboard(state, widget, input)),
55
+ pointer: after((widget, input) => pointer(state, widget, input)),
56
+ };
57
+ }
40
58
  export { resetClipboard } from "./text.js";
41
59
  export { userEvent };
42
60
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user-event/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACvG,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAqB,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA4D1D,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;AAEnC;;;GAGG;AACH,MAAM,SAAS,GAAc;IACzB,KAAK;IACL,QAAQ;IACR,WAAW;IACX,GAAG;IACH,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,GAAG;IACH,KAAK;IACL,aAAa;IACb,eAAe;IACf,KAAK;IACL,OAAO;IACP,MAAM;IACN,IAAI;IACJ,KAAK;IACL,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,WAAW;IACX,KAAK;IACL,MAAM;IACN,QAAQ,EAAE,CAAC,MAAkB,EAAE,KAAa,EAAiB,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC;IAC9F,OAAO,EAAE,CAAC,MAAkB,EAAE,KAAmB,EAAiB,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC;CACrG,CAAC;AAOF,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAkB,CAAC","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport { scroll, slide } from \"./adjustment.js\";\nimport { click, dblClick, tripleClick } from \"./click.js\";\nimport { drag, dragAndDrop, drop, hover, longPress, rotate, swipe, unhover, zoom } from \"./gesture.js\";\nimport { keyboard, tab } from \"./keyboard.js\";\nimport { pointer, type PointerInput } from \"./pointer.js\";\nimport { deselectOptions, selectOptions } from \"./selection.js\";\nimport { createInitialState } from \"./state.js\";\nimport { clear, copy, cut, paste, type } from \"./text.js\";\n\n/**\n * The set of user interaction helpers exposed by {@link userEvent}, covering clicks, typing,\n * keyboard, pointer, gestures, selection, and scrolling.\n */\ntype UserEvent = {\n /**\n * Clicks a button, toggles a switch, and otherwise activates the widget, falling back to a click\n * gesture on its nearest clickable ancestor when activation does nothing.\n */\n click: typeof click;\n /** Emits a two-press click gesture on the widget. */\n dblClick: typeof dblClick;\n /** Emits a three-press click gesture on the widget. */\n tripleClick: typeof tripleClick;\n /** Moves focus within the widget's root, forward by default and backward with `isShiftHeld`. */\n tab: typeof tab;\n /** Focuses an editable widget, applies any initial selection, and inserts the text at the cursor. */\n type: typeof type;\n /** Empties an editable widget's text. */\n clear: typeof clear;\n /** Writes an editable widget's current selection to the clipboard. */\n copy: typeof copy;\n /** Writes an editable widget's current selection to the clipboard and deletes it. */\n cut: typeof cut;\n /** Inserts the given text, or the clipboard's text, at an editable widget's cursor. */\n paste: typeof paste;\n /** Selects the items at the given positions in a list view, drop-down, combo box, or list box. */\n selectOptions: typeof selectOptions;\n /** Deselects the items at the given positions in a list view or list box. */\n deselectOptions: typeof deselectOptions;\n /** Emits a motion `enter` on the widget, adding a motion controller when it has none. */\n hover: typeof hover;\n /** Emits a motion `leave` on the widget, adding a motion controller when it has none. */\n unhover: typeof unhover;\n /** Emits `angle-changed` on the widget's rotate gestures. */\n rotate: typeof rotate;\n /** Emits `scale-changed` on the widget's zoom gestures. */\n zoom: typeof zoom;\n /** Emits `swipe` with the given velocity on the widget's swipe gestures. */\n swipe: typeof swipe;\n /** Emits `pressed` at the given point on the widget's long-press gestures. */\n longPress: typeof longPress;\n /** Runs a begin, update, and end sequence on the widget's drag gestures, ending at the given offset. */\n drag: typeof drag;\n /** Emits `drop` with the given content on the widget's drop targets. */\n drop: typeof drop;\n /** Requires a drag source on the source widget, then emits `drop` with the content on the target's drop targets. */\n dragAndDrop: typeof dragAndDrop;\n /** Emits a jump `change-value` on a Gtk.Range so it moves to the given value. */\n slide: typeof slide;\n /** Adds the delta to the adjustments of the widget itself, or of its nearest scrollable ancestor. */\n scroll: typeof scroll;\n /** Sends a key sequence, dispatching matching shortcuts and tracking held modifiers across calls. */\n keyboard: (widget: Gtk.Widget, input: string) => Promise<void>;\n /** Applies a pointer token, tracking whether the left button is held across calls. */\n pointer: (widget: Gtk.Widget, input: PointerInput) => Promise<void>;\n};\n\nconst state = createInitialState();\n\n/**\n * High-level helpers that drive widgets by dispatching the same events and gestures GTK4 delivers\n * when someone clicks, types, or drags.\n */\nconst userEvent: UserEvent = {\n click,\n dblClick,\n tripleClick,\n tab,\n type,\n clear,\n copy,\n cut,\n paste,\n selectOptions,\n deselectOptions,\n hover,\n unhover,\n rotate,\n zoom,\n swipe,\n longPress,\n drag,\n drop,\n dragAndDrop,\n slide,\n scroll,\n keyboard: (widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input),\n pointer: (widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input),\n};\n\nexport type { ScrollDelta } from \"./adjustment.js\";\nexport type { DragOffset, DragOptions, DropContent, DropOptions } from \"./gesture.js\";\nexport type { TabOptions } from \"./keyboard.js\";\nexport type { PointerInput } from \"./pointer.js\";\nexport type { TypeOptions } from \"./text.js\";\nexport { resetClipboard } from \"./text.js\";\nexport { userEvent, type UserEvent };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user-event/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport { delay } from \"../timers.js\";\nimport { scroll, slide } from \"./adjustment.js\";\nimport { click, dblClick, tripleClick } from \"./click.js\";\nimport { drag, dragAndDrop, drop, hover, longPress, rotate, swipe, unhover, zoom } from \"./gesture.js\";\nimport { keyboard, tab } from \"./keyboard.js\";\nimport { pointer, type PointerInput } from \"./pointer.js\";\nimport { deselectOptions, selectOptions } from \"./selection.js\";\nimport { createInitialState, type UserEventState } from \"./state.js\";\nimport { clear, copy, cut, paste, type } from \"./text.js\";\n\n/** Options shared by every helper on a {@link UserEvent} instance. */\ntype UserEventOptions = {\n /** Milliseconds to wait after each helper resolves; null, the default, waits not at all. */\n delay?: number | null | undefined;\n};\n\n/**\n * The set of user interaction helpers exposed by {@link userEvent}, covering clicks, typing,\n * keyboard, pointer, gestures, selection, and scrolling.\n */\ntype UserEvent = {\n /**\n * Returns a fresh instance whose helpers share one keyboard and pointer state, so held modifiers\n * and buttons carry across its calls but not across instances.\n */\n setup: (options?: UserEventOptions) => UserEvent;\n /**\n * Activates a widget that neither claims the click nor carries a click gesture of its own, and\n * otherwise presses and releases outwards from the clicked widget through every widget carrying\n * a click gesture, stopping at the first button or indexed child. A list box row or flow box\n * child receives its click gesture at the clicked position, passes one on to the gestures its\n * container carries, and is then activated, or exclusively selected when its container does not\n * activate on a single click. The press also stops at a widget whose click GTK4 implements\n * itself, where the gestures that widget carries of its own still take the press and the same\n * outcome is then applied through GTK's own public action: a list, grid, or column-view row is\n * focused and selected, and activated as well when its view activates on a single click; an\n * expandable tree expander that is itself the clicked widget toggles its expansion; a notebook\n * tab, clicked directly or through its label, focuses its notebook and switches to its page; a\n * column header sorts by its column. A column-view cell and the row carrying the column headers\n * stand in the way of the click rather than taking it, so a click on either reaches the row or\n * the view behind it.\n */\n click: typeof click;\n /**\n * Delivers a two-press click gesture along the same path a single click takes, without trying\n * activation first, activating a list box row or flow box child on that path and replacing its\n * container's selection whether or not that container activates on a single click. A widget\n * whose click GTK4 implements itself receives that outcome once per press, so a row is selected\n * and activated by the second press, a tree expander ends back where it started, a notebook tab\n * stays on the page the first press opened, and a column header inverts its order after sorting.\n */\n dblClick: typeof dblClick;\n /**\n * Delivers a three-press click gesture the same way a double click is delivered, applying the\n * same outcome to a list box row or flow box child, and applying the outcome GTK4 implements\n * itself once per press to a row, tree expander, notebook tab, or column header.\n */\n tripleClick: typeof tripleClick;\n /** Moves focus within the widget's root, forward by default and backward with `isShiftHeld`. */\n tab: typeof tab;\n /**\n * Focuses an editable widget the way clicking into it does, leaving its text unselected, applies\n * any initial selection, and inserts the text at the cursor, deleting the text the widget has\n * selected first, the way GTK4 does.\n */\n type: typeof type;\n /** Focuses an editable widget and deletes its whole text, leaving nothing selected. */\n clear: typeof clear;\n /** Writes an editable widget's current selection to the clipboard. */\n copy: typeof copy;\n /** Writes an editable widget's current selection to the clipboard and deletes it. */\n cut: typeof cut;\n /**\n * Inserts the given text, or the clipboard's text, at an editable widget's cursor, deleting the\n * text the widget has selected first, the way GTK4 does.\n */\n paste: typeof paste;\n /**\n * Selects the items at those positions in a view or drop-down, or selects the indexed children of\n * a list box or flow box through their container, without activating them.\n */\n selectOptions: typeof selectOptions;\n /** Unselects the items at those positions, leaving list box and flow box children unactivated. */\n deselectOptions: typeof deselectOptions;\n /** Emits a motion `enter` on the widget, adding a motion controller when it has none. */\n hover: typeof hover;\n /** Emits a motion `leave` on the widget, adding a motion controller when it has none. */\n unhover: typeof unhover;\n /** Emits `angle-changed` on the widget's rotate gestures. */\n rotate: typeof rotate;\n /** Emits `scale-changed` on the widget's zoom gestures. */\n zoom: typeof zoom;\n /** Emits `swipe` with the given velocity on the widget's swipe gestures. */\n swipe: typeof swipe;\n /** Emits `pressed` at the given point on the widget's long-press gestures. */\n longPress: typeof longPress;\n /** Runs a begin, update, and end sequence on the widget's drag gestures, ending at the given offset. */\n drag: typeof drag;\n /** Emits `drop` with the given content on the widget's drop targets. */\n drop: typeof drop;\n /** Requires a drag source on the source widget, then emits `drop` with the content on the target's drop targets. */\n dragAndDrop: typeof dragAndDrop;\n /** Emits a jump `change-value` on a Gtk.Range so it moves to the given value. */\n slide: typeof slide;\n /** Adds the delta to the adjustments of the widget itself, or of its nearest scrollable ancestor. */\n scroll: typeof scroll;\n /**\n * Sends a key sequence along the chain GTK4 propagates a key event through: the application's\n * accelerators first, then the capture-phase controllers from the root down to the event widget,\n * the event widget's target-phase controllers, and the bubble-phase controllers from it back up\n * to the root, so a key controller or shortcut controller a container or window above the widget\n * carries receives the key too, each in the phase it declares. The event widget is the editable\n * delegate GTK4 focuses when the widget has one, the `Gtk.Text` inside a `Gtk.Entry` for\n * instance, so the delegate stands ahead of the widget in the chain. A shortcut controller whose\n * scope is not local runs where it registered instead, the root for a global one and the nearest\n * `Gtk.ShortcutManager` for a managed one, so it fires wherever focus sits inside that subtree\n * and never at its own widget, while a shortcut whose widget is insensitive or unmapped stays\n * inert wherever it sits. The first controller that handles a press ends the chain, and a\n * `Gdk.KEY_Return` press none of them handled reaches an editable widget's activate handler.\n * Built-in key bindings take part, since GTK4 carries them on a shortcut controller: a\n * `Gtk.Text`'s arrow keys or a `Gtk.TextView`'s undo can consume a press before an ancestor\n * sees it. Key controllers GTK4 attaches itself are the exception, since they read a GDK event\n * that off-screen synthesis cannot produce, so only the key controllers the tree connected a\n * `key-pressed` or `key-released` handler to take part. Held modifiers carry across calls.\n */\n keyboard: (widget: Gtk.Widget, input: string) => Promise<void>;\n /**\n * Applies a pointer token to the click gestures the widget already carries, tracking whether the\n * left button is held across calls. On a widget whose click GTK4 implements itself, the release\n * also applies GTK's own outcome, the way {@link UserEvent.click} does, and a column-view cell\n * or the row carrying the column headers passes the token to the row or view behind it.\n */\n pointer: (widget: Gtk.Widget, input: PointerInput) => Promise<void>;\n};\n\n/**\n * High-level helpers that drive widgets by dispatching the same events and gestures GTK4 delivers\n * when someone clicks, types, or drags. Call `setup` for an instance with its own keyboard and\n * pointer state.\n */\nconst userEvent: UserEvent = {\n ...createInstance(createInitialState(), {}),\n setup: (options?: UserEventOptions): UserEvent => createInstance(createInitialState(), options ?? {}),\n};\n\nconst settle = (ms: number | null | undefined): Promise<void> => {\n if (ms === null || ms === undefined) {\n return Promise.resolve();\n }\n\n return delay(ms);\n};\n\nfunction createInstance(state: UserEventState, options: UserEventOptions): UserEvent {\n const after =\n <Args extends unknown[]>(helper: (...args: Args) => Promise<void>) =>\n async (...args: Args): Promise<void> => {\n await helper(...args);\n await settle(options.delay);\n };\n\n return {\n setup: (overrides?: UserEventOptions): UserEvent => createInstance(state, { ...options, ...overrides }),\n click: after(click),\n dblClick: after(dblClick),\n tripleClick: after(tripleClick),\n tab: after(tab),\n type: after(type),\n clear: after(clear),\n copy: after(copy),\n cut: after(cut),\n paste: after(paste),\n selectOptions: after(selectOptions),\n deselectOptions: after(deselectOptions),\n hover: after(hover),\n unhover: after(unhover),\n rotate: after(rotate),\n zoom: after(zoom),\n swipe: after(swipe),\n longPress: after(longPress),\n drag: after(drag),\n drop: after(drop),\n dragAndDrop: after(dragAndDrop),\n slide: after(slide),\n scroll: after(scroll),\n keyboard: after((widget: Gtk.Widget, input: string): Promise<void> => keyboard(state, widget, input)),\n pointer: after((widget: Gtk.Widget, input: PointerInput): Promise<void> => pointer(state, widget, input)),\n };\n}\n\nexport type { ScrollDelta } from \"./adjustment.js\";\nexport type { DragOffset, DragOptions, DropContent, DropOptions } from \"./gesture.js\";\nexport type { TabOptions } from \"./keyboard.js\";\nexport type { PointerInput } from \"./pointer.js\";\nexport type { TypeOptions } from \"./text.js\";\nexport { resetClipboard } from \"./text.js\";\nexport { userEvent, type UserEvent, type UserEventOptions };\n"]}
@@ -0,0 +1,10 @@
1
+ import type * as Gtk from "@gtkx/gi/gtk";
2
+ declare const SELECTED_PROBE = "isSelected";
3
+ declare const hasIndexedChildren: (widget: Gtk.Widget) => boolean;
4
+ declare const getChildAtIndex: (widget: Gtk.Widget, index: number) => Gtk.Widget | null;
5
+ declare const selectContainerChild: (container: Gtk.Widget, child: Gtk.Widget) => void;
6
+ declare const unselectContainerChild: (container: Gtk.Widget, child: Gtk.Widget) => void;
7
+ declare const isChildSelected: (child: Gtk.Widget) => boolean;
8
+ declare const unselectOtherChildren: (container: Gtk.Widget, child: Gtk.Widget) => void;
9
+ export { getChildAtIndex, hasIndexedChildren, isChildSelected, SELECTED_PROBE, selectContainerChild, unselectContainerChild, unselectOtherChildren, };
10
+ //# sourceMappingURL=indexed-children.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexed-children.d.ts","sourceRoot":"","sources":["../../src/user-event/indexed-children.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAC;AAOzC,QAAA,MAAM,cAAc,eAAe,CAAC;AAEpC,QAAA,MAAM,kBAAkB,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,OAC2B,CAAC;AAE7E,QAAA,MAAM,eAAe,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,OAAO,MAAM,KAAG,GAAG,CAAC,MAAM,GAAG,IAUzE,CAAC;AAcF,QAAA,MAAM,oBAAoB,GAAI,WAAW,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAG,IAExE,CAAC;AAEF,QAAA,MAAM,sBAAsB,GAAI,WAAW,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAG,IAE1E,CAAC;AAEF,QAAA,MAAM,eAAe,GAAI,OAAO,GAAG,CAAC,MAAM,KAAG,OAA4D,CAAC;AAc1G,QAAA,MAAM,qBAAqB,GAAI,WAAW,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAG,IAMzE,CAAC;AAEF,OAAO,EACH,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,GACxB,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { callBooleanGetter, getCallableMethod, hasWidgetMethod } from "../widget-getters.js";
2
+ const CHILD_AT_INDEX_GETTERS = ["getRowAtIndex", "getChildAtIndex"];
3
+ const CHILD_SELECTORS = ["selectRow", "selectChild"];
4
+ const CHILD_DESELECTORS = ["unselectRow", "unselectChild"];
5
+ const SELECTED_CHILD_GETTERS = ["getSelectedRows", "getSelectedChildren"];
6
+ const SELECTED_PROBE = "isSelected";
7
+ const hasIndexedChildren = (widget) => CHILD_AT_INDEX_GETTERS.some((getter) => hasWidgetMethod(widget, getter));
8
+ const getChildAtIndex = (widget, index) => {
9
+ for (const getter of CHILD_AT_INDEX_GETTERS) {
10
+ const fn = getCallableMethod(widget, getter);
11
+ if (fn) {
12
+ return fn(index) ?? null;
13
+ }
14
+ }
15
+ return null;
16
+ };
17
+ const applyChildMethod = (container, names, child) => {
18
+ for (const name of names) {
19
+ const fn = getCallableMethod(container, name);
20
+ if (fn) {
21
+ fn(child);
22
+ return;
23
+ }
24
+ }
25
+ };
26
+ const selectContainerChild = (container, child) => {
27
+ applyChildMethod(container, CHILD_SELECTORS, child);
28
+ };
29
+ const unselectContainerChild = (container, child) => {
30
+ applyChildMethod(container, CHILD_DESELECTORS, child);
31
+ };
32
+ const isChildSelected = (child) => callBooleanGetter(child, SELECTED_PROBE) ?? false;
33
+ const getSelectedChildren = (container) => {
34
+ for (const getter of SELECTED_CHILD_GETTERS) {
35
+ const fn = getCallableMethod(container, getter);
36
+ if (fn) {
37
+ return fn();
38
+ }
39
+ }
40
+ return [];
41
+ };
42
+ const unselectOtherChildren = (container, child) => {
43
+ for (const selected of getSelectedChildren(container)) {
44
+ if (selected !== child) {
45
+ unselectContainerChild(container, selected);
46
+ }
47
+ }
48
+ };
49
+ export { getChildAtIndex, hasIndexedChildren, isChildSelected, SELECTED_PROBE, selectContainerChild, unselectContainerChild, unselectOtherChildren, };
50
+ //# sourceMappingURL=indexed-children.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexed-children.js","sourceRoot":"","sources":["../../src/user-event/indexed-children.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE7F,MAAM,sBAAsB,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;AACpE,MAAM,eAAe,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;AACrD,MAAM,iBAAiB,GAAG,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AAC3D,MAAM,sBAAsB,GAAG,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;AAC1E,MAAM,cAAc,GAAG,YAAY,CAAC;AAEpC,MAAM,kBAAkB,GAAG,CAAC,MAAkB,EAAW,EAAE,CACvD,sBAAsB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7E,MAAM,eAAe,GAAG,CAAC,MAAkB,EAAE,KAAa,EAAqB,EAAE;IAC7E,KAAK,MAAM,MAAM,IAAI,sBAAsB,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,iBAAiB,CAA8B,MAAM,EAAE,MAAM,CAAC,CAAC;QAE1E,IAAI,EAAE,EAAE,CAAC;YACL,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;QAC7B,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,SAAqB,EAAE,KAAe,EAAE,KAAiB,EAAQ,EAAE;IACzF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,iBAAiB,CAAwB,SAAS,EAAE,IAAI,CAAC,CAAC;QAErE,IAAI,EAAE,EAAE,CAAC;YACL,EAAE,CAAC,KAAK,CAAC,CAAC;YAEV,OAAO;QACX,CAAC;IACL,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAAC,SAAqB,EAAE,KAAiB,EAAQ,EAAE;IAC5E,gBAAgB,CAAC,SAAS,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAAC,SAAqB,EAAE,KAAiB,EAAQ,EAAE;IAC9E,gBAAgB,CAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,KAAiB,EAAW,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,KAAK,CAAC;AAE1G,MAAM,mBAAmB,GAAG,CAAC,SAAqB,EAAgB,EAAE;IAChE,KAAK,MAAM,MAAM,IAAI,sBAAsB,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,iBAAiB,CAAmB,SAAS,EAAE,MAAM,CAAC,CAAC;QAElE,IAAI,EAAE,EAAE,CAAC;YACL,OAAO,EAAE,EAAE,CAAC;QAChB,CAAC;IACL,CAAC;IAED,OAAO,EAAE,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,SAAqB,EAAE,KAAiB,EAAQ,EAAE;IAC7E,KAAK,MAAM,QAAQ,IAAI,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;QACpD,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACrB,sBAAsB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;IACL,CAAC;AACL,CAAC,CAAC;AAEF,OAAO,EACH,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,GACxB,CAAC","sourcesContent":["import type * as Gtk from \"@gtkx/gi/gtk\";\nimport { callBooleanGetter, getCallableMethod, hasWidgetMethod } from \"../widget-getters.js\";\n\nconst CHILD_AT_INDEX_GETTERS = [\"getRowAtIndex\", \"getChildAtIndex\"];\nconst CHILD_SELECTORS = [\"selectRow\", \"selectChild\"];\nconst CHILD_DESELECTORS = [\"unselectRow\", \"unselectChild\"];\nconst SELECTED_CHILD_GETTERS = [\"getSelectedRows\", \"getSelectedChildren\"];\nconst SELECTED_PROBE = \"isSelected\";\n\nconst hasIndexedChildren = (widget: Gtk.Widget): boolean =>\n CHILD_AT_INDEX_GETTERS.some((getter) => hasWidgetMethod(widget, getter));\n\nconst getChildAtIndex = (widget: Gtk.Widget, index: number): Gtk.Widget | null => {\n for (const getter of CHILD_AT_INDEX_GETTERS) {\n const fn = getCallableMethod<[number], Gtk.Widget | null>(widget, getter);\n\n if (fn) {\n return fn(index) ?? null;\n }\n }\n\n return null;\n};\n\nconst applyChildMethod = (container: Gtk.Widget, names: string[], child: Gtk.Widget): void => {\n for (const name of names) {\n const fn = getCallableMethod<[Gtk.Widget], unknown>(container, name);\n\n if (fn) {\n fn(child);\n\n return;\n }\n }\n};\n\nconst selectContainerChild = (container: Gtk.Widget, child: Gtk.Widget): void => {\n applyChildMethod(container, CHILD_SELECTORS, child);\n};\n\nconst unselectContainerChild = (container: Gtk.Widget, child: Gtk.Widget): void => {\n applyChildMethod(container, CHILD_DESELECTORS, child);\n};\n\nconst isChildSelected = (child: Gtk.Widget): boolean => callBooleanGetter(child, SELECTED_PROBE) ?? false;\n\nconst getSelectedChildren = (container: Gtk.Widget): Gtk.Widget[] => {\n for (const getter of SELECTED_CHILD_GETTERS) {\n const fn = getCallableMethod<[], Gtk.Widget[]>(container, getter);\n\n if (fn) {\n return fn();\n }\n }\n\n return [];\n};\n\nconst unselectOtherChildren = (container: Gtk.Widget, child: Gtk.Widget): void => {\n for (const selected of getSelectedChildren(container)) {\n if (selected !== child) {\n unselectContainerChild(container, selected);\n }\n }\n};\n\nexport {\n getChildAtIndex,\n hasIndexedChildren,\n isChildSelected,\n SELECTED_PROBE,\n selectContainerChild,\n unselectContainerChild,\n unselectOtherChildren,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/user-event/keyboard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,mCAAmC;AACnC,KAAK,UAAU,GAAG;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAiDF,uGAAuG;AACvG,QAAA,MAAM,GAAG,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,UAAU,UAAU,KAAG,OAAO,CAAC,IAAI,CAQ9D,CAAC;AAoNP,QAAA,MAAM,QAAQ,GAAI,OAAO,cAAc,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAOnF,CAAC;AAEP,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/user-event/keyboard.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,mCAAmC;AACnC,KAAK,UAAU,GAAG;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAqDF,uGAAuG;AACvG,QAAA,MAAM,GAAG,GAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,UAAU,UAAU,KAAG,OAAO,CAAC,IAAI,CAQ9D,CAAC;AAsUP,QAAA,MAAM,QAAQ,GAAI,OAAO,cAAc,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAKnF,CAAC;AAEP,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,CAAC"}
@@ -1,8 +1,10 @@
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";
4
+ import { hasSignalListener } from "@gtkx/runtime/internal";
3
5
  import { getEditableDelegate } from "../editable.js";
4
6
  import { fireEvent } from "../fire-event.js";
5
- import { getOrCreateControllers } from "./controller.js";
7
+ import { ancestors, descendants } from "../traversal.js";
6
8
  import { wrapEvent } from "./event-wrapper.js";
7
9
  const KEY_MAP = {
8
10
  Enter: Gdk.KEY_Return,
@@ -36,6 +38,7 @@ const KEY_MAP = {
36
38
  F11: Gdk.KEY_F11,
37
39
  F12: Gdk.KEY_F12,
38
40
  };
41
+ const KEY_CONTROLLER_SIGNALS = ["key-pressed", "key-released"];
39
42
  const MODIFIER_KEYVAL_TO_MASK = {
40
43
  [Gdk.KEY_Shift_L]: Gdk.ModifierType.SHIFT_MASK,
41
44
  [Gdk.KEY_Shift_R]: Gdk.ModifierType.SHIFT_MASK,
@@ -133,15 +136,50 @@ const isTriggerMatch = (trigger, keyval, modifiers) => {
133
136
  }
134
137
  return false;
135
138
  };
136
- const didActivateShortcut = (shortcut, widget, keyval, modifiers) => {
137
- if (!isTriggerMatch(shortcut.getTrigger(), keyval, modifiers)) {
138
- return false;
139
+ const controllersOn = function* (widget) {
140
+ const controllers = widget.observeControllers();
141
+ const count = controllers.getNItems();
142
+ for (let i = 0; i < count; i++) {
143
+ const controller = controllers.getItem(i);
144
+ if (controller instanceof Gtk.EventController) {
145
+ yield controller;
146
+ }
147
+ }
148
+ };
149
+ const hasShortcut = (controller, shortcut) => {
150
+ const count = controller.getNItems();
151
+ for (let i = 0; i < count; i++) {
152
+ if (controller.getItem(i) === shortcut) {
153
+ return true;
154
+ }
139
155
  }
156
+ return false;
157
+ };
158
+ const isManagingShortcut = (controller, shortcut) => controller instanceof Gtk.ShortcutController &&
159
+ controller.getScope() !== Gtk.ShortcutScope.LOCAL &&
160
+ hasShortcut(controller, shortcut);
161
+ const managedShortcutHost = (manager, shortcut) => {
162
+ for (const current of [manager, ...descendants(manager)]) {
163
+ if (controllersOn(current).some((controller) => isManagingShortcut(controller, shortcut))) {
164
+ return current;
165
+ }
166
+ }
167
+ return null;
168
+ };
169
+ const isMovingFocus = (action) => action instanceof Gtk.SignalAction && action.getSignalName() === "move-focus";
170
+ const isShortcutLive = (host) => host.isSensitive() && host.getMapped();
171
+ const didRunShortcutAction = (shortcut, host) => {
140
172
  const action = shortcut.getAction();
141
- if (action instanceof Gtk.SignalAction && action.getSignalName() === "move-focus") {
173
+ if (action === null || isMovingFocus(action) || !isShortcutLive(host)) {
142
174
  return false;
143
175
  }
144
- return action?.activate(0, widget, shortcut.getArguments()) ?? false;
176
+ return action.activate(0, host, shortcut.getArguments());
177
+ };
178
+ const didActivateShortcut = (shortcut, widget, keyval, modifiers) => {
179
+ if (!isTriggerMatch(shortcut.getTrigger(), keyval, modifiers)) {
180
+ return false;
181
+ }
182
+ return didRunShortcutAction(shortcut, managedShortcutHost(widget, shortcut) ?? widget);
145
183
  };
146
184
  const didActivateMatchingShortcut = (controller, widget, keyval, modifiers) => {
147
185
  const count = controller.getNItems();
@@ -156,49 +194,95 @@ const didActivateMatchingShortcut = (controller, widget, keyval, modifiers) => {
156
194
  }
157
195
  return false;
158
196
  };
159
- const didDispatchShortcutsOnWidget = (widget, keyval, modifiers) => {
160
- const controllers = widget.observeControllers();
161
- for (let i = 0; i < controllers.getNItems(); i++) {
162
- const controller = controllers.getItem(i);
163
- if (controller instanceof Gtk.ShortcutController &&
164
- didActivateMatchingShortcut(controller, widget, keyval, modifiers)) {
165
- return true;
197
+ const isAccelMatch = (accel, keyval, modifiers) => {
198
+ const [isParsed, accelKeyval, accelModifiers] = Gtk.acceleratorParse(accel);
199
+ return isParsed && accelKeyval === keyval && accelModifiers === modifiers;
200
+ };
201
+ const hasMatchingAccel = (application, detailedActionName, keyval, modifiers) => application.getAccelsForAction(detailedActionName).some((accel) => isAccelMatch(accel, keyval, modifiers));
202
+ const didActivateDetailedAction = (window, detailedActionName) => {
203
+ const [isParsed, actionName, target] = Gio.Action.parseDetailedName(detailedActionName);
204
+ return isParsed && window.activateAction(actionName, target);
205
+ };
206
+ const didDispatchAccelsOnWindow = (window, keyval, modifiers) => {
207
+ const application = window.getApplication();
208
+ if (!application) {
209
+ return false;
210
+ }
211
+ for (const detailedActionName of application.listActionDescriptions()) {
212
+ if (hasMatchingAccel(application, detailedActionName, keyval, modifiers)) {
213
+ return didActivateDetailedAction(window, detailedActionName);
166
214
  }
167
215
  }
168
216
  return false;
169
217
  };
170
- const didDispatchShortcuts = (widget, keyval, modifiers) => {
218
+ const didDispatchApplicationAccels = (widget, keyval, modifiers) => {
219
+ const root = widget.getRoot();
220
+ return root instanceof Gtk.Window && didDispatchAccelsOnWindow(root, keyval, modifiers);
221
+ };
222
+ const isKeyStopController = (controller) => {
223
+ if (controller instanceof Gtk.ShortcutController) {
224
+ return controller.getScope() === Gtk.ShortcutScope.LOCAL;
225
+ }
226
+ return controller instanceof Gtk.EventControllerKey && hasSignalListener(controller, KEY_CONTROLLER_SIGNALS);
227
+ };
228
+ const keyStopsInPhase = (widget, phase) => {
229
+ const stops = [];
230
+ for (const controller of controllersOn(widget)) {
231
+ if (controller.getPropagationPhase() === phase && isKeyStopController(controller)) {
232
+ stops.push({ widget, controller });
233
+ }
234
+ }
235
+ return stops;
236
+ };
237
+ const propagationChain = (widget) => {
171
238
  const delegate = getEditableDelegate(widget);
172
- if (delegate && didDispatchShortcutsOnWidget(delegate, keyval, modifiers)) {
173
- return true;
239
+ const below = delegate === null ? [] : [delegate];
240
+ return [...below, widget, ...ancestors(widget)];
241
+ };
242
+ const keyPropagationChain = (widget) => {
243
+ const chain = propagationChain(widget);
244
+ const [eventWidget = widget] = chain;
245
+ return [
246
+ ...chain.toReversed().flatMap((current) => keyStopsInPhase(current, Gtk.PropagationPhase.CAPTURE)),
247
+ ...keyStopsInPhase(eventWidget, Gtk.PropagationPhase.TARGET),
248
+ ...chain.flatMap((current) => keyStopsInPhase(current, Gtk.PropagationPhase.BUBBLE)),
249
+ ];
250
+ };
251
+ const didStopHandlePress = (stop, keyval, modifiers) => {
252
+ const { controller, widget } = stop;
253
+ if (controller instanceof Gtk.ShortcutController) {
254
+ return didActivateMatchingShortcut(controller, widget, keyval, modifiers);
174
255
  }
175
- for (let current = widget; current; current = current.getParent()) {
176
- if (didDispatchShortcutsOnWidget(current, keyval, modifiers)) {
177
- return true;
256
+ return controller.emit("key-pressed", keyval, 0, modifiers);
257
+ };
258
+ const didHandlePress = (widget, keyval, modifiers) => didDispatchApplicationAccels(widget, keyval, modifiers) ||
259
+ keyPropagationChain(widget).some((stop) => didStopHandlePress(stop, keyval, modifiers));
260
+ const emitKeyReleased = (widget, keyval, modifiers) => {
261
+ for (const stop of keyPropagationChain(widget)) {
262
+ if (stop.controller instanceof Gtk.EventControllerKey) {
263
+ stop.controller.emit("key-released", keyval, 0, modifiers);
178
264
  }
179
265
  }
180
- return false;
181
266
  };
182
- const handleKeyPress = async (widget, keyval, modifiers) => {
183
- const isHandled = didDispatchShortcuts(widget, keyval, modifiers);
184
- if (!isHandled && keyval === Gdk.KEY_Return && widget instanceof Gtk.Editable) {
267
+ const applyKeyPress = async (widget, keyval, modifiers) => {
268
+ if (didHandlePress(widget, keyval, modifiers)) {
269
+ return;
270
+ }
271
+ if (keyval === Gdk.KEY_Return && widget instanceof Gtk.Editable) {
185
272
  await fireEvent(widget, "activate");
186
273
  }
187
274
  };
188
- const applyKeyAction = async (widget, controllers, state, action) => {
275
+ const applyKeyAction = async (widget, state, action) => {
189
276
  updateModifierState(state, action);
190
- const signalName = action.isPress ? "key-pressed" : "key-released";
191
- for (const controller of controllers) {
192
- controller.emit(signalName, action.keyval, 0, state.modifierState);
193
- }
194
277
  if (action.isPress) {
195
- await handleKeyPress(widget, action.keyval, state.modifierState);
278
+ await applyKeyPress(widget, action.keyval, state.modifierState);
279
+ return;
196
280
  }
281
+ emitKeyReleased(widget, action.keyval, state.modifierState);
197
282
  };
198
283
  const keyboard = (state, widget, input) => wrapEvent(widget, async () => {
199
- const controllers = getOrCreateControllers(widget, Gtk.EventControllerKey);
200
284
  for (const action of parseKeyboardInput(input)) {
201
- await applyKeyAction(widget, controllers, state, action);
285
+ await applyKeyAction(widget, state, action);
202
286
  }
203
287
  });
204
288
  export { tab, keyboard };