Allow for default or inherited CWD in new window, tab and split surfaces (redone for GTK-NG) (#9158)

This commit is contained in:
Leah Amelia Chen
2026-01-07 20:45:06 +08:00
committed by GitHub
11 changed files with 93 additions and 22 deletions

View File

@@ -456,6 +456,9 @@ pub const Surface = struct {
/// Wait after the command exits
wait_after_command: bool = false,
/// Context for the new surface
context: apprt.surface.NewSurfaceContext = .window,
};
pub fn init(self: *Surface, app: *App, opts: Options) !void {
@@ -477,7 +480,7 @@ pub const Surface = struct {
errdefer app.core_app.deleteSurface(self);
// Shallow copy the config so that we can modify it.
var config = try apprt.surface.newConfig(app.core_app, &app.config);
var config = try apprt.surface.newConfig(app.core_app, &app.config, opts.context);
defer config.deinit();
// If we have a working directory from the options then we set it.
@@ -894,14 +897,23 @@ pub const Surface = struct {
};
}
pub fn newSurfaceOptions(self: *const Surface) apprt.Surface.Options {
pub fn newSurfaceOptions(self: *const Surface, context: apprt.surface.NewSurfaceContext) apprt.Surface.Options {
const font_size: f32 = font_size: {
if (!self.app.config.@"window-inherit-font-size") break :font_size 0;
break :font_size self.core_surface.font_size.points;
};
const working_directory: ?[*:0]const u8 = wd: {
if (!apprt.surface.shouldInheritWorkingDirectory(context, &self.app.config)) break :wd null;
const cwd = self.core_surface.pwd(self.app.core_app.alloc) catch null orelse break :wd null;
defer self.app.core_app.alloc.free(cwd);
break :wd self.app.core_app.alloc.dupeZ(u8, cwd) catch null;
};
return .{
.font_size = font_size,
.working_directory = working_directory,
.context = context,
};
}
@@ -1523,8 +1535,11 @@ pub const CAPI = struct {
}
/// Returns the config to use for surfaces that inherit from this one.
export fn ghostty_surface_inherited_config(surface: *Surface) Surface.Options {
return surface.newSurfaceOptions();
export fn ghostty_surface_inherited_config(
surface: *Surface,
source: apprt.surface.NewSurfaceContext,
) Surface.Options {
return surface.newSurfaceOptions(source);
}
/// Update the configuration to the provided config for only this surface.

View File

@@ -2238,8 +2238,8 @@ const Action = struct {
.{},
);
// Create a new tab
win.newTab(parent);
// Create a new tab with window context (first tab in new window)
win.newTabForWindow(parent);
// Show the window
gtk.Window.present(win.as(gtk.Window));

View File

@@ -219,7 +219,7 @@ pub const SplitTree = extern struct {
// Inherit properly if we were asked to.
if (parent_) |p| {
if (p.core()) |core| {
surface.setParent(core);
surface.setParent(core, .split);
}
}

View File

@@ -671,6 +671,9 @@ pub const Surface = extern struct {
error_page: *adw.StatusPage,
terminal_page: *gtk.Overlay,
/// The context for this surface (window, tab, or split)
context: apprt.surface.NewSurfaceContext = .window,
pub var offset: c_int = 0;
};
@@ -696,6 +699,7 @@ pub const Surface = extern struct {
pub fn setParent(
self: *Self,
parent: *CoreSurface,
context: apprt.surface.NewSurfaceContext,
) void {
const priv = self.private();
@@ -706,6 +710,9 @@ pub const Surface = extern struct {
return;
}
// Store the context so initSurface can use it
priv.context = context;
// Setup our font size
const font_size_ptr = glib.ext.create(font.face.DesiredSize);
errdefer glib.ext.destroy(font_size_ptr);
@@ -716,10 +723,8 @@ pub const Surface = extern struct {
// Remainder needs a config. If there is no config we just assume
// we aren't inheriting any of these values.
if (priv.config) |config_obj| {
const config = config_obj.get();
// Setup our pwd if configured to inherit
if (config.@"window-inherit-working-directory") {
// Setup our cwd if configured to inherit
if (apprt.surface.shouldInheritWorkingDirectory(context, config_obj.get())) {
if (parent.rt_surface.surface.getPwd()) |pwd| {
priv.pwd = glib.ext.dupeZ(u8, pwd);
self.as(gobject.Object).notifyByPspec(properties.pwd.impl.param_spec);
@@ -3206,6 +3211,7 @@ pub const Surface = extern struct {
var config = try apprt.surface.newConfig(
app.core(),
priv.config.?.get(),
priv.context,
);
defer config.deinit();

View File

@@ -161,8 +161,12 @@ pub const Tab = extern struct {
/// ever created for a tab. If a surface was already created this does
/// nothing.
pub fn setParent(self: *Self, parent: *CoreSurface) void {
self.setParentWithContext(parent, .tab);
}
pub fn setParentWithContext(self: *Self, parent: *CoreSurface, context: apprt.surface.NewSurfaceContext) void {
if (self.getActiveSurface()) |surface| {
surface.setParent(parent);
surface.setParent(parent, context);
}
}

View File

@@ -361,10 +361,14 @@ pub const Window = extern struct {
/// 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 {
_ = self.newTabPage(parent_);
_ = self.newTabPage(parent_, .tab);
}
fn newTabPage(self: *Self, parent_: ?*CoreSurface) *adw.TabPage {
pub fn newTabForWindow(self: *Self, parent_: ?*CoreSurface) void {
_ = self.newTabPage(parent_, .window);
}
fn newTabPage(self: *Self, parent_: ?*CoreSurface, context: apprt.surface.NewSurfaceContext) *adw.TabPage {
const priv = self.private();
const tab_view = priv.tab_view;
@@ -372,7 +376,9 @@ pub const Window = extern struct {
const tab = gobject.ext.newInstance(Tab, .{
.config = priv.config,
});
if (parent_) |p| tab.setParent(p);
if (parent_) |p| {
tab.setParentWithContext(p, context);
}
// Get the position that we should insert the new tab at.
const config = if (priv.config) |v| v.get() else {
@@ -1231,7 +1237,7 @@ pub const Window = extern struct {
_: *adw.TabOverview,
self: *Self,
) callconv(.c) *adw.TabPage {
return self.newTabPage(if (self.getActiveSurface()) |v| v.core() else null);
return self.newTabPage(if (self.getActiveSurface()) |v| v.core() else null, .tab);
}
fn tabOverviewOpen(

View File

@@ -159,12 +159,28 @@ pub const Mailbox = struct {
}
};
/// Context for new surface creation to determine inheritance behavior
pub const NewSurfaceContext = enum(c_int) {
window = 0,
tab = 1,
split = 2,
};
pub fn shouldInheritWorkingDirectory(context: NewSurfaceContext, config: *const Config) bool {
return switch (context) {
.window => config.@"window-inherit-working-directory",
.tab => config.@"tab-inherit-working-directory",
.split => config.@"split-inherit-working-directory",
};
}
/// Returns a new config for a surface for the given app that should be
/// used for any new surfaces. The resulting config should be deinitialized
/// after the surface is initialized.
pub fn newConfig(
app: *const App,
config: *const Config,
context: NewSurfaceContext,
) Allocator.Error!Config {
// Create a shallow clone
var copy = config.shallowClone(app.alloc);
@@ -175,7 +191,7 @@ pub fn newConfig(
// Get our previously focused surface for some inherited values.
const prev = app.focusedSurface();
if (prev) |p| {
if (config.@"window-inherit-working-directory") {
if (shouldInheritWorkingDirectory(context, config)) {
if (try p.pwd(alloc)) |pwd| {
copy.@"working-directory" = pwd;
}

View File

@@ -1845,11 +1845,21 @@ keybind: Keybinds = .{},
/// This setting is only supported currently on macOS.
@"window-vsync": bool = true,
/// If true, new windows and tabs will inherit the working directory of the
/// If true, new windows will inherit the working directory of the
/// previously focused window. If no window was previously focused, the default
/// working directory will be used (the `working-directory` option).
@"window-inherit-working-directory": bool = true,
/// If true, new tabs will inherit the working directory of the
/// previously focused tab. If no tab was previously focused, the default
/// working directory will be used (the `working-directory` option).
@"tab-inherit-working-directory": bool = true,
/// If true, new split panes will inherit the working directory of the
/// previously focused split. If no split was previously focused, the default
/// working directory will be used (the `working-directory` option).
@"split-inherit-working-directory": bool = true,
/// If true, new windows and tabs will inherit the font size of the previously
/// focused window. If no window was previously focused, the default font size
/// will be used. If this is false, the default font size specified in the