refactor: add renderer abstraction layer

This commit is contained in:
CJ van den Berg 2024-04-25 22:45:02 +02:00
parent 9ff63fbed5
commit b15fa47f30
47 changed files with 1419 additions and 1023 deletions

View file

@ -1,9 +1,10 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const Plane = @import("renderer").Plane;
const Widget = @import("../Widget.zig");
const Button = @import("../Button.zig");
const tui = @import("../tui.zig");
@ -18,7 +19,7 @@ rendered: [:0]const u8 = "",
const Self = @This();
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
return Button.create_widget(Self, a, parent, .{
.ctx = .{},
.label = "",
@ -39,7 +40,7 @@ pub fn layout(self: *Self, _: *Button.State(Self)) Widget.Layout {
pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool {
const bg_style = if (btn.active) theme.editor_cursor else if (btn.hover) theme.statusbar_hover else theme.statusbar;
tui.set_base_style(&btn.plane, " ", bg_style);
btn.plane.set_base_style(" ", bg_style);
btn.plane.erase();
btn.plane.home();
_ = btn.plane.putstr(self.rendered) catch {};

View file

@ -1,10 +1,12 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const root = @import("root");
const Plane = @import("renderer").Plane;
const style = @import("renderer").style;
const Widget = @import("../Widget.zig");
const Button = @import("../Button.zig");
const command = @import("../command.zig");
@ -31,7 +33,7 @@ project: bool = false,
const project_icon = "";
const Self = @This();
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
const btn = try Button.create(Self, a, parent, .{
.ctx = .{
.a = a,
@ -73,7 +75,7 @@ pub fn layout(_: *Self, _: *Button.State(Self)) Widget.Layout {
pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool {
const frame = tracy.initZone(@src(), .{ .name = @typeName(@This()) ++ " render" });
defer frame.deinit();
tui.set_base_style(&btn.plane, " ", if (btn.active) theme.editor_cursor else theme.statusbar);
btn.plane.set_base_style(" ", if (btn.active) theme.editor_cursor else theme.statusbar);
btn.plane.erase();
btn.plane.home();
if (tui.current().mini_mode) |_|
@ -86,8 +88,8 @@ pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme)
return false;
}
fn render_mini_mode(plane: *nc.Plane, theme: *const Widget.Theme) void {
plane.off_styles(nc.style.italic);
fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void {
plane.off_styles(style.italic);
const mini_mode = if (tui.current().mini_mode) |m| m else return;
_ = plane.print(" {s}", .{mini_mode.text}) catch {};
if (mini_mode.cursor) |cursor| {
@ -95,7 +97,7 @@ fn render_mini_mode(plane: *nc.Plane, theme: *const Widget.Theme) void {
plane.cursor_move_yx(0, pos + 1) catch return;
var cell = plane.cell_init();
_ = plane.at_cursor_cell(&cell) catch return;
tui.set_cell_style(&cell, theme.editor_cursor);
cell.set_style(theme.editor_cursor);
_ = plane.putc(&cell) catch {};
}
return;
@ -109,8 +111,8 @@ fn render_mini_mode(plane: *nc.Plane, theme: *const Widget.Theme) void {
// 󱣪 Content save check
// 󱑛 Content save cog
// 󰆔 Content save all
fn render_normal(self: *Self, plane: *nc.Plane, theme: *const Widget.Theme) void {
plane.on_styles(nc.style.italic);
fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme) void {
plane.on_styles(style.italic);
_ = plane.putstr(" ") catch {};
if (self.file_icon.len > 0) {
self.render_file_icon(plane, theme);
@ -121,8 +123,8 @@ fn render_normal(self: *Self, plane: *nc.Plane, theme: *const Widget.Theme) void
return;
}
fn render_detailed(self: *Self, plane: *nc.Plane, theme: *const Widget.Theme) void {
plane.on_styles(nc.style.italic);
fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme) void {
plane.on_styles(style.italic);
_ = plane.putstr(" ") catch {};
if (self.file_icon.len > 0) {
self.render_file_icon(plane, theme);
@ -153,7 +155,7 @@ fn render_terminal_title(self: *Self) void {
if (std.mem.eql(u8, self.title, new_title)) return;
@memcpy(self.title_buf[0..new_title.len], new_title);
self.title = self.title_buf[0..new_title.len];
tui.set_terminal_title(self.title);
tui.renderer.set_terminal_title(self.title);
}
pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool {
@ -193,12 +195,11 @@ pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message
return false;
}
fn render_file_icon(self: *Self, plane: *nc.Plane, _: *const Widget.Theme) void {
fn render_file_icon(self: *Self, plane: *Plane, _: *const Widget.Theme) void {
var cell = plane.cell_init();
_ = plane.at_cursor_cell(&cell) catch return;
if (!(self.file_color == 0xFFFFFF or self.file_color == 0x000000 or self.file_color == 0x000001)) {
nc.channels_set_fg_rgb(&cell.channels, self.file_color) catch {};
nc.channels_set_fg_alpha(&cell.channels, nc.ALPHA_OPAQUE) catch {};
cell.set_fg_rgb(self.file_color) catch {};
}
_ = plane.cell_load(&cell, self.file_icon) catch {};
_ = plane.putc(&cell) catch {};

View file

@ -1,9 +1,13 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const Plane = @import("renderer").Plane;
const utils = @import("renderer").input.utils;
const key_ = @import("renderer").input.key;
const event_type = @import("renderer").input.event_type;
const Widget = @import("../Widget.zig");
const command = @import("../command.zig");
const tui = @import("../tui.zig");
@ -11,8 +15,8 @@ const EventHandler = @import("../EventHandler.zig");
const history = 8;
parent: nc.Plane,
plane: nc.Plane,
parent: Plane,
plane: Plane,
frame: u64 = 0,
idle_frame: u64 = 0,
key_active_frame: u64 = 0,
@ -28,15 +32,15 @@ const Self = @This();
const idle_msg = "🐶";
pub const width = idle_msg.len + 20;
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
const self: *Self = try a.create(Self);
self.* = try init(parent);
try tui.current().input_listeners.add(EventHandler.bind(self, listen));
return self.widget();
}
fn init(parent: nc.Plane) !Self {
var n = try nc.Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent);
fn init(parent: Plane) !Self {
var n = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent);
errdefer n.deinit();
var frame_rate = tp.env.get().num("frame-rate");
if (frame_rate == 0) frame_rate = 60;
@ -69,15 +73,15 @@ fn render_active(self: *Self) bool {
return true;
if (c > 0)
_ = self.plane.putstr(" ") catch {};
if (nc.isSuper(k.mod))
if (utils.isSuper(k.mod))
_ = self.plane.putstr("H-") catch {};
if (nc.isCtrl(k.mod))
if (utils.isCtrl(k.mod))
_ = self.plane.putstr("C-") catch {};
if (nc.isShift(k.mod))
if (utils.isShift(k.mod))
_ = self.plane.putstr("S-") catch {};
if (nc.isAlt(k.mod))
if (utils.isAlt(k.mod))
_ = self.plane.putstr("A-") catch {};
_ = self.plane.print("{s}", .{nc.key_id_string(k.id)}) catch {};
_ = self.plane.print("{s}", .{utils.key_id_string(k.id)}) catch {};
c += 1;
}
return true;
@ -91,7 +95,7 @@ fn render_idle(self: *Self) bool {
return self.animate();
} else {
const i = @mod(self.idle_frame / 8, idle_spinner.len);
_ = self.plane.print_aligned(0, .center, "{s} {s} {s}", .{ idle_spinner[i], idle_msg, idle_spinner[i] }) catch {};
_ = self.plane.print_aligned_center(0, "{s} {s} {s}", .{ idle_spinner[i], idle_msg, idle_spinner[i] }) catch {};
}
return true;
}
@ -99,7 +103,7 @@ fn render_idle(self: *Self) bool {
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
const frame = tracy.initZone(@src(), .{ .name = @typeName(@This()) ++ " render" });
defer frame.deinit();
tui.set_base_style(&self.plane, " ", if (self.hover) theme.statusbar_hover else theme.statusbar);
self.plane.set_base_style(" ", if (self.hover) theme.statusbar_hover else theme.statusbar);
self.frame += 1;
if (self.frame - self.key_active_frame > self.wipe_after_frames)
self.unset_key_all();
@ -163,20 +167,20 @@ fn set_key(self: *Self, key: Key, val: bool) void {
pub fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result {
var key: u32 = 0;
var mod: u32 = 0;
if (try m.match(.{ "I", nc.event_type.PRESS, tp.extract(&key), tp.any, tp.any, tp.extract(&mod), tp.more })) {
if (try m.match(.{ "I", event_type.PRESS, tp.extract(&key), tp.any, tp.any, tp.extract(&mod), tp.more })) {
self.set_key(.{ .id = key, .mod = mod }, true);
} else if (try m.match(.{ "I", nc.event_type.RELEASE, tp.extract(&key), tp.any, tp.any, tp.extract(&mod), tp.more })) {
} else if (try m.match(.{ "I", event_type.RELEASE, tp.extract(&key), tp.any, tp.any, tp.extract(&mod), tp.more })) {
self.set_key(.{ .id = key, .mod = mod }, false);
}
}
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
if (try m.match(.{ "B", event_type.PRESS, key_.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
command.executeName("toggle_inputview", .{}) catch {};
return true;
}
if (try m.match(.{ "H", tp.extract(&self.hover) })) {
tui.current().request_mouse_cursor_pointer(self.hover);
tui.renderer.request_mouse_cursor_pointer(self.hover);
return true;
}
@ -200,7 +204,7 @@ const eighths_l = [_][]const u8{ "█", "▉", "▊", "▋", "▌", "▍", "▎"
const eighths_r = [_][]const u8{ " ", "", "🮇", "🮈", "", "🮉", "🮊", "🮋" };
const eighths_c = eighths_l.len;
fn smooth_block_at(plane: nc.Plane, pos: u64) void {
fn smooth_block_at(plane: Plane, pos: u64) void {
const blk = @mod(pos, eighths_c) + 1;
const l = eighths_l[eighths_c - blk];
const r = eighths_r[eighths_c - blk];

View file

@ -1,9 +1,10 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const Plane = @import("renderer").Plane;
const Widget = @import("../Widget.zig");
const Button = @import("../Button.zig");
const tui = @import("../tui.zig");
@ -17,7 +18,7 @@ rendered: [:0]const u8 = "",
const Self = @This();
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
return Button.create_widget(Self, a, parent, .{
.ctx = .{},
.label = "",
@ -37,7 +38,7 @@ pub fn layout(self: *Self, _: *Button.State(Self)) Widget.Layout {
}
pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool {
tui.set_base_style(&btn.plane, " ", if (btn.active) theme.editor_cursor else if (btn.hover) theme.statusbar_hover else theme.statusbar);
btn.plane.set_base_style(" ", if (btn.active) theme.editor_cursor else if (btn.hover) theme.statusbar_hover else theme.statusbar);
btn.plane.erase();
btn.plane.home();
_ = btn.plane.putstr(self.rendered) catch {};

View file

@ -1,15 +1,16 @@
const std = @import("std");
const nc = @import("notcurses");
const tp = @import("thespian");
const log = @import("log");
const Plane = @import("renderer").Plane;
const Widget = @import("../Widget.zig");
const MessageFilter = @import("../MessageFilter.zig");
const tui = @import("../tui.zig");
const mainview = @import("../mainview.zig");
parent: nc.Plane,
plane: nc.Plane,
parent: Plane,
plane: Plane,
msg: std.ArrayList(u8),
is_error: bool = false,
clear_time: i64 = 0,
@ -18,11 +19,11 @@ const message_display_time_seconds = 2;
const error_display_time_seconds = 4;
const Self = @This();
pub fn create(a: std.mem.Allocator, parent: nc.Plane) !Widget {
pub fn create(a: std.mem.Allocator, parent: Plane) !Widget {
const self: *Self = try a.create(Self);
self.* = .{
.parent = parent,
.plane = try nc.Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.msg = std.ArrayList(u8).init(a),
};
try tui.current().message_filters.add(MessageFilter.bind(self, receive_log));
@ -43,11 +44,11 @@ pub fn layout(self: *Self) Widget.Layout {
}
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
tui.set_base_style(&self.plane, " ", if (self.msg.items.len > 0) theme.sidebar else theme.statusbar);
self.plane.set_base_style(" ", if (self.msg.items.len > 0) theme.sidebar else theme.statusbar);
self.plane.erase();
self.plane.home();
if (self.is_error)
tui.set_base_style(&self.plane, " ", theme.editor_error);
self.plane.set_base_style(" ", theme.editor_error);
_ = self.plane.print(" {s} ", .{self.msg.items}) catch {};
const curr_time = std.time.milliTimestamp();

View file

@ -1,10 +1,12 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const root = @import("root");
const Buffer = @import("Buffer");
const egc = @import("renderer").egc;
const Plane = @import("renderer").Plane;
const style = @import("renderer").style;
const Widget = @import("../Widget.zig");
const Menu = @import("../Menu.zig");
@ -13,7 +15,7 @@ const command = @import("../command.zig");
const ed = @import("../editor.zig");
const tui = @import("../tui.zig");
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
return Button.create_widget(void, a, parent, .{
.ctx = {},
.label = tui.get_mode(),
@ -25,7 +27,7 @@ pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn layout(_: *void, _: *Button.State(void)) Widget.Layout {
const name = tui.get_mode();
const width = Buffer.egc_chunk_width(name, 0);
const width = egc.chunk_width(name, 0);
const padding: usize = if (is_mini_mode()) 3 else 2;
return .{ .static = width + padding };
}
@ -39,8 +41,8 @@ fn is_overlay_mode() bool {
}
pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) bool {
tui.set_base_style(&self.plane, " ", if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover);
self.plane.on_styles(nc.style.bold);
self.plane.set_base_style(" ", if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover);
self.plane.on_styles(style.bold);
self.plane.erase();
self.plane.home();
var buf: [31:0]u8 = undefined;

View file

@ -1,16 +1,19 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const Plane = @import("renderer").Plane;
const key = @import("renderer").input.key;
const event_type = @import("renderer").input.event_type;
const utils = @import("renderer").input.utils;
const Widget = @import("../Widget.zig");
const command = @import("../command.zig");
const tui = @import("../tui.zig");
const EventHandler = @import("../EventHandler.zig");
parent: nc.Plane,
plane: nc.Plane,
plane: Plane,
ctrl: bool = false,
shift: bool = false,
alt: bool = false,
@ -20,18 +23,17 @@ const Self = @This();
pub const width = 5;
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
const self: *Self = try a.create(Self);
self.* = try init(parent);
try tui.current().input_listeners.add(EventHandler.bind(self, listen));
return self.widget();
}
fn init(parent: nc.Plane) !Self {
var n = try nc.Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent);
fn init(parent: Plane) !Self {
var n = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent);
errdefer n.deinit();
return .{
.parent = parent,
.plane = n,
};
}
@ -53,7 +55,7 @@ pub fn layout(_: *Self) Widget.Layout {
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
const frame = tracy.initZone(@src(), .{ .name = @typeName(@This()) ++ " render" });
defer frame.deinit();
tui.set_base_style(&self.plane, " ", if (self.hover) theme.statusbar_hover else theme.statusbar);
self.plane.set_base_style(" ", if (self.hover) theme.statusbar_hover else theme.statusbar);
self.plane.erase();
self.plane.home();
@ -76,14 +78,14 @@ fn render_modifier(self: *Self, state: bool, off: [:0]const u8, on: [:0]const u8
pub fn listen(self: *Self, _: tp.pid_ref, m: tp.message) tp.result {
var mod: u32 = 0;
if (try m.match(.{ "I", tp.any, tp.any, tp.any, tp.any, tp.extract(&mod), tp.more })) {
self.ctrl = nc.isCtrl(mod);
self.shift = nc.isShift(mod);
self.alt = nc.isAlt(mod);
self.ctrl = utils.isCtrl(mod);
self.shift = utils.isShift(mod);
self.alt = utils.isAlt(mod);
}
}
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
if (try m.match(.{ "B", event_type.PRESS, key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
command.executeName("toggle_inputview", .{}) catch {};
return true;
}

View file

@ -1,15 +1,15 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const nc = @import("notcurses");
const tp = @import("thespian");
const tracy = @import("tracy");
const Plane = @import("renderer").Plane;
const Widget = @import("../Widget.zig");
const ed = @import("../editor.zig");
const tui = @import("../tui.zig");
parent: nc.Plane,
plane: nc.Plane,
plane: Plane,
matches: usize = 0,
cursels: usize = 0,
selection: ?ed.Selection = null,
@ -18,18 +18,17 @@ rendered: [:0]const u8 = "",
const Self = @This();
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
pub fn create(a: Allocator, parent: Plane) !Widget {
const self: *Self = try a.create(Self);
self.* = try init(parent);
return Widget.to(self);
}
fn init(parent: nc.Plane) !Self {
var n = try nc.Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent);
fn init(parent: Plane) !Self {
var n = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent);
errdefer n.deinit();
return .{
.parent = parent,
.plane = n,
};
}
@ -46,7 +45,7 @@ pub fn layout(self: *Self) Widget.Layout {
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
const frame = tracy.initZone(@src(), .{ .name = @typeName(@This()) ++ " render" });
defer frame.deinit();
tui.set_base_style(&self.plane, " ", theme.statusbar);
self.plane.set_base_style(" ", theme.statusbar);
self.plane.erase();
self.plane.home();
_ = self.plane.putstr(self.rendered) catch {};

View file

@ -1,13 +1,9 @@
const std = @import("std");
const nc = @import("notcurses");
const Widget = @import("../Widget.zig");
const WidgetList = @import("../WidgetList.zig");
const tui = @import("../tui.zig");
parent: nc.Plane,
plane: nc.Plane,
const Self = @This();
pub fn create(a: std.mem.Allocator, parent: Widget) !Widget {