diff --git a/src/apprt/gtk-ng/class/application.zig b/src/apprt/gtk-ng/class/application.zig index a09ce4e64..eda2a49eb 100644 --- a/src/apprt/gtk-ng/class/application.zig +++ b/src/apprt/gtk-ng/class/application.zig @@ -553,6 +553,8 @@ pub const Application = extern struct { .desktop_notification => Action.desktopNotification(self, target, value), + .equalize_splits => return Action.equalizeSplits(target), + .goto_split => return Action.gotoSplit(target, value), .goto_tab => return Action.gotoTab(target, value), @@ -617,7 +619,6 @@ pub const Application = extern struct { .inspector, // TODO: splits .resize_split, - .equalize_splits, .toggle_split_zoom, => { log.warn("unimplemented action={}", .{action}); @@ -1652,6 +1653,20 @@ const Action = struct { gio_app.sendNotification(n.body, notification); } + pub fn equalizeSplits(target: apprt.Target) bool { + switch (target) { + .app => { + log.warn("equalize splits to app is unexpected", .{}); + return false; + }, + + .surface => |core| { + const surface = core.rt_surface.surface; + return surface.as(gtk.Widget).activateAction("split-tree.equalize", null) != 0; + }, + } + } + pub fn gotoSplit( target: apprt.Target, to: apprt.action.GotoSplit, diff --git a/src/apprt/gtk-ng/class/split_tree.zig b/src/apprt/gtk-ng/class/split_tree.zig index 567166329..3018afdb4 100644 --- a/src/apprt/gtk-ng/class/split_tree.zig +++ b/src/apprt/gtk-ng/class/split_tree.zig @@ -163,6 +163,8 @@ pub const SplitTree = extern struct { .{ "new-right", actionNewRight, null }, .{ "new-up", actionNewUp, null }, .{ "new-down", actionNewDown, null }, + + .{ "equalize", actionEqualize, null }, }; // We need to collect our actions into a group since we're just @@ -537,6 +539,22 @@ pub const SplitTree = extern struct { }; } + pub fn actionEqualize( + _: *gio.SimpleAction, + parameter_: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + _ = parameter_; + + const old_tree = self.getTree() orelse return; + var new_tree = old_tree.equalize(Application.default().allocator()) catch |err| { + log.warn("unable to equalize tree: {}", .{err}); + return; + }; + defer new_tree.deinit(); + self.setTree(&new_tree); + } + fn surfaceCloseRequest( surface: *Surface, scope: *const Surface.CloseScope, diff --git a/src/datastruct/split_tree.zig b/src/datastruct/split_tree.zig index 3003b73c7..6d224757b 100644 --- a/src/datastruct/split_tree.zig +++ b/src/datastruct/split_tree.zig @@ -642,6 +642,62 @@ pub fn SplitTree(comptime V: type) type { assert(reffed == nodes.len - 1); } + /// Equalize this node and all its children, returning a new node with splits + /// adjusted so that each split's ratio is based on the relative weight + /// (number of leaves) of its children. + pub fn equalize( + self: *const Self, + gpa: Allocator, + ) Allocator.Error!Self { + if (self.isEmpty()) return .empty; + + // Create a new arena allocator for the clone. + var arena = ArenaAllocator.init(gpa); + errdefer arena.deinit(); + const alloc = arena.allocator(); + + // Allocate a new nodes array and copy the existing nodes into it. + const nodes = try alloc.dupe(Node, self.nodes); + + // Go through and equalize our ratios based on weights. + for (nodes) |*node| switch (node.*) { + .leaf => {}, + .split => |*s| { + const weight_left = self.weight(s.left, s.layout, 0); + const weight_right = self.weight(s.right, s.layout, 0); + assert(weight_left > 0); + assert(weight_right > 0); + const total_f16: f16 = @floatFromInt(weight_left + weight_right); + const weight_left_f16: f16 = @floatFromInt(weight_left); + s.ratio = weight_left_f16 / total_f16; + }, + }; + + // Increase the reference count of all the views in the nodes. + try refNodes(gpa, nodes); + + return .{ + .arena = arena, + .nodes = nodes, + }; + } + + fn weight( + self: *const Self, + from: Node.Handle, + layout: Split.Layout, + acc: usize, + ) usize { + return switch (self.nodes[from]) { + .leaf => acc + 1, + .split => |s| if (s.layout == layout) + self.weight(s.left, layout, acc) + + self.weight(s.right, layout, acc) + else + 1, + }; + } + /// Spatial representation of the split tree. See spatial. pub const Spatial = struct { /// The slots of the spatial representation in the same order @@ -1563,6 +1619,24 @@ test "SplitTree: spatial goto" { const view = split.nodes[target].leaf; try testing.expectEqualStrings("A", view.label); } + + // Equalize + var equal = try split.equalize(alloc); + defer equal.deinit(); + + { + const str = try std.fmt.allocPrint(alloc, "{diagram}", .{equal}); + defer alloc.free(str); + try testing.expectEqualStrings(str, + \\+---++---+ + \\| A || B | + \\+---++---+ + \\+---++---+ + \\| C || D | + \\+---++---+ + \\ + ); + } } test "SplitTree: clone empty tree" {