diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 8c4e41111..79e8846e2 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -21,8 +21,7 @@ const Config = configpkg.Config; const CoreApp = @import("../../App.zig"); const CoreSurface = @import("../../Surface.zig"); -const build_options = @import("build_options"); - +const adwaita = @import("adwaita.zig"); const cgroup = @import("cgroup.zig"); const Surface = @import("Surface.zig"); const Window = @import("Window.zig"); @@ -143,8 +142,6 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { // Create our GTK Application which encapsulates our process. const app: *c.GtkApplication = app: { - const adwaita = build_options.libadwaita and config.@"gtk-adwaita"; - log.debug("creating GTK application id={s} single-instance={} adwaita={}", .{ app_id, single_instance, @@ -152,10 +149,14 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { }); // If not libadwaita, create a standard GTK application. - if (!adwaita) break :app @as(?*c.GtkApplication, @ptrCast(c.gtk_application_new( - app_id.ptr, - app_flags, - ))) orelse return error.GtkInitFailed; + if ((comptime adwaita.comptimeEnabled()) and + !adwaita.enabled(&config)) + { + break :app @as(?*c.GtkApplication, @ptrCast(c.gtk_application_new( + app_id.ptr, + app_flags, + ))) orelse return error.GtkInitFailed; + } // Use libadwaita if requested. Using an AdwApplication lets us use // Adwaita widgets and access things such as the color scheme. diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index ed489a98d..0d5a2b27b 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -8,7 +8,6 @@ const Window = @This(); const std = @import("std"); const builtin = @import("builtin"); const build_config = @import("../../build_config.zig"); -const build_options = @import("build_options"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const configpkg = @import("../../config.zig"); @@ -61,9 +60,10 @@ pub fn init(self: *Window, app: *App) !void { }; // Create the window - const adw_window = adwaita.enabled(&app.config) and + const adw_window = (comptime adwaita.comptimeEnabled()) and + adwaita.enabled(&app.config) and app.config.@"gtk-titlebar" and - comptime adwaita.versionAtLeast(1, 4, 0) and + (comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.versionAtLeast(1, 4, 0); const window: *c.GtkWidget = if (adw_window) c.adw_application_window_new(app.app) @@ -127,8 +127,9 @@ pub fn init(self: *Window, app: *App) !void { // This is a really common issue where people build from source in debug and performance is really bad. if (comptime std.debug.runtime_safety) { const warning_text = "⚠️ You're running a debug build of Ghostty! Performance will be degraded."; - if (adwaita.enabled(&app.config) and - comptime adwaita.versionAtLeast(1, 3, 0) and + if ((comptime adwaita.comptimeEnabled()) and + (comptime adwaita.versionAtLeast(1, 3, 0)) and + adwaita.enabled(&app.config) and adwaita.versionAtLeast(1, 3, 0)) { const banner = c.adw_banner_new(warning_text); @@ -160,11 +161,12 @@ pub fn init(self: *Window, app: *App) !void { // Our actions for the menu initActions(self); - if (adwaita.enabled(&app.config) and + if ((comptime adwaita.comptimeEnabled()) and + (comptime adwaita.versionAtLeast(1, 4, 0)) and + adwaita.enabled(&app.config) and + adwaita.versionAtLeast(1, 4, 0) and app.config.@"gtk-titlebar" and - header != null and - comptime adwaita.versionAtLeast(1, 4, 0) and - adwaita.versionAtLeast(1, 4, 0)) + header != null) { const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_toolbar_view_new()); diff --git a/src/apprt/gtk/adwaita.zig b/src/apprt/gtk/adwaita.zig index c1efd34e3..db509db3a 100644 --- a/src/apprt/gtk/adwaita.zig +++ b/src/apprt/gtk/adwaita.zig @@ -3,6 +3,11 @@ const c = @import("c.zig").c; const build_options = @import("build_options"); const Config = @import("../../config.zig").Config; +/// Returns true if Ghostty is configured to build with libadwaita. +pub fn comptimeEnabled() bool { + return build_options.libadwaita; +} + /// Returns true if Ghostty is configured to build with libadwaita and /// the configuration has enabled adwaita. pub fn enabled(config: *const Config) bool { diff --git a/src/apprt/gtk/notebook.zig b/src/apprt/gtk/notebook.zig index 5b79e3b20..a3d672f24 100644 --- a/src/apprt/gtk/notebook.zig +++ b/src/apprt/gtk/notebook.zig @@ -1,50 +1,32 @@ const std = @import("std"); +const assert = std.debug.assert; const c = @import("c.zig").c; -const build_options = @import("build_options"); -const Window = @import("./Window.zig"); -const Tab = @import("./Tab.zig"); +const Window = @import("Window.zig"); +const Tab = @import("Tab.zig"); +const adwaita = @import("adwaita.zig"); const log = std.log.scoped(.gtk); -const AdwTabView = if (build_options.libadwaita) c.AdwTabView else anyopaque; +const AdwTabView = if (adwaita.comptimeEnabled()) c.AdwTabView else anyopaque; +/// An abstraction over the GTK notebook and Adwaita tab view to manage +/// all the terminal tabs in a window. pub const Notebook = union(enum) { adw_tab_view: *AdwTabView, gtk_notebook: *c.GtkNotebook, pub fn create(window: *Window, box: *c.GtkWidget) @This() { const app = window.app; + if (adwaita.enabled(&app.config)) return initAdw(window, box); + return initGtk(window, box); + } - const adwaita = build_options.libadwaita and app.config.@"gtk-adwaita"; - - if (adwaita) { - const tab_view = c.adw_tab_view_new().?; - - c.gtk_box_append(@ptrCast(box), @ptrCast(@alignCast(tab_view))); - if (!window.app.config.@"gtk-titlebar" or c.ADW_MINOR_VERSION < 4) { - const tab_bar = c.adw_tab_bar_new(); - - switch (app.config.@"gtk-tabs-location") { - // left and right is not supported in libadwaita. - .top, .left, .right => c.gtk_box_prepend(@ptrCast(box), @ptrCast(@alignCast(tab_bar))), - .bottom => c.gtk_box_append(@ptrCast(box), @ptrCast(@alignCast(tab_bar))), - } - c.adw_tab_bar_set_view(tab_bar, tab_view); - - if (!window.app.config.@"gtk-wide-tabs") - c.adw_tab_bar_set_expand_tabs(tab_bar, 0); - } - - _ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), window, null, c.G_CONNECT_DEFAULT); - _ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), window, null, c.G_CONNECT_DEFAULT); - - return .{ .adw_tab_view = tab_view }; - } + fn initGtk(window: *Window, box: *c.GtkWidget) Notebook { + const app = window.app; // Create a notebook to hold our tabs. - const notebook_widget = c.gtk_notebook_new(); + const notebook_widget: *c.GtkWidget = c.gtk_notebook_new(); const notebook: *c.GtkNotebook = @ptrCast(notebook_widget); const notebook_tab_pos: c_uint = switch (app.config.@"gtk-tabs-location") { .top => c.GTK_POS_TOP, @@ -75,44 +57,74 @@ pub const Notebook = union(enum) { return .{ .gtk_notebook = notebook }; } - pub fn asWidget(self: Notebook) *c.GtkWidget { - return switch (self) { - .adw_tab_view => |ptr| @ptrCast(@alignCast(ptr)), - .gtk_notebook => |ptr| @ptrCast(@alignCast(ptr)), - }; + fn initAdw(window: *Window, box: *c.GtkWidget) Notebook { + const app = window.app; + assert(adwaita.enabled(&app.config)); + + const tab_view: *c.AdwTabView = c.adw_tab_view_new().?; + c.gtk_box_append(@ptrCast(box), @ptrCast(@alignCast(tab_view))); + if (comptime !adwaita.versionAtLeast(1, 4, 0) or + !adwaita.versionAtLeast(1, 4, 0) or + !app.config.@"gtk-titlebar") + { + const tab_bar: *c.AdwTabBar = c.adw_tab_bar_new().?; + switch (app.config.@"gtk-tabs-location") { + // left and right is not supported in libadwaita. + .top, + .left, + .right, + => c.gtk_box_prepend(@ptrCast(box), @ptrCast(@alignCast(tab_bar))), + + .bottom => c.gtk_box_append(@ptrCast(box), @ptrCast(@alignCast(tab_bar))), + } + c.adw_tab_bar_set_view(tab_bar, tab_view); + + if (!app.config.@"gtk-wide-tabs") { + c.adw_tab_bar_set_expand_tabs(tab_bar, 0); + } + } + + _ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), window, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), window, null, c.G_CONNECT_DEFAULT); + + return .{ .adw_tab_view = tab_view }; } pub fn nPages(self: Notebook) c_int { return switch (self) { - .adw_tab_view => |tab_view| if (build_options.libadwaita) c.adw_tab_view_get_n_pages(tab_view) else unreachable, .gtk_notebook => |notebook| c.gtk_notebook_get_n_pages(notebook), + .adw_tab_view => |tab_view| if (comptime adwaita.comptimeEnabled()) + c.adw_tab_view_get_n_pages(tab_view) + else + unreachable, }; } pub fn currentPage(self: Notebook) c_int { switch (self) { .adw_tab_view => |tab_view| { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page = c.adw_tab_view_get_selected_page(tab_view); return c.adw_tab_view_get_page_position(tab_view, page); }, + .gtk_notebook => |notebook| return c.gtk_notebook_get_current_page(notebook), } } pub fn currentTab(self: Notebook) ?*Tab { - log.info("self = {}", .{self}); const child = switch (self) { .adw_tab_view => |tab_view| child: { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page = c.adw_tab_view_get_selected_page(tab_view) orelse return null; const child = c.adw_tab_page_get_child(page); break :child child; }, + .gtk_notebook => |notebook| child: { const page = self.currentPage(); if (page == -1) return null; - log.info("currentPage_page_idx = {}", .{page}); break :child c.gtk_notebook_get_nth_page(notebook, page); }, }; @@ -124,7 +136,7 @@ pub const Notebook = union(enum) { pub fn gotoNthTab(self: Notebook, position: c_int) void { switch (self) { .adw_tab_view => |tab_view| { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page_to_select = c.adw_tab_view_get_nth_page(tab_view, position); c.adw_tab_view_set_selected_page(tab_view, page_to_select); }, @@ -135,7 +147,7 @@ pub const Notebook = union(enum) { pub fn getTabPosition(self: Notebook, tab: *Tab) ?c_int { return switch (self) { .adw_tab_view => |tab_view| page_idx: { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page = c.adw_tab_view_get_page(tab_view, @ptrCast(tab.box)) orelse return null; break :page_idx c.adw_tab_view_get_page_position(tab_view, page); }, @@ -174,7 +186,7 @@ pub const Notebook = union(enum) { pub fn setTabLabel(self: Notebook, tab: *Tab, title: [:0]const u8) void { switch (self) { .adw_tab_view => |tab_view| { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page = c.adw_tab_view_get_page(tab_view, @ptrCast(tab.box)); c.adw_tab_page_set_title(page, title.ptr); }, @@ -186,7 +198,7 @@ pub const Notebook = union(enum) { const box_widget: *c.GtkWidget = @ptrCast(tab.box); switch (self) { .adw_tab_view => |tab_view| { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page = c.adw_tab_view_append(tab_view, box_widget); c.adw_tab_page_set_title(page, title.ptr); @@ -257,7 +269,7 @@ pub const Notebook = union(enum) { const window = tab.window; switch (self) { .adw_tab_view => |tab_view| { - if (!build_options.libadwaita) unreachable; + if (comptime !adwaita.comptimeEnabled()) unreachable; const page = c.adw_tab_view_get_page(tab_view, @ptrCast(tab.box)) orelse return; c.adw_tab_view_close_page(tab_view, page);