introduce split-preserve-zoom config to maintain zoomed splits during navigation
parent
67eb480577
commit
d364e421a8
|
|
@ -621,9 +621,14 @@ class BaseTerminalController: NSWindowController,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the zoomed state for this surface tree.
|
|
||||||
if surfaceTree.zoomed != nil {
|
if surfaceTree.zoomed != nil {
|
||||||
surfaceTree = .init(root: surfaceTree.root, zoomed: nil)
|
if derivedConfig.splitPreserveZoom.contains(.navigation) {
|
||||||
|
surfaceTree = SplitTree(
|
||||||
|
root: surfaceTree.root,
|
||||||
|
zoomed: surfaceTree.root?.node(view: nextSurface))
|
||||||
|
} else {
|
||||||
|
surfaceTree = SplitTree(root: surfaceTree.root, zoomed: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move focus to the next surface
|
// Move focus to the next surface
|
||||||
|
|
@ -1188,17 +1193,20 @@ class BaseTerminalController: NSWindowController,
|
||||||
let macosTitlebarProxyIcon: Ghostty.MacOSTitlebarProxyIcon
|
let macosTitlebarProxyIcon: Ghostty.MacOSTitlebarProxyIcon
|
||||||
let windowStepResize: Bool
|
let windowStepResize: Bool
|
||||||
let focusFollowsMouse: Bool
|
let focusFollowsMouse: Bool
|
||||||
|
let splitPreserveZoom: Ghostty.Config.SplitPreserveZoom
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.macosTitlebarProxyIcon = .visible
|
self.macosTitlebarProxyIcon = .visible
|
||||||
self.windowStepResize = false
|
self.windowStepResize = false
|
||||||
self.focusFollowsMouse = false
|
self.focusFollowsMouse = false
|
||||||
|
self.splitPreserveZoom = .init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ config: Ghostty.Config) {
|
init(_ config: Ghostty.Config) {
|
||||||
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
|
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
|
||||||
self.windowStepResize = config.windowStepResize
|
self.windowStepResize = config.windowStepResize
|
||||||
self.focusFollowsMouse = config.focusFollowsMouse
|
self.focusFollowsMouse = config.focusFollowsMouse
|
||||||
|
self.splitPreserveZoom = config.splitPreserveZoom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,14 @@ extension Ghostty {
|
||||||
return .init(rawValue: v)
|
return .init(rawValue: v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var splitPreserveZoom: SplitPreserveZoom {
|
||||||
|
guard let config = self.config else { return .init() }
|
||||||
|
var v: CUnsignedInt = 0
|
||||||
|
let key = "split-preserve-zoom"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .init() }
|
||||||
|
return .init(rawValue: v)
|
||||||
|
}
|
||||||
|
|
||||||
var initialWindow: Bool {
|
var initialWindow: Bool {
|
||||||
guard let config = self.config else { return true }
|
guard let config = self.config else { return true }
|
||||||
var v = true;
|
var v = true;
|
||||||
|
|
@ -690,6 +698,12 @@ extension Ghostty.Config {
|
||||||
static let border = BellFeatures(rawValue: 1 << 4)
|
static let border = BellFeatures(rawValue: 1 << 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SplitPreserveZoom: OptionSet {
|
||||||
|
let rawValue: CUnsignedInt
|
||||||
|
|
||||||
|
static let navigation = SplitPreserveZoom(rawValue: 1 << 0)
|
||||||
|
}
|
||||||
|
|
||||||
enum MacDockDropBehavior: String {
|
enum MacDockDropBehavior: String {
|
||||||
case new_tab = "new-tab"
|
case new_tab = "new-tab"
|
||||||
case new_window = "new-window"
|
case new_window = "new-window"
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,35 @@ pub const SplitTree = extern struct {
|
||||||
const surface = tree.nodes[target.idx()].leaf;
|
const surface = tree.nodes[target.idx()].leaf;
|
||||||
surface.grabFocus();
|
surface.grabFocus();
|
||||||
|
|
||||||
|
// We also need to setup our last_focused to this because if we
|
||||||
|
// trigger a tree change like below, the grab focus above never
|
||||||
|
// actually triggers in time to set this and this ensures we
|
||||||
|
// grab focus to the right thing.
|
||||||
|
const old_last_focused = self.private().last_focused.get();
|
||||||
|
defer if (old_last_focused) |v| v.unref(); // unref strong ref from get
|
||||||
|
self.private().last_focused.set(surface);
|
||||||
|
errdefer self.private().last_focused.set(old_last_focused);
|
||||||
|
|
||||||
|
if (tree.zoomed != null) {
|
||||||
|
const app = Application.default();
|
||||||
|
const config_obj = app.getConfig();
|
||||||
|
defer config_obj.unref();
|
||||||
|
const config = config_obj.get();
|
||||||
|
|
||||||
|
if (!config.@"split-preserve-zoom".navigation) {
|
||||||
|
tree.zoomed = null;
|
||||||
|
} else {
|
||||||
|
tree.zoom(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the zoom state changes our tree state changes and
|
||||||
|
// we need to send the proper notifications to trigger
|
||||||
|
// relayout.
|
||||||
|
const object = self.as(gobject.Object);
|
||||||
|
object.notifyByPspec(properties.tree.impl.param_spec);
|
||||||
|
object.notifyByPspec(properties.@"is-zoomed".impl.param_spec);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -985,6 +985,14 @@ palette: Palette = .{},
|
||||||
/// Available since: 1.1.0
|
/// Available since: 1.1.0
|
||||||
@"split-divider-color": ?Color = null,
|
@"split-divider-color": ?Color = null,
|
||||||
|
|
||||||
|
/// Control when Ghostty preserves the zoomed state of a split. This is a packed
|
||||||
|
/// struct so more options can be added in the future. The `navigation` option
|
||||||
|
/// keeps the current split zoomed when split navigation (`goto_split`) changes
|
||||||
|
/// the focused split.
|
||||||
|
///
|
||||||
|
/// Example: `split-preserve-zoom = navigation`
|
||||||
|
@"split-preserve-zoom": SplitPreserveZoom = .{},
|
||||||
|
|
||||||
/// The foreground and background color for search matches. This only applies
|
/// The foreground and background color for search matches. This only applies
|
||||||
/// to non-focused search matches, also known as candidate matches.
|
/// to non-focused search matches, also known as candidate matches.
|
||||||
///
|
///
|
||||||
|
|
@ -7423,6 +7431,10 @@ pub const ShellIntegrationFeatures = packed struct {
|
||||||
path: bool = true,
|
path: bool = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SplitPreserveZoom = packed struct {
|
||||||
|
navigation: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
pub const RepeatableCommand = struct {
|
pub const RepeatableCommand = struct {
|
||||||
value: std.ArrayListUnmanaged(inputpkg.Command) = .empty,
|
value: std.ArrayListUnmanaged(inputpkg.Command) = .empty,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -222,3 +222,19 @@ test "c_get: background-blur" {
|
||||||
try testing.expectEqual(-2, cval);
|
try testing.expectEqual(-2, cval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "c_get: split-preserve-zoom" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var c = try Config.default(alloc);
|
||||||
|
defer c.deinit();
|
||||||
|
|
||||||
|
var bits: c_uint = undefined;
|
||||||
|
try testing.expect(get(&c, .@"split-preserve-zoom", @ptrCast(&bits)));
|
||||||
|
try testing.expectEqual(@as(c_uint, 0), bits);
|
||||||
|
|
||||||
|
c.@"split-preserve-zoom".navigation = true;
|
||||||
|
try testing.expect(get(&c, .@"split-preserve-zoom", @ptrCast(&bits)));
|
||||||
|
try testing.expectEqual(@as(c_uint, 1), bits);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue