From 1c703a196a6e88ea3004bdd493690513b448fca5 Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Tue, 19 Nov 2024 22:56:37 +0100 Subject: [PATCH] mod: use allocators for Layout and Widget types --- build.zig | 3 +- examples/container.zig | 14 +++--- examples/exec.zig | 10 ++-- examples/padding.zig | 8 +-- examples/stack.zig | 20 ++++---- examples/tui.zig | 102 ++++++++++++++++++++++++++++++++++++++ src/layout.zig | 57 +++++++++++---------- src/layout/Framing.zig | 18 +++---- src/layout/HContainer.zig | 20 ++++---- src/layout/HStack.zig | 20 ++++---- src/layout/Margin.zig | 20 ++++---- src/layout/Padding.zig | 20 ++++---- src/layout/VContainer.zig | 20 ++++---- src/layout/VStack.zig | 20 ++++---- src/widget.zig | 54 ++++++++++---------- src/widget/RawText.zig | 7 +-- src/widget/Spacer.zig | 2 +- src/widget/Text.zig | 2 +- 18 files changed, 264 insertions(+), 153 deletions(-) create mode 100644 examples/tui.zig diff --git a/build.zig b/build.zig index d146e05..092c30c 100644 --- a/build.zig +++ b/build.zig @@ -100,7 +100,8 @@ pub fn build(b: *std.Build) void { .target = target, .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); diff --git a/examples/container.zig b/examples/container.zig index 5704678..cce6288 100644 --- a/examples/container.zig +++ b/examples/container.zig @@ -30,19 +30,19 @@ pub fn main() !void { // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // -> 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, }, .{ - 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, }, .{ - Widget.createFrom(blk: { + Widget.createFrom(allocator, blk: { const file = try std.fs.cwd().openFile("./src/app.zig", .{}); defer file.close(); const widget = Widget.RawText.init(allocator, file); @@ -51,14 +51,14 @@ pub fn main() !void { 50, }, .{ - Widget.createFrom(Widget.Spacer.init(allocator)), + Widget.createFrom(allocator, Widget.Spacer.init(allocator)), 25, }, })), 70, }, .{ - Widget.createFrom(Widget.Spacer.init(allocator)), + Widget.createFrom(allocator, Widget.Spacer.init(allocator)), 15, }, })); diff --git a/examples/exec.zig b/examples/exec.zig index 06f7678..0b8f0e0 100644 --- a/examples/exec.zig +++ b/examples/exec.zig @@ -31,14 +31,14 @@ pub fn main() !void { // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // -> 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, }, .{ - Layout.createFrom(Layout.Framing.init(allocator, .{}, .{ - .widget = Widget.createFrom(Widget.Text.init(allocator, .center, &[_]Cell{ + Layout.createFrom(allocator, Layout.Framing.init(allocator, .{}, .{ + .widget = Widget.createFrom(allocator, Widget.Text.init(allocator, .center, &[_]Cell{ .{ .content = "Press " }, .{ .content = "Ctrl+n", .style = .{ .fg = .{ .index = 6 } } }, .{ .content = " to launch $EDITOR" }, @@ -47,7 +47,7 @@ pub fn main() !void { 10, }, .{ - Widget.createFrom(Widget.Spacer.init(allocator)), + Widget.createFrom(allocator, Widget.Spacer.init(allocator)), 45, }, })); diff --git a/examples/padding.zig b/examples/padding.zig index 0b2ef4f..1ca705f 100644 --- a/examples/padding.zig +++ b/examples/padding.zig @@ -30,10 +30,10 @@ pub fn main() !void { // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // -> 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, }, .{ - .layout = Layout.createFrom(Layout.Framing.init(allocator, .{ + .layout = Layout.createFrom(allocator, Layout.Framing.init(allocator, .{ .style = .{ .fg = .{ .index = 6, @@ -49,13 +49,13 @@ pub fn main() !void { }, }, }, .{ - .layout = Layout.createFrom(Layout.Margin.init( + .layout = Layout.createFrom(allocator, Layout.Margin.init( allocator, .{ .margin = 10, }, .{ - .widget = Widget.createFrom(blk: { + .widget = Widget.createFrom(allocator, blk: { const file = try std.fs.cwd().openFile("./examples/padding.zig", .{}); defer file.close(); const widget = Widget.RawText.init(allocator, file); diff --git a/examples/stack.zig b/examples/stack.zig index f96516f..2a9af83 100644 --- a/examples/stack.zig +++ b/examples/stack.zig @@ -30,7 +30,7 @@ pub fn main() !void { // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // -> 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 = .{ .fg = .{ .index = 6, @@ -46,9 +46,9 @@ pub fn main() !void { }, }, }, .{ - .layout = Layout.createFrom(Layout.HStack.init(allocator, .{ - Widget.createFrom(Widget.Spacer.init(allocator)), - Layout.createFrom(Layout.Framing.init( + .layout = Layout.createFrom(allocator, Layout.HStack.init(allocator, .{ + Widget.createFrom(allocator, Widget.Spacer.init(allocator)), + Layout.createFrom(allocator, Layout.Framing.init( allocator, .{ .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, }, .{ - .layout = Layout.createFrom(Layout.VStack.init(allocator, .{ - Widget.createFrom(blk: { + .layout = Layout.createFrom(allocator, Layout.VStack.init(allocator, .{ + Widget.createFrom(allocator, blk: { const file = try std.fs.cwd().openFile("./examples/stack.zig", .{}); defer file.close(); const widget = Widget.RawText.init(allocator, file); break :blk widget; }), - Widget.createFrom(Widget.Spacer.init(allocator)), - Widget.createFrom(blk: { + Widget.createFrom(allocator, Widget.Spacer.init(allocator)), + Widget.createFrom(allocator, blk: { const file = try std.fs.cwd().openFile("./examples/stack.zig", .{}); defer file.close(); 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(); diff --git a/examples/tui.zig b/examples/tui.zig new file mode 100644 index 0000000..ee3dc81 --- /dev/null +++ b/examples/tui.zig @@ -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); + } +} diff --git a/src/layout.zig b/src/layout.zig index b075cf9..9440a83 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -22,8 +22,8 @@ pub fn Layout(comptime Event: type, comptime Renderer: type) type { const Type = struct { const LayoutType = @This(); const Element = union(enum) { - layout: LayoutType, - widget: @import("widget.zig").Widget(Event, Renderer), + layout: *LayoutType, + widget: *@import("widget.zig").Widget(Event, Renderer), }; pub const Interface = @import("interface").Interface(.{ .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, }; + allocator: std.mem.Allocator = undefined, object: *anyopaque = 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 { this.vtable.deinit(this); + this.allocator.destroy(this); } - pub fn createFrom(object: anytype) LayoutType { - return LayoutType{ - .object = @ptrCast(@alignCast(object)), - .vtable = &.{ - .handle = struct { - // Handle the provided `Event` for this `Layout`. - fn handle(this: *LayoutType, event: Event) !*Events { - const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object)); - return try layout.handle(event); - } - }.handle, - .render = struct { - // Render the contents of this `Layout`. - fn render(this: *LayoutType, renderer: *Renderer) !void { - const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object)); - try layout.render(renderer); - } - }.render, - .deinit = struct { - fn deinit(this: *LayoutType) void { - const layout: @TypeOf(object) = @ptrCast(@alignCast(this.object)); - layout.deinit(); - } - }.deinit, - }, + pub fn createFrom(allocator: std.mem.Allocator, object: anytype) *LayoutType { + const layout = allocator.create(LayoutType) catch @panic("layout.zig: out of memory"); + layout.allocator = allocator; + layout.object = @ptrCast(object); + layout.vtable = &.{ + .handle = struct { + // Handle the provided `Event` for this `Layout`. + fn handle(this: *LayoutType, event: Event) !*Events { + const layout_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object)); + return try layout_ptr.handle(event); + } + }.handle, + .render = struct { + // Render the contents of this `Layout`. + fn render(this: *LayoutType, renderer: *Renderer) !void { + const layout_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object)); + try layout_ptr.render(renderer); + } + }.render, + .deinit = struct { + fn deinit(this: *LayoutType) void { + const layout_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object)); + layout_ptr.deinit(); + } + }.deinit, }; + return layout; } // import and export of `Layout` implementations diff --git a/src/layout/Framing.zig b/src/layout/Framing.zig index bf5a573..8de7155 100644 --- a/src/layout/Framing.zig +++ b/src/layout/Framing.zig @@ -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() { - const layout = allocator.create(@This()) catch @panic("OOM"); + const layout = allocator.create(@This()) catch @panic("Framing.zig: out of memory"); layout.allocator = allocator; layout.config = config; 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 { this.events.deinit(); switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { widget.deinit(); }, } @@ -97,11 +97,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }, }; switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -110,11 +110,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }, else => { switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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).*) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/layout/HContainer.zig b/src/layout/HContainer.zig index c25dc7b..7e3ad81 100644 --- a/src/layout/HContainer.zig +++ b/src/layout/HContainer.zig @@ -49,7 +49,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t } comptime var total_size = 0; 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| { const child = @field(children, field.name); 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)); } - const layout = allocator.create(@This()) catch @panic("OOM"); + const layout = allocator.create(@This()) catch @panic("HContainer.zig: out of memory"); layout.allocator = allocator; layout.containers = containers; layout.events = Events.init(allocator); @@ -99,10 +99,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t this.events.deinit(); for (this.containers.items) |*container| { switch (container.element) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { widget.deinit(); }, } @@ -140,11 +140,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }; offset += cols; switch (container.element) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -155,11 +155,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t else => { for (this.containers.items) |*container| { switch (container.element) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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 { for (this.containers.items) |*container| { switch (container.element) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/layout/HStack.zig b/src/layout/HStack.zig index 95cc9f1..3db9f32 100644 --- a/src/layout/HStack.zig +++ b/src/layout/HStack.zig @@ -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)); } 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| { const child = @field(children, field.name); 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)); } - const layout = allocator.create(@This()) catch @panic("OOM"); + const layout = allocator.create(@This()) catch @panic("HStack.zig: out of memory"); layout.allocator = allocator; layout.elements = elements; layout.events = Events.init(allocator); @@ -67,10 +67,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t this.events.deinit(); for (this.elements.items) |*element| { switch (element.*) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { widget.deinit(); }, } @@ -115,11 +115,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }; offset += cols; switch (element.*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -130,11 +130,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t else => { for (this.elements.items) |*element| { switch (element.*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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 { for (this.elements.items) |*element| { switch (element.*) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/layout/Margin.zig b/src/layout/Margin.zig index b1e5a5d..c221d8e 100644 --- a/src/layout/Margin.zig +++ b/src/layout/Margin.zig @@ -34,7 +34,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t config: Config = undefined, const Config = struct { - margin: ?u8 = undefined, + margin: ?u8 = null, left: u8 = 0, right: 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.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.config = config; 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 { this.events.deinit(); switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { 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 switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -129,11 +129,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }, else => { switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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).*) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/layout/Padding.zig b/src/layout/Padding.zig index 672df51..21dcad8 100644 --- a/src/layout/Padding.zig +++ b/src/layout/Padding.zig @@ -34,7 +34,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t config: Config = undefined, const Config = struct { - padding: ?u16 = undefined, + padding: ?u16 = null, left: u16 = 0, right: 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() { - const layout = allocator.create(@This()) catch @panic("OOM"); + const layout = allocator.create(@This()) catch @panic("Padding.zig: out of memory"); layout.allocator = allocator; layout.config = config; 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 { this.events.deinit(); switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { 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 switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -117,11 +117,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }, else => { switch ((&this.element).*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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).*) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/layout/VContainer.zig b/src/layout/VContainer.zig index 1e92144..55be298 100644 --- a/src/layout/VContainer.zig +++ b/src/layout/VContainer.zig @@ -49,7 +49,7 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t } comptime var total_size = 0; 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| { const child = @field(children, field.name); 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)); } - const layout = allocator.create(@This()) catch @panic("OOM"); + const layout = allocator.create(@This()) catch @panic("VContainer.zig: out of memory"); layout.allocator = allocator; layout.containers = containers; layout.events = Events.init(allocator); @@ -99,10 +99,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t this.events.deinit(); for (this.containers.items) |*container| { switch (container.element) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { widget.deinit(); }, } @@ -140,11 +140,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }; offset += rows; switch (container.element) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -155,11 +155,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t else => { for (this.containers.items) |*container| { switch (container.element) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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 { for (this.containers.items) |*container| { switch (container.element) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/layout/VStack.zig b/src/layout/VStack.zig index 5d915d9..0dc1b24 100644 --- a/src/layout/VStack.zig +++ b/src/layout/VStack.zig @@ -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)); } 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| { const child = @field(children, field.name); 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)); } - const layout = allocator.create(@This()) catch @panic("OOM"); + const layout = allocator.create(@This()) catch @panic("VStack.zig: out of memory"); layout.allocator = allocator; layout.elements = elements; layout.events = Events.init(allocator); @@ -67,10 +67,10 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t this.events.deinit(); for (this.elements.items) |*element| { switch (element.*) { - .layout => |*layout| { + .layout => |layout| { layout.deinit(); }, - .widget => |*widget| { + .widget => |widget| { widget.deinit(); }, } @@ -114,11 +114,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t }; offset += rows; switch (element.*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(sub_event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(sub_event)) |e| { try this.events.append(e); } @@ -129,11 +129,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t else => { for (this.elements.items) |*element| { switch (element.*) { - .layout => |*layout| { + .layout => |layout| { const events = try layout.handle(event); try this.events.appendSlice(events.items); }, - .widget => |*widget| { + .widget => |widget| { if (widget.handle(event)) |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 { for (this.elements.items) |*element| { switch (element.*) { - .layout => |*layout| { + .layout => |layout| { try layout.render(renderer); }, - .widget => |*widget| { + .widget => |widget| { try widget.render(renderer); }, } diff --git a/src/widget.zig b/src/widget.zig index 046d9e0..77790d7 100644 --- a/src/widget.zig +++ b/src/widget.zig @@ -10,6 +10,7 @@ //! //! When `Widget.render` is called the provided `Renderer` type is expected //! which handles how contents are rendered for a given widget. +const std = @import("std"); const isTaggedUnion = @import("event.zig").isTaggedUnion; 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, }; + allocator: std.mem.Allocator = undefined, object: *anyopaque = 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 { this.vtable.deinit(this); + this.allocator.destroy(this); } - pub fn createFrom(object: anytype) WidgetType { - return WidgetType{ - .object = @ptrCast(@alignCast(object)), - .vtable = &.{ - .handle = struct { - // Handle the provided `Event` for this `Widget`. - fn handle(this: *WidgetType, event: Event) ?Event { - const widget: @TypeOf(object) = @ptrCast(@alignCast(this.object)); - return widget.handle(event); - } - }.handle, - .render = struct { - // Return the entire content of this `Widget`. - fn render(this: *WidgetType, renderer: *Renderer) !void { - const widget: @TypeOf(object) = @ptrCast(@alignCast(this.object)); - try widget.render(renderer); - } - }.render, - .deinit = struct { - fn deinit(this: *WidgetType) void { - const widget: @TypeOf(object) = @ptrCast(@alignCast(this.object)); - widget.deinit(); - } - }.deinit, - }, + pub fn createFrom(allocator: std.mem.Allocator, object: anytype) *WidgetType { + const widget = allocator.create(WidgetType) catch @panic("widget.zig: out of memory"); + widget.allocator = allocator; + widget.object = @ptrCast(object); + widget.vtable = &.{ + .handle = struct { + // Handle the provided `Event` for this `Widget`. + fn handle(this: *WidgetType, event: Event) ?Event { + const widget_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object)); + return widget_ptr.handle(event); + } + }.handle, + .render = struct { + // Return the entire content of this `Widget`. + fn render(this: *WidgetType, renderer: *Renderer) !void { + const widget_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object)); + try widget_ptr.render(renderer); + } + }.render, + .deinit = struct { + fn deinit(this: *WidgetType) void { + const widget_ptr: @TypeOf(object) = @ptrCast(@alignCast(this.object)); + widget_ptr.deinit(); + } + }.deinit, }; + return widget; } // import and export of `Widget` implementations diff --git a/src/widget/RawText.zig b/src/widget/RawText.zig index 78bdd9c..4722849 100644 --- a/src/widget/RawText.zig +++ b/src/widget/RawText.zig @@ -26,15 +26,16 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type { var line_index = std.ArrayList(usize).init(allocator); file.reader().readAllArrayList(&contents, std.math.maxInt(usize)) catch {}; line_index.append(0) catch {}; - for (contents.items, 0..) |item, i| { + for (contents.items, 1..) |item, i| { 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.contents = contents; widget.line_index = line_index; + widget.line = 0; return widget; } diff --git a/src/widget/Spacer.zig b/src/widget/Spacer.zig index 7e58e50..abac255 100644 --- a/src/widget/Spacer.zig +++ b/src/widget/Spacer.zig @@ -16,7 +16,7 @@ pub fn Widget(comptime Event: type, comptime Renderer: type) type { size_changed: bool = false, 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; return widget; } diff --git a/src/widget/Text.zig b/src/widget/Text.zig index 715e348..1b675c3 100644 --- a/src/widget/Text.zig +++ b/src/widget/Text.zig @@ -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() { - const widget = allocator.create(@This()) catch @panic("OOM"); + const widget = allocator.create(@This()) catch @panic("Text.zig: out of memory"); widget.allocator = allocator; widget.alignment = alignment; widget.contents = contents;