Skip to content

Commit

Permalink
gtk(adw): refactor version checks into readable functions
Browse files Browse the repository at this point in the history
Doing `versionAtLeast(0, 0, 0)` to check for the existence of libadw
and `versionAtLeast(1, 4, 0)` wherever tab overviews are used gets old
after a while. We consolidate all of these checks (except for one
unconfirmed bugfix) into easily readable functions so that maintainers
don't have to memorize which libadw version added which functionality.
  • Loading branch information
pluiedev committed Jan 28, 2025
1 parent 9efa0c7 commit c8b3cc0
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 49 deletions.
4 changes: 1 addition & 3 deletions src/apprt/gtk/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
});

// If not libadwaita, create a standard GTK application.
if ((comptime !adwaita.versionAtLeast(0, 0, 0)) or
!adwaita.enabled(&config))
{
if ((comptime !adwaita.exists()) or !adwaita.enabled(&config)) {
{
const provider = c.gtk_css_provider_new();
defer c.g_object_unref(provider);
Expand Down
16 changes: 9 additions & 7 deletions src/apprt/gtk/Window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub fn init(self: *Window, app: *App) !void {

// Create the window
const window: *c.GtkWidget = window: {
if ((comptime adwaita.versionAtLeast(0, 0, 0)) and self.config.adw_enabled) {
if ((comptime adwaita.exists()) and self.config.adw_enabled) {
const window = c.adw_application_window_new(app.app);
c.gtk_widget_add_css_class(@ptrCast(window), "adw");
break :window window;
Expand Down Expand Up @@ -265,7 +265,7 @@ pub fn init(self: *Window, app: *App) !void {

// Setup our toast overlay if we have one
self.toast_overlay = toast: {
if ((comptime !adwaita.versionAtLeast(0, 0, 0)) or !self.config.adw_enabled)
if ((comptime !adwaita.exists()) or !self.config.adw_enabled)
break :toast null;

const toast_overlay = c.adw_toast_overlay_new();
Expand Down Expand Up @@ -340,7 +340,7 @@ pub fn init(self: *Window, app: *App) !void {
);
} else tab_bar: {
switch (self.notebook) {
.adw => |*adw| if (comptime adwaita.versionAtLeast(0, 0, 0)) {
.adw => |*adw| if (comptime adwaita.exists()) {
if (self.config.gtk_tabs_location == .hidden) break :tab_bar;

// In earlier adwaita versions, we need to add the tabbar manually since we do not use
Expand Down Expand Up @@ -370,8 +370,9 @@ pub fn init(self: *Window, app: *App) !void {
.gtk => {},
}

// The box is our main child
if ((comptime adwaita.versionAtLeast(0, 0, 0)) and !adwaita.supportsTabOverview() and self.config.adw_enabled) {
// If we're using an older version of libadwaita without support
// for tab overviews, we put the box directly as the main child
if ((comptime adwaita.exists()) and !adwaita.supportsTabOverview() and self.config.adw_enabled) {
c.adw_application_window_set_content(@ptrCast(gtk_window), box);
} else {
c.gtk_window_set_titlebar(gtk_window, self.headerbar.asWidget());
Expand Down Expand Up @@ -665,7 +666,8 @@ pub fn onConfigReloaded(self: *Window) void {
}

pub fn sendToast(self: *Window, title: [:0]const u8) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) return;
if (comptime !adwaita.exists()) return;

const toast_overlay = self.toast_overlay orelse return;
const toast = c.adw_toast_new(title);
c.adw_toast_set_timeout(toast, 3);
Expand Down Expand Up @@ -890,7 +892,7 @@ fn gtkKeyPressed(
//
// If someone can confidently show or explain that this is not
// necessary, please remove this check.
if (comptime adwaita.versionAtLeast(1, 4, 0)) {
if (comptime adwaita.supportsTabOverview()) {
if (self.tab_overview) |tab_overview_widget| {
const tab_overview: *c.AdwTabOverview = @ptrCast(@alignCast(tab_overview_widget));
if (c.adw_tab_overview_get_open(tab_overview) == 0) return 0;
Expand Down
17 changes: 15 additions & 2 deletions src/apprt/gtk/adwaita.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ const Config = @import("../../config.zig").Config;
/// Returns true if Ghostty is configured to build with libadwaita and
/// the configuration has enabled adwaita.
///
/// For a comptime version of this function, use `versionAtLeast` in
/// a comptime context with all the version numbers set to 0.
/// For a comptime version of this function, use `exists`.
///
/// This must be `inline` so that the comptime check noops conditional
/// paths that are not enabled.
Expand Down Expand Up @@ -74,17 +73,31 @@ test "versionAtLeast" {
try testing.expect(versionAtLeast(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION - 1, c.ADW_MICRO_VERSION + 1));
}

/// Whether libadwaita exists at all.
pub inline fn exists() bool {
return versionAtLeast(0, 0, 0);
}

/// Shortcuts for tab views is introduced in libadwaita 1.2.0.
/// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/NEWS?ref_type=heads#L1196
pub inline fn supportsTabViewShortcuts() bool {
return versionAtLeast(1, 2, 0);
}

/// Banners are introduced in libadwaita 1.3.0.
/// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/NEWS?ref_type=heads#L1020
pub inline fn supportsBanner() bool {
return versionAtLeast(1, 3, 0);
}

/// Tab overviews are introduced in libadwaita 1.3.0.
/// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/NEWS?ref_type=heads#L1021
pub inline fn supportsTabOverview() bool {
return versionAtLeast(1, 3, 0);
}

/// About dialogs are introduced in libadwaita 1.5.0.
/// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/NEWS?ref_type=heads#L497
pub inline fn supportsAboutDialog() bool {
return versionAtLeast(1, 5, 0);
}
2 changes: 1 addition & 1 deletion src/apprt/gtk/headerbar.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub const HeaderBar = union(enum) {

pub fn init(self: *HeaderBar) void {
const window: *Window = @fieldParentPtr("headerbar", self);
if ((comptime adwaita.versionAtLeast(1, 4, 0)) and adwaita.enabled(&window.app.config)) {
if ((comptime adwaita.exists()) and window.config.adw_enabled) {
HeaderBarAdw.init(self);
} else {
HeaderBarGtk.init(self);
Expand Down
34 changes: 17 additions & 17 deletions src/apprt/gtk/headerbar_adw.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const adwaita = @import("adwaita.zig");

const HeaderBar = @import("headerbar.zig").HeaderBar;

const AdwHeaderBar = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwHeaderBar else anyopaque;
const AdwWindowTitle = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwWindowTitle else anyopaque;
const AdwHeaderBar = if (adwaita.exists()) c.AdwHeaderBar else anyopaque;
const AdwWindowTitle = if (adwaita.exists()) c.AdwWindowTitle else anyopaque;

/// the window that this headerbar is attached to
window: *Window,
Expand All @@ -19,7 +19,7 @@ headerbar: *AdwHeaderBar,
title: *AdwWindowTitle,

pub fn init(headerbar: *HeaderBar) void {
if (!adwaita.versionAtLeast(0, 0, 0)) return;
if (!adwaita.exists()) return;

const window: *Window = @fieldParentPtr("headerbar", headerbar);
headerbar.* = .{
Expand Down Expand Up @@ -47,32 +47,32 @@ pub fn asWidget(self: HeaderBarAdw) *c.GtkWidget {
}

pub fn packEnd(self: HeaderBarAdw, widget: *c.GtkWidget) void {
if (comptime adwaita.versionAtLeast(0, 0, 0)) {
c.adw_header_bar_pack_end(
@ptrCast(@alignCast(self.headerbar)),
widget,
);
}
if (comptime !adwaita.exists()) return;

c.adw_header_bar_pack_end(
@ptrCast(@alignCast(self.headerbar)),
widget,
);
}

pub fn packStart(self: HeaderBarAdw, widget: *c.GtkWidget) void {
if (comptime adwaita.versionAtLeast(0, 0, 0)) {
c.adw_header_bar_pack_start(
@ptrCast(@alignCast(self.headerbar)),
widget,
);
}
if (comptime !adwaita.exists()) return;

c.adw_header_bar_pack_start(
@ptrCast(@alignCast(self.headerbar)),
widget,
);
}

pub fn setTitle(self: HeaderBarAdw, title: [:0]const u8) void {
c.gtk_window_set_title(self.window.window, title);
if (comptime adwaita.versionAtLeast(0, 0, 0)) {
if (comptime adwaita.exists()) {
c.adw_window_title_set_title(self.title, title);
}
}

pub fn setSubtitle(self: HeaderBarAdw, subtitle: [:0]const u8) void {
if (comptime adwaita.versionAtLeast(0, 0, 0)) {
if (comptime adwaita.exists()) {
c.adw_window_title_set_subtitle(self.title, subtitle);
}
}
2 changes: 1 addition & 1 deletion src/apprt/gtk/notebook.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const adwaita = @import("adwaita.zig");

const log = std.log.scoped(.gtk);

const AdwTabView = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabView else anyopaque;
const AdwTabView = if (adwaita.exists()) c.AdwTabView else anyopaque;

/// An abstraction over the GTK notebook and Adwaita tab view to manage
/// all the terminal tabs in a window.
Expand Down
39 changes: 23 additions & 16 deletions src/apprt/gtk/notebook_adw.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const adwaita = @import("adwaita.zig");

const log = std.log.scoped(.gtk);

const AdwTabView = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabView else anyopaque;
const AdwTabPage = if (adwaita.versionAtLeast(0, 0, 0)) c.AdwTabPage else anyopaque;
const AdwTabView = if (adwaita.exists()) c.AdwTabView else anyopaque;
const AdwTabPage = if (adwaita.exists()) c.AdwTabPage else anyopaque;

pub const NotebookAdw = struct {
/// the tab view
Expand All @@ -33,7 +33,7 @@ pub const NotebookAdw = struct {
const tab_view: *c.AdwTabView = c.adw_tab_view_new().?;
c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_view)), "notebook");

if (comptime adwaita.versionAtLeast(1, 2, 0) and adwaita.versionAtLeast(1, 2, 0)) {
if ((comptime adwaita.supportsTabViewShortcuts()) and adwaita.supportsTabViewShortcuts()) {
// Adwaita enables all of the shortcuts by default.
// We want to manage keybindings ourselves.
c.adw_tab_view_remove_shortcuts(tab_view, c.ADW_TAB_VIEW_SHORTCUT_ALL_SHORTCUTS);
Expand All @@ -56,23 +56,24 @@ pub const NotebookAdw = struct {
}

pub fn nPages(self: *NotebookAdw) c_int {
if (comptime adwaita.versionAtLeast(0, 0, 0))
return c.adw_tab_view_get_n_pages(self.tab_view)
else
unreachable;
if (comptime !adwaita.exists()) unreachable;

return c.adw_tab_view_get_n_pages(self.tab_view);
}

/// Returns the index of the currently selected page.
/// Returns null if the notebook has no pages.
pub fn currentPage(self: *NotebookAdw) ?c_int {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return null;
return c.adw_tab_view_get_page_position(self.tab_view, page);
}

/// Returns the currently selected tab or null if there are none.
pub fn currentTab(self: *NotebookAdw) ?*Tab {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page = c.adw_tab_view_get_selected_page(self.tab_view) orelse return null;
const child = c.adw_tab_page_get_child(page);
return @ptrCast(@alignCast(
Expand All @@ -81,45 +82,51 @@ pub const NotebookAdw = struct {
}

pub fn gotoNthTab(self: *NotebookAdw, position: c_int) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page_to_select = c.adw_tab_view_get_nth_page(self.tab_view, position);
c.adw_tab_view_set_selected_page(self.tab_view, page_to_select);
}

pub fn getTabPosition(self: *NotebookAdw, tab: *Tab) ?c_int {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return null;
return c.adw_tab_view_get_page_position(self.tab_view, page);
}

pub fn reorderPage(self: *NotebookAdw, tab: *Tab, position: c_int) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box));
_ = c.adw_tab_view_reorder_page(self.tab_view, page, position);
}

pub fn setTabLabel(self: *NotebookAdw, tab: *Tab, title: [:0]const u8) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box));
c.adw_tab_page_set_title(page, title.ptr);
}

pub fn setTabTooltip(self: *NotebookAdw, tab: *Tab, tooltip: [:0]const u8) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box));
c.adw_tab_page_set_tooltip(page, tooltip.ptr);
}

pub fn addTab(self: *NotebookAdw, tab: *Tab, position: c_int, title: [:0]const u8) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

const box_widget: *c.GtkWidget = @ptrCast(tab.box);
const page = c.adw_tab_view_insert(self.tab_view, box_widget, position);
c.adw_tab_page_set_title(page, title.ptr);
c.adw_tab_view_set_selected_page(self.tab_view, page);
}

pub fn closeTab(self: *NotebookAdw, tab: *Tab) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
if (comptime !adwaita.exists()) unreachable;

// closeTab always expects to close unconditionally so we mark this
// as true so that the close_page call below doesn't request
Expand Down
3 changes: 1 addition & 2 deletions src/apprt/gtk/winproto/x11.zig
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ pub const Window = struct {
) == 0) return error.NotX11Surface;

const blur_region: Region = blur: {
if ((comptime !adwaita.versionAtLeast(0, 0, 0)) or
gtk_window.config.adw_enabled) break :blur .{};
if ((comptime !adwaita.exists()) or gtk_window.config.adw_enabled) break :blur .{};

// NOTE(pluiedev): CSDs are a f--king mistake.
// Please, GNOME, stop this nonsense of making a window ~30% bigger
Expand Down

0 comments on commit c8b3cc0

Please sign in to comment.