Replace vaxis with zterm #1
140
src/ctlseqs.zig
Normal file
140
src/ctlseqs.zig
Normal file
@@ -0,0 +1,140 @@
|
||||
// Queries
|
||||
pub const primary_device_attrs = "\x1b[c";
|
||||
pub const tertiary_device_attrs = "\x1b[=c";
|
||||
pub const device_status_report = "\x1b[5n";
|
||||
pub const xtversion = "\x1b[>0q";
|
||||
pub const decrqm_focus = "\x1b[?1004$p";
|
||||
pub const decrqm_sgr_pixels = "\x1b[?1016$p";
|
||||
pub const decrqm_sync = "\x1b[?2026$p";
|
||||
pub const decrqm_unicode = "\x1b[?2027$p";
|
||||
pub const decrqm_color_scheme = "\x1b[?2031$p";
|
||||
pub const csi_u_query = "\x1b[?u";
|
||||
pub const kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\";
|
||||
pub const sixel_geometry_query = "\x1b[?2;1;0S";
|
||||
|
||||
// mouse. We try for button motion and any motion. terminals will enable the
|
||||
// last one we tried (any motion). This was added because zellij doesn't
|
||||
// support any motion currently
|
||||
// See: https://github.com/zellij-org/zellij/issues/1679
|
||||
pub const mouse_set = "\x1b[?1002;1003;1004;1006h";
|
||||
pub const mouse_set_pixels = "\x1b[?1002;1003;1004;1016h";
|
||||
pub const mouse_reset = "\x1b[?1002;1003;1004;1006;1016l";
|
||||
|
||||
// in-band window size reports
|
||||
pub const in_band_resize_set = "\x1b[?2048h";
|
||||
pub const in_band_resize_reset = "\x1b[?2048l";
|
||||
|
||||
// sync
|
||||
pub const sync_set = "\x1b[?2026h";
|
||||
pub const sync_reset = "\x1b[?2026l";
|
||||
|
||||
// unicode
|
||||
pub const unicode_set = "\x1b[?2027h";
|
||||
pub const unicode_reset = "\x1b[?2027l";
|
||||
|
||||
// bracketed paste
|
||||
pub const bp_set = "\x1b[?2004h";
|
||||
pub const bp_reset = "\x1b[?2004l";
|
||||
|
||||
// color scheme updates
|
||||
pub const color_scheme_request = "\x1b[?996n";
|
||||
pub const color_scheme_set = "\x1b[?2031h";
|
||||
pub const color_scheme_reset = "\x1b[?2031l";
|
||||
|
||||
// Key encoding
|
||||
pub const csi_u_push = "\x1b[>{d}u";
|
||||
pub const csi_u_pop = "\x1b[<u";
|
||||
|
||||
// Cursor
|
||||
pub const home = "\x1b[H";
|
||||
pub const cup = "\x1b[{d};{d}H";
|
||||
pub const hide_cursor = "\x1b[?25l";
|
||||
pub const show_cursor = "\x1b[?25h";
|
||||
pub const cursor_shape = "\x1b[{d} q";
|
||||
pub const ri = "\x1bM";
|
||||
pub const ind = "\n";
|
||||
pub const cuf = "\x1b[{d}C";
|
||||
pub const cub = "\x1b[{d}D";
|
||||
|
||||
// Erase
|
||||
pub const erase_below_cursor = "\x1b[J";
|
||||
|
||||
// alt screen
|
||||
pub const smcup = "\x1b[?1049h";
|
||||
pub const rmcup = "\x1b[?1049l";
|
||||
|
||||
// sgr reset all
|
||||
pub const sgr_reset = "\x1b[m";
|
||||
|
||||
// colors
|
||||
pub const fg_base = "\x1b[3{d}m";
|
||||
pub const fg_bright = "\x1b[9{d}m";
|
||||
pub const bg_base = "\x1b[4{d}m";
|
||||
pub const bg_bright = "\x1b[10{d}m";
|
||||
|
||||
pub const fg_reset = "\x1b[39m";
|
||||
pub const bg_reset = "\x1b[49m";
|
||||
pub const ul_reset = "\x1b[59m";
|
||||
pub const fg_indexed = "\x1b[38:5:{d}m";
|
||||
pub const bg_indexed = "\x1b[48:5:{d}m";
|
||||
pub const ul_indexed = "\x1b[58:5:{d}m";
|
||||
pub const fg_rgb = "\x1b[38:2:{d}:{d}:{d}m";
|
||||
pub const bg_rgb = "\x1b[48:2:{d}:{d}:{d}m";
|
||||
pub const ul_rgb = "\x1b[58:2:{d}:{d}:{d}m";
|
||||
pub const fg_indexed_legacy = "\x1b[38;5;{d}m";
|
||||
pub const bg_indexed_legacy = "\x1b[48;5;{d}m";
|
||||
pub const ul_indexed_legacy = "\x1b[58;5;{d}m";
|
||||
pub const fg_rgb_legacy = "\x1b[38;2;{d};{d};{d}m";
|
||||
pub const bg_rgb_legacy = "\x1b[48;2;{d};{d};{d}m";
|
||||
pub const ul_rgb_legacy = "\x1b[58;2;{d};{d};{d}m";
|
||||
|
||||
// Underlines
|
||||
pub const ul_off = "\x1b[24m"; // NOTE: this could be \x1b[4:0m but is not as widely supported
|
||||
pub const ul_single = "\x1b[4m";
|
||||
pub const ul_double = "\x1b[4:2m";
|
||||
pub const ul_curly = "\x1b[4:3m";
|
||||
pub const ul_dotted = "\x1b[4:4m";
|
||||
pub const ul_dashed = "\x1b[4:5m";
|
||||
|
||||
// Attributes
|
||||
pub const bold_set = "\x1b[1m";
|
||||
pub const dim_set = "\x1b[2m";
|
||||
pub const italic_set = "\x1b[3m";
|
||||
pub const blink_set = "\x1b[5m";
|
||||
pub const reverse_set = "\x1b[7m";
|
||||
pub const invisible_set = "\x1b[8m";
|
||||
pub const strikethrough_set = "\x1b[9m";
|
||||
pub const bold_dim_reset = "\x1b[22m";
|
||||
pub const italic_reset = "\x1b[23m";
|
||||
pub const blink_reset = "\x1b[25m";
|
||||
pub const reverse_reset = "\x1b[27m";
|
||||
pub const invisible_reset = "\x1b[28m";
|
||||
pub const strikethrough_reset = "\x1b[29m";
|
||||
|
||||
// OSC sequences
|
||||
pub const osc2_set_title = "\x1b]2;{s}\x1b\\";
|
||||
pub const osc7 = "\x1b]7;{;+/}\x1b\\";
|
||||
pub const osc8 = "\x1b]8;{s};{s}\x1b\\";
|
||||
pub const osc8_clear = "\x1b]8;;\x1b\\";
|
||||
pub const osc9_notify = "\x1b]9;{s}\x1b\\";
|
||||
pub const osc777_notify = "\x1b]777;notify;{s};{s}\x1b\\";
|
||||
pub const osc22_mouse_shape = "\x1b]22;{s}\x1b\\";
|
||||
pub const osc52_clipboard_copy = "\x1b]52;c;{s}\x1b\\";
|
||||
pub const osc52_clipboard_request = "\x1b]52;c;?\x1b\\";
|
||||
|
||||
// Kitty graphics
|
||||
pub const kitty_graphics_clear = "\x1b_Ga=d\x1b\\";
|
||||
pub const kitty_graphics_preamble = "\x1b_Ga=p,i={d}";
|
||||
pub const kitty_graphics_closing = ",C=1\x1b\\";
|
||||
|
||||
// Color control sequences
|
||||
pub const osc4_query = "\x1b]4;{d};?\x1b\\"; // color index {d}
|
||||
pub const osc4_reset = "\x1b]104\x1b\\"; // this resets _all_ color indexes
|
||||
pub const osc10_query = "\x1b]10;?\x1b\\"; // fg
|
||||
pub const osc10_set = "\x1b]10;rgb:{x:0>2}{x:0>2}/{x:0>2}{x:0>2}/{x:0>2}{x:0>2}\x1b\\"; // set default terminal fg
|
||||
pub const osc10_reset = "\x1b]110\x1b\\"; // reset fg to terminal default
|
||||
pub const osc11_query = "\x1b]11;?\x1b\\"; // bg
|
||||
pub const osc11_set = "\x1b]11;rgb:{x:0>2}{x:0>2}/{x:0>2}{x:0>2}/{x:0>2}{x:0>2}\x1b\\"; // set default terminal bg
|
||||
pub const osc11_reset = "\x1b]111\x1b\\"; // reset bg to terminal default
|
||||
pub const osc12_query = "\x1b]12;?\x1b\\"; // cursor color
|
||||
pub const osc12_reset = "\x1b]112\x1b\\"; // reset cursor to terminal default
|
||||
@@ -18,12 +18,13 @@ pub fn Layout(comptime Event: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
const LayoutType = @This();
|
||||
const Ptr = usize;
|
||||
|
||||
const VTable = struct {
|
||||
handle: *const fn (this: *LayoutType, event: Event) anyerror!*std.ArrayList(Event),
|
||||
handle: *const fn (this: *LayoutType, event: Event) anyerror!*Events,
|
||||
content: *const fn (this: *LayoutType) anyerror!*std.ArrayList(u8),
|
||||
deinit: *const fn (this: *LayoutType) void,
|
||||
};
|
||||
@@ -32,7 +33,7 @@ pub fn Layout(comptime Event: type) type {
|
||||
vtable: *const VTable = undefined,
|
||||
|
||||
// Handle the provided `Event` for this `Layout`.
|
||||
pub fn handle(this: *LayoutType, event: Event) !*std.ArrayList(Event) {
|
||||
pub fn handle(this: *LayoutType, event: Event) !*Events {
|
||||
return try this.vtable.handle(this, event);
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ pub fn Layout(comptime Event: type) type {
|
||||
.vtable = &.{
|
||||
.handle = struct {
|
||||
// Handle the provided `Event` for this `Layout`.
|
||||
fn handle(this: *LayoutType, event: Event) !*std.ArrayList(Event) {
|
||||
fn handle(this: *LayoutType, event: Event) !*Events {
|
||||
const layout: @TypeOf(object) = @ptrFromInt(this.object);
|
||||
return try layout.handle(event);
|
||||
}
|
||||
|
||||
@@ -2,22 +2,23 @@ const std = @import("std");
|
||||
const terminal = @import("../terminal.zig");
|
||||
const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
||||
const Error = @import("../event.zig").Error;
|
||||
const Key = @import("../key.zig");
|
||||
|
||||
pub fn Layout(comptime Event: type) type {
|
||||
if (!isTaggedUnion(Event)) {
|
||||
@compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`.");
|
||||
}
|
||||
const Widget = @import("../widget.zig").Widget(Event);
|
||||
const Events = std.ArrayList(Event);
|
||||
return struct {
|
||||
widget: Widget = undefined,
|
||||
events: std.ArrayList(Event) = undefined,
|
||||
events: Events = undefined,
|
||||
c: std.ArrayList(u8) = undefined,
|
||||
size: terminal.Size = undefined,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, widget: Widget) @This() {
|
||||
return .{
|
||||
.widget = widget,
|
||||
.events = std.ArrayList(Event).init(allocator),
|
||||
.events = Events.init(allocator),
|
||||
.c = std.ArrayList(u8).init(allocator),
|
||||
};
|
||||
}
|
||||
@@ -29,11 +30,8 @@ pub fn Layout(comptime Event: type) type {
|
||||
this.* = undefined;
|
||||
}
|
||||
|
||||
pub fn handle(this: *@This(), event: Event) !*std.ArrayList(Event) {
|
||||
pub fn handle(this: *@This(), event: Event) !*Events {
|
||||
switch (event) {
|
||||
.resize => |size| {
|
||||
this.size = size;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
this.events.clearRetainingCapacity();
|
||||
@@ -46,7 +44,7 @@ pub fn Layout(comptime Event: type) type {
|
||||
pub fn content(this: *@This()) !*std.ArrayList(u8) {
|
||||
const widget_content = try this.widget.content();
|
||||
this.c.clearRetainingCapacity();
|
||||
try this.c.appendSlice(widget_content.items);
|
||||
try this.c.appendSlice(widget_content);
|
||||
return &this.c;
|
||||
}
|
||||
};
|
||||
|
||||
11
src/main.zig
11
src/main.zig
@@ -28,10 +28,12 @@ pub fn main() !void {
|
||||
var app: App = .{};
|
||||
var renderer: App.Renderer = .{};
|
||||
|
||||
var rawText = App.Widget.RawText.init(allocator);
|
||||
const file = try std.fs.cwd().openFile("./src/main.zig", .{});
|
||||
var rawText = App.Widget.RawText.init(allocator, file);
|
||||
const widget = App.Widget.createFrom(&rawText);
|
||||
var layout = App.Layout.Pane.init(allocator, widget);
|
||||
defer layout.deinit(); // deinitializes the contained widget
|
||||
file.close();
|
||||
|
||||
try app.start();
|
||||
defer app.stop() catch unreachable;
|
||||
@@ -75,13 +77,6 @@ pub fn main() !void {
|
||||
}
|
||||
// TODO: I could use the ascii codes in vaxis
|
||||
// - see https://gist.github.com/ConnerWill/d4b6c776b509add763e17f9f113fd25b
|
||||
// how would I draw?
|
||||
// use array for screen contents? <-> support partial re-draws
|
||||
// support widget type drawing similar to the already existing widgets
|
||||
// determine the corresponding capabilities of the terminal?
|
||||
// support layouts
|
||||
// - contents of corresponding locations
|
||||
// resize event
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
286
src/style.zig
286
src/style.zig
@@ -4,19 +4,281 @@
|
||||
//!
|
||||
//! Stylings however also include highlighting for specific terminal capabilities.
|
||||
//! For example url highlighting.
|
||||
const std = @import("std");
|
||||
|
||||
// TODO: implement helper functions for the following stylings:
|
||||
// - bold
|
||||
// - italic
|
||||
// - underline
|
||||
// - curly line
|
||||
// - strike through
|
||||
// - reverse
|
||||
// - blink
|
||||
// - color:
|
||||
// - foreground
|
||||
// - background
|
||||
// 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");
|
||||
|
||||
pub const Underline = enum {
|
||||
off,
|
||||
single,
|
||||
double,
|
||||
curly,
|
||||
dotted,
|
||||
dashed,
|
||||
};
|
||||
|
||||
pub const Color = union(enum) {
|
||||
default,
|
||||
index: u8,
|
||||
rgb: [3]u8,
|
||||
|
||||
pub fn eql(a: @This(), b: @This()) bool {
|
||||
switch (a) {
|
||||
.default => return b == .default,
|
||||
.index => |a_idx| {
|
||||
switch (b) {
|
||||
.index => |b_idx| return a_idx == b_idx,
|
||||
else => return false,
|
||||
}
|
||||
},
|
||||
.rgb => |a_rgb| {
|
||||
switch (b) {
|
||||
.rgb => |b_rgb| return a_rgb[0] == b_rgb[0] and
|
||||
a_rgb[1] == b_rgb[1] and
|
||||
a_rgb[2] == b_rgb[2],
|
||||
else => return false,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rgbFromUint(val: u24) Color {
|
||||
const r_bits = val & 0b11111111_00000000_00000000;
|
||||
const g_bits = val & 0b00000000_11111111_00000000;
|
||||
const b_bits = val & 0b00000000_00000000_11111111;
|
||||
const rgb = [_]u8{
|
||||
@truncate(r_bits >> 16),
|
||||
@truncate(g_bits >> 8),
|
||||
@truncate(b_bits),
|
||||
};
|
||||
return .{ .rgb = rgb };
|
||||
}
|
||||
|
||||
/// parse an XParseColor-style rgb specification into an rgb Color. The spec
|
||||
/// is of the form: rgb:rrrr/gggg/bbbb. Generally, the high two bits will always
|
||||
/// be the same as the low two bits.
|
||||
pub fn rgbFromSpec(spec: []const u8) !Color {
|
||||
var iter = std.mem.splitScalar(u8, spec, ':');
|
||||
const prefix = iter.next() orelse return error.InvalidColorSpec;
|
||||
if (!std.mem.eql(u8, "rgb", prefix)) return error.InvalidColorSpec;
|
||||
|
||||
const spec_str = iter.next() orelse return error.InvalidColorSpec;
|
||||
|
||||
var spec_iter = std.mem.splitScalar(u8, spec_str, '/');
|
||||
|
||||
const r_raw = spec_iter.next() orelse return error.InvalidColorSpec;
|
||||
if (r_raw.len != 4) return error.InvalidColorSpec;
|
||||
|
||||
const g_raw = spec_iter.next() orelse return error.InvalidColorSpec;
|
||||
if (g_raw.len != 4) return error.InvalidColorSpec;
|
||||
|
||||
const b_raw = spec_iter.next() orelse return error.InvalidColorSpec;
|
||||
if (b_raw.len != 4) return error.InvalidColorSpec;
|
||||
|
||||
const r = try std.fmt.parseUnsigned(u8, r_raw[2..], 16);
|
||||
const g = try std.fmt.parseUnsigned(u8, g_raw[2..], 16);
|
||||
const b = try std.fmt.parseUnsigned(u8, b_raw[2..], 16);
|
||||
|
||||
return .{
|
||||
.rgb = [_]u8{ r, g, b },
|
||||
};
|
||||
}
|
||||
|
||||
test "rgbFromSpec" {
|
||||
const spec = "rgb:aaaa/bbbb/cccc";
|
||||
const actual = try rgbFromSpec(spec);
|
||||
switch (actual) {
|
||||
.rgb => |rgb| {
|
||||
try std.testing.expectEqual(0xAA, rgb[0]);
|
||||
try std.testing.expectEqual(0xBB, rgb[1]);
|
||||
try std.testing.expectEqual(0xCC, rgb[2]);
|
||||
},
|
||||
else => try std.testing.expect(false),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fg: Color = .default,
|
||||
bg: Color = .default,
|
||||
ul: Color = .default,
|
||||
ul_style: Underline = .off,
|
||||
|
||||
bold: bool = false,
|
||||
dim: bool = false,
|
||||
italic: bool = false,
|
||||
blink: bool = false,
|
||||
reverse: bool = false,
|
||||
invisible: bool = false,
|
||||
strikethrough: bool = false,
|
||||
|
||||
fn start(this: @This(), 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] });
|
||||
},
|
||||
}
|
||||
// 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: @This(), 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 format(this: @This(), writer: anytype, comptime content: []const u8, args: anytype) !void {
|
||||
try this.start(writer);
|
||||
try std.fmt.format(writer, content, args);
|
||||
try this.end(writer);
|
||||
}
|
||||
|
||||
pub fn value(this: @This(), writer: anytype, content: []const u8) !void {
|
||||
try this.start(writer);
|
||||
_ = try writer.write(content);
|
||||
try this.end(writer);
|
||||
}
|
||||
|
||||
// TODO: implement helper functions for terminal capabilities:
|
||||
// - links / url display (osc 8)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Dynamic dispatch for widget implementations.
|
||||
//! Each widget should at last implement these functions:
|
||||
//! - handle(this: *@This(), event: Event) ?Event {}
|
||||
//! - content(this: *@This()) *std.ArrayList(u8) {}
|
||||
//! - content(this: *@This()) ![]u8 {}
|
||||
//! - deinit(this: *@This()) void {}
|
||||
//!
|
||||
//! Create a `Widget` using `createFrom(object: anytype)` and use them through
|
||||
@@ -10,7 +10,6 @@
|
||||
//!
|
||||
//! Each `Widget` may cache its content and should if the contents will not
|
||||
//! change for a long time.
|
||||
const std = @import("std");
|
||||
const isTaggedUnion = @import("event.zig").isTaggedUnion;
|
||||
|
||||
pub fn Widget(comptime Event: type) type {
|
||||
@@ -23,7 +22,7 @@ pub fn Widget(comptime Event: type) type {
|
||||
|
||||
const VTable = struct {
|
||||
handle: *const fn (this: *WidgetType, event: Event) ?Event,
|
||||
content: *const fn (this: *WidgetType) anyerror!*std.ArrayList(u8),
|
||||
content: *const fn (this: *WidgetType) anyerror![]u8,
|
||||
deinit: *const fn (this: *WidgetType) void,
|
||||
};
|
||||
|
||||
@@ -36,7 +35,7 @@ pub fn Widget(comptime Event: type) type {
|
||||
}
|
||||
|
||||
// Return the entire content of this `Widget`.
|
||||
pub fn content(this: *WidgetType) !*std.ArrayList(u8) {
|
||||
pub fn content(this: *WidgetType) ![]u8 {
|
||||
return try this.vtable.content(this);
|
||||
}
|
||||
|
||||
@@ -58,7 +57,7 @@ pub fn Widget(comptime Event: type) type {
|
||||
}.handle,
|
||||
.content = struct {
|
||||
// Return the entire content of this `Widget`.
|
||||
fn content(this: *WidgetType) !*std.ArrayList(u8) {
|
||||
fn content(this: *WidgetType) ![]u8 {
|
||||
const widget: @TypeOf(object) = @ptrFromInt(this.object);
|
||||
return try widget.content();
|
||||
}
|
||||
@@ -75,8 +74,5 @@ pub fn Widget(comptime Event: type) type {
|
||||
|
||||
// TODO: import and export of `Widget` implementations (with corresponding intialization using `Event`)
|
||||
pub const RawText = @import("widget/RawText.zig").Widget(Event);
|
||||
// pub const Header = @import("widget/Header.zig");
|
||||
// pub const ViewPort = @import("widget/ViewPort.zig");
|
||||
// pub const PopupMenu = @import("widget/PopupMenu.zig");
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const terminal = @import("../terminal.zig");
|
||||
const Style = @import("../style.zig");
|
||||
|
||||
const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
||||
const Error = @import("../event.zig").Error;
|
||||
@@ -11,15 +12,25 @@ pub fn Widget(comptime Event: type) type {
|
||||
}
|
||||
return struct {
|
||||
c: std.ArrayList(u8) = undefined,
|
||||
key: Key = undefined,
|
||||
line_index: std.ArrayList(usize) = undefined,
|
||||
line: usize = 0,
|
||||
size: terminal.Size = undefined,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) @This() {
|
||||
return .{ .c = std.ArrayList(u8).init(allocator) };
|
||||
pub fn init(allocator: std.mem.Allocator, file: std.fs.File) @This() {
|
||||
var c = std.ArrayList(u8).init(allocator);
|
||||
var line_index = std.ArrayList(usize).init(allocator);
|
||||
file.reader().readAllArrayList(&c, 4192) catch {};
|
||||
for (c.items, 0..) |item, i| {
|
||||
if (item == '\n') {
|
||||
line_index.append(i) catch {};
|
||||
}
|
||||
}
|
||||
return .{ .c = c, .line_index = line_index };
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
this.c.deinit();
|
||||
this.line_index.deinit();
|
||||
this.* = undefined;
|
||||
}
|
||||
|
||||
@@ -30,19 +41,43 @@ pub fn Widget(comptime Event: type) type {
|
||||
this.size = size;
|
||||
},
|
||||
.key => |key| {
|
||||
this.key = key;
|
||||
if (key.matches(.{ .cp = 'g' })) {
|
||||
// top
|
||||
this.line = 0;
|
||||
}
|
||||
if (key.matches(.{ .cp = 'G' })) {
|
||||
// bottom
|
||||
this.line = this.line_index.items.len - 1;
|
||||
}
|
||||
if (key.matches(.{ .cp = 'j' })) {
|
||||
// down
|
||||
if (this.line < this.line_index.items.len - 1) {
|
||||
this.line +|= 1;
|
||||
}
|
||||
}
|
||||
if (key.matches(.{ .cp = 'k' })) {
|
||||
// up
|
||||
this.line -|= 1;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn content(this: *@This()) !*std.ArrayList(u8) {
|
||||
this.c.clearRetainingCapacity();
|
||||
const writer = this.c.writer();
|
||||
try std.fmt.format(writer, "The terminal has a reported size of [cols: {d}, rows: {d}]\n", .{ this.size.cols, this.size.rows });
|
||||
try std.fmt.format(writer, "User entered key: {any}\n", .{this.key});
|
||||
return &this.c;
|
||||
pub fn content(this: *@This()) ![]u8 {
|
||||
if (this.size.rows >= this.line_index.items.len) {
|
||||
return this.c.items;
|
||||
} else {
|
||||
// more rows than we can display
|
||||
const i = this.line_index.items[this.line];
|
||||
const e = this.size.rows + this.line;
|
||||
if (e >= this.line_index.items.len) {
|
||||
return this.c.items[i..];
|
||||
}
|
||||
const x = this.line_index.items[e];
|
||||
return this.c.items[i..x];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user