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.
- package/build/cli.js +131 -47
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
- package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
- package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_editor/file_reads.gd +114 -39
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
- package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
- package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
- package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
- package/build/godot/addons/gdharness_editor/reading.gd +37 -12
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
- package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +41 -28
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +181 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +66 -0
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
- package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
- package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
- package/build/godot/addons/gdharness_runtime/reading.gd +37 -12
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +38 -20
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd +211 -0
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_tests/bench.gd +4 -5
- package/build/godot/addons/gdharness_tests/event.gd +10 -5
- package/build/godot/addons/gdharness_tests/share.gd +38 -18
- package/build/godot/addons/gdharness_tests/suites.gd +3 -3
- package/build/godot/addons/gdharness_tests/wait.gd +3 -2
- package/build/godot/loader/loader.gd +9 -9
- package/build/godot/operations/animation_reads.gd +84 -35
- package/build/godot/operations/audio_buses.gd +48 -19
- package/build/godot/operations/class_cache.gd +16 -11
- package/build/godot/operations/classdb_queries.gd +89 -44
- package/build/godot/operations/declared_values.gd +68 -32
- package/build/godot/operations/dependencies.gd +139 -93
- package/build/godot/operations/editor_tools.gd +9 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +114 -39
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +105 -66
- package/build/godot/operations/gdscript_authoring.gd +138 -39
- package/build/godot/operations/godot_operations.gd +17 -11
- package/build/godot/operations/import_pipeline.gd +110 -59
- package/build/godot/operations/input_actions.gd +63 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +144 -27
- package/build/godot/operations/project_diagnostics.gd +24 -11
- package/build/godot/operations/project_file.gd +16 -16
- package/build/godot/operations/project_validation.gd +71 -44
- package/build/godot/operations/property_paths.gd +22 -12
- package/build/godot/operations/property_values.gd +143 -78
- package/build/godot/operations/reading.gd +37 -12
- package/build/godot/operations/rename_checks.gd +2 -2
- package/build/godot/operations/resource_files.gd +11 -9
- package/build/godot/operations/same_values.gd +4 -4
- package/build/godot/operations/scene_connections.gd +27 -13
- package/build/godot/operations/script_check.gd +52 -49
- package/build/godot/operations/serialisation.gd +75 -51
- package/build/godot/operations/setting_reads.gd +7 -6
- package/build/godot/operations/value_shapes.gd +5 -6
- package/build/godot/tests/listed.gd +321 -268
- 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
|
|
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:
|
|
8
|
-
## asks the root which window the pointer is over, and only
|
|
9
|
-
## platform when the pointer crosses into the window, sets
|
|
10
|
-
## anything, and on gdharness's own desktop no pointer ever
|
|
11
|
-
## A BaseButton takes a mouse press only while hovered, so
|
|
12
|
-
## was dropped while the press itself was routed to it.
|
|
13
|
-
## window holds its first-run question, and traced through Godot
|
|
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
|
|
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,
|
|
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
|
-
##
|
|
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()
|
|
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(
|
|
235
|
-
|
|
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(
|
|
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
|
-
##
|
|
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
|
|
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
|
-
##
|
|
70
|
-
##
|
|
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
|
|
76
|
-
# pointer calls to one game, and the same four hundred characters on each was most of what
|
|
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
|
-
+ "
|
|
84
|
-
+ " and get_local_mouse_position() on what is drawn there,
|
|
85
|
-
+ " rather than where this put it. What reads the event
|
|
86
|
-
+ " control under the pointer, its hover and tooltip,
|
|
87
|
-
+ " once a run; pointer_on_hidden_desktop marks
|
|
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
|
-
+ "
|
|
103
|
-
+ " takes no press. The runtime gave the controls under the pointer the hover the
|
|
104
|
-
+ " would have: their mouse_entered and mouse_exited, and the hover a Button
|
|
105
|
-
+ " and told the window the pointer is in it, so a motion reaches the
|
|
106
|
-
+ " Viewport.gui_get_hovered_control() still answers null in this
|
|
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":
|
|
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
|
|
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)),
|
|
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":
|
|
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",
|
|
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
|
|
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,
|
|
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 {
|
|
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
|
|
590
|
-
# other words, a link among them. Said either way, so a press on the middle is never read as
|
|
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 =
|
|
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,
|
|
703
|
-
# a click on a pointer spot that rebuilt its room errored reading the freed spot's filter
|
|
704
|
-
# answered nothing. The cover is asked before the press restacks windows too: hovered missed
|
|
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
|
|
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"
|
|
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"] =
|
|
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
|
|
788
|
-
## or "" when it would reach it: the point outside the viewport it is drawn in, at
|
|
789
|
-
## outside the viewport showing that one on the screen, which [param reached] holds
|
|
790
|
-
## there; or clipped away by a ScrollContainer holding it. A refusal names the
|
|
791
|
-
## at fault and the point in that one's own space, since naming another
|
|
792
|
-
## rect it was said to be outside. The note on a game with no window goes
|
|
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,
|
|
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 =
|
|
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
|
-
% [
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
|
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
|
-
|
|
924
|
-
|
|
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))
|