mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 12:16:11 +03:00
Merge pull request #2255 from pluiedev/feat/situs-inversus
DRAFT: implement splitting leftwards and upwards
This commit is contained in:
@ -351,6 +351,8 @@ typedef struct {
|
||||
typedef enum {
|
||||
GHOSTTY_SPLIT_DIRECTION_RIGHT,
|
||||
GHOSTTY_SPLIT_DIRECTION_DOWN,
|
||||
GHOSTTY_SPLIT_DIRECTION_LEFT,
|
||||
GHOSTTY_SPLIT_DIRECTION_UP,
|
||||
} ghostty_action_split_direction_e;
|
||||
|
||||
// apprt.action.GotoSplit
|
||||
|
@ -253,6 +253,15 @@ extension Ghostty {
|
||||
bottomRight.parent = self
|
||||
}
|
||||
|
||||
// Move the top left node to the bottom right and vice versa,
|
||||
// preserving the size.
|
||||
func swap() {
|
||||
let topLeft: SplitNode = self.topLeft
|
||||
self.topLeft = bottomRight
|
||||
self.bottomRight = topLeft
|
||||
self.split = 1 - self.split
|
||||
}
|
||||
|
||||
/// Resize the split by moving the split divider in the given
|
||||
/// direction by the given amount. If this container is not split
|
||||
/// in the given direction, navigate up the tree until we find a
|
||||
|
@ -220,13 +220,21 @@ extension Ghostty {
|
||||
// Determine our desired direction
|
||||
guard let directionAny = notification.userInfo?["direction"] else { return }
|
||||
guard let direction = directionAny as? ghostty_action_split_direction_e else { return }
|
||||
var splitDirection: SplitViewDirection
|
||||
let splitDirection: SplitViewDirection
|
||||
let swap: Bool
|
||||
switch (direction) {
|
||||
case GHOSTTY_SPLIT_DIRECTION_RIGHT:
|
||||
splitDirection = .horizontal
|
||||
|
||||
swap = false
|
||||
case GHOSTTY_SPLIT_DIRECTION_LEFT:
|
||||
splitDirection = .horizontal
|
||||
swap = true
|
||||
case GHOSTTY_SPLIT_DIRECTION_DOWN:
|
||||
splitDirection = .vertical
|
||||
swap = false
|
||||
case GHOSTTY_SPLIT_DIRECTION_UP:
|
||||
splitDirection = .vertical
|
||||
swap = true
|
||||
|
||||
default:
|
||||
return
|
||||
@ -240,6 +248,12 @@ extension Ghostty {
|
||||
|
||||
// See moveFocus comment, we have to run this whenever split changes.
|
||||
Ghostty.moveFocus(to: container.bottomRight.preferredFocus(), from: node!.preferredFocus())
|
||||
|
||||
// If we are swapping, swap now. We do this after our focus event
|
||||
// so that focus is in the right place.
|
||||
if swap {
|
||||
container.swap()
|
||||
}
|
||||
}
|
||||
|
||||
/// This handles the event to move the split focus (i.e. previous/next) from a keyboard event.
|
||||
|
@ -3835,7 +3835,9 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||
.new_split,
|
||||
switch (direction) {
|
||||
.right => .right,
|
||||
.left => .left,
|
||||
.down => .down,
|
||||
.up => .up,
|
||||
.auto => if (self.screen_size.width > self.screen_size.height)
|
||||
.right
|
||||
else
|
||||
|
@ -272,6 +272,8 @@ pub const Action = union(Key) {
|
||||
pub const SplitDirection = enum(c_int) {
|
||||
right,
|
||||
down,
|
||||
left,
|
||||
up,
|
||||
};
|
||||
|
||||
// This is made extern (c_int) to make interop easier with our embedded
|
||||
|
@ -768,6 +768,8 @@ fn syncActionAccelerators(self: *App) !void {
|
||||
try self.syncActionAccelerator("win.new_tab", .{ .new_tab = {} });
|
||||
try self.syncActionAccelerator("win.split_right", .{ .new_split = .right });
|
||||
try self.syncActionAccelerator("win.split_down", .{ .new_split = .down });
|
||||
try self.syncActionAccelerator("win.split_left", .{ .new_split = .left });
|
||||
try self.syncActionAccelerator("win.split_up", .{ .new_split = .up });
|
||||
try self.syncActionAccelerator("win.copy", .{ .copy_to_clipboard = {} });
|
||||
try self.syncActionAccelerator("win.paste", .{ .paste_from_clipboard = {} });
|
||||
try self.syncActionAccelerator("win.reset", .{ .reset = {} });
|
||||
|
@ -22,8 +22,8 @@ pub const Orientation = enum {
|
||||
|
||||
pub fn fromDirection(direction: apprt.action.SplitDirection) Orientation {
|
||||
return switch (direction) {
|
||||
.right => .horizontal,
|
||||
.down => .vertical,
|
||||
.right, .left => .horizontal,
|
||||
.down, .up => .vertical,
|
||||
};
|
||||
}
|
||||
|
||||
@ -80,8 +80,8 @@ pub fn init(
|
||||
|
||||
// Create the actual GTKPaned, attach the proper children.
|
||||
const orientation: c_uint = switch (direction) {
|
||||
.right => c.GTK_ORIENTATION_HORIZONTAL,
|
||||
.down => c.GTK_ORIENTATION_VERTICAL,
|
||||
.right, .left => c.GTK_ORIENTATION_HORIZONTAL,
|
||||
.down, .up => c.GTK_ORIENTATION_VERTICAL,
|
||||
};
|
||||
const paned = c.gtk_paned_new(orientation);
|
||||
errdefer c.g_object_unref(paned);
|
||||
@ -94,14 +94,25 @@ pub fn init(
|
||||
// we're inheriting its parent. The sibling points to its location
|
||||
// in the split, and the surface points to the other location.
|
||||
const container = sibling.container;
|
||||
sibling.container = .{ .split_tl = &self.top_left };
|
||||
surface.container = .{ .split_br = &self.bottom_right };
|
||||
const tl: *Surface, const br: *Surface = switch (direction) {
|
||||
.right, .down => right_down: {
|
||||
sibling.container = .{ .split_tl = &self.top_left };
|
||||
surface.container = .{ .split_br = &self.bottom_right };
|
||||
break :right_down .{ sibling, surface };
|
||||
},
|
||||
|
||||
.left, .up => left_up: {
|
||||
sibling.container = .{ .split_br = &self.bottom_right };
|
||||
surface.container = .{ .split_tl = &self.top_left };
|
||||
break :left_up .{ surface, sibling };
|
||||
},
|
||||
};
|
||||
|
||||
self.* = .{
|
||||
.paned = @ptrCast(paned),
|
||||
.container = container,
|
||||
.top_left = .{ .surface = sibling },
|
||||
.bottom_right = .{ .surface = surface },
|
||||
.top_left = .{ .surface = tl },
|
||||
.bottom_right = .{ .surface = br },
|
||||
.orientation = Orientation.fromDirection(direction),
|
||||
};
|
||||
|
||||
|
@ -373,6 +373,8 @@ fn initActions(self: *Window) void {
|
||||
.{ "new_tab", >kActionNewTab },
|
||||
.{ "split_right", >kActionSplitRight },
|
||||
.{ "split_down", >kActionSplitDown },
|
||||
.{ "split_left", >kActionSplitLeft },
|
||||
.{ "split_up", >kActionSplitUp },
|
||||
.{ "toggle_inspector", >kActionToggleInspector },
|
||||
.{ "copy", >kActionCopy },
|
||||
.{ "paste", >kActionPaste },
|
||||
@ -812,6 +814,32 @@ fn gtkActionSplitDown(
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkActionSplitLeft(
|
||||
_: *c.GSimpleAction,
|
||||
_: *c.GVariant,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
const self: *Window = @ptrCast(@alignCast(ud orelse return));
|
||||
const surface = self.actionSurface() orelse return;
|
||||
_ = surface.performBindingAction(.{ .new_split = .left }) catch |err| {
|
||||
log.warn("error performing binding action error={}", .{err});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkActionSplitUp(
|
||||
_: *c.GSimpleAction,
|
||||
_: *c.GVariant,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
const self: *Window = @ptrCast(@alignCast(ud orelse return));
|
||||
const surface = self.actionSurface() orelse return;
|
||||
_ = surface.performBindingAction(.{ .new_split = .up }) catch |err| {
|
||||
log.warn("error performing binding action error={}", .{err});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkActionToggleInspector(
|
||||
_: *c.GSimpleAction,
|
||||
_: *c.GVariant,
|
||||
|
@ -440,9 +440,9 @@ pub const Action = union(enum) {
|
||||
pub const SplitDirection = enum {
|
||||
right,
|
||||
down,
|
||||
left,
|
||||
up,
|
||||
auto, // splits along the larger direction
|
||||
|
||||
// Note: we don't support top or left yet
|
||||
};
|
||||
|
||||
pub const SplitFocusDirection = enum {
|
||||
|
Reference in New Issue
Block a user