Clicking outside an open dropdown usually closes it, but not when the pointer leaves the button and the press arrive in the same update.
The press is handled twice in the same input callback. The "click outside to close" block sets open = false, and the toggle block right below then sees the state it has just changed, so it reopens the popup:
https://github.com/ebitengine/debugui/blob/85357fb/dropdown.go#L74-L89
Instrumenting that callback shows it:
CB enter: open=true focusIsID=true justPressed=true
CLOSE OUTSIDE -> open=false
TOGGLE -> open=true <-- reopened in the same update
c.focus is still the dropdown's ID on that update because handleInputForWidget clears the focus only when the pointer is not over the widget, and then sets it again from a stale c.hover: the else if !hover branch that would clear c.hover is skipped when justPressed is true.
With a mouse this needs the pointer to leave the button and the press to land in the same update (16ms at the default 60 TPS), so it is intermittent, but any input that jumps has the same shape. With ebiten.SetTPS(10) the window is 100ms and it reproduces easily:
- pointer outside the button for at least one update, then press: the popup closes
- pointer leaves and presses in the same update: the popup stays open
I see two ways to fix it.
The cause, in handleInputForWidget (this changes the behaviour of every widget, not just the dropdown; the whole test suite passes with it):
if hover && c.pointing.justPressed() {
c.setFocus(id)
} else if c.hover == id && !hover {
c.hover = widgetID{}
}
Or locally in the dropdown, by not letting the same press be handled as a toggle after it closed the popup:
closedOutside := false
if dropdownContainer.open && c.pointing.justPressed() {
...
if !clickInButton && !clickInDropdown {
// Only close immediately if there's no close delay active
if !dropdownContainer.dropdownCloseTimer.active() {
dropdownContainer.open = false
closedOutside = true
}
}
}
if c.pointing.justPressed() && c.focus == id && !closedOutside {
Clicking outside an open dropdown usually closes it, but not when the pointer leaves the button and the press arrive in the same update.
The press is handled twice in the same input callback. The "click outside to close" block sets
open = false, and the toggle block right below then sees the state it has just changed, so it reopens the popup:https://github.com/ebitengine/debugui/blob/85357fb/dropdown.go#L74-L89
Instrumenting that callback shows it:
c.focusis still the dropdown's ID on that update becausehandleInputForWidgetclears the focus only when the pointer is not over the widget, and then sets it again from a stalec.hover: theelse if !hoverbranch that would clearc.hoveris skipped whenjustPressedis true.With a mouse this needs the pointer to leave the button and the press to land in the same update (16ms at the default 60 TPS), so it is intermittent, but any input that jumps has the same shape. With
ebiten.SetTPS(10)the window is 100ms and it reproduces easily:I see two ways to fix it.
The cause, in
handleInputForWidget(this changes the behaviour of every widget, not just the dropdown; the whole test suite passes with it):Or locally in the dropdown, by not letting the same press be handled as a toggle after it closed the popup: