From f811ac6b18fb7e2dea9c876e4a0a60e1f3b66fb5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 1 Dec 2023 09:58:57 -0800 Subject: [PATCH] apprt/gtk: gotoSplit, has some bugs --- src/apprt/gtk/Split.zig | 96 +++++++++++++++++++++++++++++++++++++++ src/apprt/gtk/Surface.zig | 11 +++++ 2 files changed, 107 insertions(+) diff --git a/src/apprt/gtk/Split.zig b/src/apprt/gtk/Split.zig index 93558dc36..fbeb748be 100644 --- a/src/apprt/gtk/Split.zig +++ b/src/apprt/gtk/Split.zig @@ -182,6 +182,102 @@ fn updateChildren(self: *const Split) void { ); } +/// A mapping of direction to the element (if any) in that direction. +pub const DirectionMap = std.EnumMap( + input.SplitFocusDirection, + ?*Surface, +); + +pub const Side = enum { top_left, bottom_right }; + +/// Returns the map that can be used to determine elements in various +/// directions (primarily for gotoSplit). +pub fn directionMap(self: *const Split, from: Side) DirectionMap { + return switch (from) { + .top_left => self.directionMapFromTopLeft(), + .bottom_right => self.directionMapFromBottomRight(), + }; +} + +fn directionMapFromTopLeft(self: *const Split) DirectionMap { + var result = DirectionMap.initFull(null); + + if (self.container.split()) |parent_split| { + const deepest_br = parent_split.deepestSurface(.bottom_right); + result.put(.previous, deepest_br); + + // This behavior matches the behavior of macOS at the time of writing + // this. There is an open issue (#524) to make this depend on the + // actual physical location of the current split. + result.put(.top, deepest_br); + result.put(.left, deepest_br); + } + + switch (self.bottom_right) { + .surface => |s| { + result.put(.next, s); + result.put(.bottom, s); + result.put(.right, s); + }, + + .split => |s| { + const deepest_tl = s.deepestSurface(.top_left); + result.put(.next, deepest_tl); + result.put(.bottom, deepest_tl); + result.put(.right, deepest_tl); + }, + } + + return result; +} + +fn directionMapFromBottomRight(self: *const Split) DirectionMap { + var result = DirectionMap.initFull(null); + + if (self.container.split()) |parent_split| { + const deepest_tl = parent_split.deepestSurface(.top_left); + result.put(.next, deepest_tl); + + // This behavior matches the behavior of macOS at the time of writing + // this. There is an open issue (#524) to make this depend on the + // actual physical location of the current split. + result.put(.top, deepest_tl); + result.put(.left, deepest_tl); + } + + switch (self.top_left) { + .surface => |s| { + result.put(.previous, s); + result.put(.bottom, s); + result.put(.right, s); + }, + + .split => |s| { + const deepest_br = s.deepestSurface(.bottom_right); + result.put(.previous, deepest_br); + result.put(.bottom, deepest_br); + result.put(.right, deepest_br); + }, + } + + return result; +} + +/// Get the most deeply nested surface for a given side. +fn deepestSurface(self: *const Split, side: Side) *Surface { + return switch (side) { + .bottom_right => switch (self.bottom_right) { + .surface => |s| s, + .split => |s| s.deepestSurface(.bottom_right), + }, + + .top_left => switch (self.top_left) { + .surface => |s| s, + .split => |s| s.deepestSurface(.top_left), + }, + }; +} + fn removeChildren(self: *const Split) void { c.gtk_paned_set_start_child(@ptrCast(self.paned), null); c.gtk_paned_set_end_child(@ptrCast(self.paned), null); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 720de99cb..fa50489a8 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -524,6 +524,17 @@ pub fn newSplit(self: *Surface, direction: input.SplitDirection) !void { _ = try Split.create(alloc, self, direction); } +pub fn gotoSplit(self: *const Surface, direction: input.SplitFocusDirection) void { + const s = self.container.split() orelse return; + const map = s.directionMap(switch (self.container) { + .split_tl => .top_left, + .split_br => .bottom_right, + .none, .tab_ => unreachable, + }); + const surface_ = map.get(direction) orelse return; + if (surface_) |surface| surface.grabFocus(); +} + pub fn newTab(self: *Surface) !void { const window = self.container.window() orelse { log.info("surface cannot create new tab when not attached to a window", .{});