Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 29s
The .resize `Event` has been adapted to include an _anchor_, which provide the full necessary information for each widget where to render on the screen with what requested size. Each Widget can then dynamically decide how and what to render (i.e. provide placeholder text in case the size is too small, etc.).
99 lines
3.4 KiB
Zig
99 lines
3.4 KiB
Zig
//! 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);
|
|
},
|
|
}
|
|
}
|
|
};
|
|
}
|