diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index bc0ba46..ada7bdf 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -17,7 +17,7 @@ jobs: - name: Setup zig installation uses: mlugg/setup-zig@v1 with: - version: 0.13.0 + version: master - name: Run tests run: zig build --release=fast - name: Release build artifacts diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml index 0f6bf71..02a6a3e 100644 --- a/.gitea/workflows/test.yaml +++ b/.gitea/workflows/test.yaml @@ -16,7 +16,7 @@ jobs: - name: Setup zig installation uses: mlugg/setup-zig@v1 with: - version: 0.13.0 + version: master - name: Lint check run: zig fmt --check . - name: Spell checking diff --git a/README.md b/README.md index e01226a..6b8082d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ `zterm` is a terminal user interface library to implement terminal (fullscreen or inline) applications. > [!NOTE] -> Currently version `0.13.0` is officially supported. Builds using the master version might not work. +> Only builds using the master version might will work. ## Usage diff --git a/build.zig b/build.zig index 062f20b..f4275e8 100644 --- a/build.zig +++ b/build.zig @@ -65,6 +65,14 @@ pub fn build(b: *std.Build) void { }); exec_example.root_module.addImport("zterm", lib); + const tui_example = b.addExecutable(.{ + .name = "tui", + .root_source_file = b.path("examples/tui.zig"), + .target = target, + .optimize = optimize, + }); + tui_example.root_module.addImport("zterm", lib); + // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default // step when running `zig build`). @@ -72,6 +80,7 @@ pub fn build(b: *std.Build) void { b.installArtifact(container_example); b.installArtifact(padding_example); b.installArtifact(exec_example); + b.installArtifact(tui_example); // Creates a step for unit testing. This only builds the test executable // but does not run it. diff --git a/build.zig.zon b/build.zig.zon index 050d4c1..3962c9e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -28,8 +28,8 @@ .hash = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40", }, .interface = .{ - .url = "git+https://github.com/nilslice/zig-interface#c6ca205de75969fdcf04542f48d813d529196594", - .hash = "1220401627a97a7b429acd084bd3447fe5838122d71bbca57061906a2c3baf1c2e98", + .url = "git+https://github.com/yves-biener/zig-interface#ef47e045df19e09250fff45c0702d014fb3d3c37", + .hash = "1220a442e8d9b813572bab7a55eef504c83b628f0b17fd283e776dbc1d1a3d98e842", }, }, .paths = .{ diff --git a/examples/tui.zig b/examples/tui.zig new file mode 100644 index 0000000..62b8e48 --- /dev/null +++ b/examples/tui.zig @@ -0,0 +1,126 @@ +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(layout: { + var layout = Layout.VContainer.init(allocator, .{ + .{ + Layout.createFrom(framing: { + var framing = Layout.Framing.init(allocator, .{ + .title = .{ + .str = "Welcome to my terminal website", + .style = .{ + .ul = .{ .index = 6 }, + .ul_style = .single, + }, + }, + }, .{ + .layout = Layout.createFrom(hcontainer: { + var hcontainer = Layout.HContainer.init(allocator, .{ + .{ + Widget.createFrom(text: { + var text = Widget.Text.init(.left, &[1]Cell{ + .{ .content = "Yves Biener", .style = .{ .bold = true } }, + }); + break :text &text; + }), + 25, + }, + .{ + Widget.createFrom(text: { + var text = Widget.Text.init(.left, &[1]Cell{ + .{ .content = "File name", .style = .{ .bold = true } }, + }); + break :text &text; + }), + 50, + }, + .{ + Widget.createFrom(text: { + var text = Widget.Text.init(.left, &[1]Cell{ + .{ .content = "Contacts", .style = .{ .bold = true } }, + }); + break :text &text; + }), + 25, + }, + }); + break :hcontainer &hcontainer; + }), + }); + break :framing &framing; + }), + 10, + }, + .{ + Layout.createFrom(margin: { + var margin = Layout.Margin.init(allocator, .{ .left = 15, .right = 15 }, .{ + .widget = Widget.createFrom(text: { + var text = Widget.Text.init(.default, &[1]Cell{ + .{ .content = "Does this change anything", .style = .{ .ul = .default, .ul_style = .single } }, + }); + break :text &text; + }), + }); + break :margin &margin; + }), + 90, + }, + }); + break :layout &layout; + }); + 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/app.zig b/src/app.zig index 6cee964..2bbbcca 100644 --- a/src/app.zig +++ b/src/app.zig @@ -69,7 +69,7 @@ pub fn App(comptime E: type, comptime R: fn (comptime bool) type, comptime fulls .mask = std.posix.empty_sigset, .flags = 0, }; - std.posix.sigaction(std.posix.SIG.WINCH, &winch_act, null) catch @panic("could not attach signal WINCH"); + std.posix.sigaction(std.posix.SIG.WINCH, &winch_act, null); try registerWinch(.{ .context = this, diff --git a/src/event.zig b/src/event.zig index 736042c..2686275 100644 --- a/src/event.zig +++ b/src/event.zig @@ -23,12 +23,12 @@ pub fn mergeTaggedUnions(comptime A: type, comptime B: type) type { if (!isTaggedUnion(A) or !isTaggedUnion(B)) { @compileError("Both types for merging tagged unions need to be of type `union(enum)`."); } - const a_fields = @typeInfo(A).Union.fields; - const a_fields_tag = @typeInfo(A).Union.tag_type.?; - const a_enum_fields = @typeInfo(a_fields_tag).Enum.fields; - const b_fields = @typeInfo(B).Union.fields; - const b_fields_tag = @typeInfo(B).Union.tag_type.?; - const b_enum_fields = @typeInfo(b_fields_tag).Enum.fields; + const a_fields = @typeInfo(A).@"union".fields; + const a_fields_tag = @typeInfo(A).@"union".tag_type.?; + const a_enum_fields = @typeInfo(a_fields_tag).@"enum".fields; + const b_fields = @typeInfo(B).@"union".fields; + const b_fields_tag = @typeInfo(B).@"union".tag_type.?; + const b_enum_fields = @typeInfo(b_fields_tag).@"enum".fields; var fields: [a_fields.len + b_fields.len]std.builtin.Type.UnionField = undefined; var enum_fields: [a_fields.len + b_fields.len]std.builtin.Type.EnumField = undefined; var i: usize = 0; @@ -49,19 +49,19 @@ pub fn mergeTaggedUnions(comptime A: type, comptime B: type) type { const log2_i = @bitSizeOf(@TypeOf(i)) - @clz(i); - const EventType = @Type(.{ .Int = .{ + const EventType = @Type(.{ .int = .{ .signedness = .unsigned, .bits = log2_i, } }); - const Event = @Type(.{ .Enum = .{ + const Event = @Type(.{ .@"enum" = .{ .tag_type = EventType, .fields = enum_fields[0..], .decls = &.{}, .is_exhaustive = true, } }); - return @Type(.{ .Union = .{ + return @Type(.{ .@"union" = .{ .layout = .auto, .tag_type = Event, .fields = fields[0..], @@ -72,7 +72,7 @@ pub fn mergeTaggedUnions(comptime A: type, comptime B: type) type { // Determine at `comptime` whether the provided type `E` is an `union(enum)`. pub fn isTaggedUnion(comptime E: type) bool { switch (@typeInfo(E)) { - .Union => |u| { + .@"union" => |u| { if (u.tag_type) |_| {} else { return false; } diff --git a/src/layout/Framing.zig b/src/layout/Framing.zig index 1399afd..f467f30 100644 --- a/src/layout/Framing.zig +++ b/src/layout/Framing.zig @@ -19,11 +19,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Events = std.ArrayList(Event); return struct { diff --git a/src/layout/HContainer.zig b/src/layout/HContainer.zig index f9eefc7..e275dad 100644 --- a/src/layout/HContainer.zig +++ b/src/layout/HContainer.zig @@ -20,19 +20,19 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Container = struct { element: Element, container_size: u8, // 0 - 100 % }; const Containers = std.ArrayList(Container); - const LayoutType = @typeInfo(Element).Union.fields[0].type; - const WidgetType = @typeInfo(Element).Union.fields[1].type; + const LayoutType = @typeInfo(Element).@"union".fields[0].type; + const WidgetType = @typeInfo(Element).@"union".fields[1].type; const Events = std.ArrayList(Event); return struct { // TODO: current focused `Element`? @@ -43,20 +43,20 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t pub fn init(allocator: std.mem.Allocator, children: anytype) @This() { const ArgsType = @TypeOf(children); const args_type_info = @typeInfo(ArgsType); - if (args_type_info != .Struct) { + if (args_type_info != .@"struct") { @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); } comptime var total_size = 0; - const fields_info = args_type_info.Struct.fields; - var containers = Containers.initCapacity(allocator, fields_info.len) catch @panic("OOM"); + const fields_info = args_type_info.@"struct".fields; + 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); const child_type_info = @typeInfo(ChildType); - if (child_type_info != .Struct) { + if (child_type_info != .@"struct") { @compileError("expected tuple or struct as child type, found " ++ @typeName(ChildType)); } - const child_fields = child_type_info.Struct.fields; + const child_fields = child_type_info.@"struct".fields; if (child_fields.len != 2) { @compileError("expected nested tuple or struct to have exactly 2 fields, but found " ++ child_fields.len); } diff --git a/src/layout/HStack.zig b/src/layout/HStack.zig index 7574b2d..a0d8642 100644 --- a/src/layout/HStack.zig +++ b/src/layout/HStack.zig @@ -18,15 +18,15 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Elements = std.ArrayList(Element); - const LayoutType = @typeInfo(Element).Union.fields[0].type; - const WidgetType = @typeInfo(Element).Union.fields[1].type; + const LayoutType = @typeInfo(Element).@"union".fields[0].type; + const WidgetType = @typeInfo(Element).@"union".fields[1].type; const Events = std.ArrayList(Event); return struct { // TODO: current focused `Element`? @@ -37,11 +37,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t pub fn init(allocator: std.mem.Allocator, children: anytype) @This() { const ArgsType = @TypeOf(children); const args_type_info = @typeInfo(ArgsType); - if (args_type_info != .Struct) { + if (args_type_info != .@"struct") { @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"); + const fields_info = args_type_info.@"struct".fields; + 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); diff --git a/src/layout/Margin.zig b/src/layout/Margin.zig index 8d4feb5..5009038 100644 --- a/src/layout/Margin.zig +++ b/src/layout/Margin.zig @@ -18,11 +18,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Events = std.ArrayList(Event); return struct { @@ -33,7 +33,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, diff --git a/src/layout/Padding.zig b/src/layout/Padding.zig index 65295e5..bf9570d 100644 --- a/src/layout/Padding.zig +++ b/src/layout/Padding.zig @@ -18,11 +18,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Events = std.ArrayList(Event); return struct { @@ -33,7 +33,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, diff --git a/src/layout/VContainer.zig b/src/layout/VContainer.zig index af1a6f5..e151b21 100644 --- a/src/layout/VContainer.zig +++ b/src/layout/VContainer.zig @@ -20,19 +20,19 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Container = struct { element: Element, container_size: u8, // 0 - 100 % }; const Containers = std.ArrayList(Container); - const LayoutType = @typeInfo(Element).Union.fields[0].type; - const WidgetType = @typeInfo(Element).Union.fields[1].type; + const LayoutType = @typeInfo(Element).@"union".fields[0].type; + const WidgetType = @typeInfo(Element).@"union".fields[1].type; const Events = std.ArrayList(Event); return struct { // TODO: current focused `Element`? @@ -43,20 +43,20 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t pub fn init(allocator: std.mem.Allocator, children: anytype) @This() { const ArgsType = @TypeOf(children); const args_type_info = @typeInfo(ArgsType); - if (args_type_info != .Struct) { + if (args_type_info != .@"struct") { @compileError("expected tuple or struct argument, found " ++ @typeName(ArgsType)); } comptime var total_size = 0; - const fields_info = args_type_info.Struct.fields; - var containers = Containers.initCapacity(allocator, fields_info.len) catch @panic("OOM"); + const fields_info = args_type_info.@"struct".fields; + 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); const child_type_info = @typeInfo(ChildType); - if (child_type_info != .Struct) { + if (child_type_info != .@"struct") { @compileError("expected tuple or struct as child type, found " ++ @typeName(ChildType)); } - const child_fields = child_type_info.Struct.fields; + const child_fields = child_type_info.@"struct".fields; if (child_fields.len != 2) { @compileError("expected nested tuple or struct to have exactly 2 fields, but found " ++ child_fields.len); } diff --git a/src/layout/VStack.zig b/src/layout/VStack.zig index eefe7e2..9213f5a 100644 --- a/src/layout/VStack.zig +++ b/src/layout/VStack.zig @@ -18,15 +18,15 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t if (!isTaggedUnion(Element)) { @compileError("Provided type `Element` for `Layout(comptime Event: type, comptime Element: type, comptime Renderer: type)` is not of type `union(enum)`."); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[0].name, "layout")) { - @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[0].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[0].name, "layout")) { + @compileError("Expected `layout: Layout` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[0].name); } - if (!std.mem.eql(u8, @typeInfo(Element).Union.fields[1].name, "widget")) { - @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).Union.fields[1].name); + if (!std.mem.eql(u8, @typeInfo(Element).@"union".fields[1].name, "widget")) { + @compileError("Expected `widget: Widget` to be the first union element, but has name: " ++ @typeInfo(Element).@"union".fields[1].name); } const Elements = std.ArrayList(Element); - const LayoutType = @typeInfo(Element).Union.fields[0].type; - const WidgetType = @typeInfo(Element).Union.fields[1].type; + const LayoutType = @typeInfo(Element).@"union".fields[0].type; + const WidgetType = @typeInfo(Element).@"union".fields[1].type; const Events = std.ArrayList(Event); return struct { // TODO: current focused `Element`? @@ -37,11 +37,11 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t pub fn init(allocator: std.mem.Allocator, children: anytype) @This() { const ArgsType = @TypeOf(children); const args_type_info = @typeInfo(ArgsType); - if (args_type_info != .Struct) { + if (args_type_info != .@"struct") { @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"); + const fields_info = args_type_info.@"struct".fields; + 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); diff --git a/src/terminal.zig b/src/terminal.zig index 077600d..df8414e 100644 --- a/src/terminal.zig +++ b/src/terminal.zig @@ -20,7 +20,7 @@ pub const ReportMode = enum { pub fn getTerminalSize() Size { var ws: std.posix.winsize = undefined; _ = std.posix.system.ioctl(std.posix.STDIN_FILENO, std.posix.T.IOCGWINSZ, @intFromPtr(&ws)); - return .{ .cols = ws.ws_col, .rows = ws.ws_row }; + return .{ .cols = ws.col, .rows = ws.row }; } pub fn saveScreen() !void {