how to bind an event handler to unhandled gui input

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By nonchip

(cross-posting from reddit)

I have a rich text label with clickable links, but want it to be hidden if i click somewhere else.

My Tree:

  • PauseMenu as PauseMenu.gd
    • CreditsButton as Button
      • Signal pressed: ._on_CreditsButton_pressed
    • CreditsLayer as Control
      • visible = false
      • Signal gui_input: .._on_CreditsLayer_gui_input
      • CreditsText as MyLabel.gd
        • mouse_filter = PASS

MyLabel.gd:

extends RichTextLabel

func _ready() -> void:
	connect('meta_clicked',self,'_on_meta_clicked')

func _on_meta_clicked(meta: String):
	OS.shell_open(meta)
	get_tree().set_input_as_handled()
	accept_event()

PauseMenu.gd:

extends Control

onready var credits_layer := $CreditsLayer

func _on_CreditsButton_pressed() -> void:
	credits_layer.show()

func _on_CreditsLayer_gui_input(event: InputEvent) -> void:
	if event is InputEventMouseButton or event is InputEventScreenTouch:
		credits_layer.hide()

I can not get the event to stop (even with it set as handled and accepted) from being passed onto the externally bound on_gui_input event.

And as far as I understand it, _input and _unhandled_input (afaik the latter would honor the as_handled flag?) are functions, not signals, so i can’t bind to them from the outside either, and my PauseMenu contains other stuff (such as the menu with a button to show the credits to begin with), so i can’t just implement it there.

:bust_in_silhouette: Reply From: MintSoda

I don’t quite understand why you need mouse_filter to be PASS, since that’s the reason input events being passed to CreditsText’s parent Node’s on_gui_input.
Did you try to set the mouse_filter to STOP for your CreditsText ?

If you have a reason to use PASS mouse_filter, maybe this is what you want:
PauseMenu.gd (and just delete MyLabel.gd)

extends Control

onready var credits_layer := $CreditsLayer
onready var credits_text := $CreditsLayer/CreditsText

func _ready() -> void:
    credits_text.connect('meta_clicked',self,'_on_CreditsText_meta_clicked')

func _on_CreditsButton_pressed() -> void:
    credits_layer.show()

func _on_CreditsLayer_gui_input(event: InputEvent) -> void:
    if event is InputEventMouseButton or event is InputEventScreenTouch:
        credits_layer.hide()

func _on_CreditsText_meta_clicked(meta: String) -> void:
    OS.shell_open(meta)

as far as i understand it i need mouse_filter to pass so i can get events on both (click a link → open a link. click anything else → hide the layer). i think i forgot to mention: the label is actually full screen, so it has to pass the events on for the hiding part to work. i just want them to stop and not hide if they were clicking on a link.

also no, i can’t just delete MyLabel.gd, because that does way more than just the meta_clicked.

and correct me if i’m wrong but your code looks equivalent to what i posted, just in one file instead of two? how would that fix anything? my issue is the event doesn’t get handled/accepted when meta_clicked was fired (so click a link → open a link AND hide the layer; which i want to prevent), and you got rid of those 2 calls? do i just not understand event handling or has that even less chance of working? :stuck_out_tongue:

EDIT: just found the difference (except which files are used, which shouldnt really make a difference i think) between our codes: you bind gui_input on the label, i did on the layer. tried on the label, still hides it when i click a link, so afraid that didn’t help either :confused:
I’m starting to get really confused, the source code seems to suggest the event is emitted in-place (not deferred or anything), so how can it possibly ignore my set_input_as_handled();accept_event()? even that very function calls accept_event() conditionally, how can that be stopped by something like emit_signal 0.o

EDIT again: seems like somehow it actually is deferred, and seems to be happening anywhere up to 3 frames later, even though that’s not how emit_signal is supposed to behave, found a very ugly workaround:

var hide_credits_layer = -1
func _process(_delta: float) -> void:
	if hide_credits_layer>0:
		hide_credits_layer-=1
	elif hide_credits_layer==0:
		credits_layer.hide()
		hide_credits_layer = -1
func _on_CreditsLabel_meta_clicked(meta: String):
	hide_credits_layer = -1
	accept_event()
	get_tree().set_input_as_handled()
func _on_CreditsLayer_gui_input(event: InputEvent) -> void:
	if credits_layer.is_visible() and event is InputEventMouseButton:
		hide_credits_layer = 3

that technically works but it’s horrifying…

nonchip | 2020-04-25 04:55