Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions macos/Sources/Features/Terminal/BaseTerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,12 @@ class BaseTerminalController: NSWindowController,
return nil
}

// If auto-equalize-splits is enabled, equalize the new tree before
// committing it so all splits in the window become evenly sized.
let finalTree = derivedConfig.autoEqualizeSplits ? newTree.equalized() : newTree

replaceSurfaceTree(
newTree,
finalTree,
moveFocusTo: newView,
moveFocusFrom: oldView,
undoAction: "New Split")
Expand Down Expand Up @@ -456,8 +460,13 @@ class BaseTerminalController: NSWindowController,
nil
}

// If auto-equalize-splits is enabled, equalize the post-removal tree
// so the surviving splits in the window become evenly sized.
let removed = surfaceTree.removing(node)
let finalTree = derivedConfig.autoEqualizeSplits ? removed.equalized() : removed

replaceSurfaceTree(
surfaceTree.removing(node),
finalTree,
moveFocusTo: nextFocus,
moveFocusFrom: focusedSurface,
undoAction: "Close Terminal"
Expand Down Expand Up @@ -1447,19 +1456,22 @@ class BaseTerminalController: NSWindowController,
let windowStepResize: Bool
let focusFollowsMouse: Bool
let splitPreserveZoom: Ghostty.Config.SplitPreserveZoom
let autoEqualizeSplits: Bool

init() {
self.macosTitlebarProxyIcon = .visible
self.windowStepResize = false
self.focusFollowsMouse = false
self.splitPreserveZoom = .init()
self.autoEqualizeSplits = false
}

init(_ config: Ghostty.Config) {
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
self.windowStepResize = config.windowStepResize
self.focusFollowsMouse = config.focusFollowsMouse
self.splitPreserveZoom = config.splitPreserveZoom
self.autoEqualizeSplits = config.autoEqualizeSplits
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions macos/Sources/Ghostty/Ghostty.Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ extension Ghostty {
return .milliseconds(v)
}

var autoEqualizeSplits: Bool {
guard let config = self.config else { return false }
var v = false
let key = "auto-equalize-splits"
_ = ghostty_config_get(config, &v, key, UInt(key.lengthOfBytes(using: .utf8)))
return v
}

var splitPreserveZoom: SplitPreserveZoom {
guard let config = self.config else { return .init() }
var v: CUnsignedInt = 0
Expand Down
27 changes: 27 additions & 0 deletions src/apprt/gtk/class/split_tree.zig
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ pub const SplitTree = extern struct {
.{ handle, direction, old_tree, &new_tree },
);

// Auto-equalize the whole tree if the user opted in. We do this
// before publishing the tree so the resize is invisible to listeners.
autoEqualize(&new_tree, alloc);

// Replace our tree
self.setTree(&new_tree);
}
Expand Down Expand Up @@ -646,6 +650,25 @@ pub const SplitTree = extern struct {
self.setTree(&new_tree);
}

/// If `auto-equalize-splits` is enabled, replace `tree` in place with an
/// equalized version. On allocation failure we log and leave `tree`
/// untouched so the create/close operation still succeeds with the
/// non-equalized layout.
fn autoEqualize(tree: *Surface.Tree, alloc: Allocator) void {
const app = Application.default();
const config_obj = app.getConfig();
defer config_obj.unref();
const config = config_obj.get();
if (!config.@"auto-equalize-splits") return;

const equalized = tree.equalize(alloc) catch |err| {
log.warn("unable to auto-equalize tree: {}", .{err});
return;
};
tree.deinit();
tree.* = equalized;
}

pub fn actionZoom(
_: *gio.SimpleAction,
_: ?*glib.Variant,
Expand Down Expand Up @@ -744,6 +767,10 @@ pub const SplitTree = extern struct {
return;
};
defer new_tree.deinit();

// Auto-equalize the surviving splits if the user opted in.
autoEqualize(&new_tree, Application.default().allocator());

self.setTree(&new_tree);

// Grab focus. We have to set this on the "last focused" because our
Expand Down
14 changes: 14 additions & 0 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,20 @@ keybind: Keybinds = .{},
/// working directory will be used (the `working-directory` option).
@"split-inherit-working-directory": bool = true,

/// If true, all splits in the current window are automatically resized to
/// be equal whenever a split is created or closed. This is equivalent to
/// invoking the `equalize_splits` action after every split create/close.
///
/// Because the equalization runs over the entire split tree of the window,
/// any prior manual resizes (e.g., via `resize_split`) within that tree are
/// reset by this option whenever any split in the same window is created or
/// closed.
///
/// Default is `false` to preserve existing behavior. Users who want every
/// split to always be evenly sized (similar to iTerm2's default) should set
/// this to `true`.
@"auto-equalize-splits": bool = false,

/// 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
Expand Down
Loading