apprt/gtk: some tweaks, avoid uninitialized memory access, use optionals

This commit is contained in:
Mitchell Hashimoto
2024-09-12 16:06:14 -07:00
parent f35671417c
commit fced4370d3
3 changed files with 70 additions and 22 deletions

View File

@ -38,8 +38,9 @@ elem: Surface.Container.Elem,
focus_child: *Surface,
/// If the notebook implementation is AdwTabView, then this is the tab's
/// AdwTabPage.
tab_page: *c.GObject,
/// AdwTabPage. If we have libadwaita disabled then this will be undefined
/// memory and is unsafe to use.
adw_tab_page: *c.GObject,
pub fn create(alloc: Allocator, window: *Window, parent_: ?*CoreSurface) !*Tab {
var tab = try alloc.create(Tab);
@ -57,7 +58,7 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
.box = undefined,
.elem = undefined,
.focus_child = undefined,
.tab_page = undefined,
.adw_tab_page = undefined,
};
// Create a Box in which we'll later keep either Surface or Split.
@ -81,7 +82,9 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
// Set the userdata of the box to point to this tab.
c.g_object_set_data(@ptrCast(box_widget), GHOSTTY_TAB, self);
self.tab_page = try window.notebook.addTab(self, "Ghostty");
if (try window.notebook.addTab(self, "Ghostty")) |page| {
self.adw_tab_page = page;
}
// Attach all events
_ = c.g_signal_connect_data(box_widget, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);

View File

@ -97,15 +97,24 @@ pub fn init(self: *Window, app: *App) !void {
const display = c.gdk_display_get_default();
c.gtk_style_context_add_provider_for_display(display, @ptrCast(app.css_provider), c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
// Create our box which will hold our widgets.
// Create our box which will hold our widgets in the main content area.
const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0);
var tab_overview: *c.GtkWidget = undefined;
if (self.isAdwWindow()) {
tab_overview = c.adw_tab_overview_new();
// If we are using an AdwWindow then we can support the tab overview.
const tab_overview_: ?*c.GtkWidget = if (self.isAdwWindow()) overview: {
const tab_overview = c.adw_tab_overview_new();
c.adw_tab_overview_set_enable_new_tab(@ptrCast(tab_overview), 1);
_ = c.g_signal_connect_data(tab_overview, "create-tab", c.G_CALLBACK(&newTabFromOverview), self, null, c.G_CONNECT_DEFAULT);
}
_ = c.g_signal_connect_data(
tab_overview,
"create-tab",
c.G_CALLBACK(&gtkNewTabFromOverview),
self,
null,
c.G_CONNECT_DEFAULT,
);
break :overview tab_overview;
} else null;
// gtk-titlebar can be used to disable the header bar (but keep
// the window manager's decorations). We create this no matter if we
@ -127,14 +136,25 @@ pub fn init(self: *Window, app: *App) !void {
else
c.gtk_header_bar_pack_end(@ptrCast(header), btn);
}
if (self.isAdwWindow()) {
// If we're using an AdwWindow then we can support the tab overview.
if (tab_overview_) |tab_overview| {
assert(self.isAdwWindow());
const btn = c.gtk_toggle_button_new();
c.gtk_widget_set_tooltip_text(btn, "Show Open Tabs");
c.gtk_button_set_icon_name(@ptrCast(btn), "view-grid-symbolic");
c.gtk_widget_set_focus_on_click(btn, c.FALSE);
c.adw_header_bar_pack_end(@ptrCast(header), btn);
_ = c.g_object_bind_property(btn, "active", tab_overview, "open", c.G_BINDING_BIDIRECTIONAL | c.G_BINDING_SYNC_CREATE);
_ = c.g_object_bind_property(
btn,
"active",
tab_overview,
"open",
c.G_BINDING_BIDIRECTIONAL | c.G_BINDING_SYNC_CREATE,
);
}
{
const btn = c.gtk_button_new_from_icon_name("tab-new-symbolic");
c.gtk_widget_set_tooltip_text(btn, "New Tab");
@ -176,8 +196,15 @@ pub fn init(self: *Window, app: *App) !void {
c.gtk_box_append(@ptrCast(box), warning_box);
}
// Setup our notebook
self.notebook = Notebook.create(self, box);
// If we have a tab overview then we can set it on our notebook.
if (tab_overview_) |tab_overview| {
assert(self.notebook == .adw_tab_view);
c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.adw_tab_view);
}
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);
c.gtk_popover_set_has_arrow(@ptrCast(@alignCast(self.context_menu)), c.False);
@ -195,7 +222,6 @@ pub fn init(self: *Window, app: *App) !void {
initActions(self);
if (self.hasAdwToolbar()) {
c.adw_tab_overview_set_view(@ptrCast(tab_overview), self.notebook.adw_tab_view);
const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_toolbar_view_new());
const header_widget: *c.GtkWidget = @ptrCast(@alignCast(self.header.?));
@ -226,8 +252,23 @@ pub fn init(self: *Window, app: *App) !void {
c.gtk_widget_set_visible(header_widget, 0);
}
c.adw_tab_overview_set_child(@ptrCast(tab_overview), @ptrCast(@alignCast(toolbar_view)));
c.adw_application_window_set_content(@ptrCast(gtk_window), @ptrCast(@alignCast(tab_overview)));
// Set our application window content. The content depends on if
// we're using an AdwTabOverview or not.
if (tab_overview_) |tab_overview| {
c.adw_tab_overview_set_child(
@ptrCast(tab_overview),
@ptrCast(@alignCast(toolbar_view)),
);
c.adw_application_window_set_content(
@ptrCast(gtk_window),
@ptrCast(@alignCast(tab_overview)),
);
} else {
c.adw_application_window_set_content(
@ptrCast(gtk_window),
@ptrCast(@alignCast(toolbar_view)),
);
}
} else {
// The box is our main child
c.gtk_window_set_child(gtk_window, box);
@ -398,12 +439,14 @@ fn gtkTabNewClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {
/// Create a new tab from the AdwTabOverview. We can't copy gtkTabNewClick
/// because we need to return an AdwTabPage from this function.
fn newTabFromOverview(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) ?*c.GObject {
fn gtkNewTabFromOverview(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) ?*c.GObject {
const self: *Window = @ptrCast(@alignCast(ud orelse return null));
assert(self.isAdwWindow());
const alloc = self.app.core_app.alloc;
const surface = self.actionSurface() orelse return null;
const tab = Tab.create(alloc, self, surface) catch return null;
return tab.tab_page;
return tab.adw_tab_page;
}
fn gtkRefocusTerm(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool {

View File

@ -198,9 +198,11 @@ pub const Notebook = union(enum) {
}
}
pub fn addTab(self: Notebook, tab: *Tab, title: [:0]const u8) !*c.GObject {
/// Adds a new tab with the given title to the notebook. If the notebook
/// is an adwaita tab view, this will return an AdwTabPage. If the notebook
/// is a GTK notebook, this will return null.
pub fn addTab(self: Notebook, tab: *Tab, title: [:0]const u8) !?*c.GObject {
const box_widget: *c.GtkWidget = @ptrCast(tab.box);
var tab_page: *c.GObject = undefined;
switch (self) {
.adw_tab_view => |tab_view| {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
@ -211,7 +213,7 @@ pub const Notebook = union(enum) {
// Switch to the new tab
c.adw_tab_view_set_selected_page(tab_view, page);
tab_page = @ptrCast(@alignCast(page));
return @ptrCast(@alignCast(page));
},
.gtk_notebook => |notebook| {
// Build the tab label
@ -268,10 +270,10 @@ pub const Notebook = union(enum) {
// Switch to the new tab
c.gtk_notebook_set_current_page(notebook, page_idx);
return null;
},
}
return tab_page;
}
pub fn closeTab(self: Notebook, tab: *Tab) void {