vxfw: add SizedBox widget

This commit is contained in:
Tim Culverhouse 2024-10-30 15:16:04 -05:00
parent fb16eafdb6
commit 38eba29d0a
2 changed files with 110 additions and 0 deletions

109
src/vxfw/SizedBox.zig Normal file
View file

@ -0,0 +1,109 @@
const std = @import("std");
const vaxis = @import("../main.zig");
const Allocator = std.mem.Allocator;
const vxfw = @import("vxfw.zig");
const SizedBox = @This();
child: vxfw.Widget,
size: vxfw.Size,
pub fn widget(self: *const SizedBox) vxfw.Widget {
return .{
.userdata = @constCast(self),
.eventHandler = typeErasedEventHandler,
.drawFn = typeErasedDrawFn,
};
}
fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void {
const self: *const SizedBox = @ptrCast(@alignCast(ptr));
return self.child.handleEvent(ctx, event);
}
fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface {
const self: *const SizedBox = @ptrCast(@alignCast(ptr));
const max: vxfw.MaxSize = .{
.width = if (ctx.max.width) |max_w| @min(max_w, self.size.width) else self.size.width,
.height = if (ctx.max.height) |max_h| @min(max_h, self.size.height) else self.size.height,
};
const min: vxfw.Size = .{
.width = @max(ctx.min.width, max.width.?),
.height = @max(ctx.min.height, max.height.?),
};
return self.child.draw(ctx.withConstraints(min, max));
}
test SizedBox {
// Create a test widget that saves the constraints it was given
const TestWidget = struct {
min: vxfw.Size,
max: vxfw.MaxSize,
pub fn widget(self: *@This()) vxfw.Widget {
return .{
.userdata = self,
.eventHandler = vxfw.noopEventHandler,
.drawFn = @This().typeErasedDrawFn,
};
}
fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) std.mem.Allocator.Error!vxfw.Surface {
const self: *@This() = @ptrCast(@alignCast(ptr));
self.min = ctx.min;
self.max = ctx.max;
return .{
.size = ctx.min,
.widget = self.widget(),
.buffer = &.{},
.children = &.{},
};
}
};
// Boiler plate draw context
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const ucd = try vaxis.Unicode.init(arena.allocator());
vxfw.DrawContext.init(&ucd, .unicode);
var draw_ctx: vxfw.DrawContext = .{
.arena = arena.allocator(),
.min = .{},
.max = .{ .width = 16, .height = 16 },
};
var test_widget: TestWidget = .{ .min = .{}, .max = .{} };
// SizedBox tries to draw the child widget at the specified size. It will shrink to fit within
// constraints
const sized_box: SizedBox = .{
.child = test_widget.widget(),
.size = .{ .width = 10, .height = 10 },
};
const box_widget = sized_box.widget();
_ = try box_widget.draw(draw_ctx);
// The sized box is smaller than the constraints, so we should be the desired size
try std.testing.expectEqual(sized_box.size, test_widget.min);
try std.testing.expectEqual(sized_box.size, test_widget.max.size());
draw_ctx.max.height = 8;
_ = try box_widget.draw(draw_ctx);
// The sized box is smaller than the constraints, so we should be that size
try std.testing.expectEqual(@as(vxfw.Size, .{ .width = 10, .height = 8 }), test_widget.min);
try std.testing.expectEqual(@as(vxfw.Size, .{ .width = 10, .height = 8 }), test_widget.max.size());
draw_ctx.max.width = 8;
_ = try box_widget.draw(draw_ctx);
// The sized box is smaller than the constraints, so we should be that size
try std.testing.expectEqual(@as(vxfw.Size, .{ .width = 8, .height = 8 }), test_widget.min);
try std.testing.expectEqual(@as(vxfw.Size, .{ .width = 8, .height = 8 }), test_widget.max.size());
}
test "refAllDecls" {
std.testing.refAllDecls(@This());
}

View file

@ -18,6 +18,7 @@ pub const FlexRow = @import("FlexRow.zig");
pub const ListView = @import("ListView.zig");
pub const Padding = @import("Padding.zig");
pub const RichText = @import("RichText.zig");
pub const SizedBox = @import("SizedBox.zig");
pub const Text = @import("Text.zig");
pub const TextField = @import("TextField.zig");