add(examples/styles): text and color styling possiblities
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 39s
Some checks failed
Zig Project Action / Lint, Spell-check and test zig project (push) Failing after 39s
This also contains some minor refactoring to improve the readability and understandability of the library (i.e. renaming of Style.Attributes to Style.Emphasis).
This commit is contained in:
26
README.md
26
README.md
@@ -90,19 +90,12 @@ the primary use-case for myself to create this library in the first place.
|
||||
- [x] horizontal
|
||||
- [x] padding
|
||||
- [x] gap
|
||||
- [x] sizing (removed - for now at least)
|
||||
- width
|
||||
- height
|
||||
- options
|
||||
- fit
|
||||
- grow
|
||||
- fixed
|
||||
- percent
|
||||
- [x] Border
|
||||
- [x] sides
|
||||
- [x] corners
|
||||
- [x] separators
|
||||
- [x] Rectangle
|
||||
- [x] min size
|
||||
- [ ] User control
|
||||
- [x] event loop handling
|
||||
- [x] mouse support
|
||||
@@ -124,7 +117,7 @@ the primary use-case for myself to create this library in the first place.
|
||||
- [ ] image support through kitty protocol (**later**)
|
||||
- [ ] Inline rendering (**later**)
|
||||
- [ ] Examples
|
||||
- [ ] Layouts
|
||||
- [x] Layouts
|
||||
- [x] vertical
|
||||
- [x] horizontal
|
||||
- [x] grid
|
||||
@@ -138,6 +131,21 @@ the primary use-case for myself to create this library in the first place.
|
||||
- [x] mouse scrolling aware of mouse position (i.e. through multiple different scrollable `Container`)
|
||||
- [ ] Styles
|
||||
- [ ] Text styles
|
||||
- [ ] Colors
|
||||
- [x] forground
|
||||
- [x] background
|
||||
- [ ] underline
|
||||
- [ ] Emphasis
|
||||
- [x] none
|
||||
- [x] bold
|
||||
- [x] dim
|
||||
- [x] italic
|
||||
- [x] underline
|
||||
- [x] blink
|
||||
- [x] invert
|
||||
- [x] hidden
|
||||
- [x] strikethrough
|
||||
- [ ] combinations
|
||||
- [x] Color palette
|
||||
- [ ] Error Handling
|
||||
- [ ] log and show error's without crashing the application
|
||||
|
||||
10
build.zig
10
build.zig
@@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void {
|
||||
grid,
|
||||
mixed,
|
||||
// styles:
|
||||
text,
|
||||
palette,
|
||||
};
|
||||
|
||||
@@ -106,6 +107,14 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
palette.root_module.addImport("zterm", lib);
|
||||
|
||||
const text = b.addExecutable(.{
|
||||
.name = "text",
|
||||
.root_source_file = b.path("examples/styles/text.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
text.root_module.addImport("zterm", lib);
|
||||
|
||||
// mapping of user selected example to compile step
|
||||
const exe = switch (example) {
|
||||
// elements:
|
||||
@@ -118,6 +127,7 @@ pub fn build(b: *std.Build) void {
|
||||
.grid => grid,
|
||||
.mixed => mixed,
|
||||
// styles:
|
||||
.text => text,
|
||||
.palette => palette,
|
||||
};
|
||||
b.installArtifact(exe);
|
||||
|
||||
@@ -63,8 +63,7 @@ pub fn main() !void {
|
||||
|
||||
inline for (std.meta.fields(zterm.Color)) |field| {
|
||||
if (comptime field.value == 0) continue; // zterm.Color.default == 0 -> skip
|
||||
const color = std.meta.stringToEnum(zterm.Color, field.name).?;
|
||||
try box.append(try App.Container.init(allocator, .{ .rectangle = .{ .fill = color } }, .{}));
|
||||
try box.append(try App.Container.init(allocator, .{ .rectangle = .{ .fill = @enumFromInt(field.value) } }, .{}));
|
||||
}
|
||||
var scrollable: App.Scrollable = .init(box);
|
||||
try container.append(try App.Container.init(allocator, .{}, scrollable.element()));
|
||||
|
||||
148
examples/styles/text.zig
Normal file
148
examples/styles/text.zig
Normal file
@@ -0,0 +1,148 @@
|
||||
const std = @import("std");
|
||||
const zterm = @import("zterm");
|
||||
|
||||
const App = zterm.App(union(enum) {});
|
||||
|
||||
const log = std.log.scoped(.default);
|
||||
|
||||
const QuitText = struct {
|
||||
const text = "Press ctrl+c to quit.";
|
||||
|
||||
pub fn element(this: *@This()) App.Element {
|
||||
return .{ .ptr = this, .vtable = &.{ .content = content } };
|
||||
}
|
||||
|
||||
pub fn content(ctx: *anyopaque, cells: []zterm.Cell, size: zterm.Size) !void {
|
||||
_ = ctx;
|
||||
std.debug.assert(cells.len == @as(usize, size.cols) * @as(usize, size.rows));
|
||||
|
||||
const row = 2;
|
||||
const col = size.cols / 2 -| (text.len / 2);
|
||||
const anchor = (row * size.cols) + col;
|
||||
|
||||
for (text, 0..) |cp, idx| {
|
||||
cells[anchor + idx].style.fg = .white;
|
||||
cells[anchor + idx].style.bg = .black;
|
||||
cells[anchor + idx].cp = cp;
|
||||
|
||||
// NOTE: do not write over the contents of this `Container`'s `Size`
|
||||
if (anchor + idx == cells.len - 1) break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const TextStyles = struct {
|
||||
const text = "Example";
|
||||
|
||||
pub fn element(this: *@This()) App.Element {
|
||||
return .{ .ptr = this, .vtable = &.{ .content = content } };
|
||||
}
|
||||
|
||||
pub fn content(ctx: *anyopaque, cells: []zterm.Cell, size: zterm.Size) !void {
|
||||
@setEvalBranchQuota(50000);
|
||||
_ = ctx;
|
||||
std.debug.assert(cells.len == @as(usize, size.cols) * @as(usize, size.rows));
|
||||
|
||||
var row: usize = 0;
|
||||
var col: usize = 0;
|
||||
|
||||
// Color
|
||||
inline for (std.meta.fields(zterm.Color)) |bg_field| {
|
||||
if (comptime bg_field.value == 0) continue; // zterm.Color.default == 0 -> skip
|
||||
|
||||
inline for (std.meta.fields(zterm.Color)) |fg_field| {
|
||||
if (comptime fg_field.value == 0) continue; // zterm.Color.default == 0 -> skip
|
||||
if (comptime fg_field.value == bg_field.value) continue;
|
||||
|
||||
// witouth any emphasis
|
||||
for (text) |cp| {
|
||||
cells[(row * size.cols) + col].style.bg = @enumFromInt(bg_field.value);
|
||||
cells[(row * size.cols) + col].style.fg = @enumFromInt(fg_field.value);
|
||||
cells[(row * size.cols) + col].cp = cp;
|
||||
col += 1;
|
||||
}
|
||||
|
||||
// emphasis (no combinations)
|
||||
inline for (std.meta.fields(zterm.Style.Emphasis)) |emp_field| {
|
||||
if (comptime emp_field.value == 0) continue; // zterm.Style.Emphasis.reset == 0 -> skip
|
||||
const emphasis: zterm.Style.Emphasis = @enumFromInt(emp_field.value);
|
||||
|
||||
for (text) |cp| {
|
||||
cells[(row * size.cols) + col].style.bg = @enumFromInt(bg_field.value);
|
||||
cells[(row * size.cols) + col].style.fg = @enumFromInt(fg_field.value);
|
||||
cells[(row * size.cols) + col].style.emphasis = &.{emphasis};
|
||||
cells[(row * size.cols) + col].cp = cp;
|
||||
col += 1;
|
||||
}
|
||||
}
|
||||
row += 1;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
errdefer |err| log.err("Application Error: {any}", .{err});
|
||||
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
||||
defer if (gpa.deinit() == .leak) {
|
||||
log.err("memory leak", .{});
|
||||
};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var app: App = .init;
|
||||
var renderer = zterm.Renderer.Buffered.init(allocator);
|
||||
defer renderer.deinit();
|
||||
|
||||
var quit_text: QuitText = .{};
|
||||
const element = quit_text.element();
|
||||
|
||||
var text_styles: TextStyles = .{};
|
||||
|
||||
var container = try App.Container.init(allocator, .{
|
||||
.layout = .{
|
||||
.gap = 2,
|
||||
.padding = .{ .top = 5, .bottom = 3, .left = 3, .right = 3 },
|
||||
},
|
||||
}, element);
|
||||
defer container.deinit();
|
||||
|
||||
var box = try App.Container.init(allocator, .{
|
||||
.layout = .{ .direction = .vertical },
|
||||
.min_size = .{
|
||||
.rows = (std.meta.fields(zterm.Color).len - 1) * (std.meta.fields(zterm.Color).len - 2),
|
||||
.cols = std.meta.fields(zterm.Style.Emphasis).len * TextStyles.text.len,
|
||||
}, // ensure enough rows and/or columns to render all text styles -> scrollable otherwise
|
||||
}, text_styles.element());
|
||||
defer box.deinit();
|
||||
|
||||
var scrollable: App.Scrollable = .init(box);
|
||||
try container.append(try App.Container.init(allocator, .{}, scrollable.element()));
|
||||
|
||||
try app.start();
|
||||
defer app.stop() catch |err| log.err("Failed to stop application: {any}", .{err});
|
||||
|
||||
while (true) {
|
||||
const event = app.nextEvent();
|
||||
log.debug("received event: {s}", .{@tagName(event)});
|
||||
|
||||
switch (event) {
|
||||
.init => continue,
|
||||
.quit => break,
|
||||
.resize => |size| try renderer.resize(size),
|
||||
.key => |key| if (key.eql(.{ .cp = 'c', .mod = .{ .ctrl = true } })) app.quit(),
|
||||
.err => |err| log.err("Received {s} with message: {s}", .{ @errorName(err.err), err.msg }),
|
||||
else => {},
|
||||
}
|
||||
|
||||
container.handle(event) catch |err| app.postEvent(.{
|
||||
.err = .{
|
||||
.err = err,
|
||||
.msg = "Container Event handling failed",
|
||||
},
|
||||
});
|
||||
try renderer.render(@TypeOf(container), &container);
|
||||
try renderer.flush();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ const Style = @import("style.zig");
|
||||
|
||||
pub const Cell = @This();
|
||||
|
||||
style: Style = .{ .attributes = &.{} },
|
||||
style: Style = .{ .emphasis = &.{} },
|
||||
// TODO: embrace `zg` dependency more due to utf-8 encoding
|
||||
cp: u21 = ' ',
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn eql(this: Cell, other: Cell) bool {
|
||||
}
|
||||
|
||||
pub fn reset(this: *Cell) void {
|
||||
this.style = .{ .attributes = &.{} };
|
||||
this.style = .{ .emphasis = &.{} };
|
||||
this.cp = ' ';
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ pub const Underline = enum {
|
||||
dashed,
|
||||
};
|
||||
|
||||
pub const Attribute = enum(u8) {
|
||||
pub const Emphasis = enum(u8) {
|
||||
reset = 0,
|
||||
bold = 1,
|
||||
dim,
|
||||
@@ -38,7 +38,7 @@ fg: Color = .white,
|
||||
bg: Color = .default,
|
||||
ul: Color = .default,
|
||||
ul_style: Underline = .off,
|
||||
attributes: []const Attribute,
|
||||
emphasis: []const Emphasis,
|
||||
|
||||
pub fn eql(this: Style, other: Style) bool {
|
||||
return std.meta.eql(this, other);
|
||||
@@ -60,9 +60,7 @@ pub fn value(this: Style, writer: anytype, cp: u21) !void {
|
||||
try std.fmt.format(writer, ";", .{});
|
||||
try this.ul.write(writer, .ul);
|
||||
// append styles (aka attributes like bold, italic, strikethrough, etc.)
|
||||
for (this.attributes) |attribute| {
|
||||
try std.fmt.format(writer, ";{d}", .{@intFromEnum(attribute)});
|
||||
}
|
||||
for (this.emphasis) |attribute| try std.fmt.format(writer, ";{d}", .{@intFromEnum(attribute)});
|
||||
try std.fmt.format(writer, "m", .{});
|
||||
// content
|
||||
try std.fmt.format(writer, "{s}", .{buffer});
|
||||
|
||||
Reference in New Issue
Block a user