gdharness 1.2.12 → 1.2.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
CHANGED
|
@@ -12175,7 +12175,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12175
12175
|
operations: {
|
|
12176
12176
|
click: {
|
|
12177
12177
|
effect: "additive",
|
|
12178
|
-
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room`,
|
|
12178
|
+
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. So is a control whose pointer a mouse_behavior_recursive set to disabled on it or above it has turned off, naming where it was set; by says, one the pointer can reach is picked over it. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room`,
|
|
12179
12179
|
requires: []
|
|
12180
12180
|
},
|
|
12181
12181
|
choose: {
|
|
@@ -570,12 +570,24 @@ func click(params: Dictionary) -> Dictionary:
|
|
|
570
570
|
## The last two answered landed, since the pointer reaches the control, while the press did nothing,
|
|
571
571
|
## measured on 4.7.2: a disabled button keeps its mouse filter and ignores the press, and a control
|
|
572
572
|
## that does not process while the game is paused is hovered and never handed the event.
|
|
573
|
+
## A control whose pointer a `mouse_behavior_recursive` above it has turned off is passed through to
|
|
574
|
+
## what is underneath, which answered `landed: false` with nothing saying why.
|
|
573
575
|
func _takes_no_click(control: Control, node_path: String) -> String:
|
|
574
576
|
if not control.is_visible_in_tree():
|
|
575
577
|
return "%s is not visible%s, so nothing can click it" % [node_path, Targets.why_hidden(control)]
|
|
576
578
|
var button: BaseButton = control as BaseButton
|
|
577
579
|
if button != null and button.disabled:
|
|
578
580
|
return "%s is disabled, so clicking it presses nothing" % node_path
|
|
581
|
+
var turned_off: Control = Targets.pointer_turned_off_by(control)
|
|
582
|
+
if turned_off != null:
|
|
583
|
+
var on: String = "itself" if turned_off == control else str(turned_off.get_path())
|
|
584
|
+
return (
|
|
585
|
+
(
|
|
586
|
+
"%s takes no pointer input, since mouse_behavior_recursive is disabled on %s,"
|
|
587
|
+
+ " so clicking it presses nothing"
|
|
588
|
+
)
|
|
589
|
+
% [node_path, on]
|
|
590
|
+
)
|
|
579
591
|
if not control.can_process():
|
|
580
592
|
var why: String = _why_it_does_not_process(control, node_path)
|
|
581
593
|
var over: Node = Targets.drawn_over(control)
|
|
@@ -12,6 +12,11 @@ const Words = preload("runtime_words.gd")
|
|
|
12
12
|
## How many of the controls saying the same words a refusal names, with the index for each.
|
|
13
13
|
const LISTED_CANDIDATES: int = 10
|
|
14
14
|
|
|
15
|
+
## What a match's rank is made of, one bit each, the highest deciding first.
|
|
16
|
+
const TAKES_POINTER: int = 4
|
|
17
|
+
const ENABLED_BUTTON: int = 2
|
|
18
|
+
const WHOLE: int = 1
|
|
19
|
+
|
|
15
20
|
|
|
16
21
|
## The control a click by [param wanted] words presses, found under [param root_path] in the tree
|
|
17
22
|
## [param root] heads, or the refusal saying why there is none or which of several:
|
|
@@ -238,9 +243,12 @@ static func control_saying(root: Node, root_path: String, wanted: String, which:
|
|
|
238
243
|
found["partOf"] = _shortened(lines[index])
|
|
239
244
|
if which == null and targets.size() > 1:
|
|
240
245
|
var others: String = (
|
|
241
|
-
"the other one on screen %s" % _passed_over(ranks[0], true)
|
|
246
|
+
"the other one on screen %s" % _passed_over(ranks[0], ranks.slice(1), true)
|
|
242
247
|
if targets.size() == 2
|
|
243
|
-
else
|
|
248
|
+
else (
|
|
249
|
+
"the other %d on screen %s"
|
|
250
|
+
% [targets.size() - 1, _passed_over(ranks[0], ranks.slice(1), false)]
|
|
251
|
+
)
|
|
244
252
|
)
|
|
245
253
|
found["picked"] = "%s; %s" % [_described(1, ranks[0], wanted, under), others]
|
|
246
254
|
if covered > 0:
|
|
@@ -248,13 +256,43 @@ static func control_saying(root: Node, root_path: String, wanted: String, which:
|
|
|
248
256
|
return {"path": str(targets[index].get_path()), "found": found}
|
|
249
257
|
|
|
250
258
|
|
|
251
|
-
## How good a match [param target] is for a click:
|
|
252
|
-
##
|
|
253
|
-
##
|
|
254
|
-
## the same words and the click was
|
|
259
|
+
## How good a match [param target] is for a click: a control the pointer can reach over one it
|
|
260
|
+
## cannot, then an enabled button over anything else, and then the whole of its words over a part
|
|
261
|
+
## of them. A disabled button ranks with what is not a button, since a click on it presses nothing:
|
|
262
|
+
## counted as a button, one tied with an enabled button saying the same words and the click was
|
|
263
|
+
## refused as not clear. A card on its way out turning the pointer off for itself and everything in
|
|
264
|
+
## it tied the same way, its Post button against the next card's.
|
|
255
265
|
static func _rank(target: Control, whole: bool) -> int:
|
|
256
266
|
var button: BaseButton = target as BaseButton
|
|
257
|
-
|
|
267
|
+
var reached: bool = pointer_turned_off_by(target) == null
|
|
268
|
+
return (
|
|
269
|
+
(TAKES_POINTER if reached else 0)
|
|
270
|
+
+ (ENABLED_BUTTON if reached and button != null and not button.disabled else 0)
|
|
271
|
+
+ (WHOLE if whole else 0)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
## The control whose `mouse_behavior_recursive` keeps the pointer from [param control], itself or
|
|
276
|
+
## the nearest above it setting one, or null when nothing does.
|
|
277
|
+
##
|
|
278
|
+
## Turned off on a control, it takes the pointer from everything under it without touching their
|
|
279
|
+
## `mouse_filter` or a button's `disabled`, so neither says the press will do nothing. Read from the
|
|
280
|
+
## engine's own answer, the filter with that override applied, rather than by walking the setting:
|
|
281
|
+
## a control below can turn it back on, and the setting stops at a parent that is not a Control.
|
|
282
|
+
## A control that ignores the pointer of its own accord, as a label does, is not turned off.
|
|
283
|
+
static func pointer_turned_off_by(control: Control) -> Control:
|
|
284
|
+
if (
|
|
285
|
+
control.mouse_filter == Control.MOUSE_FILTER_IGNORE
|
|
286
|
+
or control.get_mouse_filter_with_override() != Control.MOUSE_FILTER_IGNORE
|
|
287
|
+
):
|
|
288
|
+
return null
|
|
289
|
+
var deciding: Control = control
|
|
290
|
+
while (
|
|
291
|
+
deciding.mouse_behavior_recursive == Control.MOUSE_BEHAVIOR_INHERITED
|
|
292
|
+
and deciding.get_parent() is Control
|
|
293
|
+
):
|
|
294
|
+
deciding = deciding.get_parent()
|
|
295
|
+
return deciding
|
|
258
296
|
|
|
259
297
|
|
|
260
298
|
## Whether one of [param wanted]'s alternatives is the whole of [param said], or the whole of one of
|
|
@@ -307,31 +345,43 @@ static func _in_a_word(code: int) -> bool:
|
|
|
307
345
|
|
|
308
346
|
## [param count] matches of [param rank], as the start of a sentence.
|
|
309
347
|
static func _described(count: int, rank: int, wanted: String, under: String) -> String:
|
|
310
|
-
var what: String = "button" if rank
|
|
348
|
+
var what: String = "button" if rank & ENABLED_BUTTON else "control"
|
|
311
349
|
var how: String = '"%s"' % wanted
|
|
312
350
|
var plain: bool = true
|
|
313
351
|
for words: String in Says.alternatives(wanted):
|
|
314
352
|
plain = plain and Says.is_plain(words)
|
|
315
353
|
if plain:
|
|
316
|
-
how = ('exactly "%s"' if rank
|
|
354
|
+
how = ('exactly "%s"' if rank & WHOLE else '"%s" as part of more') % wanted
|
|
317
355
|
if count == 1:
|
|
318
356
|
return "the one %s on screen%s saying %s" % [what, under, how]
|
|
319
357
|
return "%d %ss on screen%s say %s" % [count, what, under, how]
|
|
320
358
|
|
|
321
359
|
|
|
322
|
-
## Why the matches
|
|
323
|
-
##
|
|
324
|
-
##
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
var
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
360
|
+
## Why the matches ranked [param others] were passed over for the one ranked [param best], said of
|
|
361
|
+
## one of them when [param one]: each way the best is better that one of them is not. As what the
|
|
362
|
+
## code knows, which is whether a control is an enabled button: a card taking clicks in its own
|
|
363
|
+
## `gui_input` is not one, and calling it something that cannot be pressed was a guess.
|
|
364
|
+
static func _passed_over(best: int, others: Array[int], one: bool) -> String:
|
|
365
|
+
var lacking: int = 0
|
|
366
|
+
for rank: int in others:
|
|
367
|
+
var short: int = best & ~rank
|
|
368
|
+
# One the pointer cannot reach is never ranked a button, so that is the whole of why.
|
|
369
|
+
if short & TAKES_POINTER:
|
|
370
|
+
short &= ~ENABLED_BUTTON
|
|
371
|
+
lacking |= short
|
|
372
|
+
var reasons: Array[String] = []
|
|
373
|
+
if lacking & TAKES_POINTER:
|
|
374
|
+
reasons.append("takes no pointer input" if one else "take no pointer input")
|
|
375
|
+
if lacking & ENABLED_BUTTON:
|
|
376
|
+
reasons.append("is not an enabled button" if one else "are not enabled buttons")
|
|
377
|
+
if lacking & WHOLE:
|
|
378
|
+
reasons.append("says it as part of more" if one else "say it as part of more")
|
|
379
|
+
# Ranked level with the best, a match was passed over for having the words only inside longer ones.
|
|
380
|
+
if reasons.is_empty():
|
|
381
|
+
return "says it only inside a longer word" if one else "say it only inside longer words"
|
|
382
|
+
if reasons.size() == 1:
|
|
383
|
+
return reasons[0]
|
|
384
|
+
return "%s or %s" % [", ".join(reasons.slice(0, -1)), reasons[-1]]
|
|
335
385
|
|
|
336
386
|
|
|
337
387
|
## What to add to a refusal that found nothing on screen, when words matched controls out of sight.
|
package/build/index.js
CHANGED
|
@@ -35065,7 +35065,7 @@ var TOOL_SPECS = [
|
|
|
35065
35065
|
operations: {
|
|
35066
35066
|
click: {
|
|
35067
35067
|
effect: "additive",
|
|
35068
|
-
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room`,
|
|
35068
|
+
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. So is a control whose pointer a mouse_behavior_recursive set to disabled on it or above it has turned off, naming where it was set; by says, one the pointer can reach is picked over it. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room`,
|
|
35069
35069
|
requires: []
|
|
35070
35070
|
},
|
|
35071
35071
|
choose: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.13",
|
|
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",
|