WIP: container rendering for borders + container element rendering

This commit is contained in:
2025-02-03 19:55:33 +01:00
parent 0bf79dc236
commit 2bfacc0e98
7 changed files with 189 additions and 219 deletions

View File

@@ -8,7 +8,6 @@
// taken from https://github.com/rockorager/libvaxis/blob/main/src/Cell.zig (MIT-License)
// with slight modifications
const std = @import("std");
const ctlseqs = @import("ctlseqs.zig");
const Color = @import("color.zig").Color;
@@ -23,208 +22,64 @@ pub const Underline = enum {
dashed,
};
fg: Color = .default,
bg: Color = .default,
ul: Color = .default,
ul_style: Underline = .off,
pub const Attribute = enum(u32) {
reset = 0,
bold = 1,
dim,
italic,
underline,
blink,
invert = 7,
hidden,
strikethrough,
};
bold: bool = false,
dim: bool = false,
italic: bool = false,
blink: bool = false,
reverse: bool = false,
invisible: bool = false,
strikethrough: bool = false,
fg: Color = .{ .ansi = .reset },
bg: Color = .{ .ansi = .reset },
ul: Color = .{ .ansi = .reset },
ul_style: Underline = .off,
attributes: []const Attribute,
pub fn eql(this: Style, other: Style) bool {
return this.fg.eql(other.fg) and
const ret = this.fg.eql(other.fg) and
this.bg.eql(other.bg) and
this.ul.eql(other.ul) and
other.ul_style == this.ul_style and
other.bold == this.bold and
other.dim == this.dim and
other.italic == this.italic and
other.blink == this.blink and
other.reverse == this.reverse and
other.invisible == this.invisible and
other.strikethrough == this.strikethrough;
other.attributes.len == this.attributes.len;
if (ret == false) return false;
// are the attributes also identical?
for (0..this.attributes.len) |i| {
if (this.attributes[i] != other.attributes[i])
return false;
}
return true;
}
/// Merge _other_ `Style` to _this_ style and overwrite _this_ `Style`'s value
/// if the _other_ value differs from the default value.
pub fn merge(this: *Style, other: Style) void {
if (other.fg != .default) this.fg = other.fg;
if (other.bg != .default) this.bg = other.bg;
if (other.ul != .default) this.ul = other.ul;
if (other.ul_style != .off) this.ul_style = other.ul_style;
if (other.bold != false) this.bold = other.bold;
if (other.dim != false) this.dim = other.dim;
if (other.italic != false) this.italic = other.italic;
if (other.blink != false) this.blink = other.blink;
if (other.reverse != false) this.reverse = other.reverse;
if (other.invisible != false) this.invisible = other.invisible;
if (other.strikethrough != false) this.strikethrough = other.strikethrough;
}
fn start(this: Style, writer: anytype) !void {
// foreground
switch (this.fg) {
.default => try std.fmt.format(writer, ctlseqs.fg_reset, .{}),
.index => |idx| {
switch (idx) {
0...7 => {
try std.fmt.format(writer, ctlseqs.fg_base, .{idx});
},
8...15 => {
try std.fmt.format(writer, ctlseqs.fg_bright, .{idx - 8});
},
else => {
try std.fmt.format(writer, ctlseqs.fg_indexed, .{idx});
},
}
},
.rgb => |rgb| {
try std.fmt.format(writer, ctlseqs.fg_rgb, .{ rgb[0], rgb[1], rgb[2] });
},
pub fn value(this: Style, writer: anytype, cp: u21) !void {
var buffer: [4]u8 = undefined;
const bytes = try std.unicode.utf8Encode(cp, &buffer);
std.debug.assert(bytes > 0);
// start escape sequence
try std.fmt.format(writer, "\x1b[", .{});
// colors
try this.fg.write(writer, .fg);
try std.fmt.format(writer, ";", .{});
try this.bg.write(writer, .bg);
// try std.fmt.format(writer, ":", .{});
// underline
// try this.ul.write(writer, .ul);
// try std.fmt.format(writer, ":", .{});
// attributes
for (this.attributes) |attribute| {
try std.fmt.format(writer, ":{d}", .{@intFromEnum(attribute)});
}
// background
switch (this.bg) {
.default => try std.fmt.format(writer, ctlseqs.bg_reset, .{}),
.index => |idx| {
switch (idx) {
0...7 => {
try std.fmt.format(writer, ctlseqs.bg_base, .{idx});
},
8...15 => {
try std.fmt.format(writer, ctlseqs.bg_bright, .{idx});
},
else => {
try std.fmt.format(writer, ctlseqs.bg_indexed, .{idx});
},
}
},
.rgb => |rgb| {
try std.fmt.format(writer, ctlseqs.bg_rgb, .{ rgb[0], rgb[1], rgb[2] });
},
}
// underline color
switch (this.ul) {
.default => try std.fmt.format(writer, ctlseqs.ul_reset, .{}),
.index => |idx| {
try std.fmt.format(writer, ctlseqs.ul_indexed, .{idx});
},
.rgb => |rgb| {
try std.fmt.format(writer, ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] });
},
}
// underline style
switch (this.ul_style) {
.off => try std.fmt.format(writer, ctlseqs.ul_off, .{}),
.single => try std.fmt.format(writer, ctlseqs.ul_single, .{}),
.double => try std.fmt.format(writer, ctlseqs.ul_double, .{}),
.curly => try std.fmt.format(writer, ctlseqs.ul_curly, .{}),
.dotted => try std.fmt.format(writer, ctlseqs.ul_dotted, .{}),
.dashed => try std.fmt.format(writer, ctlseqs.ul_dashed, .{}),
}
// bold
switch (this.bold) {
true => try std.fmt.format(writer, ctlseqs.bold_set, .{}),
false => try std.fmt.format(writer, ctlseqs.bold_dim_reset, .{}),
}
// dim
switch (this.dim) {
true => try std.fmt.format(writer, ctlseqs.dim_set, .{}),
false => try std.fmt.format(writer, ctlseqs.bold_dim_reset, .{}),
}
// italic
switch (this.italic) {
true => try std.fmt.format(writer, ctlseqs.italic_set, .{}),
false => try std.fmt.format(writer, ctlseqs.italic_reset, .{}),
}
// blink
switch (this.blink) {
true => try std.fmt.format(writer, ctlseqs.blink_set, .{}),
false => try std.fmt.format(writer, ctlseqs.blink_reset, .{}),
}
// reverse
switch (this.reverse) {
true => try std.fmt.format(writer, ctlseqs.reverse_set, .{}),
false => try std.fmt.format(writer, ctlseqs.reverse_reset, .{}),
}
// invisible
switch (this.invisible) {
true => try std.fmt.format(writer, ctlseqs.invisible_set, .{}),
false => try std.fmt.format(writer, ctlseqs.invisible_reset, .{}),
}
// strikethrough
switch (this.strikethrough) {
true => try std.fmt.format(writer, ctlseqs.strikethrough_set, .{}),
false => try std.fmt.format(writer, ctlseqs.strikethrough_reset, .{}),
}
}
fn end(this: Style, writer: anytype) !void {
// foreground
switch (this.fg) {
.default => {},
else => try std.fmt.format(writer, ctlseqs.fg_reset, .{}),
}
// background
switch (this.bg) {
.default => {},
else => try std.fmt.format(writer, ctlseqs.bg_reset, .{}),
}
// underline color
switch (this.ul) {
.default => {},
else => try std.fmt.format(writer, ctlseqs.ul_reset, .{}),
}
// underline style
switch (this.ul_style) {
.off => {},
else => try std.fmt.format(writer, ctlseqs.ul_off, .{}),
}
// bold
switch (this.bold) {
true => try std.fmt.format(writer, ctlseqs.bold_dim_reset, .{}),
false => {},
}
// dim
switch (this.dim) {
true => try std.fmt.format(writer, ctlseqs.bold_dim_reset, .{}),
false => {},
}
// italic
switch (this.italic) {
true => try std.fmt.format(writer, ctlseqs.italic_reset, .{}),
false => {},
}
// blink
switch (this.blink) {
true => try std.fmt.format(writer, ctlseqs.blink_reset, .{}),
false => {},
}
// reverse
switch (this.reverse) {
true => try std.fmt.format(writer, ctlseqs.reverse_reset, .{}),
false => {},
}
// invisible
switch (this.invisible) {
true => try std.fmt.format(writer, ctlseqs.invisible_reset, .{}),
false => {},
}
// strikethrough
switch (this.strikethrough) {
true => try std.fmt.format(writer, ctlseqs.strikethrough_reset, .{}),
false => {},
}
}
pub fn value(this: Style, writer: anytype, content: u8) !void {
try this.start(writer);
_ = try writer.write(&[_]u8{content});
try this.end(writer);
// end styling
try std.fmt.format(writer, "m", .{});
// content
try std.fmt.format(writer, "{s}", .{buffer});
// end escape sequence
try writer.print("\x1b[0m", .{});
}
// TODO: implement helper functions for terminal capabilities: