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.
@@ -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 "the other %d on screen %s" % [targets.size() - 1, _passed_over(ranks[0], false)]
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: an enabled button over anything else, and then
252
- ## the whole of its words over a part of them. A disabled button ranks with what is not a button,
253
- ## since a click on it presses nothing: counted as a button, one tied with an enabled button saying
254
- ## the same words and the click was refused as not clear.
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
- return (2 if button != null and not button.disabled else 0) + (1 if whole else 0)
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 >= 2 else "control"
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 % 2 == 1 else '"%s" as part of more') % wanted
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 below [param rank] were passed over, said of one of them when [param one]. As
323
- ## what the code knows, which is whether a control is an enabled button: a card taking clicks in its
324
- ## own `gui_input` is not one, and calling it something that cannot be pressed was a guess.
325
- static func _passed_over(rank: int, one: bool) -> String:
326
- var says: String = "says" if one else "say"
327
- var is_not: String = "is not an enabled button" if one else "are not enabled buttons"
328
- match rank:
329
- 3:
330
- return "%s or %s it as part of more" % [is_not, says]
331
- 2:
332
- return is_not
333
- _:
334
- return "%s it as part of more" % says
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.