Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions core/widgets/TreeWidget.zig
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,10 @@ pub const Branch = struct {
/// inside it are carried with it.
carried_source: bool = false,
expanded: bool = false,
/// Left/Right pressed on this row this frame: `expander()` toggles the branch the way a
/// click does (animated, and before the caller reads `expanded` back), so a caller passing
/// last frame's open state cannot undo it.
key_toggle: bool = false,
can_expand: bool = false,
anim: ?*dvui.AnimateWidget = null,
/// Populated while `expander_vbox` is active: clip children to `anim`'s content (animated height).
Expand Down Expand Up @@ -703,13 +707,13 @@ pub const Branch = struct {
if (self.expanded) {
dvui.tabIndexNextEx(e.num, self.tree.group.tab_index_prev, self.tree.data().id, false);
} else {
self.expanded = true;
self.key_toggle = true;
}
},
.left => {
e.handle(@src(), self.button.data());
if (self.expanded) {
self.expanded = false;
self.key_toggle = true;
} else if (self.parent_focus_id) |pid| {
dvui.focusWidget(pid, null, e.num);
} else {
Expand Down Expand Up @@ -767,7 +771,7 @@ pub const Branch = struct {
default_opts.override(opts),
);

if (clicked or self.init_options.expanded != self.expanded) {
if (clicked or self.key_toggle or self.init_options.expanded != self.expanded) {
if (self.expanded) {
self.anim.?.init_opts.easing = motion.settle;
self.anim.?.init_opts.duration = motion.duration(@divTrunc(self.init_options.animation_duration, 2));
Expand Down
54 changes: 54 additions & 0 deletions tests/integration.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7242,6 +7242,60 @@ test "tree: a drag put down is not dropped where the pointer last was" {
try std.testing.expect(CarriedTree.landed == null);
}

// A key fold sticks. The caller below keeps its own open state and passes it back each frame, as
// the settings, file and guides trees do, reading `branch.expanded` after `expander()`: Left must
// fold the branch (animated, as a click does) and leave it folded, and Right open it again.
const KeyFoldTree = struct {
const TW = fizzy.core.widgets.TreeWidget;

var focus = false;
var open = true;
var body_drawn = false;

fn frame() anyerror!dvui.App.Result {
var tree = TW.tree(@src(), .{}, .{ .expand = .both });
defer tree.deinit();
const branch = tree.branch(@src(), .{ .expanded = open }, .{ .expand = .horizontal });
defer branch.deinit();
if (focus) {
focus = false;
dvui.focusWidget(branch.button.data().id, null, null);
}
dvui.labelNoFmt(@src(), "row", .{}, .{ .min_size_content = .{ .w = 120, .h = 20 } });
body_drawn = false;
if (branch.expander(@src(), .{}, .{ .expand = .horizontal })) {
body_drawn = true;
dvui.labelNoFmt(@src(), "child", .{}, .{ .min_size_content = .{ .w = 120, .h = 20 } });
}
open = branch.expanded;
return .ok;
}
};

test "tree: a key fold stays folded when the caller passes its open state back" {
var t = try dvui.testing.init(.{ .allocator = std.testing.allocator });
defer t.deinit();
KeyFoldTree.focus = true;
KeyFoldTree.open = true;
try dvui.testing.settle(KeyFoldTree.frame);
try std.testing.expect(KeyFoldTree.open and KeyFoldTree.body_drawn);

try dvui.testing.pressKey(.left, .none);
// The fold animates: the body is still drawn the frame after the key, closing.
_ = try dvui.testing.step(KeyFoldTree.frame);
try std.testing.expect(KeyFoldTree.body_drawn);
try dvui.testing.settle(KeyFoldTree.frame);
try std.testing.expect(!KeyFoldTree.open and !KeyFoldTree.body_drawn);
_ = try dvui.testing.step(KeyFoldTree.frame);
try std.testing.expect(!KeyFoldTree.open);

try dvui.testing.pressKey(.right, .none);
try dvui.testing.settle(KeyFoldTree.frame);
try std.testing.expect(KeyFoldTree.open and KeyFoldTree.body_drawn);
_ = try dvui.testing.step(KeyFoldTree.frame);
try std.testing.expect(KeyFoldTree.open);
}

test "a frost never captures more than the picture it reads" {
const within = fizzy.core.widgets.BlurBackdrop.within;
const Rect = dvui.Rect.Physical;
Expand Down
Loading