vxfw: add Border widget
This commit is contained in:
parent
2bec962b22
commit
671048c8f0
2 changed files with 105 additions and 0 deletions
104
src/vxfw/Border.zig
Normal file
104
src/vxfw/Border.zig
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const vaxis = @import("../main.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const vxfw = @import("vxfw.zig");
|
||||||
|
|
||||||
|
const Border = @This();
|
||||||
|
|
||||||
|
child: vxfw.Widget,
|
||||||
|
style: vaxis.Style = .{},
|
||||||
|
|
||||||
|
pub fn widget(self: *const Border) vxfw.Widget {
|
||||||
|
return .{
|
||||||
|
.userdata = @constCast(self),
|
||||||
|
.drawFn = typeErasedDrawFn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface {
|
||||||
|
const self: *const Border = @ptrCast(@alignCast(ptr));
|
||||||
|
return self.draw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If Border has a bounded maximum size, it will shrink the maximum size to account for the border
|
||||||
|
/// before drawing the child. If the size is unbounded, border will draw the child and then itself
|
||||||
|
/// around the childs size
|
||||||
|
pub fn draw(self: *const Border, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface {
|
||||||
|
const max_width: ?u16 = if (ctx.max.width) |width| width -| 2 else null;
|
||||||
|
const max_height: ?u16 = if (ctx.max.height) |height| height -| 2 else null;
|
||||||
|
|
||||||
|
const child_ctx = ctx.withConstraints(ctx.min, .{
|
||||||
|
.width = max_width,
|
||||||
|
.height = max_height,
|
||||||
|
});
|
||||||
|
const child = try self.child.draw(child_ctx);
|
||||||
|
|
||||||
|
const children = try ctx.arena.alloc(vxfw.SubSurface, 1);
|
||||||
|
children[0] = .{
|
||||||
|
.origin = .{ .col = 1, .row = 1 },
|
||||||
|
.z_index = 0,
|
||||||
|
.surface = child,
|
||||||
|
};
|
||||||
|
|
||||||
|
const size: vxfw.Size = .{ .width = child.size.width + 2, .height = child.size.height + 2 };
|
||||||
|
|
||||||
|
var surf = try vxfw.Surface.initWithChildren(ctx.arena, self.widget(), size, children);
|
||||||
|
|
||||||
|
// Draw the border
|
||||||
|
const right_edge = size.width -| 1;
|
||||||
|
const bottom_edge = size.height -| 1;
|
||||||
|
surf.writeCell(0, 0, .{ .char = .{ .grapheme = "╭", .width = 1 } });
|
||||||
|
surf.writeCell(right_edge, 0, .{ .char = .{ .grapheme = "╮", .width = 1 } });
|
||||||
|
surf.writeCell(right_edge, bottom_edge, .{ .char = .{ .grapheme = "╯", .width = 1 } });
|
||||||
|
surf.writeCell(0, bottom_edge, .{ .char = .{ .grapheme = "╰", .width = 1 } });
|
||||||
|
|
||||||
|
var col: u16 = 1;
|
||||||
|
while (col < right_edge) : (col += 1) {
|
||||||
|
surf.writeCell(col, 0, .{ .char = .{ .grapheme = "─", .width = 1 } });
|
||||||
|
surf.writeCell(col, bottom_edge, .{ .char = .{ .grapheme = "─", .width = 1 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
var row: u16 = 1;
|
||||||
|
while (row < bottom_edge) : (row += 1) {
|
||||||
|
surf.writeCell(0, row, .{ .char = .{ .grapheme = "│", .width = 1 } });
|
||||||
|
surf.writeCell(right_edge, row, .{ .char = .{ .grapheme = "│", .width = 1 } });
|
||||||
|
}
|
||||||
|
return surf;
|
||||||
|
}
|
||||||
|
|
||||||
|
test Border {
|
||||||
|
const Text = @import("Text.zig");
|
||||||
|
// Will be height=1, width=3
|
||||||
|
const text: Text = .{ .text = "abc" };
|
||||||
|
|
||||||
|
const border: Border = .{ .child = text.widget() };
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Border will draw itself tightly around the child
|
||||||
|
const ctx: vxfw.DrawContext = .{
|
||||||
|
.arena = arena.allocator(),
|
||||||
|
.min = .{},
|
||||||
|
.max = .{ .width = 10, .height = 10 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const surface = try border.draw(ctx);
|
||||||
|
// Border should be the size of Text + 2
|
||||||
|
try std.testing.expectEqual(5, surface.size.width);
|
||||||
|
try std.testing.expectEqual(3, surface.size.height);
|
||||||
|
// Border has 1 child
|
||||||
|
try std.testing.expectEqual(1, surface.children.len);
|
||||||
|
const child = surface.children[0];
|
||||||
|
// The child is 1x3
|
||||||
|
try std.testing.expectEqual(3, child.surface.size.width);
|
||||||
|
try std.testing.expectEqual(1, child.surface.size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "refAllDecls" {
|
||||||
|
std.testing.refAllDecls(@This());
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ const Allocator = std.mem.Allocator;
|
||||||
pub const App = @import("App.zig");
|
pub const App = @import("App.zig");
|
||||||
|
|
||||||
// Widgets
|
// Widgets
|
||||||
|
pub const Border = @import("Border.zig");
|
||||||
pub const Button = @import("Button.zig");
|
pub const Button = @import("Button.zig");
|
||||||
pub const Center = @import("Center.zig");
|
pub const Center = @import("Center.zig");
|
||||||
pub const FlexColumn = @import("FlexColumn.zig");
|
pub const FlexColumn = @import("FlexColumn.zig");
|
||||||
|
|
Loading…
Add table
Reference in a new issue