gdharness 1.2.11 → 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/README.md +2 -1
- package/build/cli.js +23295 -23231
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +12 -0
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +72 -22
- package/build/index.js +23875 -23566
- package/build/keeper.js +46 -6731
- package/package.json +1 -1
|
@@ -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.
|