gdharness 0.5.12 → 0.6.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.
|
@@ -220,6 +220,36 @@ func inject_mouse_motion(params: Dictionary) -> Dictionary:
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
|
|
223
|
+
## The viewport a click aimed at [param control] has to be pushed into.
|
|
224
|
+
##
|
|
225
|
+
## Its own, unless that is an embedded [Window]. A ConfirmationDialog is a Window, and under
|
|
226
|
+
## `gui_embed_subwindows` a Window is drawn inside its parent rather than given one by the
|
|
227
|
+
## desktop: pushing into its own viewport delivers nothing, measured, with the pointer reading as
|
|
228
|
+
## over no control at all. What reaches it is the parent, which is what the engine does with a
|
|
229
|
+
## real pointer. Walked rather than stepped once, because a dialog can open a dialog.
|
|
230
|
+
static func _clicking_viewport(control: Control) -> Viewport:
|
|
231
|
+
var viewport: Viewport = control.get_viewport()
|
|
232
|
+
var window: Window = viewport as Window
|
|
233
|
+
while window != null and window.is_embedded() and window.get_parent() != null:
|
|
234
|
+
viewport = window.get_parent().get_viewport()
|
|
235
|
+
window = viewport as Window
|
|
236
|
+
return viewport
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
## Where [param control]'s centre is in the viewport [method _clicking_viewport] names.
|
|
240
|
+
##
|
|
241
|
+
## The transform is against the control's own viewport, so every embedded window between it and
|
|
242
|
+
## that one contributes its offset. The same walk, because the two answers have to agree about
|
|
243
|
+
## which viewport they are describing.
|
|
244
|
+
static func _centre_of(control: Control) -> Vector2:
|
|
245
|
+
var centre: Vector2 = control.get_global_transform_with_canvas() * (control.size * 0.5)
|
|
246
|
+
var window: Window = control.get_viewport() as Window
|
|
247
|
+
while window != null and window.is_embedded() and window.get_parent() != null:
|
|
248
|
+
centre += Vector2(window.position)
|
|
249
|
+
window = window.get_parent().get_viewport() as Window
|
|
250
|
+
return centre
|
|
251
|
+
|
|
252
|
+
|
|
223
253
|
## Scrolls whatever is holding [param control] until it is on screen, and answers whether
|
|
224
254
|
## anything moved.
|
|
225
255
|
##
|
|
@@ -261,8 +291,8 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
261
291
|
if not control.is_visible_in_tree():
|
|
262
292
|
return {"type": "error", "message": "%s is not visible, so nothing can click it" % node_path}
|
|
263
293
|
|
|
264
|
-
var viewport: Viewport = control
|
|
265
|
-
var centre: Vector2 =
|
|
294
|
+
var viewport: Viewport = _clicking_viewport(control)
|
|
295
|
+
var centre: Vector2 = _centre_of(control)
|
|
266
296
|
|
|
267
297
|
# A control below the fold of a ScrollContainer is not out of reach, it is one scroll away,
|
|
268
298
|
# which is what a person does without thinking about it before they click. Refusing it
|
|
@@ -314,7 +344,12 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
314
344
|
# that went to it. Read in full here, path included, because nothing about that control
|
|
315
345
|
# is guaranteed to survive the release: a button that opens the next screen takes the
|
|
316
346
|
# whole menu out of the tree, and a node that has left the tree has no path to give.
|
|
317
|
-
|
|
347
|
+
# Asked of the control's own viewport rather than the one the event went into, and for an
|
|
348
|
+
# embedded window those are two different objects: the parent takes the event and hands it on,
|
|
349
|
+
# and the window keeps the GUI state. Reading the parent reported every dialog as not landed
|
|
350
|
+
# while the button it was aimed at pressed perfectly well, which is the worst shape an answer
|
|
351
|
+
# can have, since the caller believes the miss over what the game just did.
|
|
352
|
+
var hovered: Control = control.get_viewport().gui_get_hovered_control()
|
|
318
353
|
var hovered_path: Variant = null
|
|
319
354
|
if hovered != null:
|
|
320
355
|
hovered_path = str(hovered.get_path())
|
|
@@ -75,7 +75,13 @@ func find_nodes(params: Dictionary) -> Dictionary:
|
|
|
75
75
|
truncated = true
|
|
76
76
|
break
|
|
77
77
|
found.append(_found(node, wanted_property))
|
|
78
|
-
|
|
78
|
+
# Internal children included, which they were not. A ConfirmationDialog builds its Yes and
|
|
79
|
+
# its No as internal nodes, and a ScrollContainer its bars, so a find over a screen for
|
|
80
|
+
# every Button came back without the two buttons the player is being asked to press:
|
|
81
|
+
# nothing here could see the dialog at all, and the way past it was to emit `confirmed`.
|
|
82
|
+
# A filtered query carries no cost for including them, because they only appear when they
|
|
83
|
+
# are what was asked for.
|
|
84
|
+
var children: Array[Node] = node.get_children(true)
|
|
79
85
|
for index: int in range(children.size() - 1, -1, -1):
|
|
80
86
|
pending.push_front(children[index])
|
|
81
87
|
|
|
@@ -286,7 +292,11 @@ func _serialize_node_tree(node: Node, depth: int, max_depth: int, include_proper
|
|
|
286
292
|
|
|
287
293
|
if depth < max_depth:
|
|
288
294
|
var children: Array = []
|
|
289
|
-
for
|
|
295
|
+
# Internal ones too, for the reason `find` takes them: a tree that answers "no children"
|
|
296
|
+
# over a ConfirmationDialog holding a Yes and a No is not tidier than one that says so,
|
|
297
|
+
# it is wrong, and it is what sends somebody looking for another way to press the button.
|
|
298
|
+
# `depth` is what keeps the answer a size worth reading.
|
|
299
|
+
for child: Node in node.get_children(true):
|
|
290
300
|
children.append(_serialize_node_tree(child, depth + 1, max_depth, include_properties))
|
|
291
301
|
result["children"] = children
|
|
292
302
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|