gdharness 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +322 -96
- package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
- package/build/godot/addons/gdharness_editor/bridge_client.gd +8 -8
- package/build/godot/addons/gdharness_editor/declared_values.gd +116 -0
- package/build/godot/addons/gdharness_editor/declared_values.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +65 -25
- package/build/godot/addons/gdharness_editor/headless_check.gd +7 -7
- package/build/godot/addons/gdharness_editor/plugin.gd +6 -6
- package/build/godot/addons/gdharness_editor/property_values.gd +222 -59
- package/build/godot/addons/gdharness_editor/reading.gd +65 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
- package/build/godot/addons/gdharness_editor/serialisation.gd +49 -94
- package/build/godot/addons/gdharness_editor/tool_executor.gd +24 -24
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +52 -41
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +14 -14
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +175 -74
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +17 -17
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +14 -12
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +7 -7
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +88 -84
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/value_shapes.gd +118 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +65 -0
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +29 -29
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +21 -18
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -64
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +8 -8
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -10
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +7 -7
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +41 -41
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +33 -33
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +20 -20
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +52 -25
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
- package/build/godot/addons/gdharness_runtime/serialisation.gd +49 -94
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +118 -0
- package/build/godot/addons/gdharness_runtime/value_shapes.gd.uid +1 -0
- package/build/godot/operations/animation_reads.gd +7 -19
- package/build/godot/operations/audio_buses.gd +10 -10
- package/build/godot/operations/class_cache.gd +16 -16
- package/build/godot/operations/classdb_queries.gd +25 -15
- package/build/godot/operations/declared_values.gd +116 -0
- package/build/godot/operations/dependencies.gd +141 -33
- package/build/godot/operations/editor_tools.gd +12 -12
- package/build/godot/operations/export_presets.gd +14 -14
- package/build/godot/operations/file_reads.gd +65 -25
- package/build/godot/operations/file_walk.gd +34 -0
- package/build/godot/operations/gdscript_analysis.gd +27 -22
- package/build/godot/operations/gdscript_authoring.gd +122 -17
- package/build/godot/operations/godot_operations.gd +66 -63
- package/build/godot/operations/import_pipeline.gd +59 -20
- package/build/godot/operations/input_actions.gd +90 -18
- package/build/godot/operations/logger.gd +1 -1
- package/build/godot/operations/plugins.gd +8 -8
- package/build/godot/operations/project_config.gd +83 -20
- package/build/godot/operations/project_diagnostics.gd +36 -11
- package/build/godot/operations/property_values.gd +222 -59
- package/build/godot/operations/reading.gd +65 -0
- package/build/godot/operations/rename_checks.gd +3 -3
- package/build/godot/operations/resource_files.gd +9 -6
- package/build/godot/operations/script_check.gd +23 -7
- package/build/godot/operations/serialisation.gd +49 -94
- package/build/godot/operations/value_shapes.gd +118 -0
- package/package.json +1 -1
|
@@ -4,18 +4,18 @@ extends RefCounted
|
|
|
4
4
|
## a whole click on a Control or a 3D node found by path. Typing and choosing from a menu, which
|
|
5
5
|
## reach the game the way a keyboard does, are in runtime_typing.gd.
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const GdhValues = preload("runtime_values.gd")
|
|
8
8
|
|
|
9
9
|
## Where a 3D node is drawn, which is what a click aimed at one has to work out first. Asked of the
|
|
10
10
|
## query module rather than worked out again here, so the place this aims at and the place a rect
|
|
11
11
|
## reports are the same place by construction rather than by agreement.
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
12
|
+
const GdhQueries = preload("runtime_queries.gd")
|
|
13
|
+
const GdhRead = preload("reading.gd")
|
|
14
|
+
const GdhHover = preload("runtime_hover.gd")
|
|
15
|
+
const GdhItems = preload("runtime_items.gd")
|
|
16
|
+
const GdhScreen = preload("runtime_screen.gd")
|
|
17
|
+
const GdhTargets = preload("runtime_targets.gd")
|
|
18
|
+
const GdhMouse = preload("runtime_mouse.gd")
|
|
19
19
|
|
|
20
20
|
## The distance from a capital letter to its small one in Unicode. A keycode holds the capital.
|
|
21
21
|
const TO_SMALL: int = 32
|
|
@@ -40,8 +40,8 @@ static var _pointer_explained: bool = false
|
|
|
40
40
|
static var _hover_explained: bool = false
|
|
41
41
|
|
|
42
42
|
var _host: Node
|
|
43
|
-
var _values:
|
|
44
|
-
var _hover:
|
|
43
|
+
var _values: GdhValues
|
|
44
|
+
var _hover: GdhHover = GdhHover.new()
|
|
45
45
|
|
|
46
46
|
## Where the last injected pointer event put the pointer, once one has.
|
|
47
47
|
##
|
|
@@ -59,7 +59,7 @@ var _pointer_at_usec: int = 0
|
|
|
59
59
|
var _arrival_seconds: float = 1.0 / 60.0
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
func _init(host: Node, values:
|
|
62
|
+
func _init(host: Node, values: GdhValues) -> void:
|
|
63
63
|
_host = host
|
|
64
64
|
_values = values
|
|
65
65
|
|
|
@@ -146,16 +146,16 @@ func _arrive(position: Vector2) -> Vector2:
|
|
|
146
146
|
## `pressed` is how a caller says otherwise: true holds it down, false lets go of one being held.
|
|
147
147
|
func inject_action(params: Dictionary) -> Dictionary:
|
|
148
148
|
var action: String = str(params.get("action", ""))
|
|
149
|
-
var held: bool =
|
|
149
|
+
var held: bool = GdhRead.as_bool(params.get("pressed", true), true)
|
|
150
150
|
var whole: bool = not params.has("pressed")
|
|
151
|
-
var strength: float =
|
|
151
|
+
var strength: float = GdhRead.as_float(params.get("strength", 1.0), 1.0)
|
|
152
152
|
|
|
153
153
|
if action.is_empty():
|
|
154
154
|
return {"type": "error", "message": "Action name required"}
|
|
155
155
|
|
|
156
156
|
if not InputMap.has_action(action):
|
|
157
157
|
return {"type": "error", "message": "Action not found: " + action}
|
|
158
|
-
var holding: Variant =
|
|
158
|
+
var holding: Variant = GdhValues.held_for(params)
|
|
159
159
|
if holding is String:
|
|
160
160
|
return {"type": "error", "message": holding}
|
|
161
161
|
var frames: int = holding
|
|
@@ -174,7 +174,7 @@ func inject_action(params: Dictionary) -> Dictionary:
|
|
|
174
174
|
"action": action,
|
|
175
175
|
"pressed": held and not whole,
|
|
176
176
|
"whole": whole,
|
|
177
|
-
"held_frames":
|
|
177
|
+
"held_frames": GdhValues.held_answer(whole, frames),
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
|
|
@@ -195,7 +195,7 @@ func _say_action(action: String, down: bool, strength: float) -> void:
|
|
|
195
195
|
## Presses one key, and lets go of it again unless asked to hold it. See [method inject_action].
|
|
196
196
|
func inject_key(params: Dictionary) -> Dictionary:
|
|
197
197
|
var keycode_raw: Variant = params.get("keycode", 0)
|
|
198
|
-
var held: bool =
|
|
198
|
+
var held: bool = GdhRead.as_bool(params.get("pressed", true), true)
|
|
199
199
|
var whole: bool = not params.has("pressed")
|
|
200
200
|
var key_label: String = str(params.get("key_label", ""))
|
|
201
201
|
|
|
@@ -203,8 +203,8 @@ func inject_key(params: Dictionary) -> Dictionary:
|
|
|
203
203
|
var named: String = keycode_raw
|
|
204
204
|
if not named.is_empty() and key_label.is_empty():
|
|
205
205
|
key_label = named
|
|
206
|
-
var keycode: int = 0 if keycode_raw is String else
|
|
207
|
-
var holding: Variant =
|
|
206
|
+
var keycode: int = 0 if keycode_raw is String else GdhRead.as_int(keycode_raw)
|
|
207
|
+
var holding: Variant = GdhValues.held_for(params)
|
|
208
208
|
if holding is String:
|
|
209
209
|
return {"type": "error", "message": holding}
|
|
210
210
|
var frames: int = holding
|
|
@@ -229,9 +229,9 @@ func inject_key(params: Dictionary) -> Dictionary:
|
|
|
229
229
|
event.physical_keycode = event.keycode
|
|
230
230
|
event.key_label = event.keycode
|
|
231
231
|
|
|
232
|
-
event.shift_pressed =
|
|
233
|
-
event.ctrl_pressed =
|
|
234
|
-
event.alt_pressed =
|
|
232
|
+
event.shift_pressed = GdhRead.as_bool(params.get("shift", false))
|
|
233
|
+
event.ctrl_pressed = GdhRead.as_bool(params.get("ctrl", false))
|
|
234
|
+
event.alt_pressed = GdhRead.as_bool(params.get("alt", false))
|
|
235
235
|
|
|
236
236
|
# The fourth thing a real event carries, and the only one a text field reads: LineEdit and
|
|
237
237
|
# TextEdit insert `unicode` and never consult the keycode, so an injected key could press any
|
|
@@ -250,7 +250,7 @@ func inject_key(params: Dictionary) -> Dictionary:
|
|
|
250
250
|
return {
|
|
251
251
|
"type": "input_injected",
|
|
252
252
|
"input_type": "key",
|
|
253
|
-
"held_frames":
|
|
253
|
+
"held_frames": GdhValues.held_answer(whole, frames),
|
|
254
254
|
"keycode": event.keycode,
|
|
255
255
|
"physical_keycode": event.physical_keycode,
|
|
256
256
|
"shift": event.shift_pressed,
|
|
@@ -284,22 +284,22 @@ func _glyph_of(keycode: Key, shifted: bool) -> int:
|
|
|
284
284
|
## `pressed` is how a caller asks for one half, the way a drag is made: true holds the button down,
|
|
285
285
|
## false lets go of one being held.
|
|
286
286
|
func inject_mouse_click(params: Dictionary) -> Dictionary:
|
|
287
|
-
var point: Variant =
|
|
287
|
+
var point: Variant = GdhMouse.read_point(params, "x", "y")
|
|
288
288
|
if point is String:
|
|
289
289
|
return {"type": "error", "message": point}
|
|
290
290
|
var position: Vector2 = point
|
|
291
|
-
var button: int =
|
|
291
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
292
292
|
if button < 0:
|
|
293
|
-
return
|
|
294
|
-
var held: bool =
|
|
295
|
-
var double: bool =
|
|
293
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
294
|
+
var held: bool = GdhRead.as_bool(params.get("pressed", true), true)
|
|
295
|
+
var double: bool = GdhRead.as_bool(params.get("doubleClick", false))
|
|
296
296
|
# A wheel step is a press and a release together, as a mouse sends one: there is no holding a
|
|
297
297
|
# wheel. A lone wheel press left the viewport's mouse focus on the control that took it, with
|
|
298
298
|
# the wheel's bit in the focus mask, and every click after it landed on that control rather
|
|
299
299
|
# than under the pointer, landing true and doing nothing, until a release was sent by hand.
|
|
300
|
-
var notch: bool = held and
|
|
300
|
+
var notch: bool = held and GdhMouse.mask_of(button) == 0
|
|
301
301
|
var whole: bool = notch or not params.has("pressed")
|
|
302
|
-
var holding: Variant =
|
|
302
|
+
var holding: Variant = GdhValues.held_for(params)
|
|
303
303
|
if holding is String:
|
|
304
304
|
return {"type": "error", "message": holding}
|
|
305
305
|
var frames: int = holding
|
|
@@ -328,7 +328,7 @@ func inject_mouse_click(params: Dictionary) -> Dictionary:
|
|
|
328
328
|
{
|
|
329
329
|
"type": "input_injected",
|
|
330
330
|
"input_type": "mouse_click",
|
|
331
|
-
"held_frames":
|
|
331
|
+
"held_frames": GdhValues.held_answer(whole and not notch, frames),
|
|
332
332
|
"position": [position.x, position.y],
|
|
333
333
|
"button": button,
|
|
334
334
|
"pressed": held and not whole,
|
|
@@ -349,14 +349,14 @@ func inject_mouse_click(params: Dictionary) -> Dictionary:
|
|
|
349
349
|
## would have carried. A relative the caller gives is kept, since a game may want to read a motion
|
|
350
350
|
## the position does not show.
|
|
351
351
|
func inject_mouse_motion(params: Dictionary) -> Dictionary:
|
|
352
|
-
var point: Variant =
|
|
352
|
+
var point: Variant = GdhMouse.read_point(params, "x", "y")
|
|
353
353
|
if point is String:
|
|
354
354
|
return {"type": "error", "message": point}
|
|
355
355
|
var position: Vector2 = point
|
|
356
356
|
var relative: Vector2 = _arrive(position)
|
|
357
357
|
if params.has("relativeX") or params.has("relativeY"):
|
|
358
358
|
relative = Vector2(
|
|
359
|
-
|
|
359
|
+
GdhRead.as_float(params.get("relativeX", 0.0)), GdhRead.as_float(params.get("relativeY", 0.0))
|
|
360
360
|
)
|
|
361
361
|
|
|
362
362
|
var supplied: bool = _follow_in_the_window(position)
|
|
@@ -385,26 +385,26 @@ func inject_mouse_motion(params: Dictionary) -> Dictionary:
|
|
|
385
385
|
## Each motion goes in a frame of its own because the engine merges the motions it is given within
|
|
386
386
|
## one frame into a single event, as it does a real mouse's, and a game counting motions counts one.
|
|
387
387
|
func inject_mouse_drag(params: Dictionary) -> Dictionary:
|
|
388
|
-
var stroke: Variant =
|
|
388
|
+
var stroke: Variant = GdhMouse.stroke_of(params)
|
|
389
389
|
if stroke is String:
|
|
390
390
|
return {"type": "error", "message": stroke}
|
|
391
391
|
var points: Array[Vector2] = stroke
|
|
392
|
-
var button: int =
|
|
392
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
393
393
|
if button < 0:
|
|
394
|
-
return
|
|
395
|
-
var own: int =
|
|
394
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
395
|
+
var own: int = GdhMouse.mask_of(button)
|
|
396
396
|
if own == 0:
|
|
397
397
|
return {
|
|
398
398
|
"type": "error",
|
|
399
399
|
"message": "A wheel step has no held position to drag from: give left, right or middle."
|
|
400
400
|
}
|
|
401
|
-
var frames: int =
|
|
402
|
-
if frames < 1 or frames >
|
|
401
|
+
var frames: int = GdhRead.as_int(params.get("frames", 0), 0)
|
|
402
|
+
if frames < 1 or frames > GdhValues.MOST_HELD:
|
|
403
403
|
return {
|
|
404
|
-
"type": "error", "message": "frames is %d, and it takes 1 to %d." % [frames,
|
|
404
|
+
"type": "error", "message": "frames is %d, and it takes 1 to %d." % [frames, GdhValues.MOST_HELD]
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
var lengths: Array[float] =
|
|
407
|
+
var lengths: Array[float] = GdhMouse.lengths_of(points)
|
|
408
408
|
var start: Vector2 = points[0]
|
|
409
409
|
var end: Vector2 = points[points.size() - 1]
|
|
410
410
|
var _moved: Vector2 = _arrive(start)
|
|
@@ -413,7 +413,7 @@ func inject_mouse_drag(params: Dictionary) -> Dictionary:
|
|
|
413
413
|
var travelled: Vector2 = Vector2.ZERO
|
|
414
414
|
for step: int in range(1, frames + 1):
|
|
415
415
|
await _host.get_tree().process_frame
|
|
416
|
-
var at: Vector2 =
|
|
416
|
+
var at: Vector2 = GdhMouse.along(points, lengths, float(step) / frames)
|
|
417
417
|
var relative: Vector2 = _arrive(at)
|
|
418
418
|
travelled += relative
|
|
419
419
|
supplied = _follow_in_the_window(at) or supplied
|
|
@@ -507,7 +507,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
507
507
|
var found: Variant = null
|
|
508
508
|
var item: Variant = null
|
|
509
509
|
if not wanted.is_empty():
|
|
510
|
-
var picked: Dictionary =
|
|
510
|
+
var picked: Dictionary = GdhTargets.control_saying(
|
|
511
511
|
_host.get_tree().root, "/root" if node_path.is_empty() else node_path, wanted, params.get("index")
|
|
512
512
|
)
|
|
513
513
|
if picked.has("message"):
|
|
@@ -518,7 +518,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
518
518
|
if node_path.is_empty():
|
|
519
519
|
return {"type": "error", "message": "click needs a path, or says to find the control by its words"}
|
|
520
520
|
|
|
521
|
-
var standing: Dictionary =
|
|
521
|
+
var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
|
|
522
522
|
if standing.has("message"):
|
|
523
523
|
return standing
|
|
524
524
|
var node: Node = standing["node"]
|
|
@@ -542,7 +542,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
542
542
|
var item_scrolled: bool = false
|
|
543
543
|
if item != null:
|
|
544
544
|
var found_by_words: Dictionary = found
|
|
545
|
-
var placed_item: Dictionary = await
|
|
545
|
+
var placed_item: Dictionary = await GdhItems.in_view(
|
|
546
546
|
_host.get_tree(), control, node_path, item, wanted, found_by_words
|
|
547
547
|
)
|
|
548
548
|
if placed_item.has("message"):
|
|
@@ -551,7 +551,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
551
551
|
item_scrolled = placed_item["scrolled"]
|
|
552
552
|
var rich: RichTextLabel = control as RichTextLabel
|
|
553
553
|
if found != null and rich != null:
|
|
554
|
-
var placed: Dictionary =
|
|
554
|
+
var placed: Dictionary = GdhTargets.point_on_words(rich, wanted)
|
|
555
555
|
var found_here: Dictionary = found
|
|
556
556
|
if placed.has("point"):
|
|
557
557
|
aim = placed["point"]
|
|
@@ -579,7 +579,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
579
579
|
var scrolled: bool = item_scrolled
|
|
580
580
|
if not _in_sight(control, own, local):
|
|
581
581
|
scrolled = await _scroll_into_view(control) or item_scrolled
|
|
582
|
-
var left: String =
|
|
582
|
+
var left: String = GdhValues.afterwards(control)
|
|
583
583
|
if left != "in_tree":
|
|
584
584
|
return {
|
|
585
585
|
"type": "error",
|
|
@@ -591,7 +591,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
591
591
|
}
|
|
592
592
|
local = control.get_global_transform_with_canvas() * aim
|
|
593
593
|
|
|
594
|
-
var reached: Dictionary =
|
|
594
|
+
var reached: Dictionary = GdhScreen.reach(own, local)
|
|
595
595
|
var viewport: Viewport = reached["viewport"]
|
|
596
596
|
var centre: Vector2 = reached["point"]
|
|
597
597
|
var position: Vector2 = viewport.get_final_transform() * centre
|
|
@@ -602,10 +602,10 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
602
602
|
var out_there: String = _out_of_reach(control, node_path, local, reached, scrolled)
|
|
603
603
|
if not out_there.is_empty():
|
|
604
604
|
return {"type": "error", "message": out_there}
|
|
605
|
-
var button: int =
|
|
605
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
606
606
|
if button < 0:
|
|
607
|
-
return
|
|
608
|
-
var double: bool =
|
|
607
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
608
|
+
var double: bool = GdhRead.as_bool(params.get("double", false))
|
|
609
609
|
|
|
610
610
|
# Pushed into the viewport rather than through Input: Input accumulates events and flushes
|
|
611
611
|
# them at the next frame, so the hovered control read below would be the one from before
|
|
@@ -654,7 +654,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
654
654
|
# What became of the control: still in the tree, taken out of it, or freed. A button that
|
|
655
655
|
# opened another screen is the second or the third, and the caller wants to hear that
|
|
656
656
|
# rather than guess it from a tree that has changed shape.
|
|
657
|
-
var afterwards: String =
|
|
657
|
+
var afterwards: String = GdhValues.afterwards(control)
|
|
658
658
|
|
|
659
659
|
var answer: Dictionary = {
|
|
660
660
|
"type": "clicked",
|
|
@@ -692,11 +692,11 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
692
692
|
## what is underneath, which answered `landed: false` with nothing saying why.
|
|
693
693
|
func _takes_no_click(control: Control, node_path: String) -> String:
|
|
694
694
|
if not control.is_visible_in_tree():
|
|
695
|
-
return "%s is not visible%s, so nothing can click it" % [node_path,
|
|
695
|
+
return "%s is not visible%s, so nothing can click it" % [node_path, GdhTargets.why_hidden(control)]
|
|
696
696
|
var button: BaseButton = control as BaseButton
|
|
697
697
|
if button != null and button.disabled:
|
|
698
698
|
return "%s is disabled, so clicking it presses nothing" % node_path
|
|
699
|
-
var turned_off: Control =
|
|
699
|
+
var turned_off: Control = GdhTargets.pointer_turned_off_by(control)
|
|
700
700
|
if turned_off != null:
|
|
701
701
|
var on: String = "itself" if turned_off == control else str(turned_off.get_path())
|
|
702
702
|
return (
|
|
@@ -708,7 +708,7 @@ func _takes_no_click(control: Control, node_path: String) -> String:
|
|
|
708
708
|
)
|
|
709
709
|
if not control.can_process():
|
|
710
710
|
var why: String = _why_it_does_not_process(control, node_path)
|
|
711
|
-
var over: Node =
|
|
711
|
+
var over: Node = GdhTargets.drawn_over(control)
|
|
712
712
|
var cover: String = "" if over == null else "; %s is drawn over it" % over.get_path()
|
|
713
713
|
if _host.get_tree().paused:
|
|
714
714
|
return (
|
|
@@ -773,7 +773,7 @@ func _out_of_reach(
|
|
|
773
773
|
|
|
774
774
|
|
|
775
775
|
func _no_window_note(viewport: Viewport) -> String:
|
|
776
|
-
return
|
|
776
|
+
return GdhTargets.no_window_note(viewport, _host.get_tree().root)
|
|
777
777
|
|
|
778
778
|
|
|
779
779
|
## Sends a click's press or release into [param viewport], through Input when that is the game's
|
|
@@ -787,7 +787,7 @@ func _no_window_note(viewport: Viewport) -> String:
|
|
|
787
787
|
## click meant to open it. Games read that state too, through Input.is_mouse_button_pressed.
|
|
788
788
|
##
|
|
789
789
|
## Any other viewport is pushed into directly: Input reaches only the game's window. That is a
|
|
790
|
-
## viewport with no place on the screen, since `
|
|
790
|
+
## viewport with no place on the screen, since `GdhScreen.reach` carries every other one there.
|
|
791
791
|
func _press_button(viewport: Viewport, event: InputEventMouseButton) -> void:
|
|
792
792
|
if viewport == _host.get_tree().root:
|
|
793
793
|
Input.parse_input_event(event)
|
|
@@ -813,10 +813,11 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
813
813
|
if not item.is_visible_in_tree():
|
|
814
814
|
return {
|
|
815
815
|
"type": "error",
|
|
816
|
-
"message":
|
|
816
|
+
"message":
|
|
817
|
+
"%s is not visible%s, so nothing can click it" % [node_path, GdhTargets.why_hidden(item)]
|
|
817
818
|
}
|
|
818
819
|
|
|
819
|
-
var found: Dictionary =
|
|
820
|
+
var found: Dictionary = GdhQueries.in_frame(item)
|
|
820
821
|
if found.is_empty():
|
|
821
822
|
return {
|
|
822
823
|
"type": "error",
|
|
@@ -841,7 +842,7 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
841
842
|
)
|
|
842
843
|
}
|
|
843
844
|
|
|
844
|
-
var out: Array[Dictionary] =
|
|
845
|
+
var out: Array[Dictionary] = GdhScreen.steps_out(viewport, aim)
|
|
845
846
|
var arriving: Viewport = out.back()["viewport"]
|
|
846
847
|
var reached_point: Vector2 = out.back()["point"]
|
|
847
848
|
# Checked where the pointer arrives as well as where the node is drawn: a room in a SubViewport
|
|
@@ -857,10 +858,10 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
857
858
|
)
|
|
858
859
|
}
|
|
859
860
|
var position: Vector2 = arriving.get_final_transform() * reached_point
|
|
860
|
-
var button: int =
|
|
861
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
861
862
|
if button < 0:
|
|
862
|
-
return
|
|
863
|
-
var double: bool =
|
|
863
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
864
|
+
var double: bool = GdhRead.as_bool(params.get("double", false))
|
|
864
865
|
|
|
865
866
|
arriving.push_input(_motion(position, _arrive(position)))
|
|
866
867
|
var supplied: bool = _hover.follow(arriving, reached_point)
|
|
@@ -894,7 +895,7 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
894
895
|
)
|
|
895
896
|
|
|
896
897
|
|
|
897
|
-
## Whether a pointer on its way out through [param out], the steps [method
|
|
898
|
+
## Whether a pointer on its way out through [param out], the steps [method GdhScreen.steps_out]
|
|
898
899
|
## answers, gets past the interface in every viewport, as {"landed", "hovered"}: the path of the
|
|
899
900
|
## control that took it, or of the one under it that let it by, or null.
|
|
900
901
|
##
|
|
@@ -979,6 +980,6 @@ func _button(position: Vector2, button: int, pressed: bool, double: bool) -> Inp
|
|
|
979
980
|
event.pressed = pressed
|
|
980
981
|
event.double_click = double
|
|
981
982
|
var held: int = Input.get_mouse_button_mask()
|
|
982
|
-
var own: int =
|
|
983
|
+
var own: int = GdhMouse.mask_of(button)
|
|
983
984
|
event.button_mask = (held | own) if pressed else (held & ~own)
|
|
984
985
|
return event
|
|
@@ -4,12 +4,12 @@ extends RefCounted
|
|
|
4
4
|
## a tree row or a menu bar title. Each is drawn by the control rather than held as a node, so a click
|
|
5
5
|
## on the control's path presses its middle, which is whichever item happens to sit there.
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const GdhSays = preload("runtime_says.gd")
|
|
8
|
+
const GdhValues = preload("runtime_values.gd")
|
|
9
|
+
const GdhWords = preload("runtime_words.gd")
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
## Where [param control] draws [param item], one of [method
|
|
12
|
+
## Where [param control] draws [param item], one of [method GdhWords.items_of], in the control's own
|
|
13
13
|
## space, or an empty rectangle while it draws it nowhere a pointer reaches: a tab scrolled off its
|
|
14
14
|
## bar, an entry or a row scrolled out of its list, a menu bar the operating system draws. A tree
|
|
15
15
|
## row is the cell saying [param wanted] when one does, and the whole row otherwise.
|
|
@@ -54,7 +54,7 @@ static func item_rect(control: Control, item: Variant, wanted: String) -> Rect2:
|
|
|
54
54
|
## does, its cells read together.
|
|
55
55
|
static func tree_column(tree: Tree, row: TreeItem, wanted: String) -> int:
|
|
56
56
|
for column: int in tree.columns:
|
|
57
|
-
if
|
|
57
|
+
if GdhSays.matches(GdhWords.cell_says(tree, row, column), wanted):
|
|
58
58
|
return column
|
|
59
59
|
return -1
|
|
60
60
|
|
|
@@ -96,7 +96,7 @@ static func in_view(
|
|
|
96
96
|
if scrolled:
|
|
97
97
|
# A tree places its rows by the scroll it last drew with.
|
|
98
98
|
await scene.process_frame
|
|
99
|
-
var left: String =
|
|
99
|
+
var left: String = GdhValues.afterwards(control)
|
|
100
100
|
if left != "in_tree" or (typeof(item) == TYPE_OBJECT and not is_instance_valid(item)):
|
|
101
101
|
return {
|
|
102
102
|
"type": "error",
|
|
@@ -3,9 +3,9 @@ extends RefCounted
|
|
|
3
3
|
## The menu a choice is made from and the item in it, for a choice made by what an item says or by
|
|
4
4
|
## where it is in the list.
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
const GdhRead = preload("reading.gd")
|
|
7
|
+
const GdhSays = preload("runtime_says.gd")
|
|
8
|
+
const GdhWords = preload("runtime_words.gd")
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
## The menu [param node] is, or the one it holds. An [OptionButton] and a [MenuButton] both keep
|
|
@@ -38,7 +38,7 @@ static func menu_of(node: Node) -> PopupMenu:
|
|
|
38
38
|
## still count after them, for a caller that has the key from the source.
|
|
39
39
|
static func wanted_items(menu: PopupMenu, params: Dictionary) -> Array[int]:
|
|
40
40
|
if params.has("index"):
|
|
41
|
-
var asked: int =
|
|
41
|
+
var asked: int = GdhRead.as_int(params.get("index", -1), -1)
|
|
42
42
|
return _items([asked] if asked >= 0 and asked < menu.get_item_count() else [])
|
|
43
43
|
var wanted: String = str(params.get("text", ""))
|
|
44
44
|
if wanted.is_empty():
|
|
@@ -54,14 +54,14 @@ static func wanted_items(menu: PopupMenu, params: Dictionary) -> Array[int]:
|
|
|
54
54
|
var shown: Array[String] = []
|
|
55
55
|
var held: Array[String] = []
|
|
56
56
|
for index: int in order:
|
|
57
|
-
shown.append(
|
|
57
|
+
shown.append(GdhWords.item_says(menu, index))
|
|
58
58
|
held.append(menu.get_item_text(index))
|
|
59
59
|
for words: Array[String] in [shown, held]:
|
|
60
60
|
for at: int in words.size():
|
|
61
61
|
if words[at] == wanted:
|
|
62
62
|
return _items([order[at]])
|
|
63
63
|
for at: int in words.size():
|
|
64
|
-
if
|
|
64
|
+
if GdhSays.spaced(words[at]).nocasecmp_to(GdhSays.spaced(wanted)) == 0:
|
|
65
65
|
return _items([order[at]])
|
|
66
66
|
# Separators are left out here: a heading is refused when named in full, and a pattern that
|
|
67
67
|
# matched one beside the item it heads would tie the two.
|
|
@@ -70,7 +70,7 @@ static func wanted_items(menu: PopupMenu, params: Dictionary) -> Array[int]:
|
|
|
70
70
|
var enabled: Array[int] = []
|
|
71
71
|
for at: int in words.size():
|
|
72
72
|
var index: int = order[at]
|
|
73
|
-
if menu.is_item_separator(index) or not
|
|
73
|
+
if menu.is_item_separator(index) or not GdhSays.matches(words[at], wanted):
|
|
74
74
|
continue
|
|
75
75
|
matched.append(index)
|
|
76
76
|
if not menu.is_item_disabled(index):
|
|
@@ -93,7 +93,7 @@ static func _items(indices: Array) -> Array[int]:
|
|
|
93
93
|
static func items_of(menu: PopupMenu) -> Array[String]:
|
|
94
94
|
var said: Array[String] = []
|
|
95
95
|
for index: int in menu.get_item_count():
|
|
96
|
-
var words: String =
|
|
96
|
+
var words: String = GdhWords.item_says(menu, index)
|
|
97
97
|
if not menu.is_item_separator(index):
|
|
98
98
|
said.append("%d: %s" % [index, words])
|
|
99
99
|
elif not words.strip_edges().is_empty():
|
|
@@ -3,7 +3,7 @@ extends RefCounted
|
|
|
3
3
|
## What a pointer call names: a point, the stroke of a drag, a mouse button and its mask bit. Read
|
|
4
4
|
## here and sent from runtime_input.gd, which keeps where the pointer is.
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const GdhRead = preload("reading.gd")
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
## A point the tool schema sends as two flat numbers. Answers a Vector2, or the String that says
|
|
@@ -11,7 +11,7 @@ const Read = preload("reading.gd")
|
|
|
11
11
|
static func read_point(params: Dictionary, x_key: String, y_key: String) -> Variant:
|
|
12
12
|
if not params.has(x_key) or not params.has(y_key):
|
|
13
13
|
return "%s and %s are both needed." % [x_key, y_key]
|
|
14
|
-
return Vector2(
|
|
14
|
+
return Vector2(GdhRead.as_float(params[x_key]), GdhRead.as_float(params[y_key]))
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
## The points of a drag: x, y, then each of via, then toX, toY. Answers the String saying what is
|
|
@@ -20,7 +20,7 @@ static func stroke_of(params: Dictionary) -> Variant:
|
|
|
20
20
|
for key: String in ["x", "y", "toX", "toY"]:
|
|
21
21
|
if not params.has(key):
|
|
22
22
|
return "mouse_drag needs x, y, toX and toY: %s is missing." % key
|
|
23
|
-
var points: Array[Vector2] = [Vector2(
|
|
23
|
+
var points: Array[Vector2] = [Vector2(GdhRead.as_float(params["x"]), GdhRead.as_float(params["y"]))]
|
|
24
24
|
var via: Variant = params.get("via", [])
|
|
25
25
|
if not via is Array:
|
|
26
26
|
return "via must be a list of [x, y] points."
|
|
@@ -29,8 +29,8 @@ static func stroke_of(params: Dictionary) -> Variant:
|
|
|
29
29
|
var pair: Array = point if point is Array else []
|
|
30
30
|
if pair.size() < 2:
|
|
31
31
|
return "via must be a list of [x, y] points, and holds %s." % JSON.stringify(point)
|
|
32
|
-
points.append(Vector2(
|
|
33
|
-
points.append(Vector2(
|
|
32
|
+
points.append(Vector2(GdhRead.as_float(pair[0]), GdhRead.as_float(pair[1])))
|
|
33
|
+
points.append(Vector2(GdhRead.as_float(params["toX"]), GdhRead.as_float(params["toY"])))
|
|
34
34
|
return points
|
|
35
35
|
|
|
36
36
|
|
|
@@ -100,5 +100,5 @@ static func button_of(raw: Variant) -> int:
|
|
|
100
100
|
return -1
|
|
101
101
|
# The engine's buttons run from 1 to 9: 0 is MOUSE_BUTTON_NONE, which no event carries, and a
|
|
102
102
|
# number past the two extra buttons names nothing a mask bit can be made for.
|
|
103
|
-
var number: int =
|
|
103
|
+
var number: int = GdhRead.as_int(raw, -1)
|
|
104
104
|
return number if number >= MOUSE_BUTTON_LEFT and number <= MOUSE_BUTTON_XBUTTON2 else -1
|
|
@@ -8,8 +8,8 @@ extends RefCounted
|
|
|
8
8
|
## reached it as an InputEventAction it never reads, so a controller layout could only be driven by
|
|
9
9
|
## calling the game's own methods. The engine still fires the actions bound to a button from these.
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const GdhRead = preload("reading.gd")
|
|
12
|
+
const GdhValues = preload("runtime_values.gd")
|
|
13
13
|
|
|
14
14
|
## Controller buttons by the names Godot gives them, for a caller naming one rather than numbering it.
|
|
15
15
|
const JOY_BUTTONS: Dictionary[String, int] = {
|
|
@@ -60,10 +60,10 @@ func inject_joypad_button(params: Dictionary) -> Dictionary:
|
|
|
60
60
|
var button: int = _named_or_numbered(given, JOY_BUTTONS, JOY_BUTTON_MAX)
|
|
61
61
|
if button < 0:
|
|
62
62
|
return {"type": "error", "message": _not_one_of("button", given, JOY_BUTTONS, JOY_BUTTON_MAX)}
|
|
63
|
-
var device: int =
|
|
64
|
-
var held: bool =
|
|
63
|
+
var device: int = GdhRead.as_int(params.get("device", 0), 0)
|
|
64
|
+
var held: bool = GdhRead.as_bool(params.get("pressed", true), true)
|
|
65
65
|
var whole: bool = not params.has("pressed")
|
|
66
|
-
var holding: Variant =
|
|
66
|
+
var holding: Variant = GdhValues.held_for(params)
|
|
67
67
|
if holding is String:
|
|
68
68
|
return {"type": "error", "message": holding}
|
|
69
69
|
var frames: int = holding
|
|
@@ -80,7 +80,7 @@ func inject_joypad_button(params: Dictionary) -> Dictionary:
|
|
|
80
80
|
return {
|
|
81
81
|
"type": "input_injected",
|
|
82
82
|
"input_type": "joypad_button",
|
|
83
|
-
"held_frames":
|
|
83
|
+
"held_frames": GdhValues.held_answer(whole, frames),
|
|
84
84
|
"button": button,
|
|
85
85
|
"button_name": _name_of(button, JOY_BUTTONS),
|
|
86
86
|
"device": device,
|
|
@@ -98,12 +98,12 @@ func inject_joypad_motion(params: Dictionary) -> Dictionary:
|
|
|
98
98
|
return {"type": "error", "message": _not_one_of("axis", given, JOY_AXES, JOY_AXIS_MAX)}
|
|
99
99
|
if not params.has("value"):
|
|
100
100
|
return {"type": "error", "message": "joypad_motion needs value, where the axis is, -1 to 1"}
|
|
101
|
-
var value: float =
|
|
101
|
+
var value: float = GdhRead.as_float(params.get("value"), 0.0)
|
|
102
102
|
if value < -1.0 or value > 1.0:
|
|
103
103
|
return {
|
|
104
104
|
"type": "error", "message": "value %s is outside -1 to 1, the whole travel of an axis" % value
|
|
105
105
|
}
|
|
106
|
-
var device: int =
|
|
106
|
+
var device: int = GdhRead.as_int(params.get("device", 0), 0)
|
|
107
107
|
|
|
108
108
|
var event: InputEventJoypadMotion = InputEventJoypadMotion.new()
|
|
109
109
|
event.axis = axis as JoyAxis
|
|
@@ -133,9 +133,9 @@ static func _button(button: int, device: int, down: bool) -> InputEventJoypadBut
|
|
|
133
133
|
static func _named_or_numbered(given: Variant, names: Dictionary[String, int], count: int) -> int:
|
|
134
134
|
if given is String:
|
|
135
135
|
var named: String = given
|
|
136
|
-
return
|
|
136
|
+
return GdhRead.as_int(names.get(named.strip_edges().to_lower(), -1), -1)
|
|
137
137
|
if given is int or given is float:
|
|
138
|
-
var number: int =
|
|
138
|
+
var number: int = GdhRead.as_int(given, -1)
|
|
139
139
|
return number if number >= 0 and number < count else -1
|
|
140
140
|
return -1
|
|
141
141
|
|
|
@@ -4,8 +4,8 @@ extends RefCounted
|
|
|
4
4
|
## run off the game the node keeps. Every op that reads, writes, calls or waits on a game's state
|
|
5
5
|
## walks the same way, so the walk and what it says when a step is not there live in one place.
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const GdhSerialisation = preload("serialisation.gd")
|
|
8
|
+
const GdhValues = preload("runtime_values.gd")
|
|
9
9
|
|
|
10
10
|
## The calls a path makes on a list or a map: those that take no arguments and change nothing.
|
|
11
11
|
##
|
|
@@ -96,7 +96,7 @@ const READS_ONLY: String = ", and a path, which a wait reads every frame, only c
|
|
|
96
96
|
##
|
|
97
97
|
## A step into a vector, a colour, a rectangle or a transform reads one of its components,
|
|
98
98
|
## "size:x" or "global_transform:origin:y", by the names GDScript gives them; see
|
|
99
|
-
## [method
|
|
99
|
+
## [method GdhSerialisation.components_of].
|
|
100
100
|
##
|
|
101
101
|
## Answers with a `message` instead when a step along the way is not there or holds something that
|
|
102
102
|
## is not an object, naming the step rather than the whole path: "/root/Main:_game has no property
|
|
@@ -151,7 +151,7 @@ static func object_at(root: Node, node: Node, node_path: String, given: Variant)
|
|
|
151
151
|
if path.begins_with("/"):
|
|
152
152
|
var colon: int = path.find(":")
|
|
153
153
|
start_path = path if colon == -1 else path.substr(0, colon)
|
|
154
|
-
var standing: Dictionary =
|
|
154
|
+
var standing: Dictionary = GdhValues.node_at(root, start_path)
|
|
155
155
|
if standing.has("message"):
|
|
156
156
|
return {"message": str(standing["message"])}
|
|
157
157
|
start = standing["node"]
|
|
@@ -185,7 +185,7 @@ static func can_hold(value: Variant) -> bool:
|
|
|
185
185
|
## Whether [param value] is read by its components, a vector or a colour or a transform, and is a
|
|
186
186
|
## copy wherever it is read from.
|
|
187
187
|
static func composite(value: Variant) -> bool:
|
|
188
|
-
return not
|
|
188
|
+
return not GdhSerialisation.components_of(value).is_empty()
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
## Whether [param value] is a packed list. Stepped into the way a list is, by index, and a write into
|
|
@@ -235,7 +235,7 @@ static func can_read(holder: Variant, named: String) -> bool:
|
|
|
235
235
|
return args.is_empty() and method in MAP_CALLS
|
|
236
236
|
return not _key_in(map, named).is_empty()
|
|
237
237
|
if composite(holder):
|
|
238
|
-
return method.is_empty() and named in
|
|
238
|
+
return method.is_empty() and named in GdhSerialisation.components_of(holder)
|
|
239
239
|
if holder is Object:
|
|
240
240
|
var object: Object = holder
|
|
241
241
|
if not method.is_empty():
|
|
@@ -620,7 +620,7 @@ static func nothing_there(holder: Variant, named: String, called_as: String) ->
|
|
|
620
620
|
if composite(holder):
|
|
621
621
|
var kind: String = type_string(typeof(holder))
|
|
622
622
|
var sort: String = ("an " if kind.left(1) in ["A", "E", "I", "O", "U"] else "a ") + kind
|
|
623
|
-
var has: String = ", ".join(
|
|
623
|
+
var has: String = ", ".join(GdhSerialisation.components_of(holder))
|
|
624
624
|
if not container_call.is_empty():
|
|
625
625
|
return (
|
|
626
626
|
"%s is %s, which a path reads by its components, %s, and makes no call on"
|