Skip to content

gtk: fix context menu hiding quick-terminal - #12843

Merged
jcollie merged 3 commits into
ghostty-org:mainfrom
dkinzler:fix/context-menu-hides-quick-terminal
May 30, 2026
Merged

jcollie merged 3 commits into
ghostty-org:mainfrom
dkinzler:fix/context-menu-hides-quick-terminal

Conversation

@dkinzler

Copy link
Copy Markdown
Contributor

Fixes #12783 where opening the context menu (with right click) inside the quick-terminal will hide the quick-terminal if autohide is enabled.

The cause of this issue is the quick-terminal window becoming inactive and immediately active again when you open the context-menu. When the window becomes inactive, the autohide feature hides the quick-terminal. The temporary focus loss in GTK is triggered by GDK focus change events, which probably originate from the windowing backend treating the context menu as its own window. Whereas in GTK the context menu is not a separate window but instead part of the widget tree of the window it was opened from, so even when the context menu has focus that window is still the active one in GTK.

As a fix Window.propIsActive, which implements the autohide logic, will now do its work from a timeout callback, since there is probably no reliable way to distinguish a temporary focus loss from a real one from inside GTK and I'm not sure we can make any assumptions about the timing of things happening in the windowing backend. A 100ms delay should be long enough for the focus state to settle while still hiding the quick-terminal quickly.

I reproduced the bug and verified the fix on Wayland with both Hyprland and KDE. Temporary focus loss happens on X11+KDE as well, although it doesn't matter there because there is no quick-terminal.

AI Disclosure

No AI was used, code and comments were written by myself.

dkinzler added 2 commits May 26, 2026 18:26
…settle. Depending on the backend a window might temporarily become inactive.

Fixes an issue where quick-terminal would disappear when opening the surface context menu.
@dkinzler
dkinzler requested a review from a team as a code owner May 28, 2026 13:12
@ghostty-bot ghostty-bot Bot added the gtk Issues exclusive to GTK which are not OS-specific label May 28, 2026
Comment thread src/apprt/gtk/class/window.zig Outdated
quick_terminal: bool = false,

/// Timeout source to react to this window becoming (in)active.
timeout: ?c_uint = null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable should be more descriptive of it's purpose so you don't have to refer back to the docstring to see what it does.

Comment thread src/apprt/gtk/class/window.zig Outdated
}
}

fn onTimeout(ud: ?*anyopaque) callconv(.c) c_int {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function name should be more descriptive of what it does. Should also have a short docstring describing it's function.

Comment thread src/apprt/gtk/class/window.zig Outdated
// become inactive and immediately active again. This happens
// e.g. on Wayland when opening a context menu.
if (priv.timeout == null) priv.timeout = glib.timeoutAdd(
100,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you experiment at all with shorter timeouts? We should probably document the source of this magic number.

@dkinzler dkinzler May 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review! I already did a bunch of experimenting and some more today. On my own setup with Hyprland even a small delay like 1ms or just an idle callback works reliably. In a Nix VM with KDE Plasma6 anything lower than 25ms only works some of the time, just an idle callback actually works most of the time there, but also not always.

I think a smaller timeout like 50ms together with the priority of an idle callback strikes a good balance. Since an idle callback is already enough most of the time, this should be reliable. For now there is probably also no harm in being safe and choosing a higher value, 50ms delay in hiding the quick-terminal should barely be noticeable.

Comment thread src/apprt/gtk/class/window.zig Outdated

if (priv.timeout) |v| {
if (glib.Source.remove(v) == 0) {
log.warn("unable to remove timeout source", .{});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning should be more descriptive of what timeout can't be removed.

@dkinzler
dkinzler requested a review from jcollie May 29, 2026 15:59
@jcollie
jcollie merged commit 2c62d18 into ghostty-org:main May 30, 2026
93 checks passed
@github-actions github-actions Bot added this to the 1.3.2 milestone May 30, 2026
@mitchellh mitchellh modified the milestones: 1.3.2, 1.4.0 Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gtk Issues exclusive to GTK which are not OS-specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants