gtk: use adwaita tab view when possible

add tab bar view
This commit is contained in:
Paul
2024-06-08 13:23:38 +02:00
committed by Paul Berg
parent 12bf107bcb
commit 0e22695ec4
3 changed files with 242 additions and 118 deletions

View File

@ -894,7 +894,7 @@ fn updateTitleLabels(self: *Surface) void {
// If we have a tab and are the focused child, then we have to update the tab
if (self.container.tab()) |tab| {
if (tab.focus_child == self) c.gtk_label_set_text(tab.label_text, title.ptr);
if (tab.focus_child == self) tab.setLabelText(title); // c.gtk_label_set_text(tab.label_text, title.ptr);
}
// If we have a window and are focused, then we have to update the window title.

View File

@ -55,37 +55,23 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
.focus_child = undefined,
};
// Build the tab label
const label_box_widget = c.gtk_box_new(c.GTK_ORIENTATION_HORIZONTAL, 0);
const label_box = @as(*c.GtkBox, @ptrCast(label_box_widget));
const label_text_widget = c.gtk_label_new("Ghostty");
const label_text: *c.GtkLabel = @ptrCast(label_text_widget);
c.gtk_box_append(label_box, label_text_widget);
self.label_text = label_text;
// // Wide style GTK tabs
// if (window.app.config.@"gtk-wide-tabs") {
// c.gtk_widget_set_hexpand(label_box_widget, 1);
// c.gtk_widget_set_halign(label_box_widget, c.GTK_ALIGN_FILL);
// c.gtk_widget_set_hexpand(label_text_widget, 1);
// c.gtk_widget_set_halign(label_text_widget, c.GTK_ALIGN_FILL);
// Build the close button for the tab
const label_close_widget = c.gtk_button_new_from_icon_name("window-close-symbolic");
const label_close: *c.GtkButton = @ptrCast(label_close_widget);
c.gtk_button_set_has_frame(label_close, 0);
c.gtk_box_append(label_box, label_close_widget);
// // This ensures that tabs are always equal width. If they're too
// // long, they'll be truncated with an ellipsis.
// c.gtk_label_set_max_width_chars(label_text, 1);
// c.gtk_label_set_ellipsize(label_text, c.PANGO_ELLIPSIZE_END);
// Wide style GTK tabs
if (window.app.config.@"gtk-wide-tabs") {
c.gtk_widget_set_hexpand(label_box_widget, 1);
c.gtk_widget_set_halign(label_box_widget, c.GTK_ALIGN_FILL);
c.gtk_widget_set_hexpand(label_text_widget, 1);
c.gtk_widget_set_halign(label_text_widget, c.GTK_ALIGN_FILL);
// This ensures that tabs are always equal width. If they're too
// long, they'll be truncated with an ellipsis.
c.gtk_label_set_max_width_chars(label_text, 1);
c.gtk_label_set_ellipsize(label_text, c.PANGO_ELLIPSIZE_END);
// We need to set a minimum width so that at a certain point
// the notebook will have an arrow button rather than shrinking tabs
// to an unreadably small size.
c.gtk_widget_set_size_request(label_text_widget, 100, 1);
}
// // We need to set a minimum width so that at a certain point
// // the notebook will have an arrow button rather than shrinking tabs
// // to an unreadably small size.
// c.gtk_widget_set_size_request(label_text_widget, 100, 1);
// }
// Create a Box in which we'll later keep either Surface or Split.
// Using a box makes it easier to maintain the tab contents because
@ -106,47 +92,51 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
// Add Surface to the Tab
c.gtk_box_append(self.box, surface.primaryWidget());
// Add the notebook page (create tab).
const parent_page_idx = switch (window.app.config.@"window-new-tab-position") {
.current => c.gtk_notebook_get_current_page(window.notebook) + 1,
.end => c.gtk_notebook_get_n_pages(window.notebook),
};
try window.notebook.addTab(box_widget, "Ghostty");
const page_idx = c.gtk_notebook_insert_page(
window.notebook,
box_widget,
label_box_widget,
parent_page_idx,
);
if (page_idx < 0) {
log.warn("failed to add page to notebook", .{});
return error.GtkAppendPageFailed;
}
// const notebook: *c.GtkNotebook = window.notebook.as_notebook();
// Tab settings
c.gtk_notebook_set_tab_reorderable(window.notebook, box_widget, 1);
c.gtk_notebook_set_tab_detachable(window.notebook, box_widget, 1);
// // Add the notebook page (create tab).
// const parent_page_idx = switch (window.app.config.@"window-new-tab-position") {
// .current => c.gtk_notebook_get_current_page(notebook) + 1,
// .end => c.gtk_notebook_get_n_pages(notebook),
// };
// If we have multiple tabs, show the tab bar.
if (c.gtk_notebook_get_n_pages(window.notebook) > 1) {
c.gtk_notebook_set_show_tabs(window.notebook, 1);
}
// const page_idx = c.gtk_notebook_insert_page(
// notebook,
// box_widget,
// label_box_widget,
// parent_page_idx,
// );
// if (page_idx < 0) {
// log.warn("failed to add page to notebook", .{});
// return error.GtkAppendPageFailed;
// }
// // Tab settings
// c.gtk_notebook_set_tab_reorderable(notebook, box_widget, 1);
// c.gtk_notebook_set_tab_detachable(notebook, box_widget, 1);
// // If we have multiple tabs, show the tab bar.
// if (c.gtk_notebook_get_n_pages(notebook) > 1) {
// c.gtk_notebook_set_show_tabs(notebook, 1);
// }
// Set the userdata of the box to point to this tab.
c.g_object_set_data(@ptrCast(box_widget), GHOSTTY_TAB, self);
// Clicks
const gesture_tab_click = c.gtk_gesture_click_new();
c.gtk_gesture_single_set_button(@ptrCast(gesture_tab_click), 0);
c.gtk_widget_add_controller(label_box_widget, @ptrCast(gesture_tab_click));
// // Clicks
// const gesture_tab_click = c.gtk_gesture_click_new();
// c.gtk_gesture_single_set_button(@ptrCast(gesture_tab_click), 0);
// c.gtk_widget_add_controller(label_box_widget, @ptrCast(gesture_tab_click));
// Attach all events
_ = c.g_signal_connect_data(label_close, "clicked", c.G_CALLBACK(&gtkTabCloseClick), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(box_widget, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(gesture_tab_click, "pressed", c.G_CALLBACK(&gtkTabClick), self, null, c.G_CONNECT_DEFAULT);
// // Attach all events
// _ = c.g_signal_connect_data(label_close, "clicked", c.G_CALLBACK(&gtkTabCloseClick), self, null, c.G_CONNECT_DEFAULT);
// _ = c.g_signal_connect_data(box_widget, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
// _ = c.g_signal_connect_data(gesture_tab_click, "pressed", c.G_CALLBACK(&gtkTabClick), self, null, c.G_CONNECT_DEFAULT);
// Switch to the new tab
c.gtk_notebook_set_current_page(window.notebook, page_idx);
// // Switch to the new tab
// c.gtk_notebook_set_current_page(notebook, page_idx);
// We need to grab focus after Surface and Tab is added to the window. When
// creating a Tab we want to always focus on the widget.
@ -175,6 +165,18 @@ pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void {
self.elem = elem;
}
pub fn setLabelText(self: *Tab, title: [:0]const u8) void {
switch (self.window.notebook) {
.adw_tab_view => |tab_view| {
const page = c.adw_tab_view_get_page(tab_view, @ptrCast(self.box));
c.adw_tab_page_set_title(page, title.ptr);
},
.gtk_notebook => {
c.gtk_label_set_text(self.label_text, title.ptr);
}
}
}
/// Remove this tab from the window.
pub fn remove(self: *Tab) void {
self.window.closeTab(self);

View File

@ -8,6 +8,7 @@ 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");
@ -23,13 +24,139 @@ const c = @import("c.zig").c;
const log = std.log.scoped(.gtk);
pub const Notebook = union(enum) {
adw_tab_view: *c.AdwTabView,
gtk_notebook: *c.GtkNotebook,
pub fn create(window: *Window) @This() {
const app = window.app;
// Create a notebook to hold our tabs.
const notebook_widget = 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,
.bottom => c.GTK_POS_BOTTOM,
.left => c.GTK_POS_LEFT,
.right => c.GTK_POS_RIGHT,
};
c.gtk_notebook_set_tab_pos(notebook, notebook_tab_pos);
c.gtk_notebook_set_scrollable(notebook, 1);
c.gtk_notebook_set_show_tabs(notebook, 0);
c.gtk_notebook_set_show_border(notebook, 0);
// This enables all Ghostty terminal tabs to be exchanged across windows.
c.gtk_notebook_set_group_name(notebook, "ghostty-terminal-tabs");
// This is important so the notebook expands to fit available space.
// Otherwise, it will be zero/zero in the box below.
c.gtk_widget_set_vexpand(notebook_widget, 1);
c.gtk_widget_set_hexpand(notebook_widget, 1);
// If we are in fullscreen mode, new windows start fullscreen.
if (app.config.fullscreen) c.gtk_window_fullscreen(window.window);
// All of our events
_ = c.g_signal_connect_data(window, "close-request", c.G_CALLBACK(&gtkCloseRequest), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(&gtkDestroy), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "page-added", c.G_CALLBACK(&gtkPageAdded), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "page-removed", c.G_CALLBACK(&gtkPageRemoved), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "switch-page", c.G_CALLBACK(&gtkSwitchPage), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "create-window", c.G_CALLBACK(&gtkNotebookCreateWindow), window, null, c.G_CONNECT_DEFAULT);
return .{ .gtk_notebook = notebook };
}
pub fn as_widget(self: Notebook) *c.GtkWidget {
return switch (self) {
.adw_tab_view => |ptr| @ptrCast(@alignCast(ptr)),
.gtk_notebook => |ptr| @ptrCast(@alignCast(ptr)),
};
}
pub fn as_notebook(self: Notebook) *c.GtkNotebook {
return switch (self) {
.adw_tab_view => @panic("adw tab view"),
.gtk_notebook => |notebook| notebook,
};
}
pub fn nPages(self: Notebook) c_int {
return switch (self) {
.adw_tab_view => |tab_view| c.adw_tab_view_get_n_pages(tab_view),
.gtk_notebook => |notebook| c.gtk_notebook_get_n_pages(notebook),
};
}
pub fn currentPage(self: Notebook) c_int {
switch (self) {
.adw_tab_view => |tab_view| {
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 {
const child = switch (self) {
.adw_tab_view => |tab_view| child: {
const page = c.adw_tab_view_get_selected_page(tab_view);
const child = c.adw_tab_page_get_child(page);
break :child child;
},
.gtk_notebook => |notebook| child: {
const page = self.currentPage();
break :child c.gtk_notebook_get_nth_page(notebook, page);
},
};
return @ptrCast(@alignCast(
c.g_object_get_data(@ptrCast(child), Tab.GHOSTTY_TAB) orelse return null,
));
}
pub fn addTab(self: Notebook, tab_widget: *c.GtkWidget, title: [:0]const u8) !void {
switch (self) {
.adw_tab_view => |tab_view| {
const page = c.adw_tab_view_append(tab_view, tab_widget);
c.adw_tab_page_set_title(page, title);
},
.gtk_notebook => |notebook| {
// Build the tab label
const label_box_widget = c.gtk_box_new(c.GTK_ORIENTATION_HORIZONTAL, 0);
const label_box = @as(*c.GtkBox, @ptrCast(label_box_widget));
const label_text_widget = c.gtk_label_new("Ghostty");
// const label_text: *c.GtkLabel = @ptrCast(label_text_widget);
c.gtk_box_append(label_box, label_text_widget);
// self.label_text = label_text;
// Build the close button for the tab
const label_close_widget = c.gtk_button_new_from_icon_name("window-close-symbolic");
const label_close: *c.GtkButton = @ptrCast(label_close_widget);
c.gtk_button_set_has_frame(label_close, 0);
c.gtk_box_append(label_box, label_close_widget);
const parent_page_idx = self.nPages();
const page_idx = c.gtk_notebook_insert_page(
notebook,
tab_widget,
label_box_widget,
parent_page_idx,
);
_ = page_idx;
}
}
}
};
app: *App,
/// Our window
window: *c.GtkWindow,
/// The notebook (tab grouping) for this window.
notebook: *c.GtkNotebook,
/// can be either c.GtkNotebook or c.AdwTabView.
notebook: Notebook,
context_menu: *c.GtkWidget,
@ -107,29 +234,6 @@ pub fn init(self: *Window, app: *App) !void {
c.gtk_window_set_decorated(gtk_window, 0);
}
// Create a notebook to hold our tabs.
const notebook_widget = c.gtk_notebook_new();
const notebook: *c.GtkNotebook = @ptrCast(notebook_widget);
self.notebook = notebook;
const notebook_tab_pos: c_uint = switch (app.config.@"gtk-tabs-location") {
.top => c.GTK_POS_TOP,
.bottom => c.GTK_POS_BOTTOM,
.left => c.GTK_POS_LEFT,
.right => c.GTK_POS_RIGHT,
};
c.gtk_notebook_set_tab_pos(notebook, notebook_tab_pos);
c.gtk_notebook_set_scrollable(notebook, 1);
c.gtk_notebook_set_show_tabs(notebook, 0);
c.gtk_notebook_set_show_border(notebook, 0);
// This enables all Ghostty terminal tabs to be exchanged across windows.
c.gtk_notebook_set_group_name(notebook, "ghostty-terminal-tabs");
// This is important so the notebook expands to fit available space.
// Otherwise, it will be zero/zero in the box below.
c.gtk_widget_set_vexpand(notebook_widget, 1);
c.gtk_widget_set_hexpand(notebook_widget, 1);
// Create our box which will hold our widgets.
const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0);
@ -141,7 +245,19 @@ pub fn init(self: *Window, app: *App) !void {
c.gtk_widget_set_margin_bottom(warning, 10);
c.gtk_box_append(@ptrCast(box), warning);
}
c.gtk_box_append(@ptrCast(box), notebook_widget);
const adwaita = build_options.libadwaita and app.config.@"gtk-adwaita";
if (adwaita) {
log.warn("using adwaita", .{});
const tab_view = c.adw_tab_view_new();
const tab_bar = c.adw_tab_bar_new();
c.gtk_box_append(@ptrCast(box), @ptrCast(@alignCast(tab_bar)));
c.adw_tab_bar_set_view(tab_bar, tab_view.?);
self.notebook = .{ .adw_tab_view = tab_view.? };
} else {
self.notebook = Notebook.create(self);
}
c.gtk_box_append(@ptrCast(box), self.notebook.as_widget());
self.context_menu = c.gtk_popover_menu_new_from_model(@ptrCast(@alignCast(self.app.context_menu)));
c.gtk_widget_set_parent(self.context_menu, window);
@ -155,10 +271,6 @@ pub fn init(self: *Window, app: *App) !void {
_ = c.g_signal_connect_data(self.context_menu, "closed", c.G_CALLBACK(&gtkRefocusTerm), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(window, "close-request", c.G_CALLBACK(&gtkCloseRequest), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "page-added", c.G_CALLBACK(&gtkPageAdded), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "page-removed", c.G_CALLBACK(&gtkPageRemoved), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "switch-page", c.G_CALLBACK(&gtkSwitchPage), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(notebook, "create-window", c.G_CALLBACK(&gtkNotebookCreateWindow), self, null, c.G_CONNECT_DEFAULT);
// Our actions for the menu
initActions(self);
@ -219,22 +331,23 @@ pub fn newTab(self: *Window, parent: ?*CoreSurface) !void {
/// Close the tab for the given notebook page. This will automatically
/// handle closing the window if there are no more tabs.
pub fn closeTab(self: *Window, tab: *Tab) void {
const page = c.gtk_notebook_get_page(self.notebook, @ptrCast(tab.box)) orelse return;
const notebook: *c.GtkNotebook = self.notebook.as_notebook();
const page = c.gtk_notebook_get_page(notebook, @ptrCast(tab.box)) orelse return;
// Find page and tab which we're closing
const page_idx = getNotebookPageIndex(page);
// Remove the page. This will destroy the GTK widgets in the page which
// will trigger Tab cleanup.
c.gtk_notebook_remove_page(self.notebook, page_idx);
c.gtk_notebook_remove_page(notebook, page_idx);
const remaining = c.gtk_notebook_get_n_pages(self.notebook);
const remaining = c.gtk_notebook_get_n_pages(notebook);
switch (remaining) {
// If we have no more tabs we close the window
0 => c.gtk_window_destroy(self.window),
// If we have one more tab we hide the tab bar
1 => c.gtk_notebook_set_show_tabs(self.notebook, 0),
1 => c.gtk_notebook_set_show_tabs(notebook, 0),
else => {},
}
@ -245,7 +358,7 @@ pub fn closeTab(self: *Window, tab: *Tab) void {
/// Returns true if this window has any tabs.
pub fn hasTabs(self: *const Window) bool {
return c.gtk_notebook_get_n_pages(self.notebook) > 1;
return c.gtk_notebook_get_n_pages(self.notebook.as_notebook()) > 0;
}
/// Go to the previous tab for a surface.
@ -255,19 +368,20 @@ pub fn gotoPreviousTab(self: *Window, surface: *Surface) void {
return;
};
const page = c.gtk_notebook_get_page(self.notebook, @ptrCast(tab.box)) orelse return;
const notebook: *c.GtkNotebook = self.notebook.as_notebook();
const page = c.gtk_notebook_get_page(notebook, @ptrCast(tab.box)) orelse return;
const page_idx = getNotebookPageIndex(page);
// The next index is the previous or we wrap around.
const next_idx = if (page_idx > 0) page_idx - 1 else next_idx: {
const max = c.gtk_notebook_get_n_pages(self.notebook);
const max = c.gtk_notebook_get_n_pages(notebook);
break :next_idx max -| 1;
};
// Do nothing if we have one tab
if (next_idx == page_idx) return;
c.gtk_notebook_set_current_page(self.notebook, next_idx);
c.gtk_notebook_set_current_page(notebook, next_idx);
self.focusCurrentTab();
}
@ -278,13 +392,14 @@ pub fn gotoNextTab(self: *Window, surface: *Surface) void {
return;
};
const page = c.gtk_notebook_get_page(self.notebook, @ptrCast(tab.box)) orelse return;
const notebook: *c.GtkNotebook = self.notebook.as_notebook();
const page = c.gtk_notebook_get_page(notebook, @ptrCast(tab.box)) orelse return;
const page_idx = getNotebookPageIndex(page);
const max = c.gtk_notebook_get_n_pages(self.notebook) -| 1;
const max = c.gtk_notebook_get_n_pages(notebook) -| 1;
const next_idx = if (page_idx < max) page_idx + 1 else 0;
if (next_idx == page_idx) return;
c.gtk_notebook_set_current_page(self.notebook, next_idx);
c.gtk_notebook_set_current_page(notebook, next_idx);
self.focusCurrentTab();
}
@ -298,10 +413,11 @@ pub fn gotoLastTab(self: *Window) void {
/// Go to the specific tab index.
pub fn gotoTab(self: *Window, n: usize) void {
if (n == 0) return;
const max = c.gtk_notebook_get_n_pages(self.notebook);
const notebook: *c.GtkNotebook = self.notebook.as_notebook();
const max = c.gtk_notebook_get_n_pages(notebook);
const page_idx = std.math.cast(c_int, n - 1) orelse return;
if (page_idx < max) {
c.gtk_notebook_set_current_page(self.notebook, page_idx);
c.gtk_notebook_set_current_page(notebook, page_idx);
self.focusCurrentTab();
}
}
@ -327,11 +443,13 @@ pub fn toggleWindowDecorations(self: *Window) void {
/// Grabs focus on the currently selected tab.
fn focusCurrentTab(self: *Window) void {
const page_idx = c.gtk_notebook_get_current_page(self.notebook);
const page = c.gtk_notebook_get_nth_page(self.notebook, page_idx);
const tab: *Tab = @ptrCast(@alignCast(
c.g_object_get_data(@ptrCast(page), Tab.GHOSTTY_TAB) orelse return,
));
// const notebook: *c.GtkNotebook = self.notebook.as_notebook();
// const page_idx = c.gtk_notebook_get_current_page(notebook);
// const page = c.gtk_notebook_get_nth_page(notebook, page_idx);
// const tab: *Tab = @ptrCast(@alignCast(
// c.g_object_get_data(@ptrCast(page), Tab.GHOSTTY_TAB) orelse return,
// ));
const tab = self.notebook.currentTab() orelse return;
const gl_area = @as(*c.GtkWidget, @ptrCast(tab.focus_child.gl_area));
_ = c.gtk_widget_grab_focus(gl_area);
}
@ -379,16 +497,18 @@ fn gtkPageRemoved(
) callconv(.C) void {
const self = userdataSelf(ud.?);
const notebook: *c.GtkNotebook = self.notebook.as_notebook();
// Hide the tab bar if we only have one tab after removal
const remaining = c.gtk_notebook_get_n_pages(self.notebook);
const remaining = c.gtk_notebook_get_n_pages(notebook);
if (remaining == 1) {
c.gtk_notebook_set_show_tabs(self.notebook, 0);
c.gtk_notebook_set_show_tabs(notebook, 0);
}
}
fn gtkSwitchPage(_: *c.GtkNotebook, page: *c.GtkWidget, _: usize, ud: ?*anyopaque) callconv(.C) void {
const self = userdataSelf(ud.?);
const gtk_label_box = @as(*c.GtkWidget, @ptrCast(c.gtk_notebook_get_tab_label(self.notebook, page)));
const gtk_label_box = @as(*c.GtkWidget, @ptrCast(c.gtk_notebook_get_tab_label(self.notebook.as_notebook(), page)));
const gtk_label = @as(*c.GtkLabel, @ptrCast(c.gtk_widget_get_first_child(gtk_label_box)));
const label_text = c.gtk_label_get_text(gtk_label);
c.gtk_window_set_title(self.window, label_text);
@ -417,7 +537,7 @@ fn gtkNotebookCreateWindow(
// And add it to the new window.
tab.window = window;
return window.notebook;
return window.notebook.as_notebook();
}
fn gtkRefocusTerm(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool {
@ -652,11 +772,13 @@ fn gtkActionReset(
/// Returns the surface to use for an action.
fn actionSurface(self: *Window) ?*CoreSurface {
const page_idx = c.gtk_notebook_get_current_page(self.notebook);
const page = c.gtk_notebook_get_nth_page(self.notebook, page_idx);
const tab: *Tab = @ptrCast(@alignCast(
c.g_object_get_data(@ptrCast(page), Tab.GHOSTTY_TAB) orelse return null,
));
const tab = self.notebook.currentTab() orelse return null;
// const notebook: *c.GtkNotebook = self.notebook.as_notebook();
// const page_idx = c.gtk_notebook_get_current_page(notebook);
// const page = c.gtk_notebook_get_nth_page(notebook, page_idx);
// const tab: *Tab = @ptrCast(@alignCast(
// c.g_object_get_data(@ptrCast(page), Tab.GHOSTTY_TAB) orelse return null,
// ));
return &tab.focus_child.core_surface;
}