diff --git a/src/apprt/gtk/Split.zig b/src/apprt/gtk/Split.zig index b4cecefbd..af44bc758 100644 --- a/src/apprt/gtk/Split.zig +++ b/src/apprt/gtk/Split.zig @@ -172,6 +172,43 @@ pub fn moveDivider(self: *Split, direction: input.SplitResizeDirection, amount: c.gtk_paned_set_position(self.paned, new); } +/// Equalize the splits in this split panel. Each split is equalized based on +/// its weight, i.e. the number of Surfaces it contains. +/// +/// It works recursively by equalizing the children of each split. +/// +/// It returns this split's weight. +pub fn equalize(self: *Split) f64 { + // Calculate weights of top_left/bottom_right + const top_left_weight = self.top_left.equalize(); + const bottom_right_weight = self.bottom_right.equalize(); + const weight = top_left_weight + bottom_right_weight; + + // Ratio of top_left weight to overall weight, which gives the split ratio + const ratio = top_left_weight / weight; + + // Convert split ratio into new position for divider + c.gtk_paned_set_position(self.paned, @intFromFloat(self.maxPosition() * ratio)); + + return weight; +} + +// maxPosition returns the maximum position of the GtkPaned, which is the +// "max-position" attribute. +fn maxPosition(self: *Split) f64 { + var value: c.GValue = std.mem.zeroes(c.GValue); + defer c.g_value_unset(&value); + + _ = c.g_value_init(&value, c.G_TYPE_INT); + c.g_object_get_property( + @ptrCast(@alignCast(self.paned)), + "max-position", + &value, + ); + + return @floatFromInt(c.g_value_get_int(&value)); +} + // This replaces the element at the given pointer with a new element. // The ptr must be either top_left or bottom_right (asserted in debug). // The memory of the old element must be freed or otherwise handled by diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 4a66320f5..80180b543 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -96,6 +96,13 @@ pub const Container = union(enum) { } } + pub fn equalize(self: Elem) f64 { + return switch (self) { + .surface => 1, + .split => |s| s.equalize(), + }; + } + /// 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 { @@ -582,6 +589,15 @@ pub fn resizeSplit(self: *const Surface, direction: input.SplitResizeDirection, s.moveDivider(direction, amount); } +pub fn equalizeSplits(self: *const Surface) void { + const tab = self.container.tab() orelse return; + const top_split = switch (tab.elem) { + .split => |s| s, + else => return, + }; + _ = top_split.equalize(); +} + 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", .{}); diff --git a/src/config/Config.zig b/src/config/Config.zig index de8274cab..3d68cf612 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1008,6 +1008,11 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { .{ .key = .right, .mods = .{ .super = true, .ctrl = true, .shift = true } }, .{ .resize_split = .{ .right, 10 } }, ); + try result.keybind.set.put( + alloc, + .{ .key = .equal, .mods = .{ .super = true, .ctrl = true, .shift = true } }, + .{ .equalize_splits = {} }, + ); // Viewport scrolling try result.keybind.set.put(