gdharness 1.3.41 → 1.3.43

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 (107) hide show
  1. package/build/cli.js +131 -47
  2. package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
  3. package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
  4. package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
  5. package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
  6. package/build/godot/addons/gdharness_editor/file_reads.gd +114 -39
  7. package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
  8. package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
  9. package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
  10. package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
  11. package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
  12. package/build/godot/addons/gdharness_editor/reading.gd +37 -12
  13. package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
  14. package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
  15. package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
  16. package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
  17. package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
  18. package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
  19. package/build/godot/addons/gdharness_editor/tools/animation_states.gd +41 -28
  20. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
  21. package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
  22. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
  23. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
  24. package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
  25. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
  26. package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
  27. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +181 -107
  28. package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
  29. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
  30. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
  31. package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
  32. package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +66 -0
  33. package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
  34. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
  35. package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
  36. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
  37. package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
  38. package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
  39. package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
  40. package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
  41. package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
  42. package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
  43. package/build/godot/addons/gdharness_runtime/reading.gd +37 -12
  44. package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
  45. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
  46. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
  47. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
  48. package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
  49. package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
  50. package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
  51. package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
  52. package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
  53. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +38 -20
  54. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
  55. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
  56. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
  57. package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
  58. package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
  59. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
  60. package/build/godot/addons/gdharness_runtime/runtime_touch.gd +211 -0
  61. package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
  62. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
  63. package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
  64. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
  65. package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
  66. package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
  67. package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
  68. package/build/godot/addons/gdharness_tests/bench.gd +4 -5
  69. package/build/godot/addons/gdharness_tests/event.gd +10 -5
  70. package/build/godot/addons/gdharness_tests/share.gd +38 -18
  71. package/build/godot/addons/gdharness_tests/suites.gd +3 -3
  72. package/build/godot/addons/gdharness_tests/wait.gd +3 -2
  73. package/build/godot/loader/loader.gd +9 -9
  74. package/build/godot/operations/animation_reads.gd +84 -35
  75. package/build/godot/operations/audio_buses.gd +48 -19
  76. package/build/godot/operations/class_cache.gd +16 -11
  77. package/build/godot/operations/classdb_queries.gd +89 -44
  78. package/build/godot/operations/declared_values.gd +68 -32
  79. package/build/godot/operations/dependencies.gd +139 -93
  80. package/build/godot/operations/editor_tools.gd +9 -10
  81. package/build/godot/operations/export_presets.gd +100 -54
  82. package/build/godot/operations/file_reads.gd +114 -39
  83. package/build/godot/operations/file_walk.gd +12 -5
  84. package/build/godot/operations/gdscript_analysis.gd +105 -66
  85. package/build/godot/operations/gdscript_authoring.gd +138 -39
  86. package/build/godot/operations/godot_operations.gd +17 -11
  87. package/build/godot/operations/import_pipeline.gd +110 -59
  88. package/build/godot/operations/input_actions.gd +63 -39
  89. package/build/godot/operations/patterns.gd +3 -1
  90. package/build/godot/operations/plugins.gd +36 -16
  91. package/build/godot/operations/project_config.gd +144 -27
  92. package/build/godot/operations/project_diagnostics.gd +24 -11
  93. package/build/godot/operations/project_file.gd +16 -16
  94. package/build/godot/operations/project_validation.gd +71 -44
  95. package/build/godot/operations/property_paths.gd +22 -12
  96. package/build/godot/operations/property_values.gd +143 -78
  97. package/build/godot/operations/reading.gd +37 -12
  98. package/build/godot/operations/rename_checks.gd +2 -2
  99. package/build/godot/operations/resource_files.gd +11 -9
  100. package/build/godot/operations/same_values.gd +4 -4
  101. package/build/godot/operations/scene_connections.gd +27 -13
  102. package/build/godot/operations/script_check.gd +52 -49
  103. package/build/godot/operations/serialisation.gd +75 -51
  104. package/build/godot/operations/setting_reads.gd +7 -6
  105. package/build/godot/operations/value_shapes.gd +5 -6
  106. package/build/godot/tests/listed.gd +321 -268
  107. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  extends RefCounted
2
-
3
2
  ## How long the game's last frames took, timed from the runtime's first frame. Apart from the
4
3
  ## queries that report it, because those are loaded at the first request and the frames before it
5
4
  ## count too.
@@ -15,7 +14,8 @@ var _last_frame_usec: int = 0
15
14
  var _last_physics_frames: int = 0
16
15
 
17
16
 
18
- ## Called by the autoload once a frame, whatever else it does that frame, with the time it was called.
17
+ ## Called by the autoload once a frame, whatever else it does that frame, with the time it was
18
+ ## called.
19
19
  ##
20
20
  ## The engine's own monitors do not follow the frame: TIME_PROCESS and the frame rate are set once a
21
21
  ## second, the first to the slowest process step of that second, so a game whose frames took 16.7ms
@@ -1,37 +1,35 @@
1
1
  extends RefCounted
2
-
3
2
  ## The hover Godot gives the controls under the pointer, supplied in a native window the operating
4
3
  ## system has not said the pointer entered.
5
4
  ##
6
5
  ## With display/window/subwindows/embed_subwindows off, every window is a native one, and the engine
7
- ## hovers a control in one only once the window has had its mouse-enter: Viewport::_update_mouse_over
8
- ## asks the root which window the pointer is over, and only Window::_event_callback, called by the
9
- ## platform when the pointer crosses into the window, sets it. An injected event never crosses
10
- ## anything, and on gdharness's own desktop no pointer ever enters a window, so nothing was hovered.
11
- ## A BaseButton takes a mouse press only while hovered, so every click on a button in such a window
12
- ## was dropped while the press itself was routed to it. Reported from Game C, whose strip
13
- ## window holds its first-run question, and traced through Godot 4.7.2's source.
6
+ ## hovers a control in one only once the window has had its mouse-enter:
7
+ ## Viewport::_update_mouse_over asks the root which window the pointer is over, and only
8
+ ## Window::_event_callback, called by the platform when the pointer crosses into the window, sets
9
+ ## it. An injected event never crosses anything, and on gdharness's own desktop no pointer ever
10
+ ## enters a window, so nothing was hovered. A BaseButton takes a mouse press only while hovered, so
11
+ ## every click on a button in such a window was dropped while the press itself was routed to it.
12
+ ## Reported from Game C, whose strip window holds its first-run question, and traced through Godot
13
+ ## 4.7.2's source.
14
14
  ##
15
15
  ## What is supplied is what the engine does once the window has been entered: the control its own
16
16
  ## search finds under the point, MOUSE_ENTER and mouse_entered on it and on each holder the pointer
17
17
  ## passes to, MOUSE_ENTER_SELF on it, and the exits in the engine's order when the pointer moves on.
18
18
  ## The engine's own record of the hover is not reachable from a script, so
19
- ## Viewport.gui_get_hovered_control() still answers null there; [method hovered_in] answers for both.
19
+ ## Viewport.gui_get_hovered_control() still answers null there; [method hovered_in] answers for
20
+ ## both.
20
21
 
21
- ## Per viewport, by instance id, the instance ids of the controls the supplied hover is on, outermost
22
- ## first, ending with the one under the pointer, as the engine keeps its own.
22
+ ## Per viewport, by instance id, the instance ids of the controls the supplied hover is on,
23
+ ## outermost first, ending with the one under the pointer, as the engine keeps its own.
23
24
  var _supplied: Dictionary[int, Array] = {}
24
-
25
25
  ## The windows whose mouse-enter and mouse-exit are listened for, and whether the platform has said
26
26
  ## the pointer is inside each: once it has, the engine hovers there by itself.
27
27
  var _listened: Dictionary[int, bool] = {}
28
28
  var _inside: Dictionary[int, bool] = {}
29
-
30
- ## The windows told here that the pointer is in them, and whether the telling is under way, since the
31
- ## window answers it with the same mouse_entered the platform's enter sends.
29
+ ## The windows told here that the pointer is in them, and whether the telling is under way, since
30
+ ## the window answers it with the same mouse_entered the platform's enter sends.
32
31
  var _told: Dictionary[int, bool] = {}
33
32
  var _telling: bool = false
34
-
35
33
  ## Whether a script on a control declares `_has_point`, per script, since the search asks it of
36
34
  ## every control it passes.
37
35
  var _shapes: Dictionary[Script, bool] = {}
@@ -217,7 +215,9 @@ func control_at(viewport: Viewport, point: Vector2) -> Control:
217
215
  continue
218
216
  var holder: CanvasItem = _parent_item(root)
219
217
  var transform: Transform2D = (
220
- holder.get_global_transform_with_canvas() if holder != null else root.get_canvas_transform()
218
+ holder.get_global_transform_with_canvas()
219
+ if holder != null
220
+ else root.get_canvas_transform()
221
221
  )
222
222
  var found: Control = _at(root, point, transform)
223
223
  if found != null:
@@ -231,8 +231,10 @@ func control_at(viewport: Viewport, point: Vector2) -> Control:
231
231
  ## press aimed at a slider that went to a button drawn over it answered the slider's path alone,
232
232
  ## and the caller read that as the slider having been pressed.
233
233
  func press_of(aim: Dictionary, viewport: Viewport, position: Vector2) -> Dictionary:
234
- var took: Node = _pressed_at(viewport, viewport.get_final_transform().affine_inverse() * position)
235
- var said: Dictionary = {"press_went_to": null}
234
+ var took: Node = _pressed_at(
235
+ viewport, viewport.get_final_transform().affine_inverse() * position
236
+ )
237
+ var said: Dictionary = { "press_went_to": null }
236
238
  if took != null:
237
239
  said["press_went_to"] = str(took.get_path())
238
240
  var named: Object = aim.get("node")
@@ -265,7 +267,9 @@ func _pressed_at(viewport: Viewport, point: Vector2) -> Node:
265
267
  for child: Node in container.get_children():
266
268
  var shown: SubViewport = child as SubViewport
267
269
  if shown != null and not shown.gui_disable_input:
268
- var within: Node = _pressed_at(shown, shown.get_final_transform().affine_inverse() * inside)
270
+ var within: Node = _pressed_at(
271
+ shown, shown.get_final_transform().affine_inverse() * inside
272
+ )
269
273
  if within != null:
270
274
  return within
271
275
  return container
@@ -1,11 +1,9 @@
1
1
  extends RefCounted
2
-
3
2
  ## Input handed to the running game as if a player had given it: actions, keys, the mouse, and
4
3
  ## a whole click on a Control or a 3D node found by path. Typing and choosing from a menu, which
5
4
  ## reach the game the way a keyboard does, are in runtime_typing.gd.
6
5
 
7
6
  const GdhValues = preload("runtime_values.gd")
8
-
9
7
  ## Where a 3D node is drawn, which is what a click aimed at one has to work out first. Asked of the
10
8
  ## query module rather than worked out again here, so the place this aims at and the place a rect
11
9
  ## reports are the same place by construction rather than by agreement.
@@ -18,21 +16,18 @@ const GdhTargets = preload("runtime_targets.gd")
18
16
  const GdhMouse = preload("runtime_mouse.gd")
19
17
  const GdhTyping = preload("runtime_typing.gd")
20
18
  const GdhSays = preload("runtime_says.gd")
21
-
22
19
  ## Set, to the desktop's name, for a process gdharness started on a desktop of its own and for
23
20
  ## everything that process starts. Kept in step with `DESKTOP_VARIABLE` in src/desktop.ts.
24
21
  const DESKTOP_VARIABLE: String = "GDHARNESS_DESKTOP"
25
22
 
26
23
  ## Whether this run has had the pointer note in full; the game process is the run.
27
24
  static var _pointer_explained: bool = false
28
-
29
25
  ## Whether this run has had the note on a supplied hover in full.
30
26
  static var _hover_explained: bool = false
31
27
 
32
28
  var _host: Node
33
29
  var _values: GdhValues
34
30
  var _hover: GdhHover = GdhHover.new()
35
-
36
31
  ## Where the last injected pointer event put the pointer, once one has.
37
32
  ##
38
33
  ## Kept here rather than asked of the viewport: the root viewport answers get_mouse_position
@@ -42,12 +37,10 @@ var _hover: GdhHover = GdhHover.new()
42
37
  ## the injected events, which is why that reading measured right and a played game read it wrong.
43
38
  var _pointer: Vector2 = Vector2.ZERO
44
39
  var _pointer_placed: bool = false
45
-
46
- ## When the pointer last arrived, and how long it took over the arrival before that, in seconds: what
47
- ## turns a motion's distance into the speed a real one carries.
40
+ ## When the pointer last arrived, and how long it took over the arrival before that, in seconds:
41
+ ## what turns a motion's distance into the speed a real one carries.
48
42
  var _pointer_at_usec: int = 0
49
43
  var _arrival_seconds: float = 1.0 / 60.0
50
-
51
44
  ## The buttons a press pushed into a window other than the game's holds, which Input never hears of.
52
45
  var _pushed_held: int = 0
53
46
 
@@ -61,30 +54,30 @@ func _init(host: Node, values: GdhValues) -> void:
61
54
  ## pointer when the game is on one.
62
55
  ##
63
56
  ## For the root window the engine asks Windows where the pointer is, and GetCursorPos refuses a
64
- ## process on a desktop that is not in use: on one of gdharness's own it answers false and leaves the
65
- ## point unwritten, with access denied in the user's window station and with
57
+ ## process on a desktop that is not in use: on one of gdharness's own it answers false and leaves
58
+ ## the point unwritten, with access denied in the user's window station and with
66
59
  ## ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION in the one gdharness's desktop is in. The engine does
67
60
  ## not check, so a game driven there read get_mouse_position() as x 836852416 after a motion to
68
- ## 1310, 314. Nothing here can
69
- ## put a pointer on that desktop: Windows moves one only on the desktop in use, and making this one
70
- ## that would take the screen. What reads the injected event is right, tooltips included, since the
71
- ## engine places a tooltip from the motion that reached the control.
61
+ ## 1310, 314. Nothing here can put a pointer on that desktop: Windows moves one only on the desktop
62
+ ## in use, and making this one that would take the screen. What reads the injected event is right,
63
+ ## tooltips included, since the engine places a tooltip from the motion that reached the control.
72
64
  static func _pointer_noted(answer: Dictionary) -> Dictionary:
73
65
  if OS.get_environment(DESKTOP_VARIABLE) == "":
74
66
  return answer
75
- # The fact on every answer and the explanation on the first of the run: a session sends dozens of
76
- # pointer calls to one game, and the same four hundred characters on each was most of what came
77
- # back.
67
+ # The fact on every answer and the explanation on the first of the run: a session sends dozens
68
+ # of pointer calls to one game, and the same four hundred characters on each was most of what
69
+ # came back.
78
70
  answer["pointer_on_hidden_desktop"] = true
79
71
  if not _pointer_explained:
80
72
  _pointer_explained = true
81
73
  answer["pointer_note"] = (
82
- "This game is on gdharness's own desktop, where Windows refuses to say where the"
83
- + " pointer is, so get_mouse_position() on the root window, and get_global_mouse_position()"
84
- + " and get_local_mouse_position() on what is drawn there, read a meaningless position"
85
- + " rather than where this put it. What reads the event itself gets this position: the"
86
- + " control under the pointer, its hover and tooltip, and the game's input handlers. Said"
87
- + " once a run; pointer_on_hidden_desktop marks every answer it applies to."
74
+ "This game is on gdharness's own desktop, where Windows refuses to say where the "
75
+ + "pointer is, so get_mouse_position() on the root window, and "
76
+ + "get_global_mouse_position() and get_local_mouse_position() on what is drawn there, "
77
+ + "read a meaningless position rather than where this put it. What reads the event "
78
+ + "itself gets this position: the control under the pointer, its hover and tooltip, "
79
+ + "and the game's input handlers. Said once a run; pointer_on_hidden_desktop marks "
80
+ + "every answer it applies to."
88
81
  )
89
82
  return answer
90
83
 
@@ -98,13 +91,13 @@ static func _hover_noted(answer: Dictionary, supplied: bool) -> Dictionary:
98
91
  if not _hover_explained:
99
92
  _hover_explained = true
100
93
  answer["hover_note"] = (
101
- "This window has had no pointer enter from the operating system, and with subwindow"
102
- + " embedding off Godot hovers nothing in a native window until it has, so a Button there"
103
- + " takes no press. The runtime gave the controls under the pointer the hover the engine"
104
- + " would have: their mouse_entered and mouse_exited, and the hover a Button presses on,"
105
- + " and told the window the pointer is in it, so a motion reaches the control under it."
106
- + " Viewport.gui_get_hovered_control() still answers null in this window. Said once a run;"
107
- + " hover_supplied marks every answer it applies to."
94
+ "This window has had no pointer enter from the operating system, and with subwindow "
95
+ + "embedding off Godot hovers nothing in a native window until it has, so a Button "
96
+ + "there takes no press. The runtime gave the controls under the pointer the hover the "
97
+ + "engine would have: their mouse_entered and mouse_exited, and the hover a Button "
98
+ + "presses on, and told the window the pointer is in it, so a motion reaches the "
99
+ + "control under it. Viewport.gui_get_hovered_control() still answers null in this "
100
+ + "window. Said once a run; hover_supplied marks every answer it applies to."
108
101
  )
109
102
  return answer
110
103
 
@@ -189,18 +182,18 @@ func inject_action(params: Dictionary) -> Dictionary:
189
182
  var strength: float = GdhRead.as_float(params.get("strength", 1.0), 1.0)
190
183
 
191
184
  if action.is_empty():
192
- return {"type": "error", "message": "Action name required"}
185
+ return { "type": "error", "message": "Action name required" }
193
186
 
194
187
  if not InputMap.has_action(action):
195
- return {"type": "error", "message": GdhValues.no_action(action)}
188
+ return { "type": "error", "message": GdhValues.no_action(action) }
196
189
  var holding: Variant = GdhValues.held_for(params)
197
190
  if holding is String:
198
- return {"type": "error", "message": holding}
191
+ return { "type": "error", "message": holding }
199
192
  var frames: int = holding
200
193
 
201
194
  var focused: String = GdhValues.focus_path(_host)
202
195
  _say_action(action, whole or held, strength)
203
- var shortcuts: Dictionary = {"shortcuts": [], "shortcut_pressed": null}
196
+ var shortcuts: Dictionary = { "shortcuts": [], "shortcut_pressed": null }
204
197
  if whole or held:
205
198
  shortcuts = await GdhValues.press_shortcuts(_host.get_tree().root, action)
206
199
  var lasted: Variant = null
@@ -243,7 +236,7 @@ func inject_key(params: Dictionary) -> Dictionary:
243
236
  var keycode: int = 0 if keycode_raw is String else GdhRead.as_int(keycode_raw)
244
237
  var holding: Variant = GdhValues.held_for(params)
245
238
  if holding is String:
246
- return {"type": "error", "message": holding}
239
+ return { "type": "error", "message": holding }
247
240
  var frames: int = holding
248
241
 
249
242
  var event: InputEventKey = InputEventKey.new()
@@ -252,13 +245,14 @@ func inject_key(params: Dictionary) -> Dictionary:
252
245
  if not named.is_empty():
253
246
  event.keycode = OS.find_keycode_from_string(named)
254
247
  if event.keycode == KEY_NONE:
255
- return {"type": "error", "message": GdhValues.unknown_key(named)}
248
+ return { "type": "error", "message": GdhValues.unknown_key(named) }
256
249
  elif keycode > 0:
257
250
  event.keycode = keycode as Key
258
251
  else:
259
252
  return {
260
253
  "type": "error",
261
- "message": 'keycode is required: a key name, such as "Space" or "A", or its Godot keycode.'
254
+ "message":
255
+ 'keycode is required: a key name, such as "Space" or "A", or its Godot keycode.',
262
256
  }
263
257
 
264
258
  # A key event from a real keyboard carries all three, and InputMap consults whichever one
@@ -320,11 +314,11 @@ func inject_key(params: Dictionary) -> Dictionary:
320
314
  func inject_mouse_click(params: Dictionary) -> Dictionary:
321
315
  var aimed: Variant = GdhMouse.aim_of(params, _host.get_tree().root)
322
316
  if aimed is String:
323
- return {"type": "error", "message": aimed}
317
+ return { "type": "error", "message": aimed }
324
318
  var aim: Dictionary = aimed
325
319
  var point: Variant = GdhMouse.read_point(params, "x", "y")
326
320
  if point is String:
327
- return {"type": "error", "message": point}
321
+ return { "type": "error", "message": point }
328
322
  var local: Vector2 = point
329
323
  var placed: Dictionary = GdhMouse.carried(aim, local, _host.get_tree().root)
330
324
  var viewport: Viewport = placed["viewport"]
@@ -342,12 +336,12 @@ func inject_mouse_click(params: Dictionary) -> Dictionary:
342
336
  var whole: bool = notch or not params.has("pressed")
343
337
  var holding: Variant = GdhValues.held_for(params)
344
338
  if holding is String:
345
- return {"type": "error", "message": holding}
339
+ return { "type": "error", "message": holding }
346
340
  var frames: int = holding
347
341
  if notch and params.has("holdFrames"):
348
342
  return {
349
343
  "type": "error",
350
- "message": "A wheel step is pressed and let go at once, so holdFrames cannot hold one."
344
+ "message": "A wheel step is pressed and let go at once, so holdFrames cannot hold one.",
351
345
  }
352
346
 
353
347
  # A button pressed somewhere puts the pointer there: the motion after a held press measures
@@ -376,7 +370,8 @@ func inject_mouse_click(params: Dictionary) -> Dictionary:
376
370
  # press and one that acts on the release each get a frame to do it in.
377
371
  lasted = await GdhValues.hold(_host.get_tree(), frames)
378
372
  _send(viewport, GdhMouse.button_event(position, button, false, false, _held_in(viewport)))
379
- # Input hands the events on at the next frame, so the control under the pointer is read after it.
373
+ # Input hands the events on at the next frame, so the control under the pointer is read after
374
+ # it.
380
375
  await _host.get_tree().process_frame
381
376
  var hovered_path: Variant = _hovered_path(own)
382
377
 
@@ -391,7 +386,7 @@ func inject_mouse_click(params: Dictionary) -> Dictionary:
391
386
  "actions": GdhValues.actions_of(press),
392
387
  "pressed": held and not whole,
393
388
  "whole": whole,
394
- "double": double
389
+ "double": double,
395
390
  }
396
391
  answer.merge(where)
397
392
  answer.merge(pressing)
@@ -409,11 +404,11 @@ func inject_mouse_click(params: Dictionary) -> Dictionary:
409
404
  func inject_mouse_motion(params: Dictionary) -> Dictionary:
410
405
  var aimed: Variant = GdhMouse.aim_of(params, _host.get_tree().root)
411
406
  if aimed is String:
412
- return {"type": "error", "message": aimed}
407
+ return { "type": "error", "message": aimed }
413
408
  var aim: Dictionary = aimed
414
409
  var point: Variant = GdhMouse.read_point(params, "x", "y")
415
410
  if point is String:
416
- return {"type": "error", "message": point}
411
+ return { "type": "error", "message": point }
417
412
  var local: Vector2 = point
418
413
  var placed: Dictionary = GdhMouse.carried(aim, local, _host.get_tree().root)
419
414
  var viewport: Viewport = placed["viewport"]
@@ -421,7 +416,8 @@ func inject_mouse_motion(params: Dictionary) -> Dictionary:
421
416
  var relative: Vector2 = _arrive(position)
422
417
  if params.has("relativeX") or params.has("relativeY"):
423
418
  relative = Vector2(
424
- GdhRead.as_float(params.get("relativeX", 0.0)), GdhRead.as_float(params.get("relativeY", 0.0))
419
+ GdhRead.as_float(params.get("relativeX", 0.0)),
420
+ GdhRead.as_float(params.get("relativeY", 0.0))
425
421
  )
426
422
 
427
423
  var supplied: bool = _follow_in(viewport, position)
@@ -455,11 +451,11 @@ func inject_mouse_motion(params: Dictionary) -> Dictionary:
455
451
  func inject_mouse_drag(params: Dictionary) -> Dictionary:
456
452
  var aimed: Variant = GdhMouse.aim_of(params, _host.get_tree().root)
457
453
  if aimed is String:
458
- return {"type": "error", "message": aimed}
454
+ return { "type": "error", "message": aimed }
459
455
  var aim: Dictionary = aimed
460
456
  var stroke: Variant = GdhMouse.stroke_of(params)
461
457
  if stroke is String:
462
- return {"type": "error", "message": stroke}
458
+ return { "type": "error", "message": stroke }
463
459
  # Carried once, before the press: a hand drags across the screen, not along a node that moves.
464
460
  var named: Array[Vector2] = stroke
465
461
  var points: Array[Vector2] = []
@@ -475,12 +471,14 @@ func inject_mouse_drag(params: Dictionary) -> Dictionary:
475
471
  if own == 0:
476
472
  return {
477
473
  "type": "error",
478
- "message": "A wheel step has no held position to drag from: give left, right or middle."
474
+ "message":
475
+ "A wheel step has no held position to drag from: give left, right or middle.",
479
476
  }
480
477
  var frames: int = GdhRead.as_int(params.get("frames", 0), 0)
481
478
  if frames < 1 or frames > GdhValues.MOST_HELD:
482
479
  return {
483
- "type": "error", "message": "frames is %d, and it takes 1 to %d." % [frames, GdhValues.MOST_HELD]
480
+ "type": "error",
481
+ "message": "frames is %d, and it takes 1 to %d." % [frames, GdhValues.MOST_HELD],
484
482
  }
485
483
 
486
484
  var lengths: Array[float] = GdhMouse.lengths_of(points)
@@ -504,11 +502,11 @@ func inject_mouse_drag(params: Dictionary) -> Dictionary:
504
502
  "message":
505
503
  (
506
504
  (
507
- "%s was freed after the press and %d of %d motions, so the rest of the stroke"
508
- + " and the release went nowhere"
505
+ "%s was freed after the press and %d of %d motions, so the rest of the "
506
+ + "stroke and the release went nowhere"
509
507
  )
510
508
  % [where["window"], sent, frames]
511
- )
509
+ ),
512
510
  }
513
511
  if step > frames:
514
512
  break
@@ -556,7 +554,10 @@ func click(params: Dictionary) -> Dictionary:
556
554
  var item: Variant = null
557
555
  if not wanted.is_empty():
558
556
  var picked: Dictionary = GdhTargets.control_saying(
559
- _host.get_tree().root, "/root" if node_path.is_empty() else node_path, wanted, params.get("index")
557
+ _host.get_tree().root,
558
+ "/root" if node_path.is_empty() else node_path,
559
+ wanted,
560
+ params.get("index")
560
561
  )
561
562
  if picked.has("message"):
562
563
  return picked
@@ -564,7 +565,10 @@ func click(params: Dictionary) -> Dictionary:
564
565
  found = picked["found"]
565
566
  item = picked.get("item")
566
567
  if node_path.is_empty():
567
- return {"type": "error", "message": "click needs a path, or says to find the control by its words"}
568
+ return {
569
+ "type": "error",
570
+ "message": "click needs a path, or says to find the control by its words",
571
+ }
568
572
 
569
573
  var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
570
574
  if standing.has("message"):
@@ -572,23 +576,23 @@ func click(params: Dictionary) -> Dictionary:
572
576
  var node: Node = standing["node"]
573
577
  var named_point: Variant = GdhMouse.point_in(params, node, node_path, item != null)
574
578
  if named_point is String:
575
- return {"type": "error", "message": named_point}
579
+ return { "type": "error", "message": named_point }
576
580
  if node is Node3D:
577
581
  var spatial: Node3D = node
578
582
  return await _click_in_the_world(node_path, spatial, params)
579
583
  if not node is Control:
580
584
  return {
581
585
  "type": "error",
582
- "message": "%s is a %s, not a Control or a Node3D" % [node_path, node.get_class()]
586
+ "message": "%s is a %s, not a Control or a Node3D" % [node_path, node.get_class()],
583
587
  }
584
588
  var control: Control = node
585
589
  var unmoved: String = GdhMouse.takes_no_click(control, node_path, _host.get_tree().paused)
586
590
  if not unmoved.is_empty():
587
- return {"type": "error", "message": unmoved}
591
+ return { "type": "error", "message": unmoved }
588
592
 
589
- # Words that are part of a rich label are pressed where they are drawn, since its middle can be on
590
- # other words, a link among them. Said either way, so a press on the middle is never read as one
591
- # on the words.
593
+ # Words that are part of a rich label are pressed where they are drawn, since its middle can be
594
+ # on other words, a link among them. Said either way, so a press on the middle is never read as
595
+ # one on the words.
592
596
  var aim: Vector2 = control.size * 0.5
593
597
  var at_centre: bool = true
594
598
  var item_scrolled: bool = false
@@ -644,7 +648,7 @@ func click(params: Dictionary) -> Dictionary:
644
648
  (
645
649
  "%s was %s while it was being scrolled into view, so nothing was clicked"
646
650
  % [node_path, "freed" if left == "freed" else "taken out of the tree"]
647
- )
651
+ ),
648
652
  }
649
653
  local = control.get_global_transform_with_canvas() * aim
650
654
 
@@ -670,10 +674,12 @@ func click(params: Dictionary) -> Dictionary:
670
674
  # that silently reached nothing: outside the viewport it is drawn in, outside the one that shows
671
675
  # that on the screen, or clipped away by a ScrollContainer that would not scroll it into view.
672
676
  # Each is said of the viewport or container at fault, with its own rect and the point in it.
673
- var aimed: String = "its centre" if named_point == null else "the point %s clicked" % [named_point]
677
+ var aimed: String = (
678
+ "its centre" if named_point == null else "the point %s clicked" % [named_point]
679
+ )
674
680
  var out_there: String = _out_of_reach(control, node_path, local, reached, scrolled, aimed)
675
681
  if not out_there.is_empty():
676
- return {"type": "error", "message": out_there}
682
+ return { "type": "error", "message": out_there }
677
683
  var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
678
684
  if button < 0:
679
685
  return GdhMouse.no_such_button(params.get("button"))
@@ -699,16 +705,17 @@ func click(params: Dictionary) -> Dictionary:
699
705
  if hovered != null:
700
706
  hovered_path = str(hovered.get_path())
701
707
  var landed: bool = hovered == control or (hovered != null and control.is_ancestor_of(hovered))
702
- # Why a miss missed, said before the press: the press can free the control and what covers it, and
703
- # a click on a pointer spot that rebuilt its room errored reading the freed spot's filter and
704
- # answered nothing. The cover is asked before the press restacks windows too: hovered missed a
705
- # window over it.
708
+ # Why a miss missed, said before the press: the press can free the control and what covers it,
709
+ # and a click on a pointer spot that rebuilt its room errored reading the freed spot's filter
710
+ # and answered nothing. The cover is asked before the press restacks windows too: hovered missed
711
+ # a window over it.
706
712
  var why_not_landed: String = ""
707
- # A Label and a TextureRect ignore the pointer unless told otherwise, so the click goes to what is
708
- # under them, and landed false with that control as hovered said nothing of why.
713
+ # A Label and a TextureRect ignore the pointer unless told otherwise, so the click goes to what
714
+ # is under them, and landed false with that control as hovered said nothing of why.
709
715
  if not landed and control.mouse_filter == Control.MOUSE_FILTER_IGNORE:
710
716
  why_not_landed = (
711
- "%s's mouse_filter is ignore, so the pointer passes through it to what is under it" % node_path
717
+ "%s's mouse_filter is ignore, so the pointer passes through it to what is under it"
718
+ % node_path
712
719
  )
713
720
  elif not landed:
714
721
  var cover: Node = GdhTargets.drawn_over(control)
@@ -763,7 +770,9 @@ func click(params: Dictionary) -> Dictionary:
763
770
  "scrolled_into_view": scrolled,
764
771
  }
765
772
  if on_part:
766
- answer["pressed_on"] = "the middle of the part on screen, since no scroll brings its centre into view"
773
+ answer["pressed_on"] = (
774
+ "the middle of the part on screen, since no scroll brings its centre into view"
775
+ )
767
776
  if not why_not_landed.is_empty():
768
777
  answer["why_not_landed"] = why_not_landed
769
778
  if found != null:
@@ -772,7 +781,7 @@ func click(params: Dictionary) -> Dictionary:
772
781
  var now_pressed: Variant = null
773
782
  if is_instance_valid(toggle):
774
783
  now_pressed = toggle.button_pressed
775
- answer["pressed"] = {"before": was_pressed, "after": now_pressed}
784
+ answer["pressed"] = { "before": was_pressed, "after": now_pressed }
776
785
  if toggle != null:
777
786
  answer["pressed_signal"] = fired[0]
778
787
  # Whether a link was pressed apart from what it carries: a bare [url] reports an empty meta,
@@ -784,15 +793,20 @@ func click(params: Dictionary) -> Dictionary:
784
793
  return _pointer_noted(_hover_noted(answer, supplied))
785
794
 
786
795
 
787
- ## Why a click at [param control]'s centre, or at the point [param aimed] names, would reach nothing,
788
- ## or "" when it would reach it: the point outside the viewport it is drawn in, at [param local];
789
- ## outside the viewport showing that one on the screen, which [param reached] holds with the point
790
- ## there; or clipped away by a ScrollContainer holding it. A refusal names the viewport or container
791
- ## at fault and the point in that one's own space, since naming another printed a point inside the
792
- ## rect it was said to be outside. The note on a game with no window goes only on the viewport that
793
- ## is the window's.
796
+ ## Why a click at [param control]'s centre, or at the point [param aimed] names, would reach
797
+ ## nothing, or "" when it would reach it: the point outside the viewport it is drawn in, at
798
+ ## [param local]; outside the viewport showing that one on the screen, which [param reached] holds
799
+ ## with the point there; or clipped away by a ScrollContainer holding it. A refusal names the
800
+ ## viewport or container at fault and the point in that one's own space, since naming another
801
+ ## printed a point inside the rect it was said to be outside. The note on a game with no window goes
802
+ ## only on the viewport that is the window's.
794
803
  func _out_of_reach(
795
- control: Control, node_path: String, local: Vector2, reached: Dictionary, scrolled: bool, aimed: String
804
+ control: Control,
805
+ node_path: String,
806
+ local: Vector2,
807
+ reached: Dictionary,
808
+ scrolled: bool,
809
+ aimed: String
796
810
  ) -> String:
797
811
  var after: String = (
798
812
  ". It was scrolled as far as what holds it goes and is still out there" if scrolled else ""
@@ -802,10 +816,20 @@ func _out_of_reach(
802
816
  var point: Vector2 = place[1]
803
817
  if viewport.get_visible_rect().has_point(point):
804
818
  continue
805
- var which: String = "" if viewport == _host.get_tree().root else " of %s" % viewport.get_path()
819
+ var which: String = (
820
+ "" if viewport == _host.get_tree().root else " of %s" % viewport.get_path()
821
+ )
806
822
  return (
807
823
  "%s has %s at %s, outside the viewport%s %s, so nothing can click it%s%s"
808
- % [node_path, aimed, point, which, viewport.get_visible_rect(), after, _no_window_note(viewport)]
824
+ % [
825
+ node_path,
826
+ aimed,
827
+ point,
828
+ which,
829
+ viewport.get_visible_rect(),
830
+ after,
831
+ _no_window_note(viewport),
832
+ ]
809
833
  )
810
834
  var holder: ScrollContainer = GdhScreen.clipped_by(control, local)
811
835
  if holder == null:
@@ -842,7 +866,9 @@ func _no_window_note(viewport: Viewport) -> String:
842
866
  ## engine gets one from the system: a whole click, then a second press marked as a double click and
843
867
  ## its release. Sent as the marked press alone, it pressed a button once where a player's presses it
844
868
  ## twice.
845
- func _click_button(viewport: Viewport, position: Vector2, button: int, double: bool) -> InputEventMouseButton:
869
+ func _click_button(
870
+ viewport: Viewport, position: Vector2, button: int, double: bool
871
+ ) -> InputEventMouseButton:
846
872
  if double:
847
873
  _press_button(viewport, GdhMouse.button_event(position, button, true, false))
848
874
  await _host.get_tree().process_frame
@@ -881,7 +907,10 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
881
907
  return {
882
908
  "type": "error",
883
909
  "message":
884
- "%s is not visible%s, so nothing can click it" % [node_path, GdhTargets.why_hidden(item)]
910
+ (
911
+ "%s is not visible%s, so nothing can click it"
912
+ % [node_path, GdhTargets.why_hidden(item)]
913
+ ),
885
914
  }
886
915
 
887
916
  var found: Dictionary = GdhQueries.in_frame(item)
@@ -889,12 +918,16 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
889
918
  return {
890
919
  "type": "error",
891
920
  "message":
892
- "%s is not in a viewport with a current Camera3D, so there is nowhere to click it" % node_path
921
+ (
922
+ "%s is not in a viewport with a current Camera3D, so there is nowhere to click it"
923
+ % node_path
924
+ ),
893
925
  }
894
926
  if not found.has("aim"):
895
927
  return {
896
928
  "type": "error",
897
- "message": "%s is behind the camera drawing it, so it is not on screen to click" % node_path
929
+ "message":
930
+ "%s is behind the camera drawing it, so it is not on screen to click" % node_path,
898
931
  }
899
932
 
900
933
  var viewport: Viewport = item.get_viewport()
@@ -906,7 +939,7 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
906
939
  (
907
940
  "%s is drawn at %s, outside the viewport %s, so nothing can click it%s"
908
941
  % [node_path, aim, viewport.get_visible_rect(), _no_window_note(viewport)]
909
- )
942
+ ),
910
943
  }
911
944
 
912
945
  var out: Array[Dictionary] = GdhScreen.steps_out(viewport, aim)
@@ -920,9 +953,18 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
920
953
  "type": "error",
921
954
  "message":
922
955
  (
923
- "%s is drawn at %s, which is %s on the screen, outside the viewport %s, so nothing can click it%s"
924
- % [node_path, aim, reached_point, arriving.get_visible_rect(), _no_window_note(arriving)]
925
- )
956
+ (
957
+ "%s is drawn at %s, which is %s on the screen, outside the viewport %s, so "
958
+ + "nothing can click it%s"
959
+ )
960
+ % [
961
+ node_path,
962
+ aim,
963
+ reached_point,
964
+ arriving.get_visible_rect(),
965
+ _no_window_note(arriving),
966
+ ]
967
+ ),
926
968
  }
927
969
  var position: Vector2 = arriving.get_final_transform() * reached_point
928
970
  var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))