mod: use allocators for Layout and Widget types

This commit is contained in:
2024-11-19 22:56:37 +01:00
parent cd12fb12e6
commit 1c703a196a
18 changed files with 264 additions and 153 deletions

View File

@@ -100,7 +100,8 @@ pub fn build(b: *std.Build) void {
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
lib_unit_tests.root_module.addImport("zg", zg.module("code_point")); lib_unit_tests.root_module.addImport("interface", interface.module("interface"));
lib_unit_tests.root_module.addImport("code_point", zg.module("code_point"));
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

View File

@@ -30,19 +30,19 @@ pub fn main() !void {
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
// -> size hint how much should it use? // -> size hint how much should it use?
var layout = Layout.createFrom(Layout.HContainer.init(allocator, .{ var layout = Layout.createFrom(allocator, Layout.HContainer.init(allocator, .{
.{ .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
15, 15,
}, },
.{ .{
Layout.createFrom(Layout.VContainer.init(allocator, .{ Layout.createFrom(allocator, Layout.VContainer.init(allocator, .{
.{ .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
25, 25,
}, },
.{ .{
Widget.createFrom(blk: { Widget.createFrom(allocator, blk: {
const file = try std.fs.cwd().openFile("./src/app.zig", .{}); const file = try std.fs.cwd().openFile("./src/app.zig", .{});
defer file.close(); defer file.close();
const widget = Widget.RawText.init(allocator, file); const widget = Widget.RawText.init(allocator, file);
@@ -51,14 +51,14 @@ pub fn main() !void {
50, 50,
}, },
.{ .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
25, 25,
}, },
})), })),
70, 70,
}, },
.{ .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
15, 15,
}, },
})); }));

View File

@@ -31,14 +31,14 @@ pub fn main() !void {
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
// -> size hint how much should it use? // -> size hint how much should it use?
var layout = Layout.createFrom(Layout.VContainer.init(allocator, .{ var layout = Layout.createFrom(allocator, Layout.VContainer.init(allocator, .{
.{ .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
45, 45,
}, },
.{ .{
Layout.createFrom(Layout.Framing.init(allocator, .{}, .{ Layout.createFrom(allocator, Layout.Framing.init(allocator, .{}, .{
.widget = Widget.createFrom(Widget.Text.init(allocator, .center, &[_]Cell{ .widget = Widget.createFrom(allocator, Widget.Text.init(allocator, .center, &[_]Cell{
.{ .content = "Press " }, .{ .content = "Press " },
.{ .content = "Ctrl+n", .style = .{ .fg = .{ .index = 6 } } }, .{ .content = "Ctrl+n", .style = .{ .fg = .{ .index = 6 } } },
.{ .content = " to launch $EDITOR" }, .{ .content = " to launch $EDITOR" },
@@ -47,7 +47,7 @@ pub fn main() !void {
10, 10,
}, },
.{ .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
45, 45,
}, },
})); }));

View File

@@ -30,10 +30,10 @@ pub fn main() !void {
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
// -> size hint how much should it use? // -> size hint how much should it use?
var layout = Layout.createFrom(Layout.Padding.init(allocator, .{ var layout = Layout.createFrom(allocator, Layout.Padding.init(allocator, .{
.padding = 15, .padding = 15,
}, .{ }, .{
.layout = Layout.createFrom(Layout.Framing.init(allocator, .{ .layout = Layout.createFrom(allocator, Layout.Framing.init(allocator, .{
.style = .{ .style = .{
.fg = .{ .fg = .{
.index = 6, .index = 6,
@@ -49,13 +49,13 @@ pub fn main() !void {
}, },
}, },
}, .{ }, .{
.layout = Layout.createFrom(Layout.Margin.init( .layout = Layout.createFrom(allocator, Layout.Margin.init(
allocator, allocator,
.{ .{
.margin = 10, .margin = 10,
}, },
.{ .{
.widget = Widget.createFrom(blk: { .widget = Widget.createFrom(allocator, blk: {
const file = try std.fs.cwd().openFile("./examples/padding.zig", .{}); const file = try std.fs.cwd().openFile("./examples/padding.zig", .{});
defer file.close(); defer file.close();
const widget = Widget.RawText.init(allocator, file); const widget = Widget.RawText.init(allocator, file);

View File

@@ -30,7 +30,7 @@ pub fn main() !void {
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
// -> size hint how much should it use? // -> size hint how much should it use?
var layout = Layout.createFrom(Layout.Framing.init(allocator, .{ var layout = Layout.createFrom(allocator, Layout.Framing.init(allocator, .{
.style = .{ .style = .{
.fg = .{ .fg = .{
.index = 6, .index = 6,
@@ -46,9 +46,9 @@ pub fn main() !void {
}, },
}, },
}, .{ }, .{
.layout = Layout.createFrom(Layout.HStack.init(allocator, .{ .layout = Layout.createFrom(allocator, Layout.HStack.init(allocator, .{
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
Layout.createFrom(Layout.Framing.init( Layout.createFrom(allocator, Layout.Framing.init(
allocator, allocator,
.{ .{
.style = .{ .style = .{
@@ -67,18 +67,18 @@ pub fn main() !void {
}, },
}, },
.{ .{
.layout = Layout.createFrom(Layout.Margin.init(allocator, .{ .layout = Layout.createFrom(allocator, Layout.Margin.init(allocator, .{
.margin = 10, .margin = 10,
}, .{ }, .{
.layout = Layout.createFrom(Layout.VStack.init(allocator, .{ .layout = Layout.createFrom(allocator, Layout.VStack.init(allocator, .{
Widget.createFrom(blk: { Widget.createFrom(allocator, blk: {
const file = try std.fs.cwd().openFile("./examples/stack.zig", .{}); const file = try std.fs.cwd().openFile("./examples/stack.zig", .{});
defer file.close(); defer file.close();
const widget = Widget.RawText.init(allocator, file); const widget = Widget.RawText.init(allocator, file);
break :blk widget; break :blk widget;
}), }),
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
Widget.createFrom(blk: { Widget.createFrom(allocator, blk: {
const file = try std.fs.cwd().openFile("./examples/stack.zig", .{}); const file = try std.fs.cwd().openFile("./examples/stack.zig", .{});
defer file.close(); defer file.close();
const widget = Widget.RawText.init(allocator, file); const widget = Widget.RawText.init(allocator, file);
@@ -88,7 +88,7 @@ pub fn main() !void {
})), })),
}, },
)), )),
Widget.createFrom(Widget.Spacer.init(allocator)), Widget.createFrom(allocator, Widget.Spacer.init(allocator)),
})), })),
})); }));
defer layout.deinit(); defer layout.deinit();

102
examples/tui.zig Normal file
View File

@@ -0,0 +1,102 @@
const std = @import("std");
const zterm = @import("zterm");
const App = zterm.App(
union(enum) {},
zterm.Renderer.Direct,
true,
);
const Cell = zterm.Cell;
const Key = zterm.Key;
const Layout = App.Layout;
const Widget = App.Widget;
const log = std.log.scoped(.tui);
pub fn main() !void {
errdefer |err| log.err("Application Error: {any}", .{err});
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var app: App = .{};
var renderer: App.Renderer = .{};
// FIXME: the layout creates an 'incorrect alignment'?
var layout = Layout.createFrom(allocator, Layout.VContainer.init(allocator, .{
.{
Layout.createFrom(allocator, Layout.Framing.init(allocator, .{
.title = .{
.str = "Welcome to my terminal website",
.style = .{
.ul = .{ .index = 6 },
.ul_style = .single,
},
},
}, .{
.layout = Layout.createFrom(allocator, Layout.HContainer.init(allocator, .{
.{
Widget.createFrom(allocator, Widget.Text.init(allocator, .left, &[1]Cell{
.{ .content = "Yves Biener", .style = .{ .bold = true } },
})),
25,
},
.{
Widget.createFrom(allocator, Widget.Text.init(allocator, .center, &[1]Cell{
.{ .content = "File name", .style = .{ .bold = true } },
})),
50,
},
.{
Widget.createFrom(allocator, Widget.Text.init(allocator, .right, &[1]Cell{
.{ .content = "Contact", .style = .{ .bold = true, .ul_style = .single } },
})),
25,
},
})),
})),
10,
},
.{
Layout.createFrom(allocator, Layout.Margin.init(allocator, .{ .left = 15, .right = 15 }, .{
.widget = Widget.createFrom(allocator, Widget.Text.init(allocator, .default, &[1]Cell{
.{ .content = "Does this change anything?", .style = .{ .bold = true, .ul_style = .single } },
})),
})),
90,
},
}));
defer layout.deinit();
try app.start();
defer app.stop() catch unreachable;
// App.Event loop
while (true) {
const event = app.nextEvent();
switch (event) {
.quit => break,
.resize => |size| {
renderer.resize(size);
},
.key => |key| {
// ctrl+c to quit
if (Key.matches(key, .{ .cp = 'c', .mod = .{ .ctrl = true } })) {
app.quit();
}
},
.err => |err| {
log.err("Received {any} with message: {s}", .{ err.err, err.msg });
},
}
const events = try layout.handle(event);
for (events.items) |e| {
app.postEvent(e);
}
try layout.render(&renderer);
}
}

View File

@@ -22,8 +22,8 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
const Type = struct { const Type = struct {
const LayoutType = @This(); const LayoutType = @This();
const Element = union(enum) { const Element = union(enum) {
layout: LayoutType, layout: *LayoutType,
widget: @import("widget.zig").Widget(Event, Renderer), widget: *@import("widget.zig").Widget(Event, Renderer),
}; };
pub const Interface = @import("interface").Interface(.{ pub const Interface = @import("interface").Interface(.{
.handle = fn (anytype, Event) anyerror!*Events, .handle = fn (anytype, Event) anyerror!*Events,
@@ -37,6 +37,7 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
deinit: *const fn (this: *LayoutType) void, deinit: *const fn (this: *LayoutType) void,
}; };
allocator: std.mem.Allocator = undefined,
object: *anyopaque = undefined, object: *anyopaque = undefined,
vtable: *const VTable = undefined, vtable: *const VTable = undefined,
@@ -52,34 +53,36 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type {
pub fn deinit(this: *LayoutType) void { pub fn deinit(this: *LayoutType) void {
this.vtable.deinit(this); this.vtable.deinit(this);
this.allocator.destroy(this);
} }
pub fn createFrom(object: anytype) LayoutType { pub fn createFrom(allocator: std.mem.Allocator, object: anytype) *LayoutType {
return LayoutType{ const layout = allocator.create(LayoutType) catch @panic("layout.zig: out of memory");
.object = @ptrCast(@alignCast(object)), layout.allocator = allocator;
.vtable = &.{ layout.object = @ptrCast(object);
layout.vtable = &.{
.handle = struct { .handle = struct {
// Handle the provided `Event` for this `Layout`. // Handle the provided `Event` for this `Layout`.
fn handle(this: *LayoutType, event: Event) !*Events { fn handle(this: *LayoutType, event: Event) !*Events {
const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object)); const layout_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object));
return try layout.handle(event); return try layout_ptr.handle(event);
} }
}.handle, }.handle,
.render = struct { .render = struct {
// Render the contents of this `Layout`. // Render the contents of this `Layout`.
fn render(this: *LayoutType, renderer: *Renderer) !void { fn render(this: *LayoutType, renderer: *Renderer) !void {
const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object)); const layout_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object));
try layout.render(renderer); try layout_ptr.render(renderer);
} }
}.render, }.render,
.deinit = struct { .deinit = struct {
fn deinit(this: *LayoutType) void { fn deinit(this: *LayoutType) void {
const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object)); const layout_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object));
layout.deinit(); layout_ptr.deinit();
} }
}.deinit, }.deinit,
},
}; };
return layout;
} }
// import and export of `Layout` implementations // import and export of `Layout` implementations

View File

@@ -51,7 +51,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}; };
pub fn init(allocator: std.mem.Allocator, config: Config, element: Element) *@This() { pub fn init(allocator: std.mem.Allocator, config: Config, element: Element) *@This() {
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("Framing.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.config = config; layout.config = config;
layout.element = element; layout.element = element;
@@ -62,10 +62,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn deinit(this: *@This()) void { pub fn deinit(this: *@This()) void {
this.events.deinit(); this.events.deinit();
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -97,11 +97,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}, },
}; };
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -110,11 +110,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}, },
else => { else => {
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -185,10 +185,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -49,7 +49,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
comptime var total_size = 0; comptime var total_size = 0;
const fields_info = args_type_info.@"struct".fields; const fields_info = args_type_info.@"struct".fields;
var containers = Containers.initCapacity(allocator, fields_info.len) catch @panic("OOM"); var containers = Containers.initCapacity(allocator, fields_info.len) catch @panic("HContainer.zig: out of memory");
inline for (comptime fields_info) |field| { inline for (comptime fields_info) |field| {
const child = @field(children, field.name); const child = @field(children, field.name);
const ChildType = @TypeOf(child); const ChildType = @TypeOf(child);
@@ -88,7 +88,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
@compileError("nested child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType)); @compileError("nested child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType));
} }
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("HContainer.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.containers = containers; layout.containers = containers;
layout.events = Events.init(allocator); layout.events = Events.init(allocator);
@@ -99,10 +99,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
this.events.deinit(); this.events.deinit();
for (this.containers.items) |*container| { for (this.containers.items) |*container| {
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -140,11 +140,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}; };
offset += cols; offset += cols;
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -155,11 +155,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
else => { else => {
for (this.containers.items) |*container| { for (this.containers.items) |*container| {
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -174,10 +174,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn render(this: *@This(), renderer: *Renderer) !void { pub fn render(this: *@This(), renderer: *Renderer) !void {
for (this.containers.items) |*container| { for (this.containers.items) |*container| {
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -42,7 +42,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
@compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
} }
const fields_info = args_type_info.@"struct".fields; const fields_info = args_type_info.@"struct".fields;
var elements = Elements.initCapacity(allocator, fields_info.len) catch @panic("OOM"); var elements = Elements.initCapacity(allocator, fields_info.len) catch @panic("HStack.zig: out of memory");
inline for (comptime fields_info) |field| { inline for (comptime fields_info) |field| {
const child = @field(children, field.name); const child = @field(children, field.name);
const ChildType = @TypeOf(child); const ChildType = @TypeOf(child);
@@ -56,7 +56,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
@compileError("child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType)); @compileError("child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType));
} }
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("HStack.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.elements = elements; layout.elements = elements;
layout.events = Events.init(allocator); layout.events = Events.init(allocator);
@@ -67,10 +67,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
this.events.deinit(); this.events.deinit();
for (this.elements.items) |*element| { for (this.elements.items) |*element| {
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -115,11 +115,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}; };
offset += cols; offset += cols;
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -130,11 +130,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
else => { else => {
for (this.elements.items) |*element| { for (this.elements.items) |*element| {
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -149,10 +149,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn render(this: *@This(), renderer: *Renderer) !void { pub fn render(this: *@This(), renderer: *Renderer) !void {
for (this.elements.items) |*element| { for (this.elements.items) |*element| {
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -34,7 +34,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
config: Config = undefined, config: Config = undefined,
const Config = struct { const Config = struct {
margin: ?u8 = undefined, margin: ?u8 = null,
left: u8 = 0, left: u8 = 0,
right: u8 = 0, right: u8 = 0,
top: u8 = 0, top: u8 = 0,
@@ -48,7 +48,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
std.debug.assert(config.left + config.right < 100); std.debug.assert(config.left + config.right < 100);
std.debug.assert(config.top + config.bottom < 100); std.debug.assert(config.top + config.bottom < 100);
} }
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("Margin.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.config = config; layout.config = config;
layout.element = element; layout.element = element;
@@ -59,10 +59,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn deinit(this: *@This()) void { pub fn deinit(this: *@This()) void {
this.events.deinit(); this.events.deinit();
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -116,11 +116,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
// adjust size according to the containing elements // adjust size according to the containing elements
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -129,11 +129,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}, },
else => { else => {
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -151,10 +151,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -34,7 +34,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
config: Config = undefined, config: Config = undefined,
const Config = struct { const Config = struct {
padding: ?u16 = undefined, padding: ?u16 = null,
left: u16 = 0, left: u16 = 0,
right: u16 = 0, right: u16 = 0,
top: u16 = 0, top: u16 = 0,
@@ -42,7 +42,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}; };
pub fn init(allocator: std.mem.Allocator, config: Config, element: Element) *@This() { pub fn init(allocator: std.mem.Allocator, config: Config, element: Element) *@This() {
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("Padding.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.config = config; layout.config = config;
layout.element = element; layout.element = element;
@@ -53,10 +53,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn deinit(this: *@This()) void { pub fn deinit(this: *@This()) void {
this.events.deinit(); this.events.deinit();
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -104,11 +104,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
// adjust size according to the containing elements // adjust size according to the containing elements
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -117,11 +117,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}, },
else => { else => {
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -139,10 +139,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
switch ((&this.element).*) { switch ((&this.element).*) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -49,7 +49,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
comptime var total_size = 0; comptime var total_size = 0;
const fields_info = args_type_info.@"struct".fields; const fields_info = args_type_info.@"struct".fields;
var containers = Containers.initCapacity(allocator, fields_info.len) catch @panic("OOM"); var containers = Containers.initCapacity(allocator, fields_info.len) catch @panic("VContainer.zig: out of memory");
inline for (comptime fields_info) |field| { inline for (comptime fields_info) |field| {
const child = @field(children, field.name); const child = @field(children, field.name);
const ChildType = @TypeOf(child); const ChildType = @TypeOf(child);
@@ -88,7 +88,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
@compileError("nested child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType)); @compileError("nested child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType));
} }
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("VContainer.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.containers = containers; layout.containers = containers;
layout.events = Events.init(allocator); layout.events = Events.init(allocator);
@@ -99,10 +99,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
this.events.deinit(); this.events.deinit();
for (this.containers.items) |*container| { for (this.containers.items) |*container| {
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -140,11 +140,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}; };
offset += rows; offset += rows;
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -155,11 +155,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
else => { else => {
for (this.containers.items) |*container| { for (this.containers.items) |*container| {
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -174,10 +174,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn render(this: *@This(), renderer: *Renderer) !void { pub fn render(this: *@This(), renderer: *Renderer) !void {
for (this.containers.items) |*container| { for (this.containers.items) |*container| {
switch (container.element) { switch (container.element) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -42,7 +42,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
@compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType));
} }
const fields_info = args_type_info.@"struct".fields; const fields_info = args_type_info.@"struct".fields;
var elements = Elements.initCapacity(allocator, fields_info.len) catch @panic("OOM"); var elements = Elements.initCapacity(allocator, fields_info.len) catch @panic("VStack.zig: out of memory");
inline for (comptime fields_info) |field| { inline for (comptime fields_info) |field| {
const child = @field(children, field.name); const child = @field(children, field.name);
const ChildType = @TypeOf(child); const ChildType = @TypeOf(child);
@@ -56,7 +56,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
} }
@compileError("child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType)); @compileError("child: " ++ field.name ++ " is not of type " ++ @typeName(WidgetType) ++ " or " ++ @typeName(LayoutType) ++ " but " ++ @typeName(ChildType));
} }
const layout = allocator.create(@This()) catch @panic("OOM"); const layout = allocator.create(@This()) catch @panic("VStack.zig: out of memory");
layout.allocator = allocator; layout.allocator = allocator;
layout.elements = elements; layout.elements = elements;
layout.events = Events.init(allocator); layout.events = Events.init(allocator);
@@ -67,10 +67,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
this.events.deinit(); this.events.deinit();
for (this.elements.items) |*element| { for (this.elements.items) |*element| {
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
layout.deinit(); layout.deinit();
}, },
.widget => |*widget| { .widget => |widget| {
widget.deinit(); widget.deinit();
}, },
} }
@@ -114,11 +114,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
}; };
offset += rows; offset += rows;
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(sub_event); const events = try layout.handle(sub_event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(sub_event)) |e| { if (widget.handle(sub_event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -129,11 +129,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
else => { else => {
for (this.elements.items) |*element| { for (this.elements.items) |*element| {
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
const events = try layout.handle(event); const events = try layout.handle(event);
try this.events.appendSlice(events.items); try this.events.appendSlice(events.items);
}, },
.widget => |*widget| { .widget => |widget| {
if (widget.handle(event)) |e| { if (widget.handle(event)) |e| {
try this.events.append(e); try this.events.append(e);
} }
@@ -148,10 +148,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
pub fn render(this: *@This(), renderer: *Renderer) !void { pub fn render(this: *@This(), renderer: *Renderer) !void {
for (this.elements.items) |*element| { for (this.elements.items) |*element| {
switch (element.*) { switch (element.*) {
.layout => |*layout| { .layout => |layout| {
try layout.render(renderer); try layout.render(renderer);
}, },
.widget => |*widget| { .widget => |widget| {
try widget.render(renderer); try widget.render(renderer);
}, },
} }

View File

@@ -10,6 +10,7 @@
//! //!
//! When `Widget.render` is called the provided `Renderer` type is expected //! When `Widget.render` is called the provided `Renderer` type is expected
//! which handles how contents are rendered for a given widget. //! which handles how contents are rendered for a given widget.
const std = @import("std");
const isTaggedUnion = @import("event.zig").isTaggedUnion; const isTaggedUnion = @import("event.zig").isTaggedUnion;
const log = @import("std").log.scoped(.widget); const log = @import("std").log.scoped(.widget);
@@ -32,6 +33,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
deinit: *const fn (this: *WidgetType) void, deinit: *const fn (this: *WidgetType) void,
}; };
allocator: std.mem.Allocator = undefined,
object: *anyopaque = undefined, object: *anyopaque = undefined,
vtable: *const VTable = undefined, vtable: *const VTable = undefined,
@@ -58,34 +60,36 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
pub fn deinit(this: *WidgetType) void { pub fn deinit(this: *WidgetType) void {
this.vtable.deinit(this); this.vtable.deinit(this);
this.allocator.destroy(this);
} }
pub fn createFrom(object: anytype) WidgetType { pub fn createFrom(allocator: std.mem.Allocator, object: anytype) *WidgetType {
return WidgetType{ const widget = allocator.create(WidgetType) catch @panic("widget.zig: out of memory");
.object = @ptrCast(@alignCast(object)), widget.allocator = allocator;
.vtable = &.{ widget.object = @ptrCast(object);
widget.vtable = &.{
.handle = struct { .handle = struct {
// Handle the provided `Event` for this `Widget`. // Handle the provided `Event` for this `Widget`.
fn handle(this: *WidgetType, event: Event) ?Event { fn handle(this: *WidgetType, event: Event) ?Event {
const widget: @TypeOf(object) = @ptrCast(@alignCast(this.object)); const widget_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object));
return widget.handle(event); return widget_ptr.handle(event);
} }
}.handle, }.handle,
.render = struct { .render = struct {
// Return the entire content of this `Widget`. // Return the entire content of this `Widget`.
fn render(this: *WidgetType, renderer: *Renderer) !void { fn render(this: *WidgetType, renderer: *Renderer) !void {
const widget: @TypeOf(object) = @ptrCast(@alignCast(this.object)); const widget_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object));
try widget.render(renderer); try widget_ptr.render(renderer);
} }
}.render, }.render,
.deinit = struct { .deinit = struct {
fn deinit(this: *WidgetType) void { fn deinit(this: *WidgetType) void {
const widget: @TypeOf(object) = @ptrCast(@alignCast(this.object)); const widget_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object));
widget.deinit(); widget_ptr.deinit();
} }
}.deinit, }.deinit,
},
}; };
return widget;
} }
// import and export of `Widget` implementations // import and export of `Widget` implementations

View File

@@ -26,15 +26,16 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
var line_index = std.ArrayList(usize).init(allocator); var line_index = std.ArrayList(usize).init(allocator);
file.reader().readAllArrayList(&contents, std.math.maxInt(usize)) catch {}; file.reader().readAllArrayList(&contents, std.math.maxInt(usize)) catch {};
line_index.append(0) catch {}; line_index.append(0) catch {};
for (contents.items, 0..) |item, i| { for (contents.items, 1..) |item, i| {
if (item == '\n') { if (item == '\n') {
line_index.append(i + 1) catch {}; line_index.append(i) catch {};
} }
} }
const widget = allocator.create(@This()) catch @panic("OOM"); const widget = allocator.create(@This()) catch @panic("RawText.zig: out of memory");
widget.allocator = allocator; widget.allocator = allocator;
widget.contents = contents; widget.contents = contents;
widget.line_index = line_index; widget.line_index = line_index;
widget.line = 0;
return widget; return widget;
} }

View File

@@ -16,7 +16,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
size_changed: bool = false, size_changed: bool = false,
pub fn init(allocator: std.mem.Allocator) *@This() { pub fn init(allocator: std.mem.Allocator) *@This() {
const widget = allocator.create(@This()) catch @panic("OOM"); const widget = allocator.create(@This()) catch @panic("Spacer.zig: out of memory");
widget.allocator = allocator; widget.allocator = allocator;
return widget; return widget;
} }

View File

@@ -28,7 +28,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type {
}; };
pub fn init(allocator: std.mem.Allocator, alignment: Alignment, contents: []const Cell) *@This() { pub fn init(allocator: std.mem.Allocator, alignment: Alignment, contents: []const Cell) *@This() {
const widget = allocator.create(@This()) catch @panic("OOM"); const widget = allocator.create(@This()) catch @panic("Text.zig: out of memory");
widget.allocator = allocator; widget.allocator = allocator;
widget.alignment = alignment; widget.alignment = alignment;
widget.contents = contents; widget.contents = contents;