diff --git a/src/apprt/gtk-ng/class/surface.zig b/src/apprt/gtk-ng/class/surface.zig index bb432cf6f..3544fb6d3 100644 --- a/src/apprt/gtk-ng/class/surface.zig +++ b/src/apprt/gtk-ng/class/surface.zig @@ -1050,6 +1050,13 @@ pub const Surface = extern struct { ); } + /// Focus this surface. This properly focuses the input part of + /// our surface. + pub fn grabFocus(self: *Self) void { + const priv = self.private(); + _ = priv.gl_area.as(gtk.Widget).grabFocus(); + } + //--------------------------------------------------------------- // Virtual Methods diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index 0eb514b98..59ef7055d 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -210,6 +210,9 @@ pub const Window = extern struct { /// The configuration that this surface is using. config: ?*Config = null, + /// See tabOverviewOpen for why we have this. + tab_overview_focus_timer: ?c_uint = null, + // Template bindings tab_bar: *adw.TabBar, tab_view: *adw.TabView, @@ -632,6 +635,55 @@ pub const Window = extern struct { return self.newTabPage(if (self.getActiveSurface()) |v| v.core() else null); } + fn tabOverviewOpen( + tab_overview: *adw.TabOverview, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // We only care about when the tab overview is closed. + if (tab_overview.getOpen() != 0) return; + + // On tab overview close, focus is sometimes lost. This is an + // upstream issue in libadwaita[1]. When this is resolved we + // can put a runtime version check here to avoid this workaround. + // + // Our workaround is to start a timer after 500ms to refocus + // the currently selected tab. We choose 500ms because the adw + // animation is 400ms. + // + // [1]: https://gitlab.gnome.org/GNOME/libadwaita/-/issues/670 + + // If we have an old timer remove it + const priv = self.private(); + if (priv.tab_overview_focus_timer) |timer| { + _ = glib.Source.remove(timer); + } + + // Restart our timer + priv.tab_overview_focus_timer = glib.timeoutAdd( + 500, + tabOverviewFocusTimer, + self, + ); + } + + fn tabOverviewFocusTimer( + ud: ?*anyopaque, + ) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + + // Always note our timer is removed + self.private().tab_overview_focus_timer = null; + + // Get our currently active surface which should respect the newly + // selected tab. Grab focus. + const surface = self.getActiveSurface() orelse return 0; + surface.grabFocus(); + + // Remove the timer + return 0; + } + fn windowCloseRequest( _: *gtk.Window, self: *Self, @@ -1105,6 +1157,7 @@ pub const Window = extern struct { // Template Callbacks class.bindTemplateCallback("new_tab", &btnNewTab); class.bindTemplateCallback("overview_create_tab", &tabOverviewCreateTab); + class.bindTemplateCallback("overview_notify_open", &tabOverviewOpen); class.bindTemplateCallback("close_request", &windowCloseRequest); class.bindTemplateCallback("close_page", &tabViewClosePage); class.bindTemplateCallback("page_attached", &tabViewPageAttached); diff --git a/src/apprt/gtk-ng/ui/1.5/window.blp b/src/apprt/gtk-ng/ui/1.5/window.blp index c3ec1ca29..26f303460 100644 --- a/src/apprt/gtk-ng/ui/1.5/window.blp +++ b/src/apprt/gtk-ng/ui/1.5/window.blp @@ -19,6 +19,7 @@ template $GhosttyWindow: Adw.ApplicationWindow { content: Adw.TabOverview tab_overview { create-tab => $overview_create_tab(); + notify::open => $overview_notify_open(); enable-new-tab: true; view: tab_view;