apprt/gtk-ng: new tab, slightly broken

This commit is contained in:
Mitchell Hashimoto
2025-07-29 09:53:20 -07:00
parent 0682811107
commit b475cd28d5
2 changed files with 91 additions and 13 deletions

View File

@ -500,6 +500,8 @@ pub const Application = extern struct {
.mouse_shape => Action.mouseShape(target, value), .mouse_shape => Action.mouseShape(target, value),
.mouse_visibility => Action.mouseVisibility(target, value), .mouse_visibility => Action.mouseVisibility(target, value),
.new_tab => return Action.newTab(target),
.new_window => try Action.newWindow( .new_window => try Action.newWindow(
self, self,
switch (target) { switch (target) {
@ -532,7 +534,6 @@ pub const Application = extern struct {
.toggle_fullscreen => Action.toggleFullscreen(target), .toggle_fullscreen => Action.toggleFullscreen(target),
// Unimplemented but todo on gtk-ng branch // Unimplemented but todo on gtk-ng branch
.new_tab,
.goto_tab, .goto_tab,
.move_tab, .move_tab,
.new_split, .new_split,
@ -1272,6 +1273,31 @@ const Action = struct {
} }
} }
pub fn newTab(target: apprt.Target) bool {
switch (target) {
.app => {
log.warn("new tab to app is unexpected", .{});
return false;
},
.surface => |core| {
// Get the window ancestor of the surface. Surfaces shouldn't
// be aware they might be in windows but at the app level we
// can do this.
const surface = core.rt_surface.surface;
const window_widget = surface
.as(gtk.Widget)
.getAncestor(gobject.ext.typeFor(Window)) orelse {
log.warn("surface is not in a window, ignoring new_tab", .{});
return false;
};
const window = gobject.ext.cast(Window, window_widget).?;
window.newTab(core);
return true;
},
}
}
pub fn newWindow( pub fn newWindow(
self: *Application, self: *Application,
parent: ?*CoreSurface, parent: ?*CoreSurface,

View File

@ -223,17 +223,7 @@ pub const Window = extern struct {
const self = gobject.ext.newInstance(Self, .{ const self = gobject.ext.newInstance(Self, .{
.application = app, .application = app,
}); });
self.newTab(parent_);
// Create our initial tab. This will trigger the selected-page
// signal handler which will setup the remainder of the bindings
// for this to all work.
const priv = self.private();
const tab = gobject.ext.newInstance(Tab, .{
.config = priv.config,
});
if (parent_) |p| tab.setParent(p);
_ = priv.tab_view.append(tab.as(gtk.Widget));
return self; return self;
} }
@ -280,6 +270,8 @@ pub const Window = extern struct {
const actions = .{ const actions = .{
.{ "about", actionAbout, null }, .{ "about", actionAbout, null },
.{ "close", actionClose, null }, .{ "close", actionClose, null },
.{ "close-tab", actionCloseTab, null },
.{ "new-tab", actionNewTab, null },
.{ "new-window", actionNewWindow, null }, .{ "new-window", actionNewWindow, null },
.{ "copy", actionCopy, null }, .{ "copy", actionCopy, null },
.{ "paste", actionPaste, null }, .{ "paste", actionPaste, null },
@ -305,6 +297,50 @@ pub const Window = extern struct {
} }
} }
/// Create a new tab with the given parent. The tab will be inserted
/// at the position dictated by the `window-new-tab-position` config.
/// The new tab will be selected.
pub fn newTab(self: *Self, parent_: ?*CoreSurface) void {
const priv = self.private();
const tab_view = priv.tab_view;
// Create our new tab object
const tab = gobject.ext.newInstance(Tab, .{
.config = priv.config,
});
if (parent_) |p| tab.setParent(p);
// Get the position that we should insert the new tab at.
const config = if (priv.config) |v| v.get() else {
// If we don't have a config we just append it at the end.
// This should never happen.
_ = tab_view.append(tab.as(gtk.Widget));
return;
};
const position = switch (config.@"window-new-tab-position") {
.current => current: {
const selected = tab_view.getSelectedPage() orelse
break :current tab_view.getNPages();
const current = tab_view.getPagePosition(selected);
break :current current + 1;
},
.end => tab_view.getNPages(),
};
// Add the page and select it
const page = tab_view.insert(tab.as(gtk.Widget), position);
tab_view.setSelectedPage(page);
// Create some property bindings
_ = tab.as(gobject.Object).bindProperty(
"title",
page.as(gobject.Object),
"title",
.{ .sync_create = true },
);
}
/// Updates various appearance properties. This should always be safe /// Updates various appearance properties. This should always be safe
/// to call multiple times. This should be called whenever a change /// to call multiple times. This should be called whenever a change
/// happens that might affect how the window appears (config change, /// happens that might affect how the window appears (config change,
@ -391,7 +427,7 @@ pub const Window = extern struct {
} }
/// Returns true if this window needs confirmation before quitting. /// Returns true if this window needs confirmation before quitting.
pub fn getNeedsConfirmQuit(self: *Self) bool { fn getNeedsConfirmQuit(self: *Self) bool {
const priv = self.private(); const priv = self.private();
const n = priv.tab_view.getNPages(); const n = priv.tab_view.getNPages();
assert(n >= 0); assert(n >= 0);
@ -953,6 +989,14 @@ pub const Window = extern struct {
self.as(gtk.Window).close(); self.as(gtk.Window).close();
} }
fn actionCloseTab(
_: *gio.SimpleAction,
_: ?*glib.Variant,
self: *Window,
) callconv(.c) void {
self.performBindingAction(.close_tab);
}
fn actionNewWindow( fn actionNewWindow(
_: *gio.SimpleAction, _: *gio.SimpleAction,
_: ?*glib.Variant, _: ?*glib.Variant,
@ -961,6 +1005,14 @@ pub const Window = extern struct {
self.performBindingAction(.new_window); self.performBindingAction(.new_window);
} }
fn actionNewTab(
_: *gio.SimpleAction,
_: ?*glib.Variant,
self: *Window,
) callconv(.c) void {
self.performBindingAction(.new_tab);
}
fn actionCopy( fn actionCopy(
_: *gio.SimpleAction, _: *gio.SimpleAction,
_: ?*glib.Variant, _: ?*glib.Variant,