gdharness 1.2.30 → 1.2.32
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 +7197 -6159
- package/build/godot/addons/gdharness_editor/bridge_client.gd +14 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +237 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/plugin.gd +27 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +80 -54
- package/build/godot/addons/gdharness_editor/scene_file.gd +38 -1
- package/build/godot/addons/gdharness_editor/serialisation.gd +46 -0
- package/build/godot/addons/gdharness_editor/tool_executor.gd +1 -0
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +22 -0
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +64 -1
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +82 -44
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +178 -0
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +73 -124
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +39 -0
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +285 -0
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +151 -106
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +112 -0
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +5 -4
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +1 -47
- package/build/godot/addons/gdharness_runtime/serialisation.gd +46 -0
- package/build/godot/operations/audio_buses.gd +14 -1
- package/build/godot/operations/class_cache.gd +146 -35
- package/build/godot/operations/classdb_queries.gd +19 -12
- package/build/godot/operations/file_reads.gd +237 -0
- package/build/godot/operations/gdscript_authoring.gd +136 -19
- package/build/godot/operations/godot_operations.gd +24 -0
- package/build/godot/operations/project_config.gd +16 -6
- package/build/godot/operations/property_values.gd +767 -0
- package/build/godot/operations/rename_checks.gd +5 -2
- package/build/godot/operations/serialisation.gd +46 -0
- package/build/index.js +4896 -3872
- package/build/keeper.js +184 -71
- package/package.json +1 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
extends RefCounted
|
|
2
|
+
|
|
3
|
+
## The hover Godot gives the controls under the pointer, supplied in a native window the operating
|
|
4
|
+
## system has not said the pointer entered.
|
|
5
|
+
##
|
|
6
|
+
## With display/window/subwindows/embed_subwindows off, every window is a native one, and the engine
|
|
7
|
+
## hovers a control in one only once the window has had its mouse-enter: Viewport::_update_mouse_over
|
|
8
|
+
## asks the root which window the pointer is over, and only Window::_event_callback, called by the
|
|
9
|
+
## platform when the pointer crosses into the window, sets it. An injected event never crosses
|
|
10
|
+
## anything, and on gdharness's own desktop no pointer ever enters a window, so nothing was hovered.
|
|
11
|
+
## A BaseButton takes a mouse press only while hovered, so every click on a button in such a window
|
|
12
|
+
## was dropped while the press itself was routed to it. Reported from Tall Tales Inn, whose strip
|
|
13
|
+
## window holds its first-run question, and traced through Godot 4.7.2's source.
|
|
14
|
+
##
|
|
15
|
+
## What is supplied is what the engine does once the window has been entered: the control its own
|
|
16
|
+
## search finds under the point, MOUSE_ENTER and mouse_entered on it and on each holder the pointer
|
|
17
|
+
## passes to, MOUSE_ENTER_SELF on it, and the exits in the engine's order when the pointer moves on.
|
|
18
|
+
## The engine's own record of the hover is not reachable from a script, so
|
|
19
|
+
## Viewport.gui_get_hovered_control() still answers null there; [method hovered_in] answers for both.
|
|
20
|
+
|
|
21
|
+
## Per viewport, by instance id, the instance ids of the controls the supplied hover is on, outermost
|
|
22
|
+
## first, ending with the one under the pointer, as the engine keeps its own.
|
|
23
|
+
var _supplied: Dictionary[int, Array] = {}
|
|
24
|
+
|
|
25
|
+
## The windows whose mouse-enter and mouse-exit are listened for, and whether the platform has said
|
|
26
|
+
## the pointer is inside each: once it has, the engine hovers there by itself.
|
|
27
|
+
var _listened: Dictionary[int, bool] = {}
|
|
28
|
+
var _inside: Dictionary[int, bool] = {}
|
|
29
|
+
|
|
30
|
+
## Whether a script on a control declares `_has_point`, per script, since the search asks it of
|
|
31
|
+
## every control it passes.
|
|
32
|
+
var _shapes: Dictionary[Script, bool] = {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Whether the engine cannot hover anything in [param window]: a native window, with the root not
|
|
36
|
+
## embedding windows, that the platform has not said the pointer is in.
|
|
37
|
+
func cannot_hover(window: Window) -> bool:
|
|
38
|
+
if window.is_embedded() or window.get_tree().root.gui_embed_subwindows:
|
|
39
|
+
return false
|
|
40
|
+
_listen(window)
|
|
41
|
+
return not _inside.get(window.get_instance_id(), false)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Hovers what is under [param point], in [param viewport]'s own coordinates, when the engine could
|
|
45
|
+
## not; answers whether it did. Where the engine hovers by itself, any hover supplied before is
|
|
46
|
+
## taken down first, so a control is never entered twice.
|
|
47
|
+
func follow(viewport: Viewport, point: Vector2) -> bool:
|
|
48
|
+
var window: Window = viewport as Window
|
|
49
|
+
if window == null:
|
|
50
|
+
return false
|
|
51
|
+
if not cannot_hover(window) or window.gui_get_hovered_control() != null:
|
|
52
|
+
_drop(window, 0)
|
|
53
|
+
return false
|
|
54
|
+
_hover(window, point)
|
|
55
|
+
return _supplied.has(window.get_instance_id())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## The control hovered in [param viewport], by the engine or by this.
|
|
59
|
+
func hovered_in(viewport: Viewport) -> Control:
|
|
60
|
+
var own: Control = viewport.gui_get_hovered_control()
|
|
61
|
+
if own != null:
|
|
62
|
+
return own
|
|
63
|
+
var held: Array = _supplied.get(viewport.get_instance_id(), [])
|
|
64
|
+
if held.is_empty():
|
|
65
|
+
return null
|
|
66
|
+
var top: int = held.back()
|
|
67
|
+
return instance_from_id(top) as Control
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## Whether the hover in [param viewport] is one this supplied.
|
|
71
|
+
func supplied_in(viewport: Viewport) -> bool:
|
|
72
|
+
return viewport.gui_get_hovered_control() == null and _supplied.has(viewport.get_instance_id())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
func _listen(window: Window) -> void:
|
|
76
|
+
var id: int = window.get_instance_id()
|
|
77
|
+
if _listened.has(id):
|
|
78
|
+
return
|
|
79
|
+
_listened[id] = true
|
|
80
|
+
var _entering: int = window.mouse_entered.connect(_entered.bind(window))
|
|
81
|
+
var _leaving: int = window.mouse_exited.connect(_left.bind(window))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
func _entered(window: Window) -> void:
|
|
85
|
+
_inside[window.get_instance_id()] = true
|
|
86
|
+
_drop(window, 0)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
func _left(window: Window) -> void:
|
|
90
|
+
_inside[window.get_instance_id()] = false
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## Viewport::_update_mouse_over for [param point]: the control under it entered, from the outermost
|
|
94
|
+
## holder not already entered, and what is no longer under it left. Into what a SubViewportContainer
|
|
95
|
+
## under it shows as well, as the engine carries the pointer there.
|
|
96
|
+
func _hover(viewport: Viewport, point: Vector2) -> void:
|
|
97
|
+
var over: Control = control_at(viewport, point)
|
|
98
|
+
var id: int = viewport.get_instance_id()
|
|
99
|
+
var held: Array = _supplied.get(id, [])
|
|
100
|
+
var was: int = 0
|
|
101
|
+
if not held.is_empty():
|
|
102
|
+
was = held.back()
|
|
103
|
+
var now: int = 0
|
|
104
|
+
if over != null:
|
|
105
|
+
now = over.get_instance_id()
|
|
106
|
+
if now != was:
|
|
107
|
+
var common: int = 0
|
|
108
|
+
var entering: Array[int] = []
|
|
109
|
+
var walk: CanvasItem = over
|
|
110
|
+
while walk != null:
|
|
111
|
+
var control: Control = walk as Control
|
|
112
|
+
if control != null:
|
|
113
|
+
var filter: Control.MouseFilter = control.get_mouse_filter_with_override()
|
|
114
|
+
if filter != Control.MOUSE_FILTER_IGNORE:
|
|
115
|
+
if held.has(control.get_instance_id()):
|
|
116
|
+
common = control.get_instance_id()
|
|
117
|
+
break
|
|
118
|
+
entering.append(control.get_instance_id())
|
|
119
|
+
if filter == Control.MOUSE_FILTER_STOP:
|
|
120
|
+
break
|
|
121
|
+
if walk.top_level:
|
|
122
|
+
break
|
|
123
|
+
walk = _parent_item(walk)
|
|
124
|
+
_drop(viewport, common)
|
|
125
|
+
if over != null:
|
|
126
|
+
var kept: Array = _supplied.get(id, [])
|
|
127
|
+
for index: int in range(entering.size() - 1, -1, -1):
|
|
128
|
+
var control: Control = instance_from_id(entering[index]) as Control
|
|
129
|
+
if control != null:
|
|
130
|
+
kept.append(entering[index])
|
|
131
|
+
control.notification(Control.NOTIFICATION_MOUSE_ENTER)
|
|
132
|
+
control.mouse_entered.emit()
|
|
133
|
+
_supplied[id] = kept
|
|
134
|
+
over.notification(Control.NOTIFICATION_MOUSE_ENTER_SELF)
|
|
135
|
+
var container: SubViewportContainer = over as SubViewportContainer
|
|
136
|
+
if container == null:
|
|
137
|
+
return
|
|
138
|
+
var inside: Vector2 = container.get_global_transform_with_canvas().affine_inverse() * point
|
|
139
|
+
if container.stretch:
|
|
140
|
+
inside /= container.stretch_shrink
|
|
141
|
+
for child: Node in container.get_children():
|
|
142
|
+
var shown: SubViewport = child as SubViewport
|
|
143
|
+
if shown != null and not shown.gui_disable_input:
|
|
144
|
+
_hover(shown, shown.get_final_transform().affine_inverse() * inside)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
## Viewport::_drop_mouse_over: the hovered control left, then each holder down to and not including
|
|
148
|
+
## the one whose id is [param until], innermost first. What a SubViewportContainer left showed is
|
|
149
|
+
## left too.
|
|
150
|
+
func _drop(viewport: Viewport, until: int) -> void:
|
|
151
|
+
var id: int = viewport.get_instance_id()
|
|
152
|
+
var held: Array = _supplied.get(id, [])
|
|
153
|
+
if held.is_empty():
|
|
154
|
+
var _had: bool = _supplied.erase(id)
|
|
155
|
+
return
|
|
156
|
+
var top: int = held.back()
|
|
157
|
+
var over: Control = instance_from_id(top) as Control
|
|
158
|
+
var container: SubViewportContainer = over as SubViewportContainer
|
|
159
|
+
if container != null:
|
|
160
|
+
for child: Node in container.get_children():
|
|
161
|
+
var shown: SubViewport = child as SubViewport
|
|
162
|
+
if shown != null:
|
|
163
|
+
_drop(shown, 0)
|
|
164
|
+
if over != null and over.is_inside_tree():
|
|
165
|
+
over.notification(Control.NOTIFICATION_MOUSE_EXIT_SELF)
|
|
166
|
+
var stop: int = held.find(until) + 1 if until != 0 else 0
|
|
167
|
+
for index: int in range(held.size() - 1, stop - 1, -1):
|
|
168
|
+
var leaving: int = held[index]
|
|
169
|
+
var control: Control = instance_from_id(leaving) as Control
|
|
170
|
+
if control != null and control.is_inside_tree():
|
|
171
|
+
control.notification(Control.NOTIFICATION_MOUSE_EXIT)
|
|
172
|
+
control.mouse_exited.emit()
|
|
173
|
+
var _sized: int = held.resize(stop)
|
|
174
|
+
if held.is_empty():
|
|
175
|
+
var _had: bool = _supplied.erase(id)
|
|
176
|
+
else:
|
|
177
|
+
_supplied[id] = held
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
## Viewport::gui_find_control: the control a pointer at [param point] in [param viewport] is over,
|
|
181
|
+
## searching the controls that hold no other control's place from the one drawn last.
|
|
182
|
+
##
|
|
183
|
+
## Where the engine asks a control's own has_point, a script's `_has_point` is asked and every other
|
|
184
|
+
## control is its rect: TextureButton's click mask, GraphEdit, GraphFrame and FoldableContainer
|
|
185
|
+
## answer it in C++ that a script cannot call, so for those this answers the rect they sit in.
|
|
186
|
+
func control_at(viewport: Viewport, point: Vector2) -> Control:
|
|
187
|
+
var roots: Array[Control] = []
|
|
188
|
+
_gather_roots(viewport, roots)
|
|
189
|
+
roots.sort_custom(_drawn_before)
|
|
190
|
+
for index: int in range(roots.size() - 1, -1, -1):
|
|
191
|
+
var root: Control = roots[index]
|
|
192
|
+
if not root.is_visible_in_tree():
|
|
193
|
+
continue
|
|
194
|
+
var holder: CanvasItem = _parent_item(root)
|
|
195
|
+
var transform: Transform2D = (
|
|
196
|
+
holder.get_global_transform_with_canvas() if holder != null else root.get_canvas_transform()
|
|
197
|
+
)
|
|
198
|
+
var found: Control = _at(root, point, transform)
|
|
199
|
+
if found != null:
|
|
200
|
+
return found
|
|
201
|
+
return null
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
## The controls [param node] holds that the engine keeps as roots of [param node]'s viewport: drawn
|
|
205
|
+
## on their own, or with no control above them. Not into another viewport, a window or a
|
|
206
|
+
## SubViewport, whose controls are that one's.
|
|
207
|
+
static func _gather_roots(node: Node, roots: Array[Control]) -> void:
|
|
208
|
+
for child: Node in node.get_children():
|
|
209
|
+
if child is Viewport:
|
|
210
|
+
continue
|
|
211
|
+
var control: Control = child as Control
|
|
212
|
+
if control != null and _is_root(control):
|
|
213
|
+
roots.append(control)
|
|
214
|
+
_gather_roots(child, roots)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
static func _is_root(control: Control) -> bool:
|
|
218
|
+
var walk: CanvasItem = control
|
|
219
|
+
while not walk.top_level:
|
|
220
|
+
var parent: CanvasItem = walk.get_parent() as CanvasItem
|
|
221
|
+
if parent == null:
|
|
222
|
+
return true
|
|
223
|
+
if parent is Control:
|
|
224
|
+
return false
|
|
225
|
+
walk = parent
|
|
226
|
+
return true
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
## Control::CComparator: by canvas layer, then in tree order.
|
|
230
|
+
static func _drawn_before(first: Control, second: Control) -> bool:
|
|
231
|
+
var first_layer: int = _layer_of(first)
|
|
232
|
+
var second_layer: int = _layer_of(second)
|
|
233
|
+
if first_layer == second_layer:
|
|
234
|
+
return second.is_greater_than(first)
|
|
235
|
+
return first_layer < second_layer
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
static func _layer_of(item: CanvasItem) -> int:
|
|
239
|
+
var layer: CanvasLayer = item.get_canvas_layer_node()
|
|
240
|
+
return layer.layer if layer != null else 0
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
## CanvasItem::get_parent_item, which a script cannot call.
|
|
244
|
+
static func _parent_item(item: CanvasItem) -> CanvasItem:
|
|
245
|
+
if item.top_level:
|
|
246
|
+
return null
|
|
247
|
+
return item.get_parent() as CanvasItem
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
## Viewport::_gui_find_control_at_pos.
|
|
251
|
+
func _at(item: CanvasItem, point: Vector2, holder: Transform2D) -> Control:
|
|
252
|
+
if not item.visible:
|
|
253
|
+
return null
|
|
254
|
+
var matrix: Transform2D = holder * item.get_transform()
|
|
255
|
+
if matrix.determinant() == 0.0:
|
|
256
|
+
return null
|
|
257
|
+
var control: Control = item as Control
|
|
258
|
+
var local: Vector2 = matrix.affine_inverse() * point
|
|
259
|
+
if control == null or not control.is_clipping_contents() or _has_point(control, local):
|
|
260
|
+
for index: int in range(item.get_child_count() - 1, -1, -1):
|
|
261
|
+
var child: CanvasItem = item.get_child(index) as CanvasItem
|
|
262
|
+
if child == null or child.top_level:
|
|
263
|
+
continue
|
|
264
|
+
var found: Control = _at(child, point, matrix)
|
|
265
|
+
if found != null:
|
|
266
|
+
return found
|
|
267
|
+
if control == null or control.get_mouse_filter_with_override() == Control.MOUSE_FILTER_IGNORE:
|
|
268
|
+
return null
|
|
269
|
+
if not _has_point(control, local):
|
|
270
|
+
return null
|
|
271
|
+
return control
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
func _has_point(control: Control, local: Vector2) -> bool:
|
|
275
|
+
var attached: Variant = control.get_script()
|
|
276
|
+
if attached is Script:
|
|
277
|
+
var script: Script = attached
|
|
278
|
+
if not _shapes.has(script):
|
|
279
|
+
_shapes[script] = script.get_script_method_list().any(
|
|
280
|
+
func(method: Dictionary) -> bool: return method["name"] == "_has_point"
|
|
281
|
+
)
|
|
282
|
+
if _shapes[script]:
|
|
283
|
+
var inside: bool = control.call("_has_point", local)
|
|
284
|
+
return inside
|
|
285
|
+
return Rect2(Vector2.ZERO, control.size).has_point(local)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://b2e1nm8jwjkgw
|