apprt/gtk-ng: equalize splits

pull/8211/head
Mitchell Hashimoto 2025-08-11 11:25:23 -07:00
parent a21b447c75
commit 9f037a7c23
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
3 changed files with 108 additions and 1 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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" {