From d311fb93ed7047e1697b87b41a9d3650e46eb5c4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 1 Dec 2023 13:21:31 -0800 Subject: [PATCH] apprt/gtk: gotoSplit gets proper previous/next direction --- src/apprt/gtk/Split.zig | 103 +++++++++++++++----------------------- src/apprt/gtk/Surface.zig | 24 +++++++++ 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/src/apprt/gtk/Split.zig b/src/apprt/gtk/Split.zig index fbeb748be..2f01aaa1d 100644 --- a/src/apprt/gtk/Split.zig +++ b/src/apprt/gtk/Split.zig @@ -193,89 +193,68 @@ 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); + if (self.directionPrevious(from)) |prev| { + result.put(.previous, prev); // 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); + result.put(.top, prev); + result.put(.left, prev); } - 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); - }, + if (self.directionNext(from)) |next| { + result.put(.next, next); + result.put(.bottom, next); + result.put(.right, next); } return result; } -fn directionMapFromBottomRight(self: *const Split) DirectionMap { - var result = DirectionMap.initFull(null); +fn directionPrevious(self: *const Split, from: Side) ?*Surface { + switch (from) { + // From the bottom right, our previous is the deepest surface + // in the top-left of our own split. + .bottom_right => return self.top_left.deepestSurface(.bottom_right), - if (self.container.split()) |parent_split| { - const deepest_tl = parent_split.deepestSurface(.top_left); - result.put(.next, deepest_tl); + // From the top left its more complicated. It is the de + .top_left => { + // If we have no parent split then there can be no previous. + const parent = self.container.split() orelse return null; + const side = self.container.splitSide() orelse return null; - // 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); + // The previous value is the previous of the side that we are. + return switch (side) { + .top_left => parent.directionPrevious(.top_left), + .bottom_right => parent.directionPrevious(.bottom_right), + }; }, } - - 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), - }, +fn directionNext(self: *const Split, from: Side) ?*Surface { + switch (from) { + // From the top left, our next is the earliest surface in the + // top-left direction of the bottom-right side of our split. Fun! + .top_left => return self.bottom_right.deepestSurface(.top_left), - .top_left => switch (self.top_left) { - .surface => |s| s, - .split => |s| s.deepestSurface(.top_left), + // From the bottom right is more compliated. It is the deepest + // (last) surface in the + .bottom_right => { + // If we have no parent split then there can be no next. + const parent = self.container.split() orelse return null; + const side = self.container.splitSide() orelse return null; + + // The previous value is the previous of the side that we are. + return switch (side) { + .top_left => parent.directionNext(.bottom_right), + .bottom_right => parent.directionNext(.bottom_right), + }; }, - }; + } } fn removeChildren(self: *const Split) void { diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index fa50489a8..266c67215 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -48,6 +48,9 @@ pub const Container = union(enum) { split_tl: *Elem, split_br: *Elem, + /// The side of the split. + pub const SplitSide = enum { top_left, bottom_right }; + /// Elem is the possible element of any container. A container can /// hold both a surface and a split. Any valid container should /// have an Elem value so that it can be properly used with @@ -92,6 +95,18 @@ pub const Container = union(enum) { .split => |s| s.grabFocus(), } } + + /// The last surface in this container in the direction specified. + /// Direction must be "top_left" or "bottom_right". + pub fn deepestSurface(self: Elem, side: SplitSide) ?*Surface { + return switch (self) { + .surface => |s| s, + .split => |s| (switch (side) { + .top_left => s.top_left, + .bottom_right => s.bottom_right, + }).deepestSurface(side), + }; + } }; /// Returns the window that this surface is attached to. @@ -127,6 +142,15 @@ pub const Container = union(enum) { }; } + /// The side that we are in the split. + pub fn splitSide(self: Container) ?SplitSide { + return switch (self) { + .none, .tab_ => null, + .split_tl => .top_left, + .split_br => .bottom_right, + }; + } + /// Replace the container's element with this element. This is /// used by children to modify their parents to for example change /// from a surface to a split or a split back to a surface or