Skip to content

Commit acbca59

Browse files
committed
gtk: add option to always display the tab bar
Also fixes crashes in both vanilla GTK and Adwaita implementations of `closeTab`, which erroneously close windows twice when there are no more tabs left (we probably already handle it somewhere else).
1 parent 8eacde9 commit acbca59

File tree

2 files changed

+61
-27
lines changed

2 files changed

+61
-27
lines changed

src/apprt/gtk/Window.zig

+33-26
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const builtin = @import("builtin");
1010
const Allocator = std.mem.Allocator;
1111
const assert = std.debug.assert;
1212

13+
const adw = @import("adw");
1314
const gio = @import("gio");
1415
const glib = @import("glib");
1516
const gobject = @import("gobject");
@@ -52,6 +53,9 @@ window: *c.GtkWindow,
5253
/// The header bar for the window.
5354
headerbar: HeaderBar,
5455

56+
/// The tab bar for the window.
57+
tab_bar: *adw.TabBar,
58+
5559
/// The tab overview for the window. This is possibly null since there is no
5660
/// taboverview without a AdwApplicationWindow (libadwaita >= 1.4.0).
5761
tab_overview: ?*c.GtkWidget,
@@ -80,6 +84,7 @@ pub const DerivedConfig = struct {
8084
gtk_tabs_location: configpkg.Config.GtkTabsLocation,
8185
gtk_wide_tabs: bool,
8286
gtk_toolbar_style: configpkg.Config.GtkToolbarStyle,
87+
window_show_tab_bar: configpkg.Config.WindowShowTabBar,
8388

8489
quick_terminal_position: configpkg.Config.QuickTerminalPosition,
8590
quick_terminal_size: configpkg.Config.QuickTerminalSize,
@@ -99,6 +104,7 @@ pub const DerivedConfig = struct {
99104
.gtk_tabs_location = config.@"gtk-tabs-location",
100105
.gtk_wide_tabs = config.@"gtk-wide-tabs",
101106
.gtk_toolbar_style = config.@"gtk-toolbar-style",
107+
.window_show_tab_bar = config.@"window-show-tab-bar",
102108

103109
.quick_terminal_position = config.@"quick-terminal-position",
104110
.quick_terminal_size = config.@"quick-terminal-size",
@@ -133,6 +139,7 @@ pub fn init(self: *Window, app: *App) !void {
133139
.config = DerivedConfig.init(&app.config),
134140
.window = undefined,
135141
.headerbar = undefined,
142+
.tab_bar = undefined,
136143
.tab_overview = null,
137144
.notebook = undefined,
138145
.titlebar_menu = undefined,
@@ -215,8 +222,10 @@ pub fn init(self: *Window, app: *App) !void {
215222
// If we're using an AdwWindow then we can support the tab overview.
216223
if (self.tab_overview) |tab_overview| {
217224
if (!adwaita.versionAtLeast(1, 4, 0)) unreachable;
218-
const btn = switch (self.config.gtk_tabs_location) {
219-
.top, .bottom => btn: {
225+
226+
// TODO: Move this to syncAppearance and make it reactive
227+
const btn = switch (self.config.window_show_tab_bar) {
228+
.always, .auto => btn: {
220229
const btn = c.gtk_toggle_button_new();
221230
c.gtk_widget_set_tooltip_text(btn, i18n._("View Open Tabs"));
222231
c.gtk_button_set_icon_name(@ptrCast(btn), "view-grid-symbolic");
@@ -230,8 +239,7 @@ pub fn init(self: *Window, app: *App) !void {
230239

231240
break :btn btn;
232241
},
233-
234-
.hidden => btn: {
242+
.never => btn: {
235243
const btn = c.adw_tab_button_new();
236244
c.adw_tab_button_set_view(@ptrCast(btn), @ptrCast(@alignCast(self.notebook.tab_view)));
237245
c.gtk_actionable_set_action_name(@ptrCast(btn), "overview.open");
@@ -310,23 +318,17 @@ pub fn init(self: *Window, app: *App) !void {
310318
// Our actions for the menu
311319
initActions(self);
312320

321+
self.tab_bar = adw.TabBar.new();
322+
self.tab_bar.setView(self.notebook.tab_view);
323+
313324
if (adwaita.versionAtLeast(1, 4, 0)) {
314325
const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_toolbar_view_new());
315326

316327
c.adw_toolbar_view_add_top_bar(toolbar_view, self.headerbar.asWidget());
317328

318-
if (self.config.gtk_tabs_location != .hidden) {
319-
const tab_bar = c.adw_tab_bar_new();
320-
c.adw_tab_bar_set_view(tab_bar, @ptrCast(@alignCast(self.notebook.tab_view)));
321-
322-
if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0);
323-
324-
const tab_bar_widget: *c.GtkWidget = @ptrCast(@alignCast(tab_bar));
325-
switch (self.config.gtk_tabs_location) {
326-
.top => c.adw_toolbar_view_add_top_bar(toolbar_view, tab_bar_widget),
327-
.bottom => c.adw_toolbar_view_add_bottom_bar(toolbar_view, tab_bar_widget),
328-
.hidden => unreachable,
329-
}
329+
switch (self.config.gtk_tabs_location) {
330+
.top => c.adw_toolbar_view_add_top_bar(toolbar_view, @ptrCast(@alignCast(self.tab_bar))),
331+
.bottom => c.adw_toolbar_view_add_bottom_bar(toolbar_view, @ptrCast(@alignCast(self.tab_bar))),
330332
}
331333
c.adw_toolbar_view_set_content(toolbar_view, box);
332334

@@ -347,27 +349,22 @@ pub fn init(self: *Window, app: *App) !void {
347349
@ptrCast(gtk_widget),
348350
@ptrCast(@alignCast(self.tab_overview)),
349351
);
350-
} else tab_bar: {
351-
if (self.config.gtk_tabs_location == .hidden) break :tab_bar;
352+
} else {
352353
// In earlier adwaita versions, we need to add the tabbar manually since we do not use
353354
// an AdwToolbarView.
354-
const tab_bar: *c.AdwTabBar = c.adw_tab_bar_new().?;
355-
c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_bar)), "inline");
355+
self.tab_bar.as(gtk.Widget).addCssClass("inline");
356+
356357
switch (self.config.gtk_tabs_location) {
357358
.top => c.gtk_box_insert_child_after(
358359
@ptrCast(box),
359-
@ptrCast(@alignCast(tab_bar)),
360+
@ptrCast(@alignCast(self.tab_bar)),
360361
@ptrCast(@alignCast(self.headerbar.asWidget())),
361362
),
362363
.bottom => c.gtk_box_append(
363364
@ptrCast(box),
364-
@ptrCast(@alignCast(tab_bar)),
365+
@ptrCast(@alignCast(self.tab_bar)),
365366
),
366-
.hidden => unreachable,
367367
}
368-
c.adw_tab_bar_set_view(tab_bar, @ptrCast(@alignCast(self.notebook.tab_view)));
369-
370-
if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0);
371368
}
372369

373370
// If we want the window to be maximized, we do that here.
@@ -480,6 +477,16 @@ pub fn syncAppearance(self: *Window) !void {
480477
c.adw_toolbar_view_set_bottom_bar_style(toolbar_view, toolbar_style);
481478
}
482479

480+
self.tab_bar.setExpandTabs(@intFromBool(self.config.gtk_wide_tabs));
481+
self.tab_bar.setAutohide(switch (self.config.window_show_tab_bar) {
482+
.auto, .never => @intFromBool(true),
483+
.always => @intFromBool(false),
484+
});
485+
self.tab_bar.as(gtk.Widget).setVisible(switch (self.config.window_show_tab_bar) {
486+
.always, .auto => @intFromBool(true),
487+
.never => @intFromBool(false),
488+
});
489+
483490
self.winproto.syncAppearance() catch |err| {
484491
log.warn("failed to sync winproto appearance error={}", .{err});
485492
};

src/config/Config.zig

+28-1
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,27 @@ keybind: Keybinds = .{},
13681368
/// * `end` - Insert the new tab at the end of the tab list.
13691369
@"window-new-tab-position": WindowNewTabPosition = .current,
13701370

1371+
/// Whether to show the tab bar.
1372+
///
1373+
/// Valid values:
1374+
///
1375+
/// - `always`
1376+
///
1377+
/// Always display the tab bar, even when there's only one tab.
1378+
///
1379+
/// - `auto` *(default)*
1380+
///
1381+
/// Automatically show and hide the tab bar. The tab bar is only
1382+
/// shown when there are two or more tabs present.
1383+
///
1384+
/// - `never`
1385+
///
1386+
/// Never show the tab bar. Tabs are only accessible via the tab
1387+
/// overview or by keybind actions.
1388+
///
1389+
/// Currently only supported on Linux (GTK).
1390+
@"window-show-tab-bar": WindowShowTabBar = .auto,
1391+
13711392
/// Background color for the window titlebar. This only takes effect if
13721393
/// window-theme is set to ghostty. Currently only supported in the GTK app
13731394
/// runtime.
@@ -5662,7 +5683,6 @@ pub const GtkSingleInstance = enum {
56625683
pub const GtkTabsLocation = enum {
56635684
top,
56645685
bottom,
5665-
hidden,
56665686
};
56675687

56685688
/// See gtk-toolbar-style
@@ -5705,6 +5725,13 @@ pub const WindowNewTabPosition = enum {
57055725
end,
57065726
};
57075727

5728+
/// See window-show-tab-bar
5729+
pub const WindowShowTabBar = enum {
5730+
always,
5731+
auto,
5732+
never,
5733+
};
5734+
57085735
/// See resize-overlay
57095736
pub const ResizeOverlay = enum {
57105737
always,

0 commit comments

Comments
 (0)