//! Framing layout for a nested `Layout`s or `Widget`s. //! //! # Example //! ... const std = @import("std"); const terminal = @import("../terminal.zig"); const isTaggedUnion = @import("../event.zig").isTaggedUnion; const Error = @import("../event.zig").Error; const Key = terminal.Key; const log = std.log.scoped(.layout_framing); pub fn Layout(comptime Event: type, comptime Renderer: type) type { if (!isTaggedUnion(Event)) { @compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`."); } const Element = union(enum) { layout: @import("../layout.zig").Layout(Event, Renderer), widget: @import("../widget.zig").Widget(Event, Renderer), }; const Events = std.ArrayList(Event); return struct { size: terminal.Size = undefined, element: Element = undefined, events: Events = undefined, pub fn init(allocator: std.mem.Allocator, element: Element) @This() { return .{ .element = element, .events = Events.init(allocator), }; } pub fn deinit(this: *@This()) void { this.events.deinit(); switch ((&this.element).*) { .layout => |*layout| { layout.deinit(); }, .widget => |*widget| { widget.deinit(); }, } } pub fn handle(this: *@This(), event: Event) !*Events { this.events.clearRetainingCapacity(); // order is important switch (event) { .resize => |size| { this.size = size; // adjust size according to the containing elements const sub_event = event; switch ((&this.element).*) { .layout => |*layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, .widget => |*widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } }, } }, else => { switch ((&this.element).*) { .layout => |*layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, .widget => |*widget| { if (widget.handle(event)) |e| { try this.events.append(e); } }, } }, } return &this.events; } pub fn render(this: *@This(), renderer: Renderer) !void { // TODO: padding contents accordingly switch ((&this.element).*) { .layout => |*layout| { try layout.render(renderer); }, .widget => |*widget| { const content = try widget.content(); // TODO: use renderer _ = try terminal.write(content); }, } } }; }