gdharness 1.3.11 → 1.3.13
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 +19152 -17965
- package/build/godot/addons/gdharness_editor/bridge_client.gd +49 -0
- package/build/godot/addons/gdharness_editor/file_walk.gd +7 -4
- package/build/godot/addons/gdharness_editor/serialisation.gd +2 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd +39 -0
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +19 -0
- package/build/godot/addons/gdharness_runtime/serialisation.gd +2 -0
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +39 -0
- package/build/godot/addons/gdharness_tests/bench.gd +28 -0
- package/build/godot/addons/gdharness_tests/bench.gd.uid +1 -0
- package/build/godot/addons/gdharness_tests/event.gd +92 -0
- package/build/godot/addons/gdharness_tests/event.gd.uid +1 -0
- package/build/godot/addons/gdharness_tests/share.gd +289 -0
- package/build/godot/addons/gdharness_tests/share.gd.uid +1 -0
- package/build/godot/addons/gdharness_tests/suites.gd +34 -0
- package/build/godot/addons/gdharness_tests/suites.gd.uid +1 -0
- package/build/godot/addons/gdharness_tests/suites.tscn +6 -0
- package/build/godot/addons/gdharness_tests/tweens.gd +24 -0
- package/build/godot/addons/gdharness_tests/tweens.gd.uid +1 -0
- package/build/godot/addons/gdharness_tests/wait.gd +53 -0
- package/build/godot/addons/gdharness_tests/wait.gd.uid +1 -0
- package/build/godot/loader/loader.gd +28 -0
- package/build/godot/operations/class_cache.gd +1 -1
- package/build/godot/operations/file_walk.gd +7 -4
- package/build/godot/operations/input_actions.gd +2 -2
- package/build/godot/operations/plugins.gd +2 -2
- package/build/godot/operations/project_config.gd +37 -10
- package/build/godot/operations/project_file.gd +89 -31
- package/build/godot/operations/serialisation.gd +2 -0
- package/build/godot/operations/value_shapes.gd +39 -0
- package/build/godot/tests/listed.gd +196 -20
- package/build/keeper.js +8398 -911
- package/package.json +1 -1
|
@@ -19,6 +19,9 @@ const DIGEST_MARKER: String = "res://addons/gdharness_editor/.gdharness-digest"
|
|
|
19
19
|
## step with `announcementPath` in src/bridge-announce.ts.
|
|
20
20
|
const ANNOUNCEMENT: String = "res://.godot/gdharness-bridge.json"
|
|
21
21
|
const ANNOUNCE_PROTOCOL: int = 1
|
|
22
|
+
## Where each editor of this project says which ports it serves; see [method _ports_record].
|
|
23
|
+
const EDITORS_RECORDED: String = "res://.godot/gdharness-editors"
|
|
24
|
+
const EDITORS_RECORDED_PROTOCOL: int = 1
|
|
22
25
|
const RECONNECT_DELAY: float = 3.0
|
|
23
26
|
const MAX_RECONNECT_DELAY: float = 30.0
|
|
24
27
|
|
|
@@ -128,6 +131,52 @@ func _ready() -> void:
|
|
|
128
131
|
|
|
129
132
|
set_process(true)
|
|
130
133
|
_initialized = true
|
|
134
|
+
_record_ports()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
func _exit_tree() -> void:
|
|
138
|
+
var record: String = _ports_record()
|
|
139
|
+
if FileAccess.file_exists(record):
|
|
140
|
+
var _removed: Error = DirAccess.remove_absolute(record)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
## The file saying which ports this editor serves, named by its process id, so `gdharness lsp` can
|
|
144
|
+
## find its language server when a server moved it off the default. One for each process rather
|
|
145
|
+
## than one for the project, because a gate's `--import` loads this addon too, and its plugin is
|
|
146
|
+
## shown none of the options that would say so; the reader tells the two apart by command line.
|
|
147
|
+
## Kept in step with `editorRecordsIn` in src/lsp-bridge.ts.
|
|
148
|
+
static func _ports_record() -> String:
|
|
149
|
+
return ProjectSettings.globalize_path(EDITORS_RECORDED).path_join("%d.json" % OS.get_process_id())
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
func _record_ports() -> void:
|
|
153
|
+
if not Engine.is_editor_hint():
|
|
154
|
+
return
|
|
155
|
+
var made: Error = DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(EDITORS_RECORDED))
|
|
156
|
+
if made != OK and made != ERR_ALREADY_EXISTS:
|
|
157
|
+
return
|
|
158
|
+
var file: FileAccess = FileAccess.open(_ports_record(), FileAccess.WRITE)
|
|
159
|
+
if file == null:
|
|
160
|
+
return
|
|
161
|
+
var written: bool = (
|
|
162
|
+
file
|
|
163
|
+
. store_string(
|
|
164
|
+
(
|
|
165
|
+
JSON
|
|
166
|
+
. stringify(
|
|
167
|
+
{
|
|
168
|
+
"protocol": EDITORS_RECORDED_PROTOCOL,
|
|
169
|
+
"pid": OS.get_process_id(),
|
|
170
|
+
"lsp_port": _serves(LSP_ASKED, LSP_SETTING),
|
|
171
|
+
"dap_port": _serves(DAP_ASKED, DAP_SETTING),
|
|
172
|
+
"written_at_ms": int(Time.get_unix_time_from_system() * 1000.0),
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
if not written:
|
|
179
|
+
push_warning("gdharness: could not write " + _ports_record())
|
|
131
180
|
|
|
132
181
|
|
|
133
182
|
func _process(_delta: float) -> void:
|
|
@@ -77,10 +77,13 @@ func is_installed_by_gdharness(path: String) -> bool:
|
|
|
77
77
|
return FileAccess.file_exists(path + ".gdharness-version")
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
# Files under `path` whose name ends with `extension`, which is passed with its dot.
|
|
81
|
-
|
|
80
|
+
# Files under `path` whose name ends with `extension`, which is passed with its dot. An addon
|
|
81
|
+
# gdharness installed is walked into when [param with_ours] is set, for a caller rebuilding what
|
|
82
|
+
# the engine itself would: the engine lists the global classes of every addon, and a list rebuilt
|
|
83
|
+
# without ours dropped the one class the tests addon declares.
|
|
84
|
+
func find_files(path: String, extension: String, with_ours: bool = false) -> Array[String]:
|
|
82
85
|
var files: Array[String] = []
|
|
83
|
-
if is_stepped_over(path) or is_installed_by_gdharness(path):
|
|
86
|
+
if is_stepped_over(path) or (not with_ours and is_installed_by_gdharness(path)):
|
|
84
87
|
return files
|
|
85
88
|
|
|
86
89
|
var dir: DirAccess = DirAccess.open(path)
|
|
@@ -92,7 +95,7 @@ func find_files(path: String, extension: String) -> Array[String]:
|
|
|
92
95
|
|
|
93
96
|
while file_name != "":
|
|
94
97
|
if dir.current_is_dir() and not file_name.begins_with("."):
|
|
95
|
-
files.append_array(find_files(path + file_name + "/", extension))
|
|
98
|
+
files.append_array(find_files(path + file_name + "/", extension, with_ours))
|
|
96
99
|
elif file_name.ends_with(extension):
|
|
97
100
|
files.append(path + file_name)
|
|
98
101
|
|
|
@@ -222,6 +222,8 @@ func deserialize_value(value: Variant) -> Variant:
|
|
|
222
222
|
# to compile a script that gives a Variant to a typed parameter: the operations are compiled
|
|
223
223
|
# under the target project's warning levels, so one such project lost every headless call.
|
|
224
224
|
var tag: String = str(fields["_type"])
|
|
225
|
+
if GdhShapes.LIST_TAGS.has(tag):
|
|
226
|
+
return GdhShapes.packed_tagged(fields, fitted)
|
|
225
227
|
var builder: String = BUILDERS.get(tag, "")
|
|
226
228
|
if builder.is_empty() or not GdhShapes.buildable(fields, tag):
|
|
227
229
|
return fields
|
|
@@ -20,6 +20,18 @@ const PACKED_ELEMENTS: Dictionary = {
|
|
|
20
20
|
TYPE_PACKED_VECTOR4_ARRAY: TYPE_VECTOR4,
|
|
21
21
|
TYPE_PACKED_COLOR_ARRAY: TYPE_COLOR,
|
|
22
22
|
}
|
|
23
|
+
## The tags of the packed arrays that cross as a plain list, and the type each builds.
|
|
24
|
+
const LIST_TAGS: Dictionary = {
|
|
25
|
+
"PackedInt32Array": TYPE_PACKED_INT32_ARRAY,
|
|
26
|
+
"PackedInt64Array": TYPE_PACKED_INT64_ARRAY,
|
|
27
|
+
"PackedFloat32Array": TYPE_PACKED_FLOAT32_ARRAY,
|
|
28
|
+
"PackedFloat64Array": TYPE_PACKED_FLOAT64_ARRAY,
|
|
29
|
+
"PackedStringArray": TYPE_PACKED_STRING_ARRAY,
|
|
30
|
+
"PackedVector2Array": TYPE_PACKED_VECTOR2_ARRAY,
|
|
31
|
+
"PackedVector3Array": TYPE_PACKED_VECTOR3_ARRAY,
|
|
32
|
+
"PackedVector4Array": TYPE_PACKED_VECTOR4_ARRAY,
|
|
33
|
+
"PackedColorArray": TYPE_PACKED_COLOR_ARRAY,
|
|
34
|
+
}
|
|
23
35
|
|
|
24
36
|
# What each tag has to carry before it is built. A tag naming a type whose keys are not there is a
|
|
25
37
|
# tag somebody wrote by mistake or wrote for a different shape, and building one out of defaults
|
|
@@ -44,6 +56,15 @@ const REQUIRED: Dictionary = {
|
|
|
44
56
|
"Color": [["r", "g", "b"]],
|
|
45
57
|
"NodePath": [["path"]],
|
|
46
58
|
"PackedByteArray": [["base64"]],
|
|
59
|
+
"PackedInt32Array": [["value"]],
|
|
60
|
+
"PackedInt64Array": [["value"]],
|
|
61
|
+
"PackedFloat32Array": [["value"]],
|
|
62
|
+
"PackedFloat64Array": [["value"]],
|
|
63
|
+
"PackedStringArray": [["value"]],
|
|
64
|
+
"PackedVector2Array": [["value"]],
|
|
65
|
+
"PackedVector3Array": [["value"]],
|
|
66
|
+
"PackedVector4Array": [["value"]],
|
|
67
|
+
"PackedColorArray": [["value"]],
|
|
47
68
|
"float": [["value"]],
|
|
48
69
|
"int": [["value"]],
|
|
49
70
|
"RID": [["id"]],
|
|
@@ -79,6 +100,10 @@ static func written_as(type: int) -> String:
|
|
|
79
100
|
var alternatives: Array = REQUIRED.get(tag, [])
|
|
80
101
|
if alternatives.is_empty() or tag in ["float", "int", "Dictionary"]:
|
|
81
102
|
return ""
|
|
103
|
+
# A packed list is written as the plain list wherever its type is declared, so its tag is no
|
|
104
|
+
# remedy there.
|
|
105
|
+
if PACKED_ELEMENTS.has(type) and type != TYPE_PACKED_BYTE_ARRAY:
|
|
106
|
+
return ""
|
|
82
107
|
var keys: Array = alternatives[0]
|
|
83
108
|
var optional: Array = OPTIONAL.get(tag, [])
|
|
84
109
|
var parts: Array[String] = []
|
|
@@ -172,6 +197,20 @@ static func fraction(value: Variant, type: int, elements: Array = []) -> bool:
|
|
|
172
197
|
return false
|
|
173
198
|
|
|
174
199
|
|
|
200
|
+
## A packed array tagged with its type, {"_type": "PackedStringArray", "value": [...]}, for where
|
|
201
|
+
## nothing declares what a plain list is to become: a setting the engine does not declare, or a
|
|
202
|
+
## Variant property, takes a list as an Array. Each item is fitted as a declared list's would be,
|
|
203
|
+
## and a list with an item that does not fit stays the tag it arrived as, for the caller to refuse.
|
|
204
|
+
static func packed_tagged(fields: Dictionary, fit: Callable) -> Variant:
|
|
205
|
+
var tag: String = str(fields["_type"])
|
|
206
|
+
if not buildable(fields, tag) or not fields["value"] is Array:
|
|
207
|
+
return fields
|
|
208
|
+
var type: int = LIST_TAGS[tag]
|
|
209
|
+
var items: Array = fields["value"]
|
|
210
|
+
var built: Variant = packed_from(items, type, fit)
|
|
211
|
+
return built if typeof(built) == type else fields
|
|
212
|
+
|
|
213
|
+
|
|
175
214
|
## The packed array [param type] built from [param items], each fitted by [param fit] to what the
|
|
176
215
|
## array holds, or [param items] as they are when one cannot become that, for the caller to refuse.
|
|
177
216
|
## Element by element, so a point given as its fields is read as one: converted whole, a list of
|
|
@@ -71,6 +71,10 @@ const SCRIPT_RUNS_SETTING: String = "gdharness/runtime/serve_script_runs"
|
|
|
71
71
|
## not served. Kept in step with `QUIET_SCENES_SETTING` in src/setup.ts.
|
|
72
72
|
const QUIET_SCENES_SETTING: String = "gdharness/runtime/quiet_scenes_under"
|
|
73
73
|
|
|
74
|
+
## What every user argument `gdharness spread` hands its engines starts with. Kept in step with
|
|
75
|
+
## the constants of GdhShare in addons/gdharness_tests/share.gd.
|
|
76
|
+
const SPREAD_ARGUMENTS: String = "--gdh-share"
|
|
77
|
+
|
|
74
78
|
## What the engine takes a bare argument for as the scene to run, by its extension.
|
|
75
79
|
const SCENE_EXTENSIONS: PackedStringArray = ["tscn", "scn", "escn", "res", "tres"]
|
|
76
80
|
|
|
@@ -489,6 +493,12 @@ func _start_server() -> void:
|
|
|
489
493
|
_enabled = false
|
|
490
494
|
return
|
|
491
495
|
|
|
496
|
+
# Nor an engine `gdharness spread` started, whatever folder its scene is in: its workers and the
|
|
497
|
+
# engine gathering their outcomes play a bench rather than the game, several at once.
|
|
498
|
+
if spread_engine(OS.get_cmdline_user_args()):
|
|
499
|
+
_enabled = false
|
|
500
|
+
return
|
|
501
|
+
|
|
492
502
|
_server = TCPServer.new()
|
|
493
503
|
# listen() defaults bind_address to "*", which exposes the game to the whole network.
|
|
494
504
|
var bind_address: String = str(ProjectSettings.get_setting(BIND_ADDRESS_SETTING, DEFAULT_BIND_ADDRESS))
|
|
@@ -540,6 +550,15 @@ static func quiet_scene_in(given: PackedStringArray, prefixes: Variant) -> Strin
|
|
|
540
550
|
return ""
|
|
541
551
|
|
|
542
552
|
|
|
553
|
+
## Whether [param given], the engine's user arguments, are those `gdharness spread` hands its
|
|
554
|
+
## engines, which the tests addon's GdhShare reads.
|
|
555
|
+
static func spread_engine(given: PackedStringArray) -> bool:
|
|
556
|
+
for argument: String in given:
|
|
557
|
+
if argument.begins_with(SPREAD_ARGUMENTS):
|
|
558
|
+
return true
|
|
559
|
+
return false
|
|
560
|
+
|
|
561
|
+
|
|
543
562
|
## Whether the engine was told to run a script rather than the game.
|
|
544
563
|
func _script_run() -> bool:
|
|
545
564
|
var given: PackedStringArray = OS.get_cmdline_args()
|
|
@@ -222,6 +222,8 @@ func deserialize_value(value: Variant) -> Variant:
|
|
|
222
222
|
# to compile a script that gives a Variant to a typed parameter: the operations are compiled
|
|
223
223
|
# under the target project's warning levels, so one such project lost every headless call.
|
|
224
224
|
var tag: String = str(fields["_type"])
|
|
225
|
+
if GdhShapes.LIST_TAGS.has(tag):
|
|
226
|
+
return GdhShapes.packed_tagged(fields, fitted)
|
|
225
227
|
var builder: String = BUILDERS.get(tag, "")
|
|
226
228
|
if builder.is_empty() or not GdhShapes.buildable(fields, tag):
|
|
227
229
|
return fields
|
|
@@ -20,6 +20,18 @@ const PACKED_ELEMENTS: Dictionary = {
|
|
|
20
20
|
TYPE_PACKED_VECTOR4_ARRAY: TYPE_VECTOR4,
|
|
21
21
|
TYPE_PACKED_COLOR_ARRAY: TYPE_COLOR,
|
|
22
22
|
}
|
|
23
|
+
## The tags of the packed arrays that cross as a plain list, and the type each builds.
|
|
24
|
+
const LIST_TAGS: Dictionary = {
|
|
25
|
+
"PackedInt32Array": TYPE_PACKED_INT32_ARRAY,
|
|
26
|
+
"PackedInt64Array": TYPE_PACKED_INT64_ARRAY,
|
|
27
|
+
"PackedFloat32Array": TYPE_PACKED_FLOAT32_ARRAY,
|
|
28
|
+
"PackedFloat64Array": TYPE_PACKED_FLOAT64_ARRAY,
|
|
29
|
+
"PackedStringArray": TYPE_PACKED_STRING_ARRAY,
|
|
30
|
+
"PackedVector2Array": TYPE_PACKED_VECTOR2_ARRAY,
|
|
31
|
+
"PackedVector3Array": TYPE_PACKED_VECTOR3_ARRAY,
|
|
32
|
+
"PackedVector4Array": TYPE_PACKED_VECTOR4_ARRAY,
|
|
33
|
+
"PackedColorArray": TYPE_PACKED_COLOR_ARRAY,
|
|
34
|
+
}
|
|
23
35
|
|
|
24
36
|
# What each tag has to carry before it is built. A tag naming a type whose keys are not there is a
|
|
25
37
|
# tag somebody wrote by mistake or wrote for a different shape, and building one out of defaults
|
|
@@ -44,6 +56,15 @@ const REQUIRED: Dictionary = {
|
|
|
44
56
|
"Color": [["r", "g", "b"]],
|
|
45
57
|
"NodePath": [["path"]],
|
|
46
58
|
"PackedByteArray": [["base64"]],
|
|
59
|
+
"PackedInt32Array": [["value"]],
|
|
60
|
+
"PackedInt64Array": [["value"]],
|
|
61
|
+
"PackedFloat32Array": [["value"]],
|
|
62
|
+
"PackedFloat64Array": [["value"]],
|
|
63
|
+
"PackedStringArray": [["value"]],
|
|
64
|
+
"PackedVector2Array": [["value"]],
|
|
65
|
+
"PackedVector3Array": [["value"]],
|
|
66
|
+
"PackedVector4Array": [["value"]],
|
|
67
|
+
"PackedColorArray": [["value"]],
|
|
47
68
|
"float": [["value"]],
|
|
48
69
|
"int": [["value"]],
|
|
49
70
|
"RID": [["id"]],
|
|
@@ -79,6 +100,10 @@ static func written_as(type: int) -> String:
|
|
|
79
100
|
var alternatives: Array = REQUIRED.get(tag, [])
|
|
80
101
|
if alternatives.is_empty() or tag in ["float", "int", "Dictionary"]:
|
|
81
102
|
return ""
|
|
103
|
+
# A packed list is written as the plain list wherever its type is declared, so its tag is no
|
|
104
|
+
# remedy there.
|
|
105
|
+
if PACKED_ELEMENTS.has(type) and type != TYPE_PACKED_BYTE_ARRAY:
|
|
106
|
+
return ""
|
|
82
107
|
var keys: Array = alternatives[0]
|
|
83
108
|
var optional: Array = OPTIONAL.get(tag, [])
|
|
84
109
|
var parts: Array[String] = []
|
|
@@ -172,6 +197,20 @@ static func fraction(value: Variant, type: int, elements: Array = []) -> bool:
|
|
|
172
197
|
return false
|
|
173
198
|
|
|
174
199
|
|
|
200
|
+
## A packed array tagged with its type, {"_type": "PackedStringArray", "value": [...]}, for where
|
|
201
|
+
## nothing declares what a plain list is to become: a setting the engine does not declare, or a
|
|
202
|
+
## Variant property, takes a list as an Array. Each item is fitted as a declared list's would be,
|
|
203
|
+
## and a list with an item that does not fit stays the tag it arrived as, for the caller to refuse.
|
|
204
|
+
static func packed_tagged(fields: Dictionary, fit: Callable) -> Variant:
|
|
205
|
+
var tag: String = str(fields["_type"])
|
|
206
|
+
if not buildable(fields, tag) or not fields["value"] is Array:
|
|
207
|
+
return fields
|
|
208
|
+
var type: int = LIST_TAGS[tag]
|
|
209
|
+
var items: Array = fields["value"]
|
|
210
|
+
var built: Variant = packed_from(items, type, fit)
|
|
211
|
+
return built if typeof(built) == type else fields
|
|
212
|
+
|
|
213
|
+
|
|
175
214
|
## The packed array [param type] built from [param items], each fitted by [param fit] to what the
|
|
176
215
|
## array holds, or [param items] as they are when one cannot become that, for the caller to refuse.
|
|
177
216
|
## Element by element, so a point given as its fields is read as one: converted whole, a list of
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class_name GdhBench
|
|
2
|
+
extends RefCounted
|
|
3
|
+
|
|
4
|
+
## Work played a step a frame, for the script profiler. The engine sends the profiler what each
|
|
5
|
+
## frame called and holds names for only so many functions a frame, so work played inside one frame
|
|
6
|
+
## comes back with most of its functions unnamed; spread over frames, every step is named.
|
|
7
|
+
##
|
|
8
|
+
## func _ready() -> void:
|
|
9
|
+
## var timed: Dictionary = await GdhBench.each_frame(get_tree(), 400, _run.advance)
|
|
10
|
+
## print("bench: %d days, %.2f ms a day" % [timed["steps"], timed["ms_each"]])
|
|
11
|
+
## get_tree().quit()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Calls [param step] once a frame, up to [param most] times, and stops early when it returns
|
|
15
|
+
## false. Answers how many steps ran under "steps", and under "ms_each" the wall-clock milliseconds a
|
|
16
|
+
## step took on average, the step alone and not the frame around it.
|
|
17
|
+
static func each_frame(tree: SceneTree, most: int, step: Callable) -> Dictionary:
|
|
18
|
+
var steps: int = 0
|
|
19
|
+
var spent_usec: int = 0
|
|
20
|
+
while steps < most:
|
|
21
|
+
await tree.process_frame
|
|
22
|
+
var began: int = Time.get_ticks_usec()
|
|
23
|
+
var going: Variant = step.call()
|
|
24
|
+
spent_usec += Time.get_ticks_usec() - began
|
|
25
|
+
steps += 1
|
|
26
|
+
if going is bool and going == false:
|
|
27
|
+
break
|
|
28
|
+
return {"steps": steps, "ms_each": spent_usec / 1000.0 / maxf(1.0, float(steps))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://dk4ffqm2xbsn7
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
class_name GdhEvent
|
|
2
|
+
extends RefCounted
|
|
3
|
+
## Input events built in one line, for a case that hands them to a control or to the engine.
|
|
4
|
+
##
|
|
5
|
+
## A position is the point in the window, which is where `Input.parse_input_event` delivers the
|
|
6
|
+
## event and what a control's own `_gui_input` reads when it is handed the event directly. The
|
|
7
|
+
## global position is set to the same point, as the window's own events carry it, for code that
|
|
8
|
+
## reads that field instead.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [param button] pressed at [param at], or released when [param pressed] is false.
|
|
12
|
+
static func click(
|
|
13
|
+
at: Vector2, button: MouseButton = MOUSE_BUTTON_LEFT, pressed: bool = true
|
|
14
|
+
) -> InputEventMouseButton:
|
|
15
|
+
var made: InputEventMouseButton = InputEventMouseButton.new()
|
|
16
|
+
made.button_index = button
|
|
17
|
+
made.pressed = pressed
|
|
18
|
+
made.position = at
|
|
19
|
+
made.global_position = at
|
|
20
|
+
if pressed:
|
|
21
|
+
made.button_mask = _mask_of(button)
|
|
22
|
+
return made
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## The release of [param button] at [param at], which ends the press [method click] began.
|
|
26
|
+
static func release(at: Vector2, button: MouseButton = MOUSE_BUTTON_LEFT) -> InputEventMouseButton:
|
|
27
|
+
return click(at, button, false)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## One turn of the wheel at [param at], up or down. The engine sends a wheel turn as a press.
|
|
31
|
+
static func wheel(at: Vector2, up: bool = true) -> InputEventMouseButton:
|
|
32
|
+
return click(at, MOUSE_BUTTON_WHEEL_UP if up else MOUSE_BUTTON_WHEEL_DOWN)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## The mouse moved to [param at] by [param moved], holding [param held], a mask of buttons such
|
|
36
|
+
## as MOUSE_BUTTON_MASK_LEFT for a drag.
|
|
37
|
+
static func motion(at: Vector2, moved: Vector2 = Vector2.ZERO, held: int = 0) -> InputEventMouseMotion:
|
|
38
|
+
var made: InputEventMouseMotion = InputEventMouseMotion.new()
|
|
39
|
+
made.position = at
|
|
40
|
+
made.global_position = at
|
|
41
|
+
made.relative = moved
|
|
42
|
+
made.button_mask = held
|
|
43
|
+
return made
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## [param key_pressed] pressed, or released when [param pressed] is false. The key is set as both the
|
|
47
|
+
## keycode and the physical keycode, so a binding to either matches it.
|
|
48
|
+
static func key(key_pressed: Key, pressed: bool = true) -> InputEventKey:
|
|
49
|
+
var made: InputEventKey = InputEventKey.new()
|
|
50
|
+
made.keycode = key_pressed
|
|
51
|
+
made.physical_keycode = key_pressed
|
|
52
|
+
made.pressed = pressed
|
|
53
|
+
return made
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## [param button] of the pad [param device] pressed, or released when [param pressed] is false.
|
|
57
|
+
static func pad(button: JoyButton, pressed: bool = true, device: int = 0) -> InputEventJoypadButton:
|
|
58
|
+
var made: InputEventJoypadButton = InputEventJoypadButton.new()
|
|
59
|
+
made.button_index = button
|
|
60
|
+
made.pressed = pressed
|
|
61
|
+
made.pressure = 1.0 if pressed else 0.0
|
|
62
|
+
made.device = device
|
|
63
|
+
return made
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## [param axis_moved] of the pad [param device] at [param value], from -1 to 1.
|
|
67
|
+
static func axis(axis_moved: JoyAxis, value: float, device: int = 0) -> InputEventJoypadMotion:
|
|
68
|
+
var made: InputEventJoypadMotion = InputEventJoypadMotion.new()
|
|
69
|
+
made.axis = axis_moved
|
|
70
|
+
made.axis_value = value
|
|
71
|
+
made.device = device
|
|
72
|
+
return made
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## The action [param action_named] pressed at [param strength], or released when [param pressed] is false.
|
|
76
|
+
static func action(action_named: StringName, pressed: bool = true, strength: float = 1.0) -> InputEventAction:
|
|
77
|
+
var made: InputEventAction = InputEventAction.new()
|
|
78
|
+
made.action = action_named
|
|
79
|
+
made.pressed = pressed
|
|
80
|
+
made.strength = strength if pressed else 0.0
|
|
81
|
+
return made
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
static func _mask_of(button: MouseButton) -> int:
|
|
85
|
+
match button:
|
|
86
|
+
MOUSE_BUTTON_LEFT:
|
|
87
|
+
return MOUSE_BUTTON_MASK_LEFT
|
|
88
|
+
MOUSE_BUTTON_RIGHT:
|
|
89
|
+
return MOUSE_BUTTON_MASK_RIGHT
|
|
90
|
+
MOUSE_BUTTON_MIDDLE:
|
|
91
|
+
return MOUSE_BUTTON_MASK_MIDDLE
|
|
92
|
+
return 0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://cmwm44sgxytkj
|