From 8199a2356696c7b1377906fb10ee6a2b08fe9995 Mon Sep 17 00:00:00 2001 From: Yves Biener Date: Sat, 17 Jan 2026 13:25:26 +0100 Subject: [PATCH] ref: move `Model` manipulation logic in own `Element` *website* This way no `Element` that is only there for rendering requires any internal state. --- src/content.zig | 78 ++++++++++++++++++++++++++++--------------------- src/main.zig | 6 ++-- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/content.zig b/src/content.zig index ad02262..7ec0919 100644 --- a/src/content.zig +++ b/src/content.zig @@ -1,19 +1,56 @@ -pub fn Content(App: type) type { +pub fn Website(App: type) type { return struct { - allocator: Allocator, + gpa: Allocator, - pub fn init(allocator: Allocator) @This() { - return .{ - .allocator = allocator, - }; + pub fn init(gpa: Allocator) @This() { + return .{ .gpa = gpa }; } + pub fn element(this: *@This()) App.Element { return .{ .ptr = this, .vtable = &.{ - .minSize = minSize, .handle = handle, + }, + }; + } + + fn handle(ctx: *anyopaque, model: *App.Model, event: App.Event) !void { + const this: *@This() = @ptrCast(@alignCast(ctx)); + switch (event) { + .about => { + model.page.deinit(this.gpa); + model.page = .about; + + model.document.deinit(this.gpa); + model.document = .init(try std.fs.cwd().readFileAlloc(this.gpa, "./doc/about.md", std.math.maxInt(usize))); + }, + .blog => |path| { + model.page.deinit(this.gpa); + model.document.deinit(this.gpa); + errdefer { + if (path) |p| this.gpa.free(p); + model.document = .invalidPage; + model.page = .{ .blog = null }; + } + + model.document = .init(try std.fs.cwd().readFileAlloc(this.gpa, if (path) |p| p else "./doc/blog.md", std.math.maxInt(usize))); + model.page = .{ .blog = path }; + }, + else => {}, + } + } + }; +} + +pub fn Content(App: type) type { + return struct { + pub fn element(this: *@This()) App.Element { + return .{ + .ptr = this, + .vtable = &.{ + .minSize = minSize, .content = content, }, }; @@ -25,33 +62,6 @@ pub fn Content(App: type) type { return .{ .y = @intCast(std.mem.count(u8, model.document.content, "\n") + 1) }; } - fn handle(ctx: *anyopaque, model: *App.Model, event: App.Event) !void { - // FIX this `Element` should not handle the de-/allocation of `Model` contents! - const this: *@This() = @ptrCast(@alignCast(ctx)); - switch (event) { - .about => { - model.page.deinit(this.allocator); - model.page = .about; - - model.document.deinit(this.allocator); - model.document = .init(try std.fs.cwd().readFileAlloc(this.allocator, "./doc/about.md", std.math.maxInt(usize))); - }, - .blog => |path| { - model.page.deinit(this.allocator); - model.document.deinit(this.allocator); - errdefer { - if (path) |p| this.allocator.free(p); - model.document = .invalidPage; - model.page = .{ .blog = null }; - } - - model.document = .init(try std.fs.cwd().readFileAlloc(this.allocator, if (path) |p| p else "./doc/blog.md", std.math.maxInt(usize))); - model.page = .{ .blog = path }; - }, - else => {}, - } - } - fn content(_: *anyopaque, model: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void { assert(cells.len == @as(usize, size.x) * @as(usize, size.y)); diff --git a/src/main.zig b/src/main.zig index d6470d8..4ae1b1a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -29,13 +29,14 @@ pub fn main() !void { var renderer = zterm.Renderer.Buffered.init(allocator); defer renderer.deinit(); + var website: Website = .init(allocator); var container = try App.Container.init(allocator, .{ .layout = .{ .padding = .horizontal(2), .direction = .vertical, .separator = .{ .enabled = true }, }, - }, .{}); + }, website.element()); defer container.deinit(); var content_container: App.Container = undefined; @@ -79,7 +80,7 @@ pub fn main() !void { } // main actual tui_website page content { - var content: Content = .init(allocator); + var content: Content = .{}; content_container = try .init(allocator, .{}, content.element()); var scrollable: App.Scrollable = .init(content_container, .enabled(.green, false)); @@ -185,6 +186,7 @@ const App = zterm.App( const contents = @import("content.zig"); const Model = @import("model.zig"); +const Website = contents.Website(App); const Content = contents.Content(App); const Title = contents.Title(App); const InfoBanner = contents.InfoBanner(App);