From fbc621a7d86f15333aedba1fc6a25d31583f98b6 Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Mon, 16 Sep 2024 13:47:52 +0200 Subject: [PATCH 1/2] gtk: implement splitting leftwards and upwards --- src/Surface.zig | 2 ++ src/apprt/action.zig | 2 ++ src/apprt/gtk/App.zig | 2 ++ src/apprt/gtk/Split.zig | 27 +++++++++++++++++++-------- src/apprt/gtk/Window.zig | 28 ++++++++++++++++++++++++++++ src/input/Binding.zig | 4 ++-- 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 4e311cd38d..3e2aa3d955 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -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 diff --git a/src/apprt/action.zig b/src/apprt/action.zig index 5c8ba6a019..a5da51d1d1 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -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 diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 3bed756d00..3855d27c38 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -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 = {} }); diff --git a/src/apprt/gtk/Split.zig b/src/apprt/gtk/Split.zig index 7a3645d1b1..5afac6f5bc 100644 --- a/src/apprt/gtk/Split.zig +++ b/src/apprt/gtk/Split.zig @@ -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), }; diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index efde78e618..a3d74c77fd 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -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, diff --git a/src/input/Binding.zig b/src/input/Binding.zig index dead84c9f8..9667f2d17c 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -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 { From 392875381af807c690bc98000b9d1b4517f495e0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 7 Oct 2024 15:24:18 -1000 Subject: [PATCH 2/2] macos: become aware of new split directions left and up --- include/ghostty.h | 2 ++ macos/Sources/Ghostty/Ghostty.SplitNode.swift | 9 +++++++++ .../Ghostty/Ghostty.TerminalSplit.swift | 18 ++++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index 42cfc5a423..bf13500c8e 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -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 diff --git a/macos/Sources/Ghostty/Ghostty.SplitNode.swift b/macos/Sources/Ghostty/Ghostty.SplitNode.swift index e5a86b4cac..361af1e1b3 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitNode.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitNode.swift @@ -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 diff --git a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift index fa8335416b..6d288b434e 100644 --- a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift +++ b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift @@ -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.