Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8602db4d43
|
|||
|
5acca12abf
|
@@ -1,7 +1,7 @@
|
||||
.{
|
||||
.name = .tui_website,
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
.version = "0.0.1",
|
||||
.version = "0.0.2",
|
||||
.fingerprint = 0x93d98a4d9d000e9c, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.16.0-dev.463+f624191f9",
|
||||
.dependencies = .{
|
||||
|
||||
@@ -53,6 +53,89 @@ pub fn Content(App: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Title(App: type) type {
|
||||
return struct {
|
||||
text: []const u8,
|
||||
|
||||
pub fn init() @This() {
|
||||
return .{
|
||||
.text = "<Title>",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn element(this: *@This()) App.Element {
|
||||
return .{
|
||||
.ptr = this,
|
||||
.vtable = &.{
|
||||
.content = content,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
||||
const this: *const @This() = @ptrCast(@alignCast(ctx));
|
||||
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||
for (0.., this.text) |idx, cp| {
|
||||
cells[idx].style.fg = .green;
|
||||
cells[idx].style.emphasis = &.{.bold};
|
||||
cells[idx].cp = cp;
|
||||
|
||||
// NOTE do not write over the contents of this `Container`'s `Size`
|
||||
if (idx == cells.len - 1) break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn InfoBanner(App: type) type {
|
||||
return struct {
|
||||
left_text: []const u8,
|
||||
right_text: []const u8,
|
||||
|
||||
pub fn init() @This() {
|
||||
return .{
|
||||
.left_text = "Build with zig",
|
||||
.right_text = "Yves Biener (@yves-biener)",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn element(this: *@This()) App.Element {
|
||||
return .{
|
||||
.ptr = this,
|
||||
.vtable = &.{
|
||||
.content = content,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
||||
const this: *const @This() = @ptrCast(@alignCast(ctx));
|
||||
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||
|
||||
for (0.., this.left_text) |idx, cp| {
|
||||
// NOTE do not write over the contents of this `Container`'s `Size`
|
||||
if (idx == cells.len) break;
|
||||
|
||||
cells[idx].style.fg = .default;
|
||||
cells[idx].style.emphasis = &.{.dim};
|
||||
cells[idx].cp = cp;
|
||||
}
|
||||
|
||||
var start_idx = size.x -| this.right_text.len;
|
||||
if (start_idx <= this.left_text.len) start_idx = this.left_text.len + 1;
|
||||
|
||||
for (start_idx.., this.right_text) |idx, cp| {
|
||||
// NOTE do not write over the contents of this `Container`'s `Size`
|
||||
if (idx == cells.len) break;
|
||||
|
||||
cells[idx].style.fg = .default;
|
||||
cells[idx].style.emphasis = &.{.dim};
|
||||
cells[idx].cp = cp;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
88
src/main.zig
88
src/main.zig
@@ -19,41 +19,65 @@ pub fn main() !void {
|
||||
}, .{});
|
||||
defer container.deinit();
|
||||
|
||||
var header: App.Container = try .init(allocator, .{
|
||||
.size = .{
|
||||
.dim = .{ .y = 1 },
|
||||
.grow = .horizontal,
|
||||
},
|
||||
}, .{});
|
||||
var navigation: App.Container = try .init(allocator, .{
|
||||
.layout = .{
|
||||
.separator = .{ .enabled = true },
|
||||
},
|
||||
}, .{});
|
||||
// about navigation button
|
||||
// header with navigation buttons and content's title
|
||||
{
|
||||
var button: App.Button(.about) = .init(&app.queue, .init(.default, "about"));
|
||||
try navigation.append(try .init(allocator, .{
|
||||
var header: App.Container = try .init(allocator, .{
|
||||
.size = .{
|
||||
.dim = .{ .x = 5 + 2 },
|
||||
.grow = .vertical,
|
||||
.dim = .{ .y = 1 },
|
||||
.grow = .horizontal,
|
||||
},
|
||||
}, button.element()));
|
||||
.layout = .{
|
||||
.separator = .{ .enabled = true },
|
||||
},
|
||||
}, .{});
|
||||
// title
|
||||
{
|
||||
var title: Title = .init();
|
||||
try header.append(try .init(allocator, .{}, title.element()));
|
||||
}
|
||||
// about navigation button
|
||||
{
|
||||
var button: NavigationButton(.about) = .init(&app.model, &app.queue);
|
||||
try header.append(try .init(allocator, .{
|
||||
.size = .{
|
||||
.dim = .{ .x = 5 + 2 },
|
||||
.grow = .vertical,
|
||||
},
|
||||
}, button.element()));
|
||||
}
|
||||
// blog navigation button
|
||||
{
|
||||
var button: NavigationButton(.blog) = .init(&app.model, &app.queue);
|
||||
try header.append(try .init(allocator, .{
|
||||
.size = .{
|
||||
.dim = .{ .x = 4 + 2 },
|
||||
.grow = .vertical,
|
||||
},
|
||||
}, button.element()));
|
||||
}
|
||||
try container.append(header);
|
||||
}
|
||||
// blog navigation button
|
||||
// main actual tui_website page content
|
||||
{
|
||||
var button: App.Button(.blog) = .init(&app.queue, .init(.default, "blog"));
|
||||
try navigation.append(try .init(allocator, .{
|
||||
.size = .{
|
||||
.dim = .{ .x = 4 + 2 },
|
||||
.grow = .vertical,
|
||||
},
|
||||
}, button.element()));
|
||||
// intermediate container for *padding*
|
||||
var content: Content = .init(allocator);
|
||||
var content_container: App.Container = try .init(allocator, .{
|
||||
.layout = .{ .padding = .horizontal(2) },
|
||||
}, .{});
|
||||
try content_container.append(try .init(allocator, .{}, content.element()));
|
||||
try container.append(content_container);
|
||||
}
|
||||
// footer
|
||||
{
|
||||
var info_banner: InfoBanner = .init();
|
||||
const footer: App.Container = try .init(allocator, .{
|
||||
.size = .{
|
||||
.dim = .{ .y = 1 },
|
||||
.grow = .horizontal,
|
||||
},
|
||||
}, info_banner.element());
|
||||
try container.append(footer);
|
||||
}
|
||||
try header.append(navigation);
|
||||
try container.append(header);
|
||||
var content: Content = .init(allocator);
|
||||
try container.append(try .init(allocator, .{}, content.element()));
|
||||
|
||||
try app.start();
|
||||
defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err});
|
||||
@@ -107,8 +131,12 @@ const App = zterm.App(
|
||||
},
|
||||
);
|
||||
|
||||
const contents = @import("content.zig");
|
||||
const Model = @import("model.zig");
|
||||
const Content = @import("content.zig").Content(App);
|
||||
const Content = contents.Content(App);
|
||||
const Title = contents.Title(App);
|
||||
const InfoBanner = contents.InfoBanner(App);
|
||||
const NavigationButton = @import("navigation.zig").NavigationButton(App);
|
||||
|
||||
test {
|
||||
std.testing.refAllDeclsRecursive(@This());
|
||||
|
||||
63
src/navigation.zig
Normal file
63
src/navigation.zig
Normal file
@@ -0,0 +1,63 @@
|
||||
pub fn NavigationButton(App: type) fn (std.meta.FieldEnum(App.Event)) type {
|
||||
const navigation_struct = struct {
|
||||
fn navigation_fn(page: std.meta.FieldEnum(App.Event)) type {
|
||||
return struct {
|
||||
text: []const u8,
|
||||
highlight: bool,
|
||||
queue: *App.Queue,
|
||||
|
||||
pub fn init(model: *const App.Model, queue: *App.Queue) @This() {
|
||||
return .{
|
||||
.text = @tagName(page),
|
||||
.highlight = switch (page) {
|
||||
.about => model.page == .about,
|
||||
.blog => model.page == .blog,
|
||||
else => @compileError("NavigationButton initiated with non page `App.Event` variant"),
|
||||
},
|
||||
.queue = queue,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn element(this: *@This()) App.Element {
|
||||
return .{
|
||||
.ptr = this,
|
||||
.vtable = &.{
|
||||
.handle = handle,
|
||||
.content = content,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn handle(ctx: *anyopaque, _: *App.Model, event: App.Event) !void {
|
||||
const this: *@This() = @ptrCast(@alignCast(ctx));
|
||||
switch (event) {
|
||||
.about, .blog => this.highlight = event == page,
|
||||
// TODO accept key input too?
|
||||
.mouse => |mouse| if (mouse.button == .left and mouse.kind == .release) this.queue.push(page),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn content(ctx: *anyopaque, _: *const App.Model, cells: []zterm.Cell, size: zterm.Point) !void {
|
||||
const this: *const @This() = @ptrCast(@alignCast(ctx));
|
||||
assert(cells.len == @as(usize, size.x) * @as(usize, size.y));
|
||||
assert(size.x == this.text.len + 2);
|
||||
assert(size.y == 1);
|
||||
|
||||
for (1.., this.text) |idx, cp| {
|
||||
cells[idx].style.fg = if (this.highlight) .black else .default;
|
||||
cells[idx].style.bg = if (this.highlight) .green else .default;
|
||||
cells[idx].style.emphasis = &.{.underline};
|
||||
cells[idx].cp = cp;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
return navigation_struct.navigation_fn;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const zterm = @import("zterm");
|
||||
Reference in New Issue
Block a user