gdharness 1.2.42 → 1.3.1
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 +571 -363
- package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
- package/build/godot/addons/gdharness_editor/bridge_client.gd +9 -9
- package/build/godot/addons/gdharness_editor/file_reads.gd +60 -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 +124 -62
- package/build/godot/addons/gdharness_editor/reading.gd +28 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
- package/build/godot/addons/gdharness_editor/serialisation.gd +45 -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 +57 -50
- 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 +82 -78
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/value_shapes.gd +111 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +28 -0
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +32 -32
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +50 -18
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -69
- 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 +13 -21
- 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 +25 -24
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
- package/build/godot/addons/gdharness_runtime/serialisation.gd +45 -94
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +111 -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 +12 -12
- package/build/godot/operations/dependencies.gd +33 -29
- package/build/godot/operations/editor_tools.gd +12 -12
- package/build/godot/operations/export_presets.gd +10 -8
- package/build/godot/operations/file_reads.gd +60 -25
- package/build/godot/operations/gdscript_analysis.gd +20 -20
- package/build/godot/operations/gdscript_authoring.gd +17 -15
- package/build/godot/operations/godot_operations.gd +64 -63
- package/build/godot/operations/import_pipeline.gd +20 -20
- package/build/godot/operations/input_actions.gd +17 -17
- package/build/godot/operations/logger.gd +1 -1
- package/build/godot/operations/plugins.gd +8 -8
- package/build/godot/operations/project_config.gd +24 -17
- package/build/godot/operations/project_diagnostics.gd +9 -9
- package/build/godot/operations/property_values.gd +124 -62
- package/build/godot/operations/reading.gd +28 -0
- package/build/godot/operations/rename_checks.gd +3 -3
- package/build/godot/operations/resource_files.gd +5 -5
- package/build/godot/operations/script_check.gd +7 -7
- package/build/godot/operations/serialisation.gd +45 -94
- package/build/godot/operations/value_shapes.gd +111 -0
- package/build/keeper.js +10 -4
- package/package.json +1 -2
- package/build/index.js +0 -45101
|
@@ -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,20 +349,15 @@ 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
|
-
elif params.has("relative"):
|
|
362
|
-
var movement: Variant = Mouse.read_point(params, "relativeX", "relativeY", "relative")
|
|
363
|
-
if movement is String:
|
|
364
|
-
return {"type": "error", "message": movement}
|
|
365
|
-
relative = movement
|
|
366
361
|
|
|
367
362
|
var supplied: bool = _follow_in_the_window(position)
|
|
368
363
|
Input.parse_input_event(_motion(position, relative))
|
|
@@ -390,26 +385,26 @@ func inject_mouse_motion(params: Dictionary) -> Dictionary:
|
|
|
390
385
|
## Each motion goes in a frame of its own because the engine merges the motions it is given within
|
|
391
386
|
## one frame into a single event, as it does a real mouse's, and a game counting motions counts one.
|
|
392
387
|
func inject_mouse_drag(params: Dictionary) -> Dictionary:
|
|
393
|
-
var stroke: Variant =
|
|
388
|
+
var stroke: Variant = GdhMouse.stroke_of(params)
|
|
394
389
|
if stroke is String:
|
|
395
390
|
return {"type": "error", "message": stroke}
|
|
396
391
|
var points: Array[Vector2] = stroke
|
|
397
|
-
var button: int =
|
|
392
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
398
393
|
if button < 0:
|
|
399
|
-
return
|
|
400
|
-
var own: int =
|
|
394
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
395
|
+
var own: int = GdhMouse.mask_of(button)
|
|
401
396
|
if own == 0:
|
|
402
397
|
return {
|
|
403
398
|
"type": "error",
|
|
404
399
|
"message": "A wheel step has no held position to drag from: give left, right or middle."
|
|
405
400
|
}
|
|
406
|
-
var frames: int =
|
|
407
|
-
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:
|
|
408
403
|
return {
|
|
409
|
-
"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]
|
|
410
405
|
}
|
|
411
406
|
|
|
412
|
-
var lengths: Array[float] =
|
|
407
|
+
var lengths: Array[float] = GdhMouse.lengths_of(points)
|
|
413
408
|
var start: Vector2 = points[0]
|
|
414
409
|
var end: Vector2 = points[points.size() - 1]
|
|
415
410
|
var _moved: Vector2 = _arrive(start)
|
|
@@ -418,7 +413,7 @@ func inject_mouse_drag(params: Dictionary) -> Dictionary:
|
|
|
418
413
|
var travelled: Vector2 = Vector2.ZERO
|
|
419
414
|
for step: int in range(1, frames + 1):
|
|
420
415
|
await _host.get_tree().process_frame
|
|
421
|
-
var at: Vector2 =
|
|
416
|
+
var at: Vector2 = GdhMouse.along(points, lengths, float(step) / frames)
|
|
422
417
|
var relative: Vector2 = _arrive(at)
|
|
423
418
|
travelled += relative
|
|
424
419
|
supplied = _follow_in_the_window(at) or supplied
|
|
@@ -512,7 +507,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
512
507
|
var found: Variant = null
|
|
513
508
|
var item: Variant = null
|
|
514
509
|
if not wanted.is_empty():
|
|
515
|
-
var picked: Dictionary =
|
|
510
|
+
var picked: Dictionary = GdhTargets.control_saying(
|
|
516
511
|
_host.get_tree().root, "/root" if node_path.is_empty() else node_path, wanted, params.get("index")
|
|
517
512
|
)
|
|
518
513
|
if picked.has("message"):
|
|
@@ -523,7 +518,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
523
518
|
if node_path.is_empty():
|
|
524
519
|
return {"type": "error", "message": "click needs a path, or says to find the control by its words"}
|
|
525
520
|
|
|
526
|
-
var standing: Dictionary =
|
|
521
|
+
var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
|
|
527
522
|
if standing.has("message"):
|
|
528
523
|
return standing
|
|
529
524
|
var node: Node = standing["node"]
|
|
@@ -547,7 +542,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
547
542
|
var item_scrolled: bool = false
|
|
548
543
|
if item != null:
|
|
549
544
|
var found_by_words: Dictionary = found
|
|
550
|
-
var placed_item: Dictionary = await
|
|
545
|
+
var placed_item: Dictionary = await GdhItems.in_view(
|
|
551
546
|
_host.get_tree(), control, node_path, item, wanted, found_by_words
|
|
552
547
|
)
|
|
553
548
|
if placed_item.has("message"):
|
|
@@ -556,7 +551,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
556
551
|
item_scrolled = placed_item["scrolled"]
|
|
557
552
|
var rich: RichTextLabel = control as RichTextLabel
|
|
558
553
|
if found != null and rich != null:
|
|
559
|
-
var placed: Dictionary =
|
|
554
|
+
var placed: Dictionary = GdhTargets.point_on_words(rich, wanted)
|
|
560
555
|
var found_here: Dictionary = found
|
|
561
556
|
if placed.has("point"):
|
|
562
557
|
aim = placed["point"]
|
|
@@ -584,7 +579,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
584
579
|
var scrolled: bool = item_scrolled
|
|
585
580
|
if not _in_sight(control, own, local):
|
|
586
581
|
scrolled = await _scroll_into_view(control) or item_scrolled
|
|
587
|
-
var left: String =
|
|
582
|
+
var left: String = GdhValues.afterwards(control)
|
|
588
583
|
if left != "in_tree":
|
|
589
584
|
return {
|
|
590
585
|
"type": "error",
|
|
@@ -596,7 +591,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
596
591
|
}
|
|
597
592
|
local = control.get_global_transform_with_canvas() * aim
|
|
598
593
|
|
|
599
|
-
var reached: Dictionary =
|
|
594
|
+
var reached: Dictionary = GdhScreen.reach(own, local)
|
|
600
595
|
var viewport: Viewport = reached["viewport"]
|
|
601
596
|
var centre: Vector2 = reached["point"]
|
|
602
597
|
var position: Vector2 = viewport.get_final_transform() * centre
|
|
@@ -607,10 +602,10 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
607
602
|
var out_there: String = _out_of_reach(control, node_path, local, reached, scrolled)
|
|
608
603
|
if not out_there.is_empty():
|
|
609
604
|
return {"type": "error", "message": out_there}
|
|
610
|
-
var button: int =
|
|
605
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
611
606
|
if button < 0:
|
|
612
|
-
return
|
|
613
|
-
var double: bool =
|
|
607
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
608
|
+
var double: bool = GdhRead.as_bool(params.get("double", false))
|
|
614
609
|
|
|
615
610
|
# Pushed into the viewport rather than through Input: Input accumulates events and flushes
|
|
616
611
|
# them at the next frame, so the hovered control read below would be the one from before
|
|
@@ -659,7 +654,7 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
659
654
|
# What became of the control: still in the tree, taken out of it, or freed. A button that
|
|
660
655
|
# opened another screen is the second or the third, and the caller wants to hear that
|
|
661
656
|
# rather than guess it from a tree that has changed shape.
|
|
662
|
-
var afterwards: String =
|
|
657
|
+
var afterwards: String = GdhValues.afterwards(control)
|
|
663
658
|
|
|
664
659
|
var answer: Dictionary = {
|
|
665
660
|
"type": "clicked",
|
|
@@ -697,11 +692,11 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
697
692
|
## what is underneath, which answered `landed: false` with nothing saying why.
|
|
698
693
|
func _takes_no_click(control: Control, node_path: String) -> String:
|
|
699
694
|
if not control.is_visible_in_tree():
|
|
700
|
-
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)]
|
|
701
696
|
var button: BaseButton = control as BaseButton
|
|
702
697
|
if button != null and button.disabled:
|
|
703
698
|
return "%s is disabled, so clicking it presses nothing" % node_path
|
|
704
|
-
var turned_off: Control =
|
|
699
|
+
var turned_off: Control = GdhTargets.pointer_turned_off_by(control)
|
|
705
700
|
if turned_off != null:
|
|
706
701
|
var on: String = "itself" if turned_off == control else str(turned_off.get_path())
|
|
707
702
|
return (
|
|
@@ -713,7 +708,7 @@ func _takes_no_click(control: Control, node_path: String) -> String:
|
|
|
713
708
|
)
|
|
714
709
|
if not control.can_process():
|
|
715
710
|
var why: String = _why_it_does_not_process(control, node_path)
|
|
716
|
-
var over: Node =
|
|
711
|
+
var over: Node = GdhTargets.drawn_over(control)
|
|
717
712
|
var cover: String = "" if over == null else "; %s is drawn over it" % over.get_path()
|
|
718
713
|
if _host.get_tree().paused:
|
|
719
714
|
return (
|
|
@@ -778,7 +773,7 @@ func _out_of_reach(
|
|
|
778
773
|
|
|
779
774
|
|
|
780
775
|
func _no_window_note(viewport: Viewport) -> String:
|
|
781
|
-
return
|
|
776
|
+
return GdhTargets.no_window_note(viewport, _host.get_tree().root)
|
|
782
777
|
|
|
783
778
|
|
|
784
779
|
## Sends a click's press or release into [param viewport], through Input when that is the game's
|
|
@@ -792,7 +787,7 @@ func _no_window_note(viewport: Viewport) -> String:
|
|
|
792
787
|
## click meant to open it. Games read that state too, through Input.is_mouse_button_pressed.
|
|
793
788
|
##
|
|
794
789
|
## Any other viewport is pushed into directly: Input reaches only the game's window. That is a
|
|
795
|
-
## viewport with no place on the screen, since `
|
|
790
|
+
## viewport with no place on the screen, since `GdhScreen.reach` carries every other one there.
|
|
796
791
|
func _press_button(viewport: Viewport, event: InputEventMouseButton) -> void:
|
|
797
792
|
if viewport == _host.get_tree().root:
|
|
798
793
|
Input.parse_input_event(event)
|
|
@@ -818,10 +813,11 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
818
813
|
if not item.is_visible_in_tree():
|
|
819
814
|
return {
|
|
820
815
|
"type": "error",
|
|
821
|
-
"message":
|
|
816
|
+
"message":
|
|
817
|
+
"%s is not visible%s, so nothing can click it" % [node_path, GdhTargets.why_hidden(item)]
|
|
822
818
|
}
|
|
823
819
|
|
|
824
|
-
var found: Dictionary =
|
|
820
|
+
var found: Dictionary = GdhQueries.in_frame(item)
|
|
825
821
|
if found.is_empty():
|
|
826
822
|
return {
|
|
827
823
|
"type": "error",
|
|
@@ -846,7 +842,7 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
846
842
|
)
|
|
847
843
|
}
|
|
848
844
|
|
|
849
|
-
var out: Array[Dictionary] =
|
|
845
|
+
var out: Array[Dictionary] = GdhScreen.steps_out(viewport, aim)
|
|
850
846
|
var arriving: Viewport = out.back()["viewport"]
|
|
851
847
|
var reached_point: Vector2 = out.back()["point"]
|
|
852
848
|
# Checked where the pointer arrives as well as where the node is drawn: a room in a SubViewport
|
|
@@ -862,10 +858,10 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
862
858
|
)
|
|
863
859
|
}
|
|
864
860
|
var position: Vector2 = arriving.get_final_transform() * reached_point
|
|
865
|
-
var button: int =
|
|
861
|
+
var button: int = GdhMouse.button_of(params.get("button", MOUSE_BUTTON_LEFT))
|
|
866
862
|
if button < 0:
|
|
867
|
-
return
|
|
868
|
-
var double: bool =
|
|
863
|
+
return GdhMouse.no_such_button(params.get("button"))
|
|
864
|
+
var double: bool = GdhRead.as_bool(params.get("double", false))
|
|
869
865
|
|
|
870
866
|
arriving.push_input(_motion(position, _arrive(position)))
|
|
871
867
|
var supplied: bool = _hover.follow(arriving, reached_point)
|
|
@@ -899,7 +895,7 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
|
|
|
899
895
|
)
|
|
900
896
|
|
|
901
897
|
|
|
902
|
-
## 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]
|
|
903
899
|
## answers, gets past the interface in every viewport, as {"landed", "hovered"}: the path of the
|
|
904
900
|
## control that took it, or of the one under it that let it by, or null.
|
|
905
901
|
##
|
|
@@ -984,6 +980,6 @@ func _button(position: Vector2, button: int, pressed: bool, double: bool) -> Inp
|
|
|
984
980
|
event.pressed = pressed
|
|
985
981
|
event.double_click = double
|
|
986
982
|
var held: int = Input.get_mouse_button_mask()
|
|
987
|
-
var own: int =
|
|
983
|
+
var own: int = GdhMouse.mask_of(button)
|
|
988
984
|
event.button_mask = (held | own) if pressed else (held & ~own)
|
|
989
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,23 +3,15 @@ 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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## A point the tool schema sends as two flat numbers, or the
|
|
10
|
-
##
|
|
11
|
-
static func read_point(params: Dictionary, x_key: String, y_key: String
|
|
12
|
-
if params.has(x_key)
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
if raw is Vector2:
|
|
16
|
-
return raw
|
|
17
|
-
if raw is Array:
|
|
18
|
-
var pair: Array = raw
|
|
19
|
-
if pair.size() < 2:
|
|
20
|
-
return "%s array must contain [x, y]" % pair_key
|
|
21
|
-
return Vector2(Read.as_float(pair[0]), Read.as_float(pair[1]))
|
|
22
|
-
return "%s must be Vector2 or [x, y]" % pair_key
|
|
6
|
+
const GdhRead = preload("reading.gd")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## A point the tool schema sends as two flat numbers. Answers a Vector2, or the String that says
|
|
10
|
+
## which was missing.
|
|
11
|
+
static func read_point(params: Dictionary, x_key: String, y_key: String) -> Variant:
|
|
12
|
+
if not params.has(x_key) or not params.has(y_key):
|
|
13
|
+
return "%s and %s are both needed." % [x_key, y_key]
|
|
14
|
+
return Vector2(GdhRead.as_float(params[x_key]), GdhRead.as_float(params[y_key]))
|
|
23
15
|
|
|
24
16
|
|
|
25
17
|
## The points of a drag: x, y, then each of via, then toX, toY. Answers the String saying what is
|
|
@@ -28,7 +20,7 @@ static func stroke_of(params: Dictionary) -> Variant:
|
|
|
28
20
|
for key: String in ["x", "y", "toX", "toY"]:
|
|
29
21
|
if not params.has(key):
|
|
30
22
|
return "mouse_drag needs x, y, toX and toY: %s is missing." % key
|
|
31
|
-
var points: Array[Vector2] = [Vector2(
|
|
23
|
+
var points: Array[Vector2] = [Vector2(GdhRead.as_float(params["x"]), GdhRead.as_float(params["y"]))]
|
|
32
24
|
var via: Variant = params.get("via", [])
|
|
33
25
|
if not via is Array:
|
|
34
26
|
return "via must be a list of [x, y] points."
|
|
@@ -37,8 +29,8 @@ static func stroke_of(params: Dictionary) -> Variant:
|
|
|
37
29
|
var pair: Array = point if point is Array else []
|
|
38
30
|
if pair.size() < 2:
|
|
39
31
|
return "via must be a list of [x, y] points, and holds %s." % JSON.stringify(point)
|
|
40
|
-
points.append(Vector2(
|
|
41
|
-
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"])))
|
|
42
34
|
return points
|
|
43
35
|
|
|
44
36
|
|
|
@@ -108,5 +100,5 @@ static func button_of(raw: Variant) -> int:
|
|
|
108
100
|
return -1
|
|
109
101
|
# The engine's buttons run from 1 to 9: 0 is MOUSE_BUTTON_NONE, which no event carries, and a
|
|
110
102
|
# number past the two extra buttons names nothing a mask bit can be made for.
|
|
111
|
-
var number: int =
|
|
103
|
+
var number: int = GdhRead.as_int(raw, -1)
|
|
112
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"
|