WIP: container rendering for borders + container element rendering
This commit is contained in:
@@ -23,6 +23,7 @@ pub fn main() !void {
|
||||
defer renderer.deinit();
|
||||
|
||||
var container = try App.Container.init(allocator, .{});
|
||||
try container.append(try App.Container.init(allocator, .{}));
|
||||
defer container.deinit();
|
||||
|
||||
// NOTE: should the min-size here be required?
|
||||
|
||||
12
src/cell.zig
12
src/cell.zig
@@ -3,18 +3,18 @@ const Style = @import("style.zig");
|
||||
|
||||
pub const Cell = @This();
|
||||
|
||||
style: Style = .{},
|
||||
rune: u8 = ' ',
|
||||
style: Style = .{ .attributes = &.{} },
|
||||
cp: u21 = ' ',
|
||||
|
||||
pub fn eql(this: Cell, other: Cell) bool {
|
||||
return this.rune == other.rune and this.style.eql(other.style);
|
||||
return this.cp == other.cp and this.style.eql(other.style);
|
||||
}
|
||||
|
||||
pub fn reset(this: *Cell) void {
|
||||
this.style = .{ .fg = .default, .bg = .default, .ul = .default, .ul_style = .off };
|
||||
this.rune = ' ';
|
||||
this.style = .{ .attributes = &.{} };
|
||||
this.cp = ' ';
|
||||
}
|
||||
|
||||
pub fn value(this: Cell, writer: anytype) !void {
|
||||
try this.style.value(writer, this.rune);
|
||||
try this.style.value(writer, this.cp);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const Color = union(enum) {
|
||||
default,
|
||||
index: u8,
|
||||
ansi: enum(u32) {
|
||||
reset = 0,
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white,
|
||||
},
|
||||
rgb: [3]u8,
|
||||
|
||||
pub fn eql(a: Color, b: Color) bool {
|
||||
switch (a) {
|
||||
.default => return b == .default,
|
||||
.index => |a_idx| {
|
||||
.ansi => |a_idx| {
|
||||
switch (b) {
|
||||
.index => |b_idx| return a_idx == b_idx,
|
||||
.ansi => |b_idx| return a_idx == b_idx,
|
||||
else => return false,
|
||||
}
|
||||
},
|
||||
@@ -25,6 +41,30 @@ pub const Color = union(enum) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(this: Color, writer: anytype, comptime coloring: enum { fg, bg, ul }) !void {
|
||||
switch (this) {
|
||||
.ansi => |index| blk: {
|
||||
if (index == .reset)
|
||||
break :blk switch (coloring) {
|
||||
.fg => try std.fmt.format(writer, "39", .{}),
|
||||
.bg => try std.fmt.format(writer, "49", .{}),
|
||||
.ul => try std.fmt.format(writer, "59", .{}),
|
||||
};
|
||||
|
||||
break :blk switch (coloring) {
|
||||
.fg => try std.fmt.format(writer, "38;5;{d}", .{@intFromEnum(index)}),
|
||||
.bg => try std.fmt.format(writer, "48;5;{d}", .{@intFromEnum(index)}),
|
||||
.ul => try std.fmt.format(writer, "58;5;{d}", .{@intFromEnum(index)}),
|
||||
};
|
||||
},
|
||||
.rgb => |rgb| switch (coloring) {
|
||||
.fg => try std.fmt.format(writer, "38;2;{d};{d};{d}", .{ rgb[0], rgb[1], rgb[2] }),
|
||||
.bg => try std.fmt.format(writer, "48;2;{d};{d};{d}", .{ rgb[0], rgb[1], rgb[2] }),
|
||||
.ul => try std.fmt.format(writer, "58;2;{d};{d};{d}", .{ rgb[0], rgb[1], rgb[2] }),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rgbFromUint(val: u24) Color {
|
||||
const r_bits = val & 0b11111111_00000000_00000000;
|
||||
const g_bits = val & 0b00000000_11111111_00000000;
|
||||
|
||||
@@ -10,8 +10,10 @@ const log = std.log.scoped(.container);
|
||||
|
||||
/// Border configuration struct
|
||||
pub const Border = struct {
|
||||
pub const rounded_border: [6]u21 = .{ '╭', '─', '╮', '│', '╰', '╯' };
|
||||
pub const squared_border: [6]u21 = .{ '┌', '─', '┐', '│', '└', '┘' };
|
||||
/// Color to use for the border
|
||||
color: Color = .default,
|
||||
color: Color = .{ .ansi = .reset },
|
||||
/// Configure the corner type to be used for the border
|
||||
corners: enum(u1) {
|
||||
squared,
|
||||
@@ -27,13 +29,55 @@ pub const Border = struct {
|
||||
/// Configure separator borders between child element to added to the layout
|
||||
separator: struct {
|
||||
enabled: bool = false,
|
||||
color: Color = .default,
|
||||
color: Color = .{ .ansi = .reset },
|
||||
line: enum {
|
||||
line,
|
||||
dotted,
|
||||
// TODO: add more variations which could be used for the separator
|
||||
} = .line,
|
||||
} = .{},
|
||||
|
||||
// NOTE: caller owns `cells` slice and ensures that `cells.len == size.cols * size.rows`
|
||||
pub fn contents(this: @This(), cells: []Cell, size: Size) void {
|
||||
std.debug.assert(cells.len == size.cols * size.rows);
|
||||
|
||||
const frame = switch (this.corners) {
|
||||
.rounded => Border.rounded_border,
|
||||
.squared => Border.squared_border,
|
||||
};
|
||||
std.debug.assert(frame.len == 6);
|
||||
// TODO: respect color configuration
|
||||
// TODO: respect sides configuration
|
||||
// render top and bottom border
|
||||
for (0..size.cols) |col| {
|
||||
const last_row = (size.rows - 1) * size.cols;
|
||||
if (col == 0) {
|
||||
// top left corner
|
||||
cells[col].cp = frame[0];
|
||||
// bottom left corner
|
||||
cells[last_row + col].cp = frame[4];
|
||||
} else if (col == size.cols - 1) {
|
||||
// top right corner
|
||||
cells[col].cp = frame[2];
|
||||
// bottom left corner
|
||||
cells[last_row + col].cp = frame[5];
|
||||
} else {
|
||||
// top side
|
||||
cells[col].cp = frame[1];
|
||||
// bottom side
|
||||
cells[last_row + col].cp = frame[1];
|
||||
}
|
||||
// TODO: fix rendering of styling?
|
||||
cells[col].style = .{ .fg = .{ .ansi = .red }, .attributes = &.{} };
|
||||
cells[last_row + col].style = .{ .fg = .{ .ansi = .red }, .attributes = &.{} };
|
||||
}
|
||||
// render left and right border
|
||||
for (1..size.rows - 1) |row| {
|
||||
const idx = (row * size.cols);
|
||||
cells[idx].cp = frame[3]; // left
|
||||
cells[idx + size.cols - 1].cp = frame[3]; // right
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Rectangle configuration struct
|
||||
@@ -41,7 +85,7 @@ pub const Rectangle = struct {
|
||||
/// `Color` to use to fill the `Rectangle` with
|
||||
/// NOTE: used as background color when rendering! such that it renders the
|
||||
/// children accordingly without removing the coloring of the `Rectangle`
|
||||
fill: Color = .default,
|
||||
fill: Color = .{ .ansi = .reset },
|
||||
/// Configure the corners of the `Rectangle`
|
||||
corners: enum(u1) {
|
||||
squared,
|
||||
@@ -105,6 +149,7 @@ pub fn Container(comptime Event: type) type {
|
||||
@compileError("Provided user event `Event` for `Container(comptime Event: type)`");
|
||||
}
|
||||
return struct {
|
||||
allocator: std.mem.Allocator,
|
||||
size: Size,
|
||||
properties: Properties,
|
||||
elements: std.ArrayList(@This()),
|
||||
@@ -121,6 +166,7 @@ pub fn Container(comptime Event: type) type {
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, properties: Properties) !@This() {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.size = .{ .cols = 0, .rows = 0 },
|
||||
.properties = properties,
|
||||
.elements = std.ArrayList(@This()).init(allocator),
|
||||
@@ -197,7 +243,25 @@ pub fn Container(comptime Event: type) type {
|
||||
offset += rows;
|
||||
},
|
||||
}
|
||||
// TODO; adjust size according to the layout of the `Container`
|
||||
|
||||
// border resizing
|
||||
const sides = this.properties.border.sides;
|
||||
if (sides.top) {
|
||||
element_size.anchor.row += 1;
|
||||
element_size.rows -|= 1;
|
||||
}
|
||||
if (sides.bottom) {
|
||||
element_size.rows -|= 1;
|
||||
}
|
||||
if (sides.left) {
|
||||
element_size.anchor.col += 1;
|
||||
element_size.cols -|= 1;
|
||||
}
|
||||
if (sides.right) {
|
||||
element_size.cols -|= 1;
|
||||
}
|
||||
|
||||
// TODO: adjust size according to the layout of the `Container`
|
||||
if (element.handle(.{ .resize = element_size })) |e| {
|
||||
_ = e;
|
||||
}
|
||||
@@ -219,8 +283,10 @@ pub fn Container(comptime Event: type) type {
|
||||
|
||||
pub fn contents(this: *const @This()) []const Cell {
|
||||
// TODO: use the size and the corresponding contents to determine what should be show in form of a `Cell` array
|
||||
_ = this;
|
||||
return &[0]Cell{};
|
||||
const cells = this.allocator.alloc(Cell, this.size.cols * this.size.rows) catch @panic("Container::contents: Out of memory.");
|
||||
@memset(cells, .{}); // reset all cells
|
||||
this.properties.border.contents(cells, this.size);
|
||||
return cells;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ pub const Buffered = struct {
|
||||
log.debug("renderer::clear", .{});
|
||||
var vs = this.virtual_screen;
|
||||
const anchor = (size.anchor.row * this.size.rows) + size.anchor.col;
|
||||
// TODO: use memset to effectivly reset the coresponding cells?
|
||||
for (0..size.rows) |row| {
|
||||
for (0..size.cols) |col| {
|
||||
vs[anchor + (row * this.size.cols) + col].reset();
|
||||
@@ -72,31 +73,38 @@ pub const Buffered = struct {
|
||||
|
||||
/// Render provided cells at size (anchor and dimension) into the *virtual screen*.
|
||||
pub fn render(this: *@This(), comptime T: type, container: *T) void {
|
||||
const cells: []const Cell = container.contents();
|
||||
const size: Size = container.size;
|
||||
log.debug("renderer:render: cells: {any} @ {any}", .{ cells, size });
|
||||
const cells: []const Cell = container.contents();
|
||||
|
||||
// log.debug("renderer:render: cells: {any} @ {any}", .{ cells, size });
|
||||
|
||||
if (cells.len == 0) return;
|
||||
|
||||
var idx: usize = 0;
|
||||
var vs = this.virtual_screen;
|
||||
const anchor = (size.anchor.row * this.size.rows) + size.anchor.col;
|
||||
const anchor = (size.anchor.row * this.size.cols) + size.anchor.col;
|
||||
|
||||
for (0..size.rows) |row| {
|
||||
blk: for (0..size.rows) |row| {
|
||||
for (0..size.cols) |col| {
|
||||
const cell = cells[idx];
|
||||
idx += 1;
|
||||
|
||||
vs[anchor + (row * this.size.cols) + col].style = cell.style;
|
||||
vs[anchor + (row * this.size.cols) + col].rune = cell.rune;
|
||||
vs[anchor + (row * this.size.cols) + col].cp = cell.cp;
|
||||
|
||||
if (cells.len == idx) return;
|
||||
if (cells.len == idx) break :blk;
|
||||
}
|
||||
}
|
||||
container.allocator.free(cells);
|
||||
|
||||
for (container.elements.items) |*element| {
|
||||
this.render(T, element);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write *virtual screen* to alternate screen (should be called once and last during each render loop iteration in the main loop).
|
||||
pub fn flush(this: *@This()) !void {
|
||||
// TODO: measure timings of rendered frames?
|
||||
log.debug("renderer::flush", .{});
|
||||
const writer = terminal.writer();
|
||||
const s = this.screen;
|
||||
@@ -110,7 +118,7 @@ pub const Buffered = struct {
|
||||
continue;
|
||||
// render differences found in virtual screen
|
||||
// TODO: improve the writing speed (many unnecessary writes (i.e. the style for every character..))
|
||||
try terminal.setCursorPosition(.{ .row = @truncate(row), .col = @truncate(col) });
|
||||
try terminal.setCursorPosition(.{ .row = @truncate(row + 1), .col = @truncate(col + 1) });
|
||||
try cvs.value(writer);
|
||||
// update screen to be the virtual screen for the next frame
|
||||
s[idx] = vs[idx];
|
||||
|
||||
243
src/style.zig
243
src/style.zig
@@ -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:
|
||||
|
||||
@@ -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.col - 1, .rows = ws.row - 1 };
|
||||
return .{ .cols = ws.col, .rows = ws.row };
|
||||
}
|
||||
|
||||
pub fn saveScreen() !void {
|
||||
|
||||
Reference in New Issue
Block a user