refactor: add renderer abstraction layer
This commit is contained in:
parent
9ff63fbed5
commit
b15fa47f30
47 changed files with 1419 additions and 1023 deletions
18
build.zig
18
build.zig
|
@ -55,6 +55,7 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = dependency_optimize,
|
||||
.use_system_notcurses = use_system_notcurses,
|
||||
});
|
||||
const notcurses_mod = notcurses_dep.module("notcurses");
|
||||
|
||||
const clap_dep = b.dependency("clap", .{
|
||||
.target = target,
|
||||
|
@ -94,7 +95,6 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
const thespian_mod = thespian_dep.module("thespian");
|
||||
const cbor_mod = thespian_dep.module("cbor");
|
||||
const notcurses_mod = notcurses_dep.module("notcurses");
|
||||
|
||||
const help_mod = b.createModule(.{
|
||||
.root_source_file = .{ .path = "help.md" },
|
||||
|
@ -118,10 +118,22 @@ pub fn build(b: *std.Build) void {
|
|||
.root_source_file = .{ .path = "src/color.zig" },
|
||||
});
|
||||
|
||||
const notcurses_renderer_mod = b.createModule(.{
|
||||
.root_source_file = .{ .path = "src/renderer/notcurses/renderer.zig" },
|
||||
.imports = &.{
|
||||
.{ .name = "notcurses", .module = notcurses_mod },
|
||||
.{ .name = "theme", .module = themes_dep.module("theme") },
|
||||
.{ .name = "cbor", .module = cbor_mod },
|
||||
.{ .name = "log", .module = log_mod },
|
||||
},
|
||||
});
|
||||
|
||||
const renderer_mod = notcurses_renderer_mod;
|
||||
|
||||
const Buffer_mod = b.createModule(.{
|
||||
.root_source_file = .{ .path = "src/buffer/Buffer.zig" },
|
||||
.imports = &.{
|
||||
.{ .name = "notcurses", .module = notcurses_mod },
|
||||
.{ .name = "renderer", .module = renderer_mod },
|
||||
.{ .name = "cbor", .module = cbor_mod },
|
||||
},
|
||||
});
|
||||
|
@ -176,7 +188,7 @@ pub fn build(b: *std.Build) void {
|
|||
const tui_mod = b.createModule(.{
|
||||
.root_source_file = .{ .path = "src/tui/tui.zig" },
|
||||
.imports = &.{
|
||||
.{ .name = "notcurses", .module = notcurses_mod },
|
||||
.{ .name = "renderer", .module = renderer_mod },
|
||||
.{ .name = "thespian", .module = thespian_mod },
|
||||
.{ .name = "cbor", .module = cbor_mod },
|
||||
.{ .name = "config", .module = config_mod },
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const nc = @import("notcurses");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
const cwd = std.fs.cwd;
|
||||
|
||||
const egc_length = @import("renderer").egc.length;
|
||||
const egc_chunk_width = @import("renderer").egc.chunk_width;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const default_leaf_capacity = 64;
|
||||
|
@ -156,7 +158,7 @@ pub const Leaf = struct {
|
|||
buf = buf[1..];
|
||||
pos.* -= 1;
|
||||
} else {
|
||||
const bytes = egc_len(buf, &cols, abs_col);
|
||||
const bytes = egc_length(buf, &cols, abs_col);
|
||||
buf = buf[bytes..];
|
||||
pos.* -= bytes;
|
||||
}
|
||||
|
@ -179,7 +181,7 @@ pub const Leaf = struct {
|
|||
return while (buf.len > 0) {
|
||||
if (col == 0)
|
||||
break @intFromPtr(buf.ptr) - @intFromPtr(self.buf.ptr);
|
||||
const bytes = egc_len(buf, &cols, abs_col);
|
||||
const bytes = egc_length(buf, &cols, abs_col);
|
||||
buf = buf[bytes..];
|
||||
if (col < cols)
|
||||
break @intFromPtr(buf.ptr) - @intFromPtr(self.buf.ptr);
|
||||
|
@ -212,7 +214,7 @@ pub const Leaf = struct {
|
|||
buf = buf[1..];
|
||||
},
|
||||
else => {
|
||||
const bytes = egc_len(buf, &cols, 0);
|
||||
const bytes = egc_length(buf, &cols, 0);
|
||||
var buf_: [4096]u8 = undefined;
|
||||
try l.appendSlice(try std.fmt.bufPrint(&buf_, "{s}", .{std.fmt.fmtSliceEscapeLower(buf[0..bytes])}));
|
||||
buf = buf[bytes..];
|
||||
|
@ -471,7 +473,7 @@ const Node = union(enum) {
|
|||
var buf: []const u8 = leaf.buf;
|
||||
while (buf.len > 0) {
|
||||
var cols: c_int = undefined;
|
||||
const bytes = egc_len(buf, &cols, ctx.abs_col);
|
||||
const bytes = egc_length(buf, &cols, ctx.abs_col);
|
||||
const ret = ctx.walker_f(ctx.walker_ctx, buf[0..bytes], @intCast(cols));
|
||||
if (ret.err) |e| return .{ .err = e };
|
||||
buf = buf[bytes..];
|
||||
|
@ -1142,29 +1144,3 @@ pub fn redo(self: *Self) error{Stop}![]const u8 {
|
|||
self.push_undo(u);
|
||||
return h.meta;
|
||||
}
|
||||
|
||||
fn egc_len(egcs: []const u8, colcount: *c_int, abs_col: usize) usize {
|
||||
if (egcs[0] == '\t') {
|
||||
colcount.* = @intCast(8 - abs_col % 8);
|
||||
return 1;
|
||||
}
|
||||
return nc.ncegc_len(egcs, colcount) catch ret: {
|
||||
colcount.* = 1;
|
||||
break :ret 1;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn egc_chunk_width(chunk_: []const u8, abs_col_: usize) usize {
|
||||
var abs_col = abs_col_;
|
||||
var chunk = chunk_;
|
||||
var colcount: usize = 0;
|
||||
var cols: c_int = 0;
|
||||
while (chunk.len > 0) {
|
||||
const bytes = egc_len(chunk, &cols, abs_col);
|
||||
colcount += @intCast(cols);
|
||||
abs_col += @intCast(cols);
|
||||
if (chunk.len < bytes) break;
|
||||
chunk = chunk[bytes..];
|
||||
}
|
||||
return colcount;
|
||||
}
|
||||
|
|
38
src/renderer/notcurses/Cell.zig
Normal file
38
src/renderer/notcurses/Cell.zig
Normal file
|
@ -0,0 +1,38 @@
|
|||
const nc = @import("notcurses");
|
||||
const Style = @import("theme").Style;
|
||||
const channels = @import("channels.zig");
|
||||
|
||||
pub const Cell = struct {
|
||||
cell: nc.Cell,
|
||||
|
||||
pub inline fn set_style(cell: *Cell, style_: Style) void {
|
||||
channels.from_style(&cell.cell.channels, style_);
|
||||
if (style_.fs) |fs| switch (fs) {
|
||||
.normal => nc.cell_set_styles(&cell.cell, nc.style.none),
|
||||
.bold => nc.cell_set_styles(&cell.cell, nc.style.bold),
|
||||
.italic => nc.cell_set_styles(&cell.cell, nc.style.italic),
|
||||
.underline => nc.cell_set_styles(&cell.cell, nc.style.underline),
|
||||
.undercurl => nc.cell_set_styles(&cell.cell, nc.style.undercurl),
|
||||
.strikethrough => nc.cell_set_styles(&cell.cell, nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn set_style_fg(cell: *Cell, style_: Style) void {
|
||||
channels.fg_from_style(&cell.cell.channels, style_);
|
||||
}
|
||||
|
||||
pub inline fn set_style_bg(cell: *Cell, style_: Style) void {
|
||||
channels.bg_from_style(&cell.cell.channels, style_);
|
||||
}
|
||||
|
||||
pub inline fn set_fg_rgb(cell: *Cell, arg_rgb: c_uint) !void {
|
||||
return channels.set_fg_rgb(&cell.cell.channels, arg_rgb);
|
||||
}
|
||||
pub inline fn set_bg_rgb(cell: *Cell, arg_rgb: c_uint) !void {
|
||||
return channels.set_bg_rgb(&cell.cell.channels, arg_rgb);
|
||||
}
|
||||
|
||||
pub fn columns(cell: *const Cell) usize {
|
||||
return nc.cell_cols(&cell.cell);
|
||||
}
|
||||
};
|
247
src/renderer/notcurses/Plane.zig
Normal file
247
src/renderer/notcurses/Plane.zig
Normal file
|
@ -0,0 +1,247 @@
|
|||
const nc = @import("notcurses");
|
||||
const Style = @import("theme").Style;
|
||||
const channels = @import("channels.zig");
|
||||
const StyleBits = @import("style.zig").StyleBits;
|
||||
const Cell = @import("Cell.zig").Cell;
|
||||
|
||||
pub const Plane = struct {
|
||||
plane: nc.Plane,
|
||||
|
||||
pub const Options = struct {
|
||||
y: usize = 0,
|
||||
x: usize = 0,
|
||||
rows: usize = 0,
|
||||
cols: usize = 0,
|
||||
name: [*:0]const u8,
|
||||
flags: option = .none,
|
||||
};
|
||||
|
||||
pub const option = enum {
|
||||
none,
|
||||
VSCROLL,
|
||||
};
|
||||
|
||||
pub fn init(nopts: *const Options, parent_: Plane) !Plane {
|
||||
var nopts_: nc.Plane.Options = .{
|
||||
.y = @intCast(nopts.y),
|
||||
.x = @intCast(nopts.x),
|
||||
.rows = @intCast(nopts.rows),
|
||||
.cols = @intCast(nopts.cols),
|
||||
.name = nopts.name,
|
||||
};
|
||||
switch (nopts.flags) {
|
||||
.none => {},
|
||||
.VSCROLL => nopts_.flags = nc.Plane.option.VSCROLL,
|
||||
}
|
||||
|
||||
return .{ .plane = nc.Plane.init(&nopts_, parent_.plane) catch |e| return e };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Plane) void {
|
||||
self.plane.deinit();
|
||||
}
|
||||
|
||||
pub fn name(self: Plane, buf: []u8) []u8 {
|
||||
return self.plane.name(buf);
|
||||
}
|
||||
|
||||
pub fn parent(self: Plane) Plane {
|
||||
return .{ .plane = self.plane.parent() };
|
||||
}
|
||||
|
||||
pub fn above(self: Plane) ?Plane {
|
||||
return .{ .plane = self.plane.above() orelse return null };
|
||||
}
|
||||
|
||||
pub fn below(self: Plane) ?Plane {
|
||||
return .{ .plane = self.plane.below() orelse return null };
|
||||
}
|
||||
|
||||
pub fn erase(self: Plane) void {
|
||||
return self.plane.erase();
|
||||
}
|
||||
|
||||
pub fn abs_y(self: Plane) c_int {
|
||||
return self.plane.abs_y();
|
||||
}
|
||||
|
||||
pub fn abs_x(self: Plane) c_int {
|
||||
return self.plane.abs_x();
|
||||
}
|
||||
|
||||
pub fn dim_y(self: Plane) c_uint {
|
||||
return self.plane.dim_y();
|
||||
}
|
||||
|
||||
pub fn dim_x(self: Plane) c_uint {
|
||||
return self.plane.dim_x();
|
||||
}
|
||||
|
||||
pub fn abs_yx_to_rel(self: Plane, y: ?*c_int, x: ?*c_int) void {
|
||||
self.plane.abs_yx_to_rel(y, x);
|
||||
}
|
||||
|
||||
pub fn rel_yx_to_abs(self: Plane, y: ?*c_int, x: ?*c_int) void {
|
||||
self.plane.rel_yx_to_abs(y, x);
|
||||
}
|
||||
|
||||
pub fn move_bottom(self: Plane) void {
|
||||
self.plane.move_bottom();
|
||||
}
|
||||
|
||||
pub fn move_yx(self: Plane, y: c_int, x: c_int) !void {
|
||||
return self.plane.move_yx(y, x);
|
||||
}
|
||||
|
||||
pub fn resize_simple(self: Plane, ylen: c_uint, xlen: c_uint) !void {
|
||||
return self.plane.resize_simple(ylen, xlen);
|
||||
}
|
||||
|
||||
pub fn home(self: Plane) void {
|
||||
return self.plane.home();
|
||||
}
|
||||
|
||||
pub fn print(self: Plane, comptime fmt: anytype, args: anytype) !usize {
|
||||
return self.plane.print(fmt, args);
|
||||
}
|
||||
|
||||
pub fn print_aligned_right(self: Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize {
|
||||
return self.plane.print_aligned(y, .right, fmt, args);
|
||||
}
|
||||
|
||||
pub fn print_aligned_center(self: Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize {
|
||||
return self.plane.print_aligned(y, .center, fmt, args);
|
||||
}
|
||||
|
||||
pub fn putstr(self: Plane, gclustarr: [*:0]const u8) !usize {
|
||||
return self.plane.putstr(gclustarr);
|
||||
}
|
||||
|
||||
pub fn putc(self: Plane, cell: *const Cell) !usize {
|
||||
return self.plane.putc(&cell.cell);
|
||||
}
|
||||
|
||||
pub fn putc_yx(self: Plane, y: c_int, x: c_int, cell: *const Cell) !usize {
|
||||
return self.plane.put_yx(y, x, &cell.cell);
|
||||
}
|
||||
|
||||
pub fn cursor_yx(self: Plane, y: *c_uint, x: *c_uint) void {
|
||||
self.plane.cursor_yx(y, x);
|
||||
}
|
||||
|
||||
pub fn cursor_y(self: Plane) c_uint {
|
||||
return self.plane.cursor_y();
|
||||
}
|
||||
|
||||
pub fn cursor_x(self: Plane) c_uint {
|
||||
return self.plane.cursor_x();
|
||||
}
|
||||
|
||||
pub fn cursor_move_yx(self: Plane, y: c_int, x: c_int) !void {
|
||||
return self.plane.cursor_move_yx(y, x);
|
||||
}
|
||||
|
||||
pub fn cursor_move_rel(self: Plane, y: c_int, x: c_int) !void {
|
||||
return self.plane.cursor_move_rel(y, x);
|
||||
}
|
||||
|
||||
pub fn cell_init(self: Plane) Cell {
|
||||
return .{ .cell = self.plane.cell_init() };
|
||||
}
|
||||
|
||||
pub fn cell_load(self: Plane, cell: *Cell, gcluster: [:0]const u8) !usize {
|
||||
return self.plane.cell_load(&cell.cell, gcluster);
|
||||
}
|
||||
|
||||
pub fn at_cursor_cell(self: Plane, cell: *Cell) !usize {
|
||||
return self.plane.at_cursor_cell(&cell.cell);
|
||||
}
|
||||
|
||||
pub fn set_styles(self: Plane, stylebits: StyleBits) void {
|
||||
return self.plane.set_styles(@intCast(@as(u5, @bitCast(stylebits))));
|
||||
}
|
||||
|
||||
pub fn on_styles(self: Plane, stylebits: StyleBits) void {
|
||||
return self.plane.on_styles(@intCast(@as(u5, @bitCast(stylebits))));
|
||||
}
|
||||
|
||||
pub fn off_styles(self: Plane, stylebits: StyleBits) void {
|
||||
return self.plane.off_styles(@intCast(@as(u5, @bitCast(stylebits))));
|
||||
}
|
||||
|
||||
pub fn set_fg_rgb(self: Plane, channel: u32) !void {
|
||||
return self.plane.set_fg_rgb(channel);
|
||||
}
|
||||
|
||||
pub fn set_bg_rgb(self: Plane, channel: u32) !void {
|
||||
return self.plane.set_bg_rgb(channel);
|
||||
}
|
||||
|
||||
pub fn set_fg_palindex(self: Plane, idx: c_uint) !void {
|
||||
return self.plane.set_fg_palindex(idx);
|
||||
}
|
||||
|
||||
pub fn set_bg_palindex(self: Plane, idx: c_uint) !void {
|
||||
return self.plane.set_bg_palindex(idx);
|
||||
}
|
||||
|
||||
pub fn set_channels(self: Plane, channels_: u64) void {
|
||||
return self.plane.set_channels(channels_);
|
||||
}
|
||||
|
||||
pub inline fn set_base_style(plane: *const Plane, egc_: [*c]const u8, style_: Style) void {
|
||||
var channels_: u64 = 0;
|
||||
channels.from_style(&channels_, style_);
|
||||
if (style_.fg) |fg| plane.plane.set_fg_rgb(fg) catch {};
|
||||
if (style_.bg) |bg| plane.plane.set_bg_rgb(bg) catch {};
|
||||
_ = plane.plane.set_base(egc_, 0, channels_) catch {};
|
||||
}
|
||||
|
||||
pub fn set_base_style_transparent(plane: Plane, egc_: [*:0]const u8, style_: Style) void {
|
||||
var channels_: u64 = 0;
|
||||
channels.from_style(&channels_, style_);
|
||||
if (style_.fg) |fg| plane.plane.set_fg_rgb(fg) catch {};
|
||||
if (style_.bg) |bg| plane.plane.set_bg_rgb(bg) catch {};
|
||||
channels.set_fg_transparent(&channels_);
|
||||
channels.set_bg_transparent(&channels_);
|
||||
_ = plane.plane.set_base(egc_, 0, channels_) catch {};
|
||||
}
|
||||
|
||||
pub fn set_base_style_bg_transparent(plane: Plane, egc_: [*:0]const u8, style_: Style) void {
|
||||
var channels_: u64 = 0;
|
||||
channels.from_style(&channels_, style_);
|
||||
if (style_.fg) |fg| plane.plane.set_fg_rgb(fg) catch {};
|
||||
if (style_.bg) |bg| plane.plane.set_bg_rgb(bg) catch {};
|
||||
channels.set_bg_transparent(&channels_);
|
||||
_ = plane.plane.set_base(egc_, 0, channels_) catch {};
|
||||
}
|
||||
|
||||
pub inline fn set_style(plane: *const Plane, style_: Style) void {
|
||||
var channels_: u64 = 0;
|
||||
channels.from_style(&channels_, style_);
|
||||
plane.plane.set_channels(channels_);
|
||||
if (style_.fs) |fs| switch (fs) {
|
||||
.normal => plane.plane.set_styles(nc.style.none),
|
||||
.bold => plane.plane.set_styles(nc.style.bold),
|
||||
.italic => plane.plane.set_styles(nc.style.italic),
|
||||
.underline => plane.plane.set_styles(nc.style.underline),
|
||||
.undercurl => plane.plane.set_styles(nc.style.undercurl),
|
||||
.strikethrough => plane.plane.set_styles(nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn set_style_bg_transparent(plane: *const Plane, style_: Style) void {
|
||||
var channels_: u64 = 0;
|
||||
channels.from_style(&channels_, style_);
|
||||
channels.set_bg_transparent(&channels_);
|
||||
plane.plane.set_channels(channels_);
|
||||
if (style_.fs) |fs| switch (fs) {
|
||||
.normal => plane.plane.set_styles(nc.style.none),
|
||||
.bold => plane.plane.set_styles(nc.style.bold),
|
||||
.italic => plane.plane.set_styles(nc.style.italic),
|
||||
.underline => plane.plane.set_styles(nc.style.underline),
|
||||
.undercurl => plane.plane.set_styles(nc.style.undercurl),
|
||||
.strikethrough => plane.plane.set_styles(nc.style.struck),
|
||||
};
|
||||
}
|
||||
};
|
40
src/renderer/notcurses/channels.zig
Normal file
40
src/renderer/notcurses/channels.zig
Normal file
|
@ -0,0 +1,40 @@
|
|||
const Style = @import("theme").Style;
|
||||
const nc = @import("notcurses");
|
||||
|
||||
pub const set_fg_rgb = nc.channels_set_fg_rgb;
|
||||
pub const set_bg_rgb = nc.channels_set_bg_rgb;
|
||||
|
||||
pub fn set_fg_opaque(channels_: *u64) void {
|
||||
nc.channels_set_fg_alpha(channels_, nc.ALPHA_OPAQUE) catch {};
|
||||
}
|
||||
|
||||
pub fn set_bg_opaque(channels_: *u64) void {
|
||||
nc.channels_set_bg_alpha(channels_, nc.ALPHA_OPAQUE) catch {};
|
||||
}
|
||||
|
||||
pub fn set_fg_transparent(channels_: *u64) void {
|
||||
nc.channels_set_fg_alpha(channels_, nc.ALPHA_TRANSPARENT) catch {};
|
||||
}
|
||||
|
||||
pub fn set_bg_transparent(channels_: *u64) void {
|
||||
nc.channels_set_bg_alpha(channels_, nc.ALPHA_TRANSPARENT) catch {};
|
||||
}
|
||||
|
||||
pub inline fn fg_from_style(channels_: *u64, style_: Style) void {
|
||||
if (style_.fg) |fg| {
|
||||
set_fg_rgb(channels_, fg) catch {};
|
||||
set_fg_opaque(channels_);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn bg_from_style(channels_: *u64, style_: Style) void {
|
||||
if (style_.bg) |bg| {
|
||||
set_bg_rgb(channels_, bg) catch {};
|
||||
set_bg_opaque(channels_);
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn from_style(channels_: *u64, style_: Style) void {
|
||||
fg_from_style(channels_, style_);
|
||||
bg_from_style(channels_, style_);
|
||||
}
|
29
src/renderer/notcurses/egc.zig
Normal file
29
src/renderer/notcurses/egc.zig
Normal file
|
@ -0,0 +1,29 @@
|
|||
const nc = @import("notcurses");
|
||||
|
||||
pub fn length(egcs: []const u8, colcount: *c_int, abs_col: usize) usize {
|
||||
if (egcs[0] == '\t') {
|
||||
colcount.* = @intCast(8 - abs_col % 8);
|
||||
return 1;
|
||||
}
|
||||
return nc.ncegc_len(egcs, colcount) catch ret: {
|
||||
colcount.* = 1;
|
||||
break :ret 1;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn chunk_width(chunk_: []const u8, abs_col_: usize) usize {
|
||||
var abs_col = abs_col_;
|
||||
var chunk = chunk_;
|
||||
var colcount: usize = 0;
|
||||
var cols: c_int = 0;
|
||||
while (chunk.len > 0) {
|
||||
const bytes = length(chunk, &cols, abs_col);
|
||||
colcount += @intCast(cols);
|
||||
abs_col += @intCast(cols);
|
||||
if (chunk.len < bytes) break;
|
||||
chunk = chunk[bytes..];
|
||||
}
|
||||
return colcount;
|
||||
}
|
||||
|
||||
pub const ucs32_to_utf8 = nc.ucs32_to_utf8;
|
14
src/renderer/notcurses/input.zig
Normal file
14
src/renderer/notcurses/input.zig
Normal file
|
@ -0,0 +1,14 @@
|
|||
const nc = @import("notcurses");
|
||||
|
||||
pub const key = nc.key;
|
||||
pub const modifier = nc.mod;
|
||||
pub const event_type = nc.event_type;
|
||||
|
||||
pub const utils = struct {
|
||||
pub const isSuper = nc.isSuper;
|
||||
pub const isCtrl = nc.isCtrl;
|
||||
pub const isShift = nc.isShift;
|
||||
pub const isAlt = nc.isAlt;
|
||||
pub const key_id_string = nc.key_id_string;
|
||||
pub const key_string = nc.key_string;
|
||||
};
|
508
src/renderer/notcurses/renderer.zig
Normal file
508
src/renderer/notcurses/renderer.zig
Normal file
|
@ -0,0 +1,508 @@
|
|||
const std = @import("std");
|
||||
const cbor = @import("cbor");
|
||||
const log = @import("log");
|
||||
const nc = @import("notcurses");
|
||||
const Style = @import("theme").Style;
|
||||
|
||||
pub const egc = @import("egc.zig");
|
||||
pub const input = @import("input.zig");
|
||||
|
||||
pub const Plane = @import("Plane.zig").Plane;
|
||||
pub const Cell = @import("Cell.zig").Cell;
|
||||
pub const channels = @import("channels.zig");
|
||||
|
||||
pub const style = @import("style.zig").StyleBits;
|
||||
|
||||
const mod = input.modifier;
|
||||
const key = input.key;
|
||||
const event_type = input.event_type;
|
||||
|
||||
const Self = @This();
|
||||
const log_name = "ncrender";
|
||||
|
||||
a: std.mem.Allocator,
|
||||
ctx: nc.Context,
|
||||
|
||||
escape_state: EscapeState = .none,
|
||||
escape_initial: ?nc.Input = null,
|
||||
escape_code: std.ArrayList(u8),
|
||||
|
||||
event_buffer: std.ArrayList(u8),
|
||||
mods: ModState = .{},
|
||||
drag: bool = false,
|
||||
drag_event: nc.Input = nc.input(),
|
||||
bracketed_paste: bool = false,
|
||||
bracketed_paste_buffer: std.ArrayList(u8),
|
||||
|
||||
handler_ctx: *anyopaque,
|
||||
dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||
dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null,
|
||||
dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, dragging: bool, cbor_msg: []const u8) void = null,
|
||||
dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||
|
||||
logger: log.Logger,
|
||||
|
||||
const EscapeState = enum { none, init, OSC, st, CSI };
|
||||
const ModState = struct { ctrl: bool = false, shift: bool = false, alt: bool = false };
|
||||
|
||||
pub fn init(a: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool) !Self {
|
||||
var opts = nc.Context.Options{
|
||||
.termtype = null,
|
||||
.loglevel = @intFromEnum(nc.LogLevel.silent),
|
||||
.margin_t = 0,
|
||||
.margin_r = 0,
|
||||
.margin_b = 0,
|
||||
.margin_l = 0,
|
||||
.flags = nc.Context.option.SUPPRESS_BANNERS | nc.Context.option.INHIBIT_SETLOCALE | nc.Context.option.NO_WINCH_SIGHANDLER,
|
||||
};
|
||||
if (no_alternate)
|
||||
opts.flags |= nc.Context.option.NO_ALTERNATE_SCREEN;
|
||||
const nc_ = try nc.Context.core_init(&opts, null);
|
||||
nc_.mice_enable(nc.mice.ALL_EVENTS) catch {};
|
||||
try nc_.linesigs_disable();
|
||||
bracketed_paste_enable();
|
||||
|
||||
return .{
|
||||
.a = a,
|
||||
.ctx = nc_,
|
||||
.escape_code = std.ArrayList(u8).init(a),
|
||||
.event_buffer = std.ArrayList(u8).init(a),
|
||||
.bracketed_paste_buffer = std.ArrayList(u8).init(a),
|
||||
.handler_ctx = handler_ctx,
|
||||
.logger = log.logger(log_name),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.escape_code.deinit();
|
||||
self.event_buffer.deinit();
|
||||
self.bracketed_paste_buffer.deinit();
|
||||
}
|
||||
|
||||
pub fn render(self: Self) !void {
|
||||
return self.ctx.render();
|
||||
}
|
||||
|
||||
pub fn refresh(self: Self) !void {
|
||||
return self.ctx.refresh();
|
||||
}
|
||||
|
||||
pub fn stop(self: Self) void {
|
||||
return self.ctx.stop();
|
||||
}
|
||||
|
||||
pub fn stdplane(self: Self) Plane {
|
||||
return .{ .plane = self.ctx.stdplane() };
|
||||
}
|
||||
|
||||
pub fn input_fd(self: Self) i32 {
|
||||
return self.ctx.inputready_fd();
|
||||
}
|
||||
|
||||
pub fn leave_alternate_screen(self: Self) void {
|
||||
return self.ctx.leave_alternate_screen();
|
||||
}
|
||||
|
||||
pub fn process_input(self: *Self) !void {
|
||||
var input_buffer: [256]nc.Input = undefined;
|
||||
|
||||
while (true) {
|
||||
const nivec = try self.ctx.getvec_nblock(&input_buffer);
|
||||
if (nivec.len == 0)
|
||||
break;
|
||||
for (nivec) |*ni| {
|
||||
if (ni.id == 27 or self.escape_state != .none) {
|
||||
try self.handle_escape(ni);
|
||||
continue;
|
||||
}
|
||||
self.dispatch_input_event(ni) catch |e|
|
||||
self.logger.err("input dispatch", e);
|
||||
}
|
||||
}
|
||||
if (self.escape_state == .init)
|
||||
try self.handle_escape_short();
|
||||
}
|
||||
|
||||
fn fmtmsg(self: *Self, value: anytype) ![]const u8 {
|
||||
self.event_buffer.clearRetainingCapacity();
|
||||
try cbor.writeValue(self.event_buffer.writer(), value);
|
||||
return self.event_buffer.items;
|
||||
}
|
||||
|
||||
fn dispatch_input_event(self: *Self, ni: *nc.Input) !void {
|
||||
const keypress: u32 = ni.id;
|
||||
ni.modifiers &= mod.CTRL | mod.SHIFT | mod.ALT | mod.SUPER | mod.META | mod.HYPER;
|
||||
if (keypress == key.RESIZE) return;
|
||||
try self.sync_mod_state(keypress, ni.modifiers);
|
||||
if (keypress == key.MOTION) {
|
||||
if (ni.y == 0 and ni.x == 0 and ni.ypx == -1 and ni.xpx == -1) return;
|
||||
if (self.dispatch_mouse) |f| f(
|
||||
self.handler_ctx,
|
||||
ni.y,
|
||||
ni.x,
|
||||
try self.fmtmsg(.{
|
||||
"M",
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}),
|
||||
);
|
||||
} else if (keypress > key.MOTION and keypress <= key.BUTTON11) {
|
||||
if (ni.y == 0 and ni.x == 0 and ni.ypx == -1 and ni.xpx == -1) return;
|
||||
if (try self.detect_drag(ni)) return;
|
||||
if (self.dispatch_mouse) |f| f(
|
||||
self.handler_ctx,
|
||||
ni.y,
|
||||
ni.x,
|
||||
try self.fmtmsg(.{
|
||||
"B",
|
||||
ni.evtype,
|
||||
keypress,
|
||||
input.utils.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
const cbor_msg = try self.fmtmsg(.{
|
||||
"I",
|
||||
normalized_evtype(ni.evtype),
|
||||
keypress,
|
||||
if (@hasField(nc.Input, "eff_text")) ni.eff_text[0] else keypress,
|
||||
input.utils.key_string(ni),
|
||||
ni.modifiers,
|
||||
});
|
||||
if (self.bracketed_paste and self.handle_bracketed_paste_input(cbor_msg) catch |e| {
|
||||
self.bracketed_paste_buffer.clearAndFree();
|
||||
self.bracketed_paste = false;
|
||||
return e;
|
||||
}) {} else if (self.dispatch_input) |f| f(self.handler_ctx, cbor_msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_bracketed_paste_input(self: *Self, cbor_msg: []const u8) !bool {
|
||||
var keypress: u32 = undefined;
|
||||
var egc_: u32 = undefined;
|
||||
if (try cbor.match(cbor_msg, .{ "I", cbor.number, cbor.extract(&keypress), cbor.extract(&egc_), cbor.string, 0 })) {
|
||||
switch (keypress) {
|
||||
key.ENTER => try self.bracketed_paste_buffer.appendSlice("\n"),
|
||||
else => if (!key.synthesized_p(keypress)) {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = try egc.ucs32_to_utf8(&[_]u32{egc_}, &buf);
|
||||
try self.bracketed_paste_buffer.appendSlice(buf[0..bytes]);
|
||||
} else {
|
||||
try self.handle_bracketed_paste_end();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn normalized_evtype(evtype: c_uint) c_uint {
|
||||
return if (evtype == event_type.UNKNOWN) @as(c_uint, @intCast(event_type.PRESS)) else evtype;
|
||||
}
|
||||
|
||||
fn sync_mod_state(self: *Self, keypress: u32, modifiers: u32) !void {
|
||||
if (input.utils.isCtrl(modifiers) and !self.mods.ctrl and !(keypress == key.LCTRL or keypress == key.RCTRL))
|
||||
try self.send_sync_key(event_type.PRESS, key.LCTRL, "lctrl", modifiers);
|
||||
if (!input.utils.isCtrl(modifiers) and self.mods.ctrl and !(keypress == key.LCTRL or keypress == key.RCTRL))
|
||||
try self.send_sync_key(event_type.RELEASE, key.LCTRL, "lctrl", modifiers);
|
||||
if (input.utils.isAlt(modifiers) and !self.mods.alt and !(keypress == key.LALT or keypress == key.RALT))
|
||||
try self.send_sync_key(event_type.PRESS, key.LALT, "lalt", modifiers);
|
||||
if (!input.utils.isAlt(modifiers) and self.mods.alt and !(keypress == key.LALT or keypress == key.RALT))
|
||||
try self.send_sync_key(event_type.RELEASE, key.LALT, "lalt", modifiers);
|
||||
if (input.utils.isShift(modifiers) and !self.mods.shift and !(keypress == key.LSHIFT or keypress == key.RSHIFT))
|
||||
try self.send_sync_key(event_type.PRESS, key.LSHIFT, "lshift", modifiers);
|
||||
if (!input.utils.isShift(modifiers) and self.mods.shift and !(keypress == key.LSHIFT or keypress == key.RSHIFT))
|
||||
try self.send_sync_key(event_type.RELEASE, key.LSHIFT, "lshift", modifiers);
|
||||
self.mods = .{
|
||||
.ctrl = input.utils.isCtrl(modifiers),
|
||||
.alt = input.utils.isAlt(modifiers),
|
||||
.shift = input.utils.isShift(modifiers),
|
||||
};
|
||||
}
|
||||
|
||||
fn send_sync_key(self: *Self, event_type_: c_int, keypress: u32, key_string: []const u8, modifiers: u32) !void {
|
||||
if (self.dispatch_input) |f| f(
|
||||
self.handler_ctx,
|
||||
try self.fmtmsg(.{
|
||||
"I",
|
||||
event_type_,
|
||||
keypress,
|
||||
keypress,
|
||||
key_string,
|
||||
modifiers,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn detect_drag(self: *Self, ni: *nc.Input) !bool {
|
||||
return switch (ni.id) {
|
||||
key.BUTTON1...key.BUTTON3, key.BUTTON6...key.BUTTON9 => if (self.drag) self.detect_drag_end(ni) else self.detect_drag_begin(ni),
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn detect_drag_begin(self: *Self, ni: *nc.Input) !bool {
|
||||
if (ni.evtype == event_type.PRESS and self.drag_event.id == ni.id) {
|
||||
self.drag_event = ni.*;
|
||||
self.drag = true;
|
||||
if (self.dispatch_mouse_drag) |f| f(
|
||||
self.handler_ctx,
|
||||
ni.y,
|
||||
ni.x,
|
||||
true,
|
||||
try self.fmtmsg(.{
|
||||
"D",
|
||||
event_type.PRESS,
|
||||
ni.id,
|
||||
input.utils.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (ni.evtype == event_type.PRESS)
|
||||
self.drag_event = ni.*
|
||||
else
|
||||
self.drag_event = nc.input();
|
||||
return false;
|
||||
}
|
||||
|
||||
fn detect_drag_end(self: *Self, ni: *nc.Input) !bool {
|
||||
if (ni.id == self.drag_event.id and ni.evtype != event_type.PRESS) {
|
||||
if (self.dispatch_mouse_drag) |f| f(
|
||||
self.handler_ctx,
|
||||
ni.y,
|
||||
ni.x,
|
||||
false,
|
||||
try self.fmtmsg(.{
|
||||
"D",
|
||||
event_type.RELEASE,
|
||||
ni.id,
|
||||
input.utils.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}),
|
||||
);
|
||||
self.drag = false;
|
||||
self.drag_event = nc.input();
|
||||
} else if (self.dispatch_mouse_drag) |f| f(
|
||||
self.handler_ctx,
|
||||
ni.y,
|
||||
ni.x,
|
||||
true,
|
||||
try self.fmtmsg(.{
|
||||
"D",
|
||||
ni.evtype,
|
||||
ni.id,
|
||||
input.utils.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn handle_escape(self: *Self, ni: *nc.Input) !void {
|
||||
switch (self.escape_state) {
|
||||
.none => switch (ni.id) {
|
||||
'\x1B' => {
|
||||
self.escape_state = .init;
|
||||
self.escape_initial = ni.*;
|
||||
},
|
||||
else => unreachable,
|
||||
},
|
||||
.init => switch (ni.id) {
|
||||
']' => self.escape_state = .OSC,
|
||||
'[' => self.escape_state = .CSI,
|
||||
else => {
|
||||
try self.handle_escape_short();
|
||||
_ = try self.dispatch_input_event(ni);
|
||||
},
|
||||
},
|
||||
.OSC => switch (ni.id) {
|
||||
'\x1B' => self.escape_state = .st,
|
||||
'\\' => try self.handle_OSC_escape_code(),
|
||||
' '...'\\' - 1, '\\' + 1...127 => {
|
||||
const p = try self.escape_code.addOne();
|
||||
p.* = @intCast(ni.id);
|
||||
},
|
||||
else => try self.handle_OSC_escape_code(),
|
||||
},
|
||||
.st => switch (ni.id) {
|
||||
'\\' => try self.handle_OSC_escape_code(),
|
||||
else => try self.handle_OSC_escape_code(),
|
||||
},
|
||||
.CSI => switch (ni.id) {
|
||||
'0'...'9', ';', ' ', '-', '?' => {
|
||||
const p = try self.escape_code.addOne();
|
||||
p.* = @intCast(ni.id);
|
||||
},
|
||||
else => {
|
||||
const p = try self.escape_code.addOne();
|
||||
p.* = @intCast(ni.id);
|
||||
try self.handle_CSI_escape_code();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_escape_short(self: *Self) !void {
|
||||
self.escape_code.clearAndFree();
|
||||
self.escape_state = .none;
|
||||
defer self.escape_initial = null;
|
||||
if (self.escape_initial) |*ni|
|
||||
_ = try self.dispatch_input_event(ni);
|
||||
}
|
||||
|
||||
fn match_code(self: Self, match: []const u8, skip: usize) bool {
|
||||
const code = self.escape_code.items;
|
||||
if (!(code.len >= match.len - skip)) return false;
|
||||
const code_prefix = code[0 .. match.len - skip];
|
||||
return std.mem.eql(u8, match[skip..], code_prefix);
|
||||
}
|
||||
|
||||
const OSC = "\x1B]"; // Operating System Command
|
||||
const ST = "\x1B\\"; // String Terminator
|
||||
const BEL = "\x07";
|
||||
const OSC0_title = OSC ++ "0;";
|
||||
const OSC52_clipboard = OSC ++ "52;c;";
|
||||
const OSC52_clipboard_paste = OSC ++ "52;p;";
|
||||
const OSC22_cursor = OSC ++ "22;";
|
||||
const OSC22_cursor_reply = OSC ++ "22:";
|
||||
|
||||
fn handle_OSC_escape_code(self: *Self) !void {
|
||||
self.escape_state = .none;
|
||||
self.escape_initial = null;
|
||||
defer self.escape_code.clearAndFree();
|
||||
const code = self.escape_code.items;
|
||||
if (self.match_code(OSC52_clipboard, OSC.len))
|
||||
return self.handle_system_clipboard(code[OSC52_clipboard.len - OSC.len ..]);
|
||||
if (self.match_code(OSC52_clipboard_paste, OSC.len))
|
||||
return self.handle_system_clipboard(code[OSC52_clipboard_paste.len - OSC.len ..]);
|
||||
if (self.match_code(OSC22_cursor_reply, OSC.len))
|
||||
return self.handle_mouse_cursor(code[OSC22_cursor_reply.len - OSC.len ..]);
|
||||
self.logger.print("ignored escape code: OSC {s}", .{std.fmt.fmtSliceEscapeLower(code)});
|
||||
}
|
||||
|
||||
fn handle_system_clipboard(self: *Self, base64: []const u8) !void {
|
||||
const decoder = std.base64.standard.Decoder;
|
||||
const text = try self.a.alloc(u8, try decoder.calcSizeForSlice(base64));
|
||||
defer self.a.free(text);
|
||||
try decoder.decode(text, base64);
|
||||
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", text }));
|
||||
}
|
||||
|
||||
fn handle_mouse_cursor(self: Self, text: []const u8) !void {
|
||||
self.logger.print("mouse cursor report: {s}", .{text});
|
||||
}
|
||||
|
||||
const CSI = "\x1B["; // Control Sequence Introducer
|
||||
const CSI_bracketed_paste_enable = CSI ++ "?2004h";
|
||||
const CSI_bracketed_paste_disable = CSI ++ "?2004h";
|
||||
const CIS_bracketed_paste_begin = CSI ++ "200~";
|
||||
const CIS_bracketed_paste_end = CSI ++ "201~";
|
||||
|
||||
fn bracketed_paste_enable() void {
|
||||
write_stdout(CSI_bracketed_paste_enable);
|
||||
}
|
||||
|
||||
fn bracketed_paste_disable() void {
|
||||
write_stdout(CSI_bracketed_paste_disable);
|
||||
}
|
||||
|
||||
fn handle_CSI_escape_code(self: *Self) !void {
|
||||
self.escape_state = .none;
|
||||
self.escape_initial = null;
|
||||
defer self.escape_code.clearAndFree();
|
||||
const code = self.escape_code.items;
|
||||
if (self.match_code(CIS_bracketed_paste_begin, CSI.len))
|
||||
return self.handle_bracketed_paste_begin();
|
||||
if (self.match_code(CIS_bracketed_paste_end, CSI.len))
|
||||
return self.handle_bracketed_paste_end();
|
||||
self.logger.print("ignored escape code: CSI {s}", .{std.fmt.fmtSliceEscapeLower(code)});
|
||||
}
|
||||
|
||||
fn handle_bracketed_paste_begin(self: *Self) !void {
|
||||
self.bracketed_paste_buffer.clearAndFree();
|
||||
self.bracketed_paste = true;
|
||||
}
|
||||
|
||||
fn handle_bracketed_paste_end(self: *Self) !void {
|
||||
defer self.bracketed_paste_buffer.clearAndFree();
|
||||
if (!self.bracketed_paste) return;
|
||||
self.bracketed_paste = false;
|
||||
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items }));
|
||||
}
|
||||
|
||||
pub fn set_terminal_title(text: []const u8) void {
|
||||
var writer = std.io.getStdOut().writer();
|
||||
var buf: [std.posix.PATH_MAX]u8 = undefined;
|
||||
const term_cmd = std.fmt.bufPrint(&buf, OSC0_title ++ "{s}" ++ BEL, .{text}) catch return;
|
||||
_ = writer.write(term_cmd) catch return;
|
||||
}
|
||||
|
||||
pub fn copy_to_system_clipboard(tmp_a: std.mem.Allocator, text: []const u8) void {
|
||||
copy_to_system_clipboard_with_errors(tmp_a, text) catch |e| log.logger(log_name).err("copy_to_system_clipboard", e);
|
||||
}
|
||||
|
||||
fn copy_to_system_clipboard_with_errors(tmp_a: std.mem.Allocator, text: []const u8) !void {
|
||||
var writer = std.io.getStdOut().writer();
|
||||
const encoder = std.base64.standard.Encoder;
|
||||
const size = OSC52_clipboard.len + encoder.calcSize(text.len) + ST.len;
|
||||
const buf = try tmp_a.alloc(u8, size);
|
||||
defer tmp_a.free(buf);
|
||||
@memcpy(buf[0..OSC52_clipboard.len], OSC52_clipboard);
|
||||
const b64 = encoder.encode(buf[OSC52_clipboard.len..], text);
|
||||
@memcpy(buf[OSC52_clipboard.len + b64.len ..], ST);
|
||||
_ = try writer.write(buf);
|
||||
}
|
||||
|
||||
pub fn request_system_clipboard() void {
|
||||
write_stdout(OSC52_clipboard ++ "?" ++ ST);
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_text(push_or_pop: bool) void {
|
||||
if (push_or_pop) mouse_cursor_push("text") else mouse_cursor_pop();
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_pointer(push_or_pop: bool) void {
|
||||
if (push_or_pop) mouse_cursor_push("pointer") else mouse_cursor_pop();
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_default(push_or_pop: bool) void {
|
||||
if (push_or_pop) mouse_cursor_push("default") else mouse_cursor_pop();
|
||||
}
|
||||
|
||||
fn mouse_cursor_push(comptime name: []const u8) void {
|
||||
write_stdout(OSC22_cursor ++ name ++ ST);
|
||||
}
|
||||
|
||||
fn mouse_cursor_pop() void {
|
||||
write_stdout(OSC22_cursor ++ "default" ++ ST);
|
||||
}
|
||||
|
||||
fn write_stdout(bytes: []const u8) void {
|
||||
_ = std.io.getStdOut().writer().write(bytes) catch |e| log.logger(log_name).err("stdout", e);
|
||||
}
|
||||
|
||||
pub fn cursor_enable(self: Self, y: c_int, x: c_int) !void {
|
||||
return self.ctx.cursor_enable(y, x);
|
||||
}
|
||||
|
||||
pub fn cursor_disable(self: Self) void {
|
||||
self.ctx.cursor_disable() catch {};
|
||||
}
|
14
src/renderer/notcurses/style.zig
Normal file
14
src/renderer/notcurses/style.zig
Normal file
|
@ -0,0 +1,14 @@
|
|||
pub const StyleBits = packed struct(u5) {
|
||||
struck: bool = false,
|
||||
bold: bool = false,
|
||||
undercurl: bool = false,
|
||||
underline: bool = false,
|
||||
italic: bool = false,
|
||||
|
||||
pub const struck: StyleBits = .{ .struck = true };
|
||||
pub const bold: StyleBits = .{ .bold = true };
|
||||
pub const undercurl: StyleBits = .{ .undercurl = true };
|
||||
pub const underline: StyleBits = .{ .underline = true };
|
||||
pub const italic: StyleBits = .{ .italic = true };
|
||||
pub const normal: StyleBits = .{};
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
const Plane = @import("notcurses").Plane;
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
@ -8,14 +8,14 @@ h: usize = 1,
|
|||
w: usize = 1,
|
||||
|
||||
pub fn opts(self: Self, name_: [:0]const u8) Plane.Options {
|
||||
return self.opts_flags(name_, 0);
|
||||
return self.opts_flags(name_, Plane.option.none);
|
||||
}
|
||||
|
||||
pub fn opts_vscroll(self: Self, name_: [:0]const u8) Plane.Options {
|
||||
return self.opts_flags(name_, Plane.option.VSCROLL);
|
||||
}
|
||||
|
||||
fn opts_flags(self: Self, name_: [:0]const u8, flags: u64) Plane.Options {
|
||||
fn opts_flags(self: Self, name_: [:0]const u8, flags: Plane.option) Plane.Options {
|
||||
return Plane.Options{
|
||||
.y = @intCast(self.y),
|
||||
.x = @intCast(self.x),
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
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");
|
||||
|
@ -23,7 +26,7 @@ pub fn Options(context: type) type {
|
|||
pub fn do_nothing(_: *context, _: *State(Context)) void {}
|
||||
|
||||
pub fn on_render_default(_: *context, self: *State(Context), theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar);
|
||||
self.plane.set_base_style(" ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar);
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
_ = self.plane.print(" {s} ", .{self.opts.label}) catch {};
|
||||
|
@ -40,10 +43,10 @@ pub fn Options(context: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !*State(ctx_type) {
|
||||
pub fn create(ctx_type: type, a: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) !*State(ctx_type) {
|
||||
const Self = State(ctx_type);
|
||||
const self = try a.create(Self);
|
||||
var n = try nc.Plane.init(&opts.pos.opts(@typeName(Self)), parent);
|
||||
var n = try Plane.init(&opts.pos.opts(@typeName(Self)), parent);
|
||||
errdefer n.deinit();
|
||||
self.* = .{
|
||||
.a = a,
|
||||
|
@ -55,15 +58,15 @@ pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Opti
|
|||
return self;
|
||||
}
|
||||
|
||||
pub fn create_widget(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !Widget {
|
||||
pub fn create_widget(ctx_type: type, a: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) !Widget {
|
||||
return Widget.to(try create(ctx_type, a, parent, opts));
|
||||
}
|
||||
|
||||
pub fn State(ctx_type: type) type {
|
||||
return struct {
|
||||
a: std.mem.Allocator,
|
||||
parent: nc.Plane,
|
||||
plane: nc.Plane,
|
||||
parent: Plane,
|
||||
plane: Plane,
|
||||
active: bool = false,
|
||||
hover: bool = false,
|
||||
opts: Options(ctx_type),
|
||||
|
@ -87,22 +90,22 @@ pub fn State(ctx_type: type) type {
|
|||
|
||||
pub fn receive(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
var btn: u32 = 0;
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
if (try m.match(.{ "B", event_type.PRESS, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.active = true;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "B", nc.event_type.RELEASE, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
} else if (try m.match(.{ "B", event_type.RELEASE, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.call_click_handler(btn);
|
||||
self.active = false;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "D", nc.event_type.RELEASE, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
} else if (try m.match(.{ "D", event_type.RELEASE, tp.extract(&btn), tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.call_click_handler(btn);
|
||||
self.active = false;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else 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);
|
||||
tui.need_render();
|
||||
return true;
|
||||
}
|
||||
|
@ -112,9 +115,9 @@ pub fn State(ctx_type: type) type {
|
|||
fn call_click_handler(self: *Self, btn: u32) void {
|
||||
if (!self.hover) return;
|
||||
switch (btn) {
|
||||
nc.key.BUTTON1 => self.opts.on_click(&self.opts.ctx, self),
|
||||
nc.key.BUTTON2 => self.opts.on_click2(&self.opts.ctx, self),
|
||||
nc.key.BUTTON3 => self.opts.on_click3(&self.opts.ctx, self),
|
||||
key.BUTTON1 => self.opts.on_click(&self.opts.ctx, self),
|
||||
key.BUTTON2 => self.opts.on_click2(&self.opts.ctx, self),
|
||||
key.BUTTON3 => self.opts.on_click3(&self.opts.ctx, self),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
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");
|
||||
|
@ -20,7 +23,7 @@ pub fn Options(context: type) type {
|
|||
pub fn do_nothing(_: context, _: *State(Context)) void {}
|
||||
|
||||
pub fn on_render_default(_: context, self: *State(Context), theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", if (self.text.items.len > 0) theme.input else theme.input_placeholder);
|
||||
self.plane.set_base_style(" ", if (self.text.items.len > 0) theme.input else theme.input_placeholder);
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
if (self.text.items.len > 0) {
|
||||
|
@ -33,7 +36,7 @@ pub fn Options(context: type) type {
|
|||
self.plane.cursor_move_yx(0, pos + 1) catch return false;
|
||||
var cell = self.plane.cell_init();
|
||||
_ = self.plane.at_cursor_cell(&cell) catch return false;
|
||||
tui.set_cell_style(&cell, theme.editor_cursor);
|
||||
cell.set_style(theme.editor_cursor);
|
||||
_ = self.plane.putc(&cell) catch {};
|
||||
}
|
||||
return false;
|
||||
|
@ -45,10 +48,10 @@ pub fn Options(context: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !Widget {
|
||||
pub fn create(ctx_type: type, a: std.mem.Allocator, parent: Plane, opts: Options(ctx_type)) !Widget {
|
||||
const Self = State(ctx_type);
|
||||
const self = try a.create(Self);
|
||||
var n = try nc.Plane.init(&opts.pos.opts(@typeName(Self)), parent);
|
||||
var n = try Plane.init(&opts.pos.opts(@typeName(Self)), parent);
|
||||
errdefer n.deinit();
|
||||
self.* = .{
|
||||
.parent = parent,
|
||||
|
@ -64,8 +67,8 @@ pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Opti
|
|||
|
||||
pub fn State(ctx_type: type) type {
|
||||
return struct {
|
||||
parent: nc.Plane,
|
||||
plane: nc.Plane,
|
||||
parent: Plane,
|
||||
plane: Plane,
|
||||
active: bool = false,
|
||||
hover: bool = false,
|
||||
label: std.ArrayList(u8),
|
||||
|
@ -92,22 +95,22 @@ pub fn State(ctx_type: type) type {
|
|||
}
|
||||
|
||||
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 })) {
|
||||
self.active = true;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "B", nc.event_type.RELEASE, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
} else if (try m.match(.{ "B", event_type.RELEASE, key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.opts.on_click(self.opts.ctx, self);
|
||||
self.active = false;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "D", nc.event_type.RELEASE, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
} else if (try m.match(.{ "D", event_type.RELEASE, key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.opts.on_click(self.opts.ctx, self);
|
||||
self.active = false;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else 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);
|
||||
tui.need_render();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const planeutils = @import("renderer").planeutils;
|
||||
|
||||
const Widget = @import("Widget.zig");
|
||||
const WidgetList = @import("WidgetList.zig");
|
||||
const Button = @import("Button.zig");
|
||||
|
@ -21,8 +22,7 @@ pub fn Options(context: type) type {
|
|||
|
||||
pub fn on_render_default(_: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme, selected: bool) bool {
|
||||
const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor;
|
||||
const bg_alpha: c_uint = if (button.active or button.hover or selected) nc.ALPHA_OPAQUE else nc.ALPHA_TRANSPARENT;
|
||||
try tui.set_base_style_alpha(button.plane, " ", style_base, nc.ALPHA_TRANSPARENT, bg_alpha);
|
||||
button.plane.set_base_style(" ", style_base);
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
_ = button.plane.print(" {s} ", .{button.opts.label}) catch {};
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
pub const Box = @import("Box.zig");
|
||||
pub const EventHandler = @import("EventHandler.zig");
|
||||
pub const Theme = @import("theme");
|
||||
|
@ -10,7 +11,7 @@ pub const themes = @import("themes").themes;
|
|||
pub const scopes = @import("themes").scopes;
|
||||
|
||||
ptr: *anyopaque,
|
||||
plane: *nc.Plane,
|
||||
plane: *Plane,
|
||||
vtable: *const VTable,
|
||||
|
||||
const Self = @This();
|
||||
|
@ -209,10 +210,10 @@ pub fn walk(self: *Self, walk_ctx: *anyopaque, f: WalkFn) bool {
|
|||
return if (self.vtable.walk(self.ptr, walk_ctx, f, self)) true else f(walk_ctx, self);
|
||||
}
|
||||
|
||||
pub fn empty(a: Allocator, parent: nc.Plane, layout_: Layout) !Self {
|
||||
const child: type = struct { plane: nc.Plane, layout: Layout };
|
||||
pub fn empty(a: Allocator, parent: Plane, layout_: Layout) !Self {
|
||||
const child: type = struct { plane: Plane, layout: Layout };
|
||||
const widget = try a.create(child);
|
||||
const n = try nc.Plane.init(&(Box{}).opts("empty"), parent);
|
||||
const n = try Plane.init(&(Box{}).opts("empty"), parent);
|
||||
widget.* = .{ .plane = n, .layout = layout_ };
|
||||
return .{
|
||||
.ptr = widget,
|
||||
|
|
|
@ -2,8 +2,10 @@ const std = @import("std");
|
|||
const Allocator = std.mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
const Widget = @import("Widget.zig");
|
||||
const Box = @import("Box.zig");
|
||||
|
||||
|
@ -17,8 +19,8 @@ const WidgetState = struct {
|
|||
layout: Layout = .{},
|
||||
};
|
||||
|
||||
plane: nc.Plane,
|
||||
parent: nc.Plane,
|
||||
plane: Plane,
|
||||
parent: Plane,
|
||||
a: Allocator,
|
||||
widgets: ArrayList(WidgetState),
|
||||
layout: Layout,
|
||||
|
@ -51,7 +53,7 @@ pub fn createBox(a: Allocator, parent: Widget, name: [:0]const u8, dir: Directio
|
|||
|
||||
fn init(a: Allocator, parent: Widget, name: [:0]const u8, dir: Direction, layout_: Layout, box: Box) !Self {
|
||||
return .{
|
||||
.plane = try nc.Plane.init(&box.opts(name), parent.plane.*),
|
||||
.plane = try Plane.init(&box.opts(name), parent.plane.*),
|
||||
.parent = parent.plane.*,
|
||||
.a = a,
|
||||
.widgets = ArrayList(WidgetState).init(a),
|
||||
|
|
|
@ -3,7 +3,6 @@ const Allocator = std.mem.Allocator;
|
|||
const ArrayList = std.ArrayList;
|
||||
const eql = std.mem.eql;
|
||||
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const Widget = @import("Widget.zig");
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const cbor = @import("cbor");
|
||||
const log = @import("log");
|
||||
|
@ -11,6 +10,11 @@ const text_manip = @import("text_manip");
|
|||
const syntax = @import("syntax");
|
||||
const project_manager = @import("project_manager");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
const Cell = @import("renderer").Cell;
|
||||
const key = @import("renderer").input.key;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
|
||||
const scrollbar_v = @import("scrollbar_v.zig");
|
||||
const editor_gutter = @import("editor_gutter.zig");
|
||||
const EventHandler = @import("EventHandler.zig");
|
||||
|
@ -31,9 +35,6 @@ const fmt = std.fmt;
|
|||
const split = std.mem.split;
|
||||
const time = std.time;
|
||||
|
||||
const A = nc.Align;
|
||||
const key = nc.key;
|
||||
|
||||
const scroll_step_small = 3;
|
||||
const scroll_page_ratio = 3;
|
||||
const scroll_cursor_min_border_distance = 5;
|
||||
|
@ -188,7 +189,7 @@ pub const Editor = struct {
|
|||
pub const Target = Self;
|
||||
|
||||
a: Allocator,
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
logger: log.Logger,
|
||||
|
||||
file_path: ?[]const u8,
|
||||
|
@ -307,7 +308,7 @@ pub const Editor = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn init(self: *Self, a: Allocator, n: nc.Plane) void {
|
||||
fn init(self: *Self, a: Allocator, n: Plane) void {
|
||||
const logger = log.logger("editor");
|
||||
var frame_rate = tp.env.get().num("frame-rate");
|
||||
if (frame_rate == 0) frame_rate = 60;
|
||||
|
@ -429,7 +430,7 @@ pub const Editor = struct {
|
|||
self.buffer = null;
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
self.plane.context().cursor_disable() catch {};
|
||||
tui.current().rdr.cursor_disable();
|
||||
_ = try self.handlers.msg(.{ "E", "close" });
|
||||
if (self.syntax) |_| if (self.file_path) |file_path|
|
||||
project_manager.did_close(file_path) catch {};
|
||||
|
@ -718,7 +719,7 @@ pub const Editor = struct {
|
|||
const frame = tracy.initZone(@src(), .{ .name = "editor render screen" });
|
||||
defer frame.deinit();
|
||||
|
||||
tui.set_base_style(&self.plane, " ", theme.editor);
|
||||
self.plane.set_base_style(" ", theme.editor);
|
||||
self.plane.erase();
|
||||
if (hl_row) |_|
|
||||
self.render_line_highlight(&self.get_primary().cursor, theme) catch {};
|
||||
|
@ -735,9 +736,9 @@ pub const Editor = struct {
|
|||
var y: c_int = @intCast(cursor.row);
|
||||
var x: c_int = @intCast(cursor.col);
|
||||
self.plane.rel_yx_to_abs(&y, &x);
|
||||
self.plane.context().cursor_enable(y, x) catch {};
|
||||
tui.current().rdr.cursor_enable(y, x) catch {};
|
||||
} else {
|
||||
self.plane.context().cursor_disable() catch {};
|
||||
tui.current().rdr.cursor_disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,7 +773,7 @@ pub const Editor = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_matches(self: *const Self, last_idx: *usize, theme: *const Widget.Theme, cell: *nc.Cell) void {
|
||||
fn render_matches(self: *const Self, last_idx: *usize, theme: *const Widget.Theme, cell: *Cell) void {
|
||||
var y: c_uint = undefined;
|
||||
var x: c_uint = undefined;
|
||||
self.plane.cursor_yx(&y, &x);
|
||||
|
@ -791,7 +792,7 @@ pub const Editor = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_selections(self: *const Self, theme: *const Widget.Theme, cell: *nc.Cell) void {
|
||||
fn render_selections(self: *const Self, theme: *const Widget.Theme, cell: *Cell) void {
|
||||
var y: c_uint = undefined;
|
||||
var x: c_uint = undefined;
|
||||
self.plane.cursor_yx(&y, &x);
|
||||
|
@ -837,7 +838,7 @@ pub const Editor = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_line_end_space_begin(plane: nc.Plane, screen_width: usize, screen_row: usize) usize {
|
||||
fn get_line_end_space_begin(plane: Plane, screen_width: usize, screen_row: usize) usize {
|
||||
var pos = screen_width;
|
||||
var cell = plane.cell_init();
|
||||
while (pos > 0) : (pos -= 1) {
|
||||
|
@ -849,49 +850,49 @@ pub const Editor = struct {
|
|||
}
|
||||
|
||||
fn render_diagnostic_message(self: *const Self, message: []const u8, y: usize, max_space: usize, style: Widget.Theme.Style) void {
|
||||
tui.set_style(&self.plane, style);
|
||||
_ = self.plane.print_aligned(@intCast(y), nc.Align.right, "{s}", .{message[0..@min(max_space, message.len)]}) catch {};
|
||||
self.plane.set_style(style);
|
||||
_ = self.plane.print_aligned_right(@intCast(y), "{s}", .{message[0..@min(max_space, message.len)]}) catch {};
|
||||
}
|
||||
|
||||
inline fn render_diagnostic_cell(self: *const Self, _: Widget.Theme.Style) void {
|
||||
var cell = self.plane.cell_init();
|
||||
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style(&cell, .{ .fs = .undercurl });
|
||||
cell.set_style(.{ .fs = .undercurl });
|
||||
_ = self.plane.putc(&cell) catch {};
|
||||
}
|
||||
|
||||
inline fn render_cursor_cell(self: *const Self, theme: *const Widget.Theme) void {
|
||||
var cell = self.plane.cell_init();
|
||||
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style(&cell, theme.editor_cursor);
|
||||
cell.set_style(theme.editor_cursor);
|
||||
_ = self.plane.putc(&cell) catch {};
|
||||
}
|
||||
|
||||
inline fn render_selection_cell(_: *const Self, theme: *const Widget.Theme, cell: *nc.Cell) void {
|
||||
tui.set_cell_style_bg(cell, theme.editor_selection);
|
||||
inline fn render_selection_cell(_: *const Self, theme: *const Widget.Theme, cell: *Cell) void {
|
||||
cell.set_style_bg(theme.editor_selection);
|
||||
}
|
||||
|
||||
inline fn render_match_cell(_: *const Self, theme: *const Widget.Theme, cell: *nc.Cell, match: Match) void {
|
||||
tui.set_cell_style_bg(cell, if (match.style) |style| style else theme.editor_match);
|
||||
inline fn render_match_cell(_: *const Self, theme: *const Widget.Theme, cell: *Cell, match: Match) void {
|
||||
cell.set_style_bg(if (match.style) |style| style else theme.editor_match);
|
||||
}
|
||||
|
||||
inline fn render_line_highlight_cell(_: *const Self, theme: *const Widget.Theme, cell: *nc.Cell) void {
|
||||
tui.set_cell_style_bg(cell, theme.editor_line_highlight);
|
||||
inline fn render_line_highlight_cell(_: *const Self, theme: *const Widget.Theme, cell: *Cell) void {
|
||||
cell.set_style_bg(theme.editor_line_highlight);
|
||||
}
|
||||
|
||||
inline fn render_control_code(self: *const Self, c: *nc.Cell, n: nc.Plane, code: u8, theme: *const Widget.Theme) struct { usize, usize } {
|
||||
inline fn render_control_code(self: *const Self, c: *Cell, n: Plane, code: u8, theme: *const Widget.Theme) struct { usize, usize } {
|
||||
const val = Buffer.unicode.control_code_to_unicode(code);
|
||||
if (self.show_whitespace)
|
||||
tui.set_cell_style(c, theme.editor_whitespace);
|
||||
c.set_style(theme.editor_whitespace);
|
||||
_ = n.cell_load(c, val) catch {};
|
||||
return .{ 1, 1 };
|
||||
}
|
||||
|
||||
inline fn render_eol(self: *const Self, n: nc.Plane, theme: *const Widget.Theme) nc.Cell {
|
||||
inline fn render_eol(self: *const Self, n: Plane, theme: *const Widget.Theme) Cell {
|
||||
var cell = n.cell_init();
|
||||
const c = &cell;
|
||||
if (self.show_whitespace) {
|
||||
tui.set_cell_style(c, theme.editor_whitespace);
|
||||
c.set_style(theme.editor_whitespace);
|
||||
//_ = n.cell_load(c, "$") catch {};
|
||||
//_ = n.cell_load(c, " ") catch {};
|
||||
//_ = n.cell_load(c, "⏎") catch {};
|
||||
|
@ -912,16 +913,16 @@ pub const Editor = struct {
|
|||
return cell;
|
||||
}
|
||||
|
||||
inline fn render_terminator(n: nc.Plane, theme: *const Widget.Theme) nc.Cell {
|
||||
inline fn render_terminator(n: Plane, theme: *const Widget.Theme) Cell {
|
||||
var cell = n.cell_init();
|
||||
tui.set_cell_style(&cell, theme.editor);
|
||||
cell.set_style(theme.editor);
|
||||
_ = n.cell_load(&cell, "\u{2003}") catch unreachable;
|
||||
return cell;
|
||||
}
|
||||
|
||||
inline fn render_space(self: *const Self, c: *nc.Cell, n: nc.Plane, theme: *const Widget.Theme) struct { usize, usize } {
|
||||
inline fn render_space(self: *const Self, c: *Cell, n: Plane, theme: *const Widget.Theme) struct { usize, usize } {
|
||||
if (self.show_whitespace) {
|
||||
tui.set_cell_style(c, theme.editor_whitespace);
|
||||
c.set_style(theme.editor_whitespace);
|
||||
_ = n.cell_load(c, "·") catch {};
|
||||
//_ = n.cell_load(c, "•") catch {};
|
||||
//_ = n.cell_load(c, "⁃") catch {};
|
||||
|
@ -940,9 +941,9 @@ pub const Editor = struct {
|
|||
return .{ 1, 1 };
|
||||
}
|
||||
|
||||
inline fn render_tab(self: *const Self, c: *nc.Cell, n: nc.Plane, abs_col: usize, theme: *const Widget.Theme) struct { usize, usize } {
|
||||
inline fn render_tab(self: *const Self, c: *Cell, n: Plane, abs_col: usize, theme: *const Widget.Theme) struct { usize, usize } {
|
||||
if (self.show_whitespace) {
|
||||
tui.set_cell_style(c, theme.editor_whitespace);
|
||||
c.set_style(theme.editor_whitespace);
|
||||
_ = n.cell_load(c, "→") catch {};
|
||||
//_ = n.cell_load(c, "⭲") catch {};
|
||||
} else {
|
||||
|
@ -951,9 +952,9 @@ pub const Editor = struct {
|
|||
return .{ 1, 9 - (abs_col % 8) };
|
||||
}
|
||||
|
||||
inline fn render_egc(c: *nc.Cell, n: nc.Plane, egc: [:0]const u8) struct { usize, usize } {
|
||||
inline fn render_egc(c: *Cell, n: Plane, egc: [:0]const u8) struct { usize, usize } {
|
||||
const bytes = n.cell_load(c, egc) catch return .{ 1, 1 };
|
||||
const colcount = nc.cell_cols(c);
|
||||
const colcount = c.columns();
|
||||
return .{ bytes, colcount };
|
||||
}
|
||||
|
||||
|
@ -1007,7 +1008,7 @@ pub const Editor = struct {
|
|||
ctx.self.plane.cursor_move_yx(@intCast(y), @intCast(x)) catch return;
|
||||
var cell = ctx.self.plane.cell_init();
|
||||
_ = ctx.self.plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style(&cell, style);
|
||||
cell.set_style(style);
|
||||
_ = ctx.self.plane.putc(&cell) catch {};
|
||||
}
|
||||
};
|
||||
|
@ -1850,7 +1851,7 @@ pub const Editor = struct {
|
|||
if (self.clipboard) |old|
|
||||
self.a.free(old);
|
||||
self.clipboard = text;
|
||||
tui.current().copy_to_system_clipboard(text);
|
||||
tui.renderer.copy_to_system_clipboard(self.a, text);
|
||||
}
|
||||
|
||||
fn copy_selection(root: Buffer.Root, sel: Selection, text_a: Allocator) ![]const u8 {
|
||||
|
@ -1985,7 +1986,7 @@ pub const Editor = struct {
|
|||
}
|
||||
|
||||
pub fn system_paste(_: *Self, _: command.Context) tp.result {
|
||||
tui.current().request_system_clipboard();
|
||||
tui.renderer.request_system_clipboard();
|
||||
}
|
||||
|
||||
pub fn delete_forward(self: *Self, _: command.Context) tp.result {
|
||||
|
@ -2857,12 +2858,12 @@ pub const Editor = struct {
|
|||
|
||||
pub fn enable_jump_mode(self: *Self, _: command.Context) tp.result {
|
||||
self.jump_mode = true;
|
||||
tui.current().request_mouse_cursor_pointer(true);
|
||||
tui.renderer.request_mouse_cursor_pointer(true);
|
||||
}
|
||||
|
||||
pub fn disable_jump_mode(self: *Self, _: command.Context) tp.result {
|
||||
self.jump_mode = false;
|
||||
tui.current().request_mouse_cursor_text(true);
|
||||
tui.renderer.request_mouse_cursor_text(true);
|
||||
}
|
||||
|
||||
fn update_syntax(self: *Self) !void {
|
||||
|
@ -3530,8 +3531,8 @@ pub fn create(a: Allocator, parent: Widget) !Widget {
|
|||
}
|
||||
|
||||
pub const EditorWidget = struct {
|
||||
plane: nc.Plane,
|
||||
parent: nc.Plane,
|
||||
plane: Plane,
|
||||
parent: Plane,
|
||||
|
||||
editor: Editor,
|
||||
commands: Commands = undefined,
|
||||
|
@ -3558,7 +3559,7 @@ pub const EditorWidget = struct {
|
|||
}
|
||||
|
||||
fn init(self: *Self, a: Allocator, parent: Widget) !void {
|
||||
var n = try nc.Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
|
||||
var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
|
||||
errdefer n.deinit();
|
||||
|
||||
self.* = .{
|
||||
|
@ -3612,9 +3613,9 @@ pub const EditorWidget = struct {
|
|||
self.editor.add_match(m) catch {};
|
||||
} else if (try m.match(.{ "H", tp.extract(&self.hover) })) {
|
||||
if (self.editor.jump_mode)
|
||||
tui.current().request_mouse_cursor_pointer(self.hover)
|
||||
tui.renderer.request_mouse_cursor_pointer(self.hover)
|
||||
else
|
||||
tui.current().request_mouse_cursor_text(self.hover);
|
||||
tui.renderer.request_mouse_cursor_text(self.hover);
|
||||
} else if (try m.match(.{ "show_whitespace", tp.extract(&self.editor.show_whitespace) })) {
|
||||
_ = "";
|
||||
} else {
|
||||
|
@ -3624,15 +3625,15 @@ pub const EditorWidget = struct {
|
|||
}
|
||||
|
||||
fn mouse_click_event(self: *Self, evtype: c_int, btn: c_int, y: c_int, x: c_int, ypx: c_int, xpx: c_int) tp.result {
|
||||
if (evtype != nc.event_type.PRESS) return;
|
||||
if (evtype != event_type.PRESS) return;
|
||||
const ret = (switch (btn) {
|
||||
nc.key.BUTTON1 => &mouse_click_button1,
|
||||
nc.key.BUTTON2 => &mouse_click_button2,
|
||||
nc.key.BUTTON3 => &mouse_click_button3,
|
||||
nc.key.BUTTON4 => &mouse_click_button4,
|
||||
nc.key.BUTTON5 => &mouse_click_button5,
|
||||
nc.key.BUTTON8 => &mouse_click_button8, //back
|
||||
nc.key.BUTTON9 => &mouse_click_button9, //forward
|
||||
key.BUTTON1 => &mouse_click_button1,
|
||||
key.BUTTON2 => &mouse_click_button2,
|
||||
key.BUTTON3 => &mouse_click_button3,
|
||||
key.BUTTON4 => &mouse_click_button4,
|
||||
key.BUTTON5 => &mouse_click_button5,
|
||||
key.BUTTON8 => &mouse_click_button8, //back
|
||||
key.BUTTON9 => &mouse_click_button9, //forward
|
||||
else => return,
|
||||
})(self, y, x, ypx, xpx);
|
||||
self.last_btn = btn;
|
||||
|
@ -3641,11 +3642,11 @@ pub const EditorWidget = struct {
|
|||
}
|
||||
|
||||
fn mouse_drag_event(self: *Self, evtype: c_int, btn: c_int, y: c_int, x: c_int, ypx: c_int, xpx: c_int) tp.result {
|
||||
if (evtype != nc.event_type.PRESS) return;
|
||||
if (evtype != event_type.PRESS) return;
|
||||
return (switch (btn) {
|
||||
nc.key.BUTTON1 => &mouse_drag_button1,
|
||||
nc.key.BUTTON2 => &mouse_drag_button2,
|
||||
nc.key.BUTTON3 => &mouse_drag_button3,
|
||||
key.BUTTON1 => &mouse_drag_button1,
|
||||
key.BUTTON2 => &mouse_drag_button2,
|
||||
key.BUTTON3 => &mouse_drag_button3,
|
||||
else => return,
|
||||
})(self, y, x, ypx, xpx);
|
||||
}
|
||||
|
@ -3653,7 +3654,7 @@ pub const EditorWidget = struct {
|
|||
fn mouse_click_button1(self: *Self, y: c_int, x: c_int, _: c_int, _: c_int) tp.result {
|
||||
var y_, var x_ = .{ y, x };
|
||||
self.editor.plane.abs_yx_to_rel(&y_, &x_);
|
||||
if (self.last_btn == nc.key.BUTTON1) {
|
||||
if (self.last_btn == key.BUTTON1) {
|
||||
const click_time_ms = time.milliTimestamp() - self.last_btn_time_ms;
|
||||
if (click_time_ms <= double_click_time_ms) {
|
||||
if (self.last_btn_count == 2) {
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const tracy = @import("tracy");
|
||||
const diff = @import("diff");
|
||||
const cbor = @import("cbor");
|
||||
const root = @import("root");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
const style = @import("renderer").style;
|
||||
const key = @import("renderer").input.key;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
|
||||
const Widget = @import("Widget.zig");
|
||||
const WidgetList = @import("WidgetList.zig");
|
||||
const EventHandler = @import("EventHandler.zig");
|
||||
|
@ -16,7 +20,7 @@ const command = @import("command.zig");
|
|||
const ed = @import("editor.zig");
|
||||
|
||||
a: Allocator,
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
parent: Widget,
|
||||
|
||||
lines: u32 = 0,
|
||||
|
@ -40,7 +44,7 @@ pub fn create(a: Allocator, parent: Widget, event_source: Widget, editor: *ed.Ed
|
|||
const self: *Self = try a.create(Self);
|
||||
self.* = .{
|
||||
.a = a,
|
||||
.plane = try nc.Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent.plane.*),
|
||||
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent.plane.*),
|
||||
.parent = parent,
|
||||
.linenum = tui.current().config.gutter_line_numbers,
|
||||
.relative = tui.current().config.gutter_line_numbers_relative,
|
||||
|
@ -87,15 +91,15 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
var y: i32 = undefined;
|
||||
var ypx: i32 = undefined;
|
||||
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) }))
|
||||
if (try m.match(.{ "B", event_type.PRESS, key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) }))
|
||||
return self.primary_click(y);
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON3, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) }))
|
||||
if (try m.match(.{ "B", event_type.PRESS, key.BUTTON3, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) }))
|
||||
return self.secondary_click();
|
||||
if (try m.match(.{ "D", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) }))
|
||||
if (try m.match(.{ "D", event_type.PRESS, key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) }))
|
||||
return self.primary_drag(y);
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON4, tp.more }))
|
||||
if (try m.match(.{ "B", event_type.PRESS, key.BUTTON4, tp.more }))
|
||||
return self.mouse_click_button4();
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON5, tp.more }))
|
||||
if (try m.match(.{ "B", event_type.PRESS, key.BUTTON5, tp.more }))
|
||||
return self.mouse_click_button5();
|
||||
|
||||
return false;
|
||||
|
@ -119,7 +123,7 @@ inline fn get_width(self: *Self) usize {
|
|||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
const frame = tracy.initZone(@src(), .{ .name = "gutter render" });
|
||||
defer frame.deinit();
|
||||
tui.set_base_style(&self.plane, " ", theme.editor_gutter);
|
||||
self.plane.set_base_style(" ", theme.editor_gutter);
|
||||
self.plane.erase();
|
||||
if (self.linenum) {
|
||||
const relative = self.relative or if (tui.current().input_mode) |mode| mode.line_numbers == .relative else false;
|
||||
|
@ -158,13 +162,13 @@ pub fn render_linear(self: *Self, theme: *const Widget.Theme) void {
|
|||
while (rows > 0) : (rows -= 1) {
|
||||
if (linenum > self.lines) return;
|
||||
if (linenum == self.line + 1) {
|
||||
tui.set_base_style(&self.plane, " ", theme.editor_gutter_active);
|
||||
self.plane.on_styles(nc.style.bold);
|
||||
self.plane.set_base_style(" ", theme.editor_gutter_active);
|
||||
self.plane.on_styles(style.bold);
|
||||
} else {
|
||||
tui.set_base_style(&self.plane, " ", theme.editor_gutter);
|
||||
self.plane.off_styles(nc.style.bold);
|
||||
self.plane.set_base_style(" ", theme.editor_gutter);
|
||||
self.plane.off_styles(style.bold);
|
||||
}
|
||||
_ = self.plane.putstr_aligned(@intCast(pos), nc.Align.right, std.fmt.bufPrintZ(&buf, "{d} ", .{linenum}) catch return) catch {};
|
||||
_ = self.plane.print_aligned_right(@intCast(pos), "{s}", .{std.fmt.bufPrintZ(&buf, "{d} ", .{linenum}) catch return}) catch {};
|
||||
if (self.highlight and linenum == self.line + 1)
|
||||
self.render_line_highlight(pos, theme);
|
||||
self.render_diff_symbols(&diff_symbols, pos, linenum, theme);
|
||||
|
@ -184,10 +188,10 @@ pub fn render_relative(self: *Self, theme: *const Widget.Theme) void {
|
|||
var buf: [31:0]u8 = undefined;
|
||||
while (rows > 0) : (rows -= 1) {
|
||||
if (pos > self.lines - row) return;
|
||||
tui.set_base_style(&self.plane, " ", if (linenum == 0) theme.editor_gutter_active else theme.editor_gutter);
|
||||
self.plane.set_base_style(" ", if (linenum == 0) theme.editor_gutter_active else theme.editor_gutter);
|
||||
const val = @abs(if (linenum == 0) line else linenum);
|
||||
const fmt = std.fmt.bufPrintZ(&buf, "{d} ", .{val}) catch return;
|
||||
_ = self.plane.putstr_aligned(@intCast(pos), nc.Align.right, if (fmt.len > 6) "==> " else fmt) catch {};
|
||||
_ = self.plane.print_aligned_right(@intCast(pos), "{s}", .{if (fmt.len > 6) "==> " else fmt}) catch {};
|
||||
if (self.highlight and linenum == 0)
|
||||
self.render_line_highlight(pos, theme);
|
||||
self.render_diff_symbols(&diff_symbols, pos, abs_linenum, theme);
|
||||
|
@ -202,7 +206,7 @@ inline fn render_line_highlight(self: *Self, pos: usize, theme: *const Widget.Th
|
|||
self.plane.cursor_move_yx(@intCast(pos), @intCast(i)) catch return;
|
||||
var cell = self.plane.cell_init();
|
||||
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style_bg(&cell, theme.editor_line_highlight);
|
||||
cell.set_style_bg(theme.editor_line_highlight);
|
||||
_ = self.plane.putc(&cell) catch {};
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +231,7 @@ inline fn render_diff_symbols(self: *Self, diff_symbols: *[]Symbol, pos: usize,
|
|||
self.plane.cursor_move_yx(@intCast(pos), @intCast(self.get_width() - 1)) catch return;
|
||||
var cell = self.plane.cell_init();
|
||||
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style_fg(&cell, switch (sym.kind) {
|
||||
cell.set_style_fg(switch (sym.kind) {
|
||||
.insert => theme.editor_gutter_added,
|
||||
.modified => theme.editor_gutter_modified,
|
||||
.delete => theme.editor_gutter_deleted,
|
||||
|
@ -243,7 +247,7 @@ fn render_diagnostics(self: *Self, theme: *const Widget.Theme) void {
|
|||
fn render_diagnostic(self: *Self, diag: *const ed.Diagnostic, theme: *const Widget.Theme) void {
|
||||
const row = diag.sel.begin.row;
|
||||
if (!(self.row < row and row < self.row + self.rows)) return;
|
||||
const style = switch (diag.get_severity()) {
|
||||
const style_ = switch (diag.get_severity()) {
|
||||
.Error => theme.editor_error,
|
||||
.Warning => theme.editor_warning,
|
||||
.Information => theme.editor_information,
|
||||
|
@ -259,7 +263,7 @@ fn render_diagnostic(self: *Self, diag: *const ed.Diagnostic, theme: *const Widg
|
|||
self.plane.cursor_move_yx(@intCast(y), 0) catch return;
|
||||
var cell = self.plane.cell_init();
|
||||
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style_fg(&cell, style);
|
||||
cell.set_style_fg(style_);
|
||||
_ = self.plane.cell_load(&cell, icon) catch {};
|
||||
_ = self.plane.putc(&cell) catch {};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
const nc = @import("notcurses");
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
pub fn print_string_large(n: nc.Plane, s: []const u8) !void {
|
||||
pub fn print_string_large(n: Plane, s: []const u8) !void {
|
||||
for (s) |c|
|
||||
print_char_large(n, c) catch break;
|
||||
}
|
||||
|
||||
pub fn print_char_large(n: nc.Plane, char: u8) !void {
|
||||
pub fn print_char_large(n: Plane, char: u8) !void {
|
||||
const bitmap = font8x8[char];
|
||||
for (0..8) |y| {
|
||||
for (0..8) |x| {
|
||||
|
@ -21,14 +21,14 @@ pub fn print_char_large(n: nc.Plane, char: u8) !void {
|
|||
n.cursor_move_rel(-8, 8) catch {};
|
||||
}
|
||||
|
||||
pub fn print_string_medium(n: nc.Plane, s: []const u8) !void {
|
||||
pub fn print_string_medium(n: Plane, s: []const u8) !void {
|
||||
for (s) |c|
|
||||
print_char_medium(n, c) catch break;
|
||||
}
|
||||
|
||||
const QUADBLOCKS = [_][:0]const u8{ " ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█" };
|
||||
|
||||
pub fn print_char_medium(n: nc.Plane, char: u8) !void {
|
||||
pub fn print_char_medium(n: Plane, char: u8) !void {
|
||||
const bitmap = font8x8[char];
|
||||
for (0..4) |y| {
|
||||
for (0..4) |x| {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
const planeutils = @import("renderer").planeutils;
|
||||
const channels_ = @import("renderer").channels;
|
||||
const style_ = @import("renderer").style;
|
||||
|
||||
const Widget = @import("Widget.zig");
|
||||
const WidgetList = @import("WidgetList.zig");
|
||||
const Button = @import("Button.zig");
|
||||
|
@ -11,9 +15,9 @@ const command = @import("command.zig");
|
|||
const fonts = @import("fonts.zig");
|
||||
|
||||
a: std.mem.Allocator,
|
||||
background: nc.Plane,
|
||||
plane: nc.Plane,
|
||||
parent: nc.Plane,
|
||||
background: Plane,
|
||||
plane: Plane,
|
||||
parent: Plane,
|
||||
fire: ?Fire = null,
|
||||
commands: Commands = undefined,
|
||||
menu: *Menu.State(*Self),
|
||||
|
@ -22,9 +26,9 @@ const Self = @This();
|
|||
|
||||
pub fn create(a: std.mem.Allocator, parent: Widget) !Widget {
|
||||
const self: *Self = try a.create(Self);
|
||||
var background = try nc.Plane.init(&(Widget.Box{}).opts("background"), parent.plane.*);
|
||||
var background = try Plane.init(&(Widget.Box{}).opts("background"), parent.plane.*);
|
||||
errdefer background.deinit();
|
||||
var n = try nc.Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
|
||||
var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
|
||||
errdefer n.deinit();
|
||||
|
||||
const w = Widget.to(self);
|
||||
|
@ -68,49 +72,31 @@ pub fn walk(self: *Self, walk_ctx: *anyopaque, f: Widget.WalkFn, w: *Widget) boo
|
|||
pub fn receive(_: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
var hover: bool = false;
|
||||
if (try m.match(.{ "H", tp.extract(&hover) })) {
|
||||
tui.current().request_mouse_cursor_default(hover);
|
||||
tui.renderer.request_mouse_cursor_default(hover);
|
||||
tui.need_render();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn set_style(plane: nc.Plane, style: Widget.Theme.Style) void {
|
||||
var channels: u64 = 0;
|
||||
if (style.fg) |fg| {
|
||||
nc.channels_set_fg_rgb(&channels, fg) catch {};
|
||||
nc.channels_set_fg_alpha(&channels, nc.ALPHA_OPAQUE) catch {};
|
||||
}
|
||||
if (style.bg) |bg| {
|
||||
nc.channels_set_bg_rgb(&channels, bg) catch {};
|
||||
nc.channels_set_bg_alpha(&channels, nc.ALPHA_TRANSPARENT) catch {};
|
||||
}
|
||||
plane.set_channels(channels);
|
||||
if (style.fs) |fs| switch (fs) {
|
||||
.normal => plane.set_styles(nc.style.none),
|
||||
.bold => plane.set_styles(nc.style.bold),
|
||||
.italic => plane.set_styles(nc.style.italic),
|
||||
.underline => plane.set_styles(nc.style.underline),
|
||||
.undercurl => plane.set_styles(nc.style.undercurl),
|
||||
.strikethrough => plane.set_styles(nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
fn menu_on_render(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool {
|
||||
const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor;
|
||||
const bg_alpha: c_uint = if (button.active or button.hover or selected) nc.ALPHA_OPAQUE else nc.ALPHA_TRANSPARENT;
|
||||
try tui.set_base_style_alpha(button.plane, " ", style_base, nc.ALPHA_OPAQUE, bg_alpha);
|
||||
if (button.active or button.hover or selected) {
|
||||
button.plane.set_base_style(" ", style_base);
|
||||
} else {
|
||||
button.plane.set_base_style_bg_transparent(" ", style_base);
|
||||
}
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor;
|
||||
const style_text = if (tui.find_scope_style(theme, "keyword")) |sty| sty.style else theme.editor;
|
||||
const style_keybind = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else theme.editor;
|
||||
const sep = std.mem.indexOfScalar(u8, button.opts.label, ':') orelse button.opts.label.len;
|
||||
set_style(button.plane, style_subtext);
|
||||
set_style(button.plane, style_text);
|
||||
button.plane.set_style(style_subtext);
|
||||
button.plane.set_style(style_text);
|
||||
const pointer = if (selected) "⏵" else " ";
|
||||
_ = button.plane.print("{s}{s}", .{ pointer, button.opts.label[0..sep] }) catch {};
|
||||
set_style(button.plane, style_keybind);
|
||||
button.plane.set_style(style_keybind);
|
||||
_ = button.plane.print("{s}", .{button.opts.label[sep + 1 ..]}) catch {};
|
||||
return false;
|
||||
}
|
||||
|
@ -146,10 +132,10 @@ fn menu_action_quit(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self))
|
|||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
const more = self.menu.render(theme);
|
||||
|
||||
try tui.set_base_style_alpha(self.background, " ", theme.editor, nc.ALPHA_OPAQUE, nc.ALPHA_OPAQUE);
|
||||
self.background.set_base_style(" ", theme.editor);
|
||||
self.background.erase();
|
||||
self.background.home();
|
||||
try tui.set_base_style_alpha(self.plane, "", theme.editor, nc.ALPHA_TRANSPARENT, nc.ALPHA_TRANSPARENT);
|
||||
self.plane.set_base_style_transparent("", theme.editor);
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
if (self.fire) |*fire| fire.render();
|
||||
|
@ -161,31 +147,31 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
|||
const subtext = "a programmer's text editor";
|
||||
|
||||
if (self.plane.dim_x() > 120 and self.plane.dim_y() > 22) {
|
||||
set_style(self.plane, style_title);
|
||||
self.plane.set_style_bg_transparent(style_title);
|
||||
self.plane.cursor_move_yx(2, 4) catch return more;
|
||||
fonts.print_string_large(self.plane, title) catch return more;
|
||||
|
||||
set_style(self.plane, style_subtext);
|
||||
self.plane.set_style_bg_transparent(style_subtext);
|
||||
self.plane.cursor_move_yx(10, 8) catch return more;
|
||||
fonts.print_string_medium(self.plane, subtext) catch return more;
|
||||
|
||||
self.menu.resize(.{ .y = 15, .x = 10, .w = 32 });
|
||||
} else if (self.plane.dim_x() > 55 and self.plane.dim_y() > 16) {
|
||||
set_style(self.plane, style_title);
|
||||
self.plane.set_style_bg_transparent(style_title);
|
||||
self.plane.cursor_move_yx(2, 4) catch return more;
|
||||
fonts.print_string_medium(self.plane, title) catch return more;
|
||||
|
||||
set_style(self.plane, style_subtext);
|
||||
self.plane.set_style_bg_transparent(style_subtext);
|
||||
self.plane.cursor_move_yx(7, 6) catch return more;
|
||||
_ = self.plane.print(subtext, .{}) catch {};
|
||||
|
||||
self.menu.resize(.{ .y = 9, .x = 8, .w = 32 });
|
||||
} else {
|
||||
set_style(self.plane, style_title);
|
||||
self.plane.set_style_bg_transparent(style_title);
|
||||
self.plane.cursor_move_yx(1, 4) catch return more;
|
||||
_ = self.plane.print(title, .{}) catch return more;
|
||||
|
||||
set_style(self.plane, style_subtext);
|
||||
self.plane.set_style_bg_transparent(style_subtext);
|
||||
self.plane.cursor_move_yx(3, 6) catch return more;
|
||||
_ = self.plane.print(subtext, .{}) catch {};
|
||||
|
||||
|
@ -235,7 +221,7 @@ const Fire = struct {
|
|||
const px = "▀";
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
prng: std.rand.DefaultPrng,
|
||||
|
||||
//scope cache - spread fire
|
||||
|
@ -253,7 +239,7 @@ const Fire = struct {
|
|||
const MAX_COLOR = 256;
|
||||
const LAST_COLOR = MAX_COLOR - 1;
|
||||
|
||||
fn init(a: std.mem.Allocator, plane: nc.Plane, pos: Widget.Box) !Fire {
|
||||
fn init(a: std.mem.Allocator, plane: Plane, pos: Widget.Box) !Fire {
|
||||
const FIRE_H = @as(u16, @intCast(pos.h)) * 2;
|
||||
const FIRE_W = @as(u16, @intCast(pos.w));
|
||||
var self: Fire = .{
|
||||
|
|
|
@ -4,19 +4,18 @@ const time = @import("std").time;
|
|||
const Allocator = @import("std").mem.Allocator;
|
||||
const Mutex = @import("std").Thread.Mutex;
|
||||
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
const tui = @import("tui.zig");
|
||||
const Widget = @import("Widget.zig");
|
||||
const EventHandler = @import("EventHandler.zig");
|
||||
|
||||
const A = nc.Align;
|
||||
|
||||
pub const name = "inputview";
|
||||
|
||||
parent: nc.Plane,
|
||||
plane: nc.Plane,
|
||||
parent: Plane,
|
||||
plane: Plane,
|
||||
lastbuf: [4096]u8 = undefined,
|
||||
last: []u8 = "",
|
||||
last_count: u64 = 0,
|
||||
|
@ -25,15 +24,15 @@ last_tdiff: i64 = 0,
|
|||
|
||||
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);
|
||||
try tui.current().input_listeners.add(EventHandler.bind(self, listen));
|
||||
return Widget.to(self);
|
||||
}
|
||||
|
||||
fn init(parent: nc.Plane) !Self {
|
||||
var n = try nc.Plane.init(&(Widget.Box{}).opts_vscroll(@typeName(Self)), parent);
|
||||
fn init(parent: Plane) !Self {
|
||||
var n = try Plane.init(&(Widget.Box{}).opts_vscroll(@typeName(Self)), parent);
|
||||
errdefer n.deinit();
|
||||
return .{
|
||||
.parent = parent,
|
||||
|
@ -49,7 +48,7 @@ pub fn deinit(self: *Self, a: Allocator) void {
|
|||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", theme.panel);
|
||||
self.plane.set_base_style(" ", theme.panel);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,23 +3,23 @@ const fmt = @import("std").fmt;
|
|||
const time = @import("std").time;
|
||||
const Allocator = @import("std").mem.Allocator;
|
||||
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const Buffer = @import("Buffer");
|
||||
const color = @import("color");
|
||||
const syntax = @import("syntax");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
const style = @import("renderer").style;
|
||||
|
||||
const tui = @import("tui.zig");
|
||||
const Widget = @import("Widget.zig");
|
||||
const EventHandler = @import("EventHandler.zig");
|
||||
const mainview = @import("mainview.zig");
|
||||
const ed = @import("editor.zig");
|
||||
|
||||
const A = nc.Align;
|
||||
|
||||
pub const name = @typeName(Self);
|
||||
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
editor: *ed.Editor,
|
||||
need_render: bool = true,
|
||||
need_clear: bool = false,
|
||||
|
@ -30,11 +30,11 @@ last_node: usize = 0,
|
|||
|
||||
const Self = @This();
|
||||
|
||||
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
|
||||
pub fn create(a: Allocator, parent: Plane) !Widget {
|
||||
if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| {
|
||||
const self: *Self = try a.create(Self);
|
||||
self.* = .{
|
||||
.plane = try nc.Plane.init(&(Widget.Box{}).opts_vscroll(name), parent),
|
||||
.plane = try Plane.init(&(Widget.Box{}).opts_vscroll(name), parent),
|
||||
.editor = editor,
|
||||
.pos_cache = try ed.PosToWidthCache.init(a),
|
||||
};
|
||||
|
@ -189,33 +189,33 @@ fn show_color(self: *Self, tag: []const u8, c_: ?Widget.Theme.Color) void {
|
|||
fn show_font(self: *Self, font: ?Widget.Theme.FontStyle) void {
|
||||
if (font) |fs| switch (fs) {
|
||||
.normal => {
|
||||
self.plane.set_styles(nc.style.none);
|
||||
self.plane.set_styles(style.normal);
|
||||
_ = self.plane.print(" normal", .{}) catch return;
|
||||
},
|
||||
.bold => {
|
||||
self.plane.set_styles(nc.style.bold);
|
||||
self.plane.set_styles(style.bold);
|
||||
_ = self.plane.print(" bold", .{}) catch return;
|
||||
},
|
||||
.italic => {
|
||||
self.plane.set_styles(nc.style.italic);
|
||||
self.plane.set_styles(style.italic);
|
||||
_ = self.plane.print(" italic", .{}) catch return;
|
||||
},
|
||||
.underline => {
|
||||
self.plane.set_styles(nc.style.underline);
|
||||
self.plane.set_styles(style.underline);
|
||||
_ = self.plane.print(" underline", .{}) catch return;
|
||||
},
|
||||
.undercurl => {
|
||||
self.plane.set_styles(nc.style.undercurl);
|
||||
self.plane.set_styles(style.undercurl);
|
||||
_ = self.plane.print(" undercurl", .{}) catch return;
|
||||
},
|
||||
.strikethrough => {
|
||||
self.plane.set_styles(nc.style.struck);
|
||||
self.plane.set_styles(style.struck);
|
||||
_ = self.plane.print(" strikethrough", .{}) catch return;
|
||||
},
|
||||
};
|
||||
self.plane.set_styles(nc.style.none);
|
||||
self.plane.set_styles(style.normal);
|
||||
}
|
||||
|
||||
fn reset_style(self: *Self) void {
|
||||
tui.set_base_style(&self.plane, " ", (self.theme orelse return).panel);
|
||||
self.plane.set_base_style(" ", (self.theme orelse return).panel);
|
||||
}
|
||||
|
|
|
@ -4,20 +4,20 @@ const time = @import("std").time;
|
|||
const Allocator = @import("std").mem.Allocator;
|
||||
const Mutex = @import("std").Thread.Mutex;
|
||||
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const log = @import("log");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
const tui = @import("tui.zig");
|
||||
const Widget = @import("Widget.zig");
|
||||
const MessageFilter = @import("MessageFilter.zig");
|
||||
|
||||
const escape = fmt.fmtSliceEscapeLower;
|
||||
const A = nc.Align;
|
||||
|
||||
pub const name = @typeName(Self);
|
||||
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
lastbuf_src: [128]u8 = undefined,
|
||||
lastbuf_msg: [log.max_log_message]u8 = undefined,
|
||||
last_src: []u8 = "",
|
||||
|
@ -28,15 +28,15 @@ last_tdiff: i64 = 0,
|
|||
|
||||
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.* = init(parent) catch |e| return tp.exit_error(e);
|
||||
try tui.current().message_filters.add(MessageFilter.bind(self, log_receive));
|
||||
return Widget.to(self);
|
||||
}
|
||||
|
||||
fn init(parent: nc.Plane) !Self {
|
||||
var n = try nc.Plane.init(&(Widget.Box{}).opts_vscroll(name), parent);
|
||||
fn init(parent: Plane) !Self {
|
||||
var n = try Plane.init(&(Widget.Box{}).opts_vscroll(name), parent);
|
||||
errdefer n.deinit();
|
||||
return .{
|
||||
.plane = n,
|
||||
|
@ -51,7 +51,7 @@ pub fn deinit(self: *Self, a: Allocator) void {
|
|||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", theme.panel);
|
||||
self.plane.set_base_style(" ", theme.panel);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const cbor = @import("cbor");
|
||||
const tracy = @import("tracy");
|
||||
|
@ -7,6 +6,8 @@ const root = @import("root");
|
|||
const location_history = @import("location_history");
|
||||
const project_manager = @import("project_manager");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
|
||||
const tui = @import("tui.zig");
|
||||
const command = @import("command.zig");
|
||||
const Box = @import("Box.zig");
|
||||
|
@ -21,7 +22,7 @@ const Self = @This();
|
|||
const Commands = command.Collection(cmds);
|
||||
|
||||
a: std.mem.Allocator,
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
widgets: *WidgetList,
|
||||
widgets_widget: Widget,
|
||||
floating_views: WidgetStack,
|
||||
|
@ -43,7 +44,7 @@ const NavState = struct {
|
|||
matches: usize = 0,
|
||||
};
|
||||
|
||||
pub fn create(a: std.mem.Allocator, n: nc.Plane) !Widget {
|
||||
pub fn create(a: std.mem.Allocator, n: Plane) !Widget {
|
||||
try project_manager.open_cwd();
|
||||
const self = try a.create(Self);
|
||||
self.* = .{
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
const Allocator = @import("std").mem.Allocator;
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const Widget = @import("Widget.zig");
|
||||
const tui = @import("tui.zig");
|
||||
|
||||
pub const name = @typeName(Self);
|
||||
const Self = @This();
|
||||
|
||||
plane: nc.Plane,
|
||||
|
||||
const y_pos = 10;
|
||||
const y_pos_hidden = -15;
|
||||
const x_pos = 10;
|
||||
|
||||
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
|
||||
const self: *Self = try a.create(Self);
|
||||
self.* = try init(parent);
|
||||
return Widget.to(self);
|
||||
}
|
||||
|
||||
pub fn init(parent: nc.Plane) !Self {
|
||||
var n = try nc.Plane.init(&(Widget.Box{}).opts_vscroll(name), parent);
|
||||
errdefer n.deinit();
|
||||
return .{
|
||||
.plane = n,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, a: Allocator) void {
|
||||
self.plane.deinit();
|
||||
a.destroy(self);
|
||||
}
|
||||
|
||||
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
_ = self;
|
||||
_ = m;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", theme.sidebar);
|
||||
return false;
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const root = @import("root");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const command = @import("../../command.zig");
|
||||
const EventHandler = @import("../../EventHandler.zig");
|
||||
|
@ -10,8 +14,6 @@ const Allocator = @import("std").mem.Allocator;
|
|||
const ArrayList = @import("std").ArrayList;
|
||||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
const input_buffer_size = 1024;
|
||||
|
@ -60,9 +62,9 @@ pub fn add_keybind() void {}
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -248,7 +250,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const root = @import("root");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const command = @import("../../command.zig");
|
||||
const EventHandler = @import("../../EventHandler.zig");
|
||||
|
@ -41,8 +44,8 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, modifiers),
|
||||
event_type.PRESS => self.mapPress(keypress, modifiers),
|
||||
event_type.REPEAT => self.mapPress(keypress, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -50,7 +53,7 @@ fn mapEvent(self: *Self, evtype: u32, keypress: u32, modifiers: u32) tp.result {
|
|||
fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
||||
const keynormal = if ('a' <= keypress and keypress <= 'z') keypress - ('a' - 'A') else keypress;
|
||||
return switch (modifiers) {
|
||||
nc.mod.CTRL => switch (keynormal) {
|
||||
mod.CTRL => switch (keynormal) {
|
||||
'F' => self.sheeran(),
|
||||
'J' => self.cmd("toggle_logview", .{}),
|
||||
'Q' => self.cmd("quit", .{}),
|
||||
|
@ -60,7 +63,7 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
'/' => self.cmd("open_help", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.CTRL | nc.mod.SHIFT => switch (keynormal) {
|
||||
mod.CTRL | mod.SHIFT => switch (keynormal) {
|
||||
'Q' => self.cmd("quit_without_saving", .{}),
|
||||
'R' => self.cmd("restart", .{}),
|
||||
'F' => self.cmd("enter_find_in_files_mode", .{}),
|
||||
|
@ -69,11 +72,11 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
'/' => self.cmd("open_help", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.ALT => switch (keynormal) {
|
||||
mod.ALT => switch (keynormal) {
|
||||
'L' => self.cmd("toggle_logview", .{}),
|
||||
'I' => self.cmd("toggle_inputview", .{}),
|
||||
nc.key.LEFT => self.cmd("jump_back", .{}),
|
||||
nc.key.RIGHT => self.cmd("jump_forward", .{}),
|
||||
key.LEFT => self.cmd("jump_back", .{}),
|
||||
key.RIGHT => self.cmd("jump_forward", .{}),
|
||||
else => {},
|
||||
},
|
||||
0 => switch (keypress) {
|
||||
|
@ -85,15 +88,15 @@ fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
|||
'c' => self.cmd("open_config", .{}),
|
||||
'q' => self.cmd("quit", .{}),
|
||||
|
||||
nc.key.F01 => self.cmd("open_help", .{}),
|
||||
nc.key.F06 => self.cmd("open_config", .{}),
|
||||
nc.key.F09 => self.cmd("theme_prev", .{}),
|
||||
nc.key.F10 => self.cmd("theme_next", .{}),
|
||||
nc.key.F11 => self.cmd("toggle_logview", .{}),
|
||||
nc.key.F12 => self.cmd("toggle_inputview", .{}),
|
||||
nc.key.UP => self.cmd("home_menu_up", .{}),
|
||||
nc.key.DOWN => self.cmd("home_menu_down", .{}),
|
||||
nc.key.ENTER => self.cmd("home_menu_activate", .{}),
|
||||
key.F01 => self.cmd("open_help", .{}),
|
||||
key.F06 => self.cmd("open_config", .{}),
|
||||
key.F09 => self.cmd("theme_prev", .{}),
|
||||
key.F10 => self.cmd("theme_next", .{}),
|
||||
key.F11 => self.cmd("toggle_logview", .{}),
|
||||
key.F12 => self.cmd("toggle_inputview", .{}),
|
||||
key.UP => self.cmd("home_menu_up", .{}),
|
||||
key.DOWN => self.cmd("home_menu_down", .{}),
|
||||
key.ENTER => self.cmd("home_menu_activate", .{}),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const root = @import("root");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../../tui.zig");
|
||||
const command = @import("../../../command.zig");
|
||||
const EventHandler = @import("../../../EventHandler.zig");
|
||||
|
@ -10,8 +14,6 @@ const Allocator = @import("std").mem.Allocator;
|
|||
const ArrayList = @import("std").ArrayList;
|
||||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
const input_buffer_size = 1024;
|
||||
|
@ -63,9 +65,9 @@ pub fn add_keybind() void {}
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -249,7 +251,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const root = @import("root");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../../tui.zig");
|
||||
const command = @import("../../../command.zig");
|
||||
const EventHandler = @import("../../../EventHandler.zig");
|
||||
|
@ -10,8 +14,6 @@ const Allocator = @import("std").mem.Allocator;
|
|||
const ArrayList = @import("std").ArrayList;
|
||||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
const input_buffer_size = 1024;
|
||||
|
@ -64,9 +66,9 @@ pub fn add_keybind() void {}
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -451,7 +453,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const root = @import("root");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../../tui.zig");
|
||||
const command = @import("../../../command.zig");
|
||||
const EventHandler = @import("../../../EventHandler.zig");
|
||||
|
@ -10,8 +14,6 @@ const Allocator = @import("std").mem.Allocator;
|
|||
const ArrayList = @import("std").ArrayList;
|
||||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
const input_buffer_size = 1024;
|
||||
|
@ -64,9 +66,9 @@ pub fn add_keybind() void {}
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -409,7 +411,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result {
|
|||
if (self.input.items.len + 4 > input_buffer_size)
|
||||
try self.flush_input();
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -10,8 +14,6 @@ const ed = @import("../../editor.zig");
|
|||
const Allocator = @import("std").mem.Allocator;
|
||||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
@ -83,9 +85,9 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
switch (evtype) {
|
||||
nc.event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => try self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => try self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => try self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +161,7 @@ fn mapRelease(self: *Self, keypress: u32, _: u32, _: u32) tp.result {
|
|||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
if (self.input.len + 16 > self.buf.len)
|
||||
try self.flush_input();
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
self.input = self.buf[0 .. self.input.len + bytes];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -10,8 +14,6 @@ const ed = @import("../../editor.zig");
|
|||
const Allocator = @import("std").mem.Allocator;
|
||||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
@ -82,9 +84,9 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
switch (evtype) {
|
||||
nc.event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => try self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => try self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => try self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +158,7 @@ fn mapRelease(self: *Self, keypress: u32, _: u32, _: u32) tp.result {
|
|||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
if (self.input.len + 16 > self.buf.len)
|
||||
try self.flush_input();
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e);
|
||||
self.input = self.buf[0 .. self.input.len + bytes];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -10,8 +13,6 @@ const Allocator = @import("std").mem.Allocator;
|
|||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const fmt = @import("std").fmt;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
@ -64,8 +65,8 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, modifiers: u32) tp.result {
|
||||
switch (evtype) {
|
||||
nc.event_type.PRESS => try self.mapPress(keypress, modifiers),
|
||||
nc.event_type.REPEAT => try self.mapPress(keypress, modifiers),
|
||||
event_type.PRESS => try self.mapPress(keypress, modifiers),
|
||||
event_type.REPEAT => try self.mapPress(keypress, modifiers),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -10,8 +14,6 @@ const Allocator = @import("std").mem.Allocator;
|
|||
const json = @import("std").json;
|
||||
const eql = @import("std").mem.eql;
|
||||
const fmt = @import("std").fmt;
|
||||
const mod = nc.mod;
|
||||
const key = nc.key;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
@ -76,7 +78,7 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
switch (evtype) {
|
||||
nc.event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +114,7 @@ fn execute_operation(self: *Self, c: u32) void {
|
|||
},
|
||||
};
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch return;
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch return;
|
||||
command.executeName(cmd, command.fmt(.{buf[0..bytes]})) catch {};
|
||||
command.executeName("exit_mini_mode", .{}) catch {};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
const command = @import("../../command.zig");
|
||||
|
@ -68,9 +72,9 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
switch (evtype) {
|
||||
nc.event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => try self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => try self.mapRelease(keypress, egc, modifiers),
|
||||
event_type.PRESS => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => try self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => try self.mapRelease(keypress, egc, modifiers),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +82,7 @@ fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) t
|
|||
fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
const keynormal = if ('a' <= keypress and keypress <= 'z') keypress - ('a' - 'A') else keypress;
|
||||
return switch (modifiers) {
|
||||
nc.mod.CTRL => switch (keynormal) {
|
||||
mod.CTRL => switch (keynormal) {
|
||||
'Q' => self.cmd("quit", .{}),
|
||||
'V' => self.cmd("system_paste", .{}),
|
||||
'U' => self.file_path.clearRetainingCapacity(),
|
||||
|
@ -86,30 +90,30 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
|||
'C' => self.cancel(),
|
||||
'L' => self.cmd("scroll_view_center", .{}),
|
||||
'I' => self.insert_bytes("\t"),
|
||||
nc.key.SPACE => self.cancel(),
|
||||
nc.key.BACKSPACE => self.file_path.clearRetainingCapacity(),
|
||||
key.SPACE => self.cancel(),
|
||||
key.BACKSPACE => self.file_path.clearRetainingCapacity(),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.ALT => switch (keynormal) {
|
||||
mod.ALT => switch (keynormal) {
|
||||
'V' => self.cmd("system_paste", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.ALT | nc.mod.SHIFT => switch (keynormal) {
|
||||
mod.ALT | mod.SHIFT => switch (keynormal) {
|
||||
'V' => self.cmd("system_paste", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.SHIFT => switch (keypress) {
|
||||
else => if (!nc.key.synthesized_p(keypress))
|
||||
mod.SHIFT => switch (keypress) {
|
||||
else => if (!key.synthesized_p(keypress))
|
||||
self.insert_code_point(egc)
|
||||
else {},
|
||||
},
|
||||
0 => switch (keypress) {
|
||||
nc.key.ESC => self.cancel(),
|
||||
nc.key.ENTER => self.navigate(),
|
||||
nc.key.BACKSPACE => if (self.file_path.items.len > 0) {
|
||||
key.ESC => self.cancel(),
|
||||
key.ENTER => self.navigate(),
|
||||
key.BACKSPACE => if (self.file_path.items.len > 0) {
|
||||
self.file_path.shrinkRetainingCapacity(self.file_path.items.len - 1);
|
||||
},
|
||||
else => if (!nc.key.synthesized_p(keypress))
|
||||
else => if (!key.synthesized_p(keypress))
|
||||
self.insert_code_point(egc)
|
||||
else {},
|
||||
},
|
||||
|
@ -121,7 +125,7 @@ fn mapRelease(_: *Self, _: u32, _: u32, _: u32) tp.result {}
|
|||
|
||||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
var buf: [32]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.file_path.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const log = @import("log");
|
||||
const cbor = @import("cbor");
|
||||
|
||||
const Plane = @import("renderer").Plane;
|
||||
const planeutils = @import("renderer").planeutils;
|
||||
const key = @import("renderer").input.key;
|
||||
const mod = @import("renderer").input.modifier;
|
||||
const event_type = @import("renderer").input.event_type;
|
||||
const egc_ = @import("renderer").egc;
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const command = @import("../../command.zig");
|
||||
const EventHandler = @import("../../EventHandler.zig");
|
||||
|
@ -66,7 +72,7 @@ pub fn deinit(self: *Self) void {
|
|||
|
||||
fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool {
|
||||
const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor_widget;
|
||||
try tui.set_base_style_alpha(button.plane, " ", style_base, nc.ALPHA_OPAQUE, nc.ALPHA_OPAQUE);
|
||||
button.plane.set_base_style(" ", style_base);
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
var file_path: []const u8 = undefined;
|
||||
|
@ -89,11 +95,11 @@ fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c
|
|||
return false;
|
||||
}
|
||||
|
||||
fn render_cell(plane: nc.Plane, y: usize, x: usize, style: Widget.Theme.Style) !void {
|
||||
fn render_cell(plane: Plane, y: usize, x: usize, style: Widget.Theme.Style) !void {
|
||||
plane.cursor_move_yx(@intCast(y), @intCast(x)) catch return;
|
||||
var cell = plane.cell_init();
|
||||
_ = plane.at_cursor_cell(&cell) catch return;
|
||||
tui.set_cell_style(&cell, style);
|
||||
cell.set_style(style);
|
||||
_ = plane.putc(&cell) catch {};
|
||||
}
|
||||
|
||||
|
@ -203,9 +209,9 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
nc.event_type.RELEASE => self.mapRelease(keypress, modifiers),
|
||||
event_type.PRESS => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.REPEAT => self.mapPress(keypress, egc, modifiers),
|
||||
event_type.RELEASE => self.mapRelease(keypress, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -213,7 +219,7 @@ fn mapEvent(self: *Self, evtype: u32, keypress: u32, egc: u32, modifiers: u32) t
|
|||
fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
||||
const keynormal = if ('a' <= keypress and keypress <= 'z') keypress - ('a' - 'A') else keypress;
|
||||
return switch (modifiers) {
|
||||
nc.mod.CTRL => switch (keynormal) {
|
||||
mod.CTRL => switch (keynormal) {
|
||||
'J' => self.cmd("toggle_logview", .{}),
|
||||
'Q' => self.cmd("quit", .{}),
|
||||
'W' => self.cmd("close_file", .{}),
|
||||
|
@ -223,14 +229,14 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
|||
'V' => self.cmd("system_paste", .{}),
|
||||
'C' => self.cmd("exit_overlay_mode", .{}),
|
||||
'G' => self.cmd("exit_overlay_mode", .{}),
|
||||
nc.key.ESC => self.cmd("exit_overlay_mode", .{}),
|
||||
nc.key.UP => self.cmd("open_recent_menu_up", .{}),
|
||||
nc.key.DOWN => self.cmd("open_recent_menu_down", .{}),
|
||||
nc.key.ENTER => self.cmd("open_recent_menu_activate", .{}),
|
||||
nc.key.BACKSPACE => self.delete_word(),
|
||||
key.ESC => self.cmd("exit_overlay_mode", .{}),
|
||||
key.UP => self.cmd("open_recent_menu_up", .{}),
|
||||
key.DOWN => self.cmd("open_recent_menu_down", .{}),
|
||||
key.ENTER => self.cmd("open_recent_menu_activate", .{}),
|
||||
key.BACKSPACE => self.delete_word(),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.CTRL | nc.mod.SHIFT => switch (keynormal) {
|
||||
mod.CTRL | mod.SHIFT => switch (keynormal) {
|
||||
'Q' => self.cmd("quit_without_saving", .{}),
|
||||
'W' => self.cmd("close_file_without_saving", .{}),
|
||||
'R' => self.cmd("restart", .{}),
|
||||
|
@ -239,27 +245,27 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
|||
'E' => self.cmd("open_recent_menu_up", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.ALT => switch (keynormal) {
|
||||
mod.ALT => switch (keynormal) {
|
||||
'L' => self.cmd("toggle_logview", .{}),
|
||||
'I' => self.cmd("toggle_inputview", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.SHIFT => switch (keypress) {
|
||||
else => if (!nc.key.synthesized_p(keypress))
|
||||
mod.SHIFT => switch (keypress) {
|
||||
else => if (!key.synthesized_p(keypress))
|
||||
self.insert_code_point(egc)
|
||||
else {},
|
||||
},
|
||||
0 => switch (keypress) {
|
||||
nc.key.F09 => self.cmd("theme_prev", .{}),
|
||||
nc.key.F10 => self.cmd("theme_next", .{}),
|
||||
nc.key.F11 => self.cmd("toggle_logview", .{}),
|
||||
nc.key.F12 => self.cmd("toggle_inputview", .{}),
|
||||
nc.key.ESC => self.cmd("exit_overlay_mode", .{}),
|
||||
nc.key.UP => self.cmd("open_recent_menu_up", .{}),
|
||||
nc.key.DOWN => self.cmd("open_recent_menu_down", .{}),
|
||||
nc.key.ENTER => self.cmd("open_recent_menu_activate", .{}),
|
||||
nc.key.BACKSPACE => self.delete_code_point(),
|
||||
else => if (!nc.key.synthesized_p(keypress))
|
||||
key.F09 => self.cmd("theme_prev", .{}),
|
||||
key.F10 => self.cmd("theme_next", .{}),
|
||||
key.F11 => self.cmd("toggle_logview", .{}),
|
||||
key.F12 => self.cmd("toggle_inputview", .{}),
|
||||
key.ESC => self.cmd("exit_overlay_mode", .{}),
|
||||
key.UP => self.cmd("open_recent_menu_up", .{}),
|
||||
key.DOWN => self.cmd("open_recent_menu_down", .{}),
|
||||
key.ENTER => self.cmd("open_recent_menu_activate", .{}),
|
||||
key.BACKSPACE => self.delete_code_point(),
|
||||
else => if (!key.synthesized_p(keypress))
|
||||
self.insert_code_point(egc)
|
||||
else {},
|
||||
},
|
||||
|
@ -269,7 +275,7 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
|||
|
||||
fn mapRelease(self: *Self, keypress: u32, _: u32) tp.result {
|
||||
return switch (keypress) {
|
||||
nc.key.LCTRL, nc.key.RCTRL => if (self.menu.selected orelse 0 > 0) return self.cmd("open_recent_menu_activate", .{}),
|
||||
key.LCTRL, key.RCTRL => if (self.menu.selected orelse 0 > 0) return self.cmd("open_recent_menu_activate", .{}),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
@ -307,7 +313,7 @@ fn delete_code_point(self: *Self) tp.result {
|
|||
|
||||
fn insert_code_point(self: *Self, c: u32) tp.result {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = nc.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e);
|
||||
self.inputbox.text.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e);
|
||||
self.inputbox.cursor = self.inputbox.text.items.len;
|
||||
return self.start_query();
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
const Allocator = @import("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 Widget = @import("Widget.zig");
|
||||
const EventHandler = @import("EventHandler.zig");
|
||||
const tui = @import("tui.zig");
|
||||
|
||||
plane: nc.Plane,
|
||||
plane: Plane,
|
||||
pos_scrn: u32 = 0,
|
||||
view_scrn: u32 = 8,
|
||||
size_scrn: u32 = 8,
|
||||
|
@ -33,7 +36,7 @@ pub fn create(a: Allocator, parent: Widget, event_source: Widget) !Widget {
|
|||
|
||||
fn init(parent: Widget) !Self {
|
||||
return .{
|
||||
.plane = try nc.Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent.plane.*),
|
||||
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent.plane.*),
|
||||
.parent = parent,
|
||||
};
|
||||
}
|
||||
|
@ -63,21 +66,21 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
var y: i32 = undefined;
|
||||
var ypx: i32 = undefined;
|
||||
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) {
|
||||
if (try m.match(.{ "B", event_type.PRESS, key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) {
|
||||
self.active = true;
|
||||
self.move_to(y, ypx);
|
||||
return true;
|
||||
} else if (try m.match(.{ "B", nc.event_type.RELEASE, tp.more })) {
|
||||
} else if (try m.match(.{ "B", event_type.RELEASE, tp.more })) {
|
||||
self.active = false;
|
||||
return true;
|
||||
} else if (try m.match(.{ "D", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) {
|
||||
} else if (try m.match(.{ "D", event_type.PRESS, key.BUTTON1, tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) {
|
||||
self.active = true;
|
||||
self.move_to(y, ypx);
|
||||
return true;
|
||||
} else if (try m.match(.{ "B", nc.event_type.RELEASE, tp.more })) {
|
||||
} else if (try m.match(.{ "B", event_type.RELEASE, tp.more })) {
|
||||
self.active = false;
|
||||
return true;
|
||||
} else if (try m.match(.{ "D", nc.event_type.RELEASE, tp.more })) {
|
||||
} else if (try m.match(.{ "D", event_type.RELEASE, tp.more })) {
|
||||
self.active = false;
|
||||
return true;
|
||||
} else if (try m.match(.{ "H", tp.extract(&self.hover) })) {
|
||||
|
@ -118,7 +121,7 @@ fn pos_scrn_to_virt(self: Self, pos_scrn_: u32) u32 {
|
|||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
const frame = tracy.initZone(@src(), .{ .name = "scrollbar_v render" });
|
||||
defer frame.deinit();
|
||||
tui.set_base_style(&self.plane, " ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar);
|
||||
self.plane.set_base_style(" ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar);
|
||||
self.plane.erase();
|
||||
smooth_bar_at(self.plane, @intCast(self.pos_scrn), @intCast(self.view_scrn)) catch {};
|
||||
return false;
|
||||
|
@ -148,7 +151,7 @@ const eighths_b = [_][]const u8{ "█", "▇", "▆", "▅", "▄", "▃", "▂"
|
|||
const eighths_t = [_][]const u8{ " ", "▔", "🮂", "🮃", "▀", "🮄", "🮅", "🮆" };
|
||||
const eighths_c: i32 = @intCast(eighths_b.len);
|
||||
|
||||
fn smooth_bar_at(plane: nc.Plane, pos_: i32, size_: i32) !void {
|
||||
fn smooth_bar_at(plane: Plane, pos_: i32, size_: i32) !void {
|
||||
const height: i32 = @intCast(plane.dim_y());
|
||||
var size = @max(size_, 8);
|
||||
const pos: i32 = @min(height * eighths_c - size, pos_);
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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 {
|
||||
|
|
617
src/tui/tui.zig
617
src/tui/tui.zig
|
@ -1,14 +1,14 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const log = @import("log");
|
||||
const config = @import("config");
|
||||
const project_manager = @import("project_manager");
|
||||
const build_options = @import("build_options");
|
||||
const root = @import("root");
|
||||
|
||||
const tracy = @import("tracy");
|
||||
|
||||
pub const renderer = @import("renderer");
|
||||
|
||||
const command = @import("command.zig");
|
||||
const WidgetStack = @import("WidgetStack.zig");
|
||||
const Widget = @import("Widget.zig");
|
||||
|
@ -25,7 +25,7 @@ const Mutex = std.Thread.Mutex;
|
|||
const maxInt = std.math.maxInt;
|
||||
|
||||
a: Allocator,
|
||||
nc: nc.Context,
|
||||
rdr: renderer,
|
||||
config: config,
|
||||
frame_time: usize, // in microseconds
|
||||
frame_clock: tp.metronome,
|
||||
|
@ -43,32 +43,17 @@ mini_mode: ?MiniModeState = null,
|
|||
hover_focus: ?*Widget = null,
|
||||
commands: Commands = undefined,
|
||||
logger: log.Logger,
|
||||
drag: bool = false,
|
||||
drag_event: nc.Input = nc.input(),
|
||||
drag_source: ?*Widget = null,
|
||||
theme: Widget.Theme,
|
||||
escape_state: EscapeState = .none,
|
||||
escape_initial: ?nc.Input = null,
|
||||
escape_code: ArrayList(u8),
|
||||
bracketed_paste: bool = false,
|
||||
bracketed_paste_buffer: ArrayList(u8),
|
||||
idle_frame_count: usize = 0,
|
||||
unrendered_input_events_count: usize = 0,
|
||||
unflushed_events_count: usize = 0,
|
||||
init_timer: ?tp.timeout,
|
||||
sigwinch_signal: ?tp.signal = null,
|
||||
no_sleep: bool = false,
|
||||
mods: ModState = .{},
|
||||
final_exit: []const u8 = "normal",
|
||||
|
||||
const idle_frames = 1;
|
||||
|
||||
const ModState = struct {
|
||||
ctrl: bool = false,
|
||||
shift: bool = false,
|
||||
alt: bool = false,
|
||||
};
|
||||
|
||||
const init_delay = 1; // ms
|
||||
|
||||
const Self = @This();
|
||||
|
@ -91,20 +76,8 @@ fn start(args: StartArgs) tp.result {
|
|||
}
|
||||
|
||||
fn init(a: Allocator) !*Self {
|
||||
var opts = nc.Context.Options{
|
||||
.termtype = null,
|
||||
.loglevel = @intFromEnum(nc.LogLevel.silent),
|
||||
.margin_t = 0,
|
||||
.margin_r = 0,
|
||||
.margin_b = 0,
|
||||
.margin_l = 0,
|
||||
.flags = nc.Context.option.SUPPRESS_BANNERS | nc.Context.option.INHIBIT_SETLOCALE | nc.Context.option.NO_WINCH_SIGHANDLER,
|
||||
};
|
||||
if (tp.env.get().is("no-alternate"))
|
||||
opts.flags |= nc.Context.option.NO_ALTERNATE_SCREEN;
|
||||
const nc_ = try nc.Context.core_init(&opts, null);
|
||||
nc_.mice_enable(nc.mice.ALL_EVENTS) catch {};
|
||||
try nc_.linesigs_disable();
|
||||
var self = try a.create(Self);
|
||||
const ctx = try renderer.init(a, self, tp.env.get().is("no-alternate"));
|
||||
|
||||
var conf_buf: ?[]const u8 = null;
|
||||
var conf = root.read_config(a, &conf_buf);
|
||||
|
@ -120,18 +93,17 @@ fn init(a: Allocator) !*Self {
|
|||
const frame_time = std.time.us_per_s / conf.frame_rate;
|
||||
const frame_clock = try tp.metronome.init(frame_time);
|
||||
|
||||
const fd_stdin = try tp.file_descriptor.init("stdin", nc_.inputready_fd());
|
||||
const fd_stdin = try tp.file_descriptor.init("stdin", ctx.input_fd());
|
||||
// const fd_stdin = try tp.file_descriptor.init("stdin", std.os.STDIN_FILENO);
|
||||
const n = nc_.stdplane();
|
||||
const n = ctx.stdplane();
|
||||
|
||||
try frame_clock.start();
|
||||
try fd_stdin.wait_read();
|
||||
|
||||
var self = try a.create(Self);
|
||||
self.* = .{
|
||||
.a = a,
|
||||
.config = conf,
|
||||
.nc = nc_,
|
||||
.rdr = ctx,
|
||||
.frame_time = frame_time,
|
||||
.frame_clock = frame_clock,
|
||||
.frame_clock_running = true,
|
||||
|
@ -142,23 +114,23 @@ fn init(a: Allocator) !*Self {
|
|||
.input_mode = null,
|
||||
.input_listeners = EventHandler.List.init(a),
|
||||
.logger = log.logger("tui"),
|
||||
.escape_code = ArrayList(u8).init(a),
|
||||
.bracketed_paste_buffer = ArrayList(u8).init(a),
|
||||
.init_timer = try tp.timeout.init_ms(init_delay, tp.message.fmt(.{"init"})),
|
||||
.theme = theme,
|
||||
.no_sleep = tp.env.get().is("no-sleep"),
|
||||
};
|
||||
instance_ = self;
|
||||
defer instance_ = null;
|
||||
self.rdr.handler_ctx = self;
|
||||
self.rdr.dispatch_input = dispatch_input;
|
||||
self.rdr.dispatch_mouse = dispatch_mouse;
|
||||
self.rdr.dispatch_mouse_drag = dispatch_mouse_drag;
|
||||
self.rdr.dispatch_event = dispatch_event;
|
||||
try self.commands.init(self);
|
||||
errdefer self.deinit();
|
||||
try self.listen_sigwinch();
|
||||
self.mainview = try mainview.create(a, n);
|
||||
try self.initUI();
|
||||
try nc_.render();
|
||||
try ctx.render();
|
||||
try self.save_config();
|
||||
// self.request_mouse_cursor_support_detect();
|
||||
self.bracketed_paste_enable();
|
||||
if (tp.env.get().is("restore-session")) {
|
||||
command.executeName("restore_session", .{}) catch |e| self.logger.err("restore_session", e);
|
||||
self.logger.print("session restored", .{});
|
||||
|
@ -166,24 +138,11 @@ fn init(a: Allocator) !*Self {
|
|||
return self;
|
||||
}
|
||||
|
||||
pub fn initUI(self: *Self) !void {
|
||||
const n = self.nc.stdplane();
|
||||
var channels: u64 = 0;
|
||||
try nc.channels_set_fg_rgb(&channels, 0x88aa00);
|
||||
try nc.channels_set_bg_rgb(&channels, 0x000088);
|
||||
try nc.channels_set_bg_alpha(&channels, nc.ALPHA_TRANSPARENT);
|
||||
_ = try n.set_base(" ", 0, channels);
|
||||
try n.set_fg_rgb(0x40f040);
|
||||
try n.set_fg_rgb(0x00dddd);
|
||||
}
|
||||
|
||||
fn init_delayed(self: *Self) tp.result {
|
||||
if (self.input_mode) |_| {} else return cmds.enter_mode(self, command.Context.fmt(.{self.config.input_mode}));
|
||||
}
|
||||
|
||||
fn deinit(self: *Self) void {
|
||||
self.bracketed_paste_buffer.deinit();
|
||||
self.escape_code.deinit();
|
||||
if (self.input_mode) |*m| m.deinit();
|
||||
self.commands.deinit();
|
||||
self.fd_stdin.deinit();
|
||||
|
@ -194,7 +153,7 @@ fn deinit(self: *Self) void {
|
|||
self.frame_clock.stop() catch {};
|
||||
if (self.sigwinch_signal) |sig| sig.deinit();
|
||||
self.frame_clock.deinit();
|
||||
self.nc.stop();
|
||||
self.rdr.stop();
|
||||
self.logger.deinit();
|
||||
self.a.destroy(self);
|
||||
}
|
||||
|
@ -211,7 +170,7 @@ fn receive(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
|
|||
defer instance_ = null;
|
||||
errdefer self.deinit();
|
||||
errdefer self.fd_stdin.cancel() catch {};
|
||||
errdefer self.nc.leave_alternate_screen();
|
||||
errdefer self.rdr.leave_alternate_screen();
|
||||
self.receive_safe(from, m) catch |e| {
|
||||
if (std.mem.eql(u8, "normal", tp.error_text()))
|
||||
return e;
|
||||
|
@ -253,8 +212,8 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
|
|||
|
||||
if (try m.match(.{"sigwinch"})) {
|
||||
try self.listen_sigwinch();
|
||||
self.nc.refresh() catch |e| return self.logger.err("refresh", e);
|
||||
self.mainview.resize(Widget.Box.from(self.nc.stdplane()));
|
||||
self.rdr.refresh() catch |e| return self.logger.err("refresh", e);
|
||||
self.mainview.resize(Widget.Box.from(self.rdr.stdplane()));
|
||||
need_render();
|
||||
return;
|
||||
}
|
||||
|
@ -265,7 +224,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
|
|||
return;
|
||||
}
|
||||
|
||||
if (self.dispatch_input(m) catch |e| b: {
|
||||
if (self.dispatch_input_fd(m) catch |e| b: {
|
||||
self.logger.err("input dispatch", e);
|
||||
break :b true;
|
||||
})
|
||||
|
@ -341,14 +300,14 @@ fn render(self: *Self, current_time: i64) void {
|
|||
const more = ret: {
|
||||
const frame = tracy.initZone(@src(), .{ .name = "tui render" });
|
||||
defer frame.deinit();
|
||||
self.nc.stdplane().erase();
|
||||
self.rdr.stdplane().erase();
|
||||
break :ret self.mainview.render(&self.theme);
|
||||
};
|
||||
|
||||
{
|
||||
const frame = tracy.initZone(@src(), .{ .name = "notcurses render" });
|
||||
defer frame.deinit();
|
||||
self.nc.render() catch |e| self.logger.err("render", e);
|
||||
self.rdr.render() catch |e| self.logger.err("render", e);
|
||||
}
|
||||
|
||||
self.idle_frame_count = if (self.unrendered_input_events_count > 0)
|
||||
|
@ -370,14 +329,23 @@ fn render(self: *Self, current_time: i64) void {
|
|||
}
|
||||
}
|
||||
|
||||
fn dispatch_input(self: *Self, m: tp.message) error{Exit}!bool {
|
||||
fn dispatch_flush_input_event(self: *Self) tp.result {
|
||||
var buf: [32]u8 = undefined;
|
||||
if (self.input_mode) |mode|
|
||||
try mode.handler.send(tp.self_pid(), tp.message.fmtbuf(&buf, .{"F"}) catch |e| return tp.exit_error(e));
|
||||
}
|
||||
|
||||
fn dispatch_input_fd(self: *Self, m: tp.message) error{Exit}!bool {
|
||||
const frame = tracy.initZone(@src(), .{ .name = "tui input" });
|
||||
defer frame.deinit();
|
||||
var err: i64 = 0;
|
||||
var err_msg: []u8 = "";
|
||||
if (try m.match(.{ "fd", "stdin", "read_ready" })) {
|
||||
self.fd_stdin.wait_read() catch |e| return tp.exit_error(e);
|
||||
try self.dispatch_notcurses();
|
||||
self.rdr.process_input() catch |e| return tp.exit_error(e);
|
||||
try self.dispatch_flush_input_event();
|
||||
if (self.unrendered_input_events_count > 0 and !self.frame_clock_running)
|
||||
need_render();
|
||||
return true; // consume message
|
||||
}
|
||||
if (try m.match(.{ "fd", "stdin", "read_error", tp.extract(&err), tp.extract(&err_msg) })) {
|
||||
|
@ -386,199 +354,50 @@ fn dispatch_input(self: *Self, m: tp.message) error{Exit}!bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
fn dispatch_notcurses(self: *Self) tp.result {
|
||||
var input_buffer: [256]nc.Input = undefined;
|
||||
|
||||
while (true) {
|
||||
const nivec = self.nc.getvec_nblock(&input_buffer) catch |e| return tp.exit_error(e);
|
||||
if (nivec.len == 0)
|
||||
break;
|
||||
for (nivec) |*ni| {
|
||||
if (ni.id == 27 or self.escape_state != .none) {
|
||||
try self.handle_escape(ni);
|
||||
continue;
|
||||
}
|
||||
self.dispatch_input_event(ni) catch |e|
|
||||
self.logger.err("input dispatch", e);
|
||||
}
|
||||
}
|
||||
if (self.escape_state == .init)
|
||||
try self.handle_escape_short();
|
||||
if (self.unflushed_events_count > 0)
|
||||
_ = try self.dispatch_flush_input_event();
|
||||
if (self.unrendered_input_events_count > 0 and !self.frame_clock_running)
|
||||
need_render();
|
||||
}
|
||||
|
||||
fn dispatch_input_event(self: *Self, ni: *nc.Input) tp.result {
|
||||
const keypress: u32 = ni.id;
|
||||
var buf: [256]u8 = undefined;
|
||||
fn dispatch_input(ctx: *anyopaque, cbor_msg: []const u8) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
const m: tp.message = .{ .buf = cbor_msg };
|
||||
const from = tp.self_pid();
|
||||
self.unrendered_input_events_count += 1;
|
||||
ni.modifiers &= nc.mod.CTRL | nc.mod.SHIFT | nc.mod.ALT | nc.mod.SUPER | nc.mod.META | nc.mod.HYPER;
|
||||
if (keypress == nc.key.RESIZE) return;
|
||||
try self.sync_mod_state(keypress, ni.modifiers);
|
||||
if (keypress == nc.key.MOTION) {
|
||||
if (ni.y == 0 and ni.x == 0 and ni.ypx == -1 and ni.xpx == -1) return;
|
||||
self.dispatch_mouse(ni.y, ni.x, tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"M",
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
} else if (keypress > nc.key.MOTION and keypress <= nc.key.BUTTON11) {
|
||||
if (ni.y == 0 and ni.x == 0 and ni.ypx == -1 and ni.xpx == -1) return;
|
||||
if (try self.detect_drag(ni)) return;
|
||||
self.dispatch_mouse(ni.y, ni.x, tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"B",
|
||||
ni.evtype,
|
||||
keypress,
|
||||
nc.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
} else {
|
||||
self.unflushed_events_count += 1;
|
||||
self.send_input(tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"I",
|
||||
normalized_evtype(ni.evtype),
|
||||
keypress,
|
||||
if (@hasField(nc.Input, "eff_text")) ni.eff_text[0] else keypress,
|
||||
nc.key_string(ni),
|
||||
ni.modifiers,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_flush_input_event(self: *Self) error{Exit}!bool {
|
||||
var buf: [32]u8 = undefined;
|
||||
self.unflushed_events_count = 0;
|
||||
tp.trace(tp.channel.input, m);
|
||||
self.input_listeners.send(from, m) catch {};
|
||||
if (self.keyboard_focus) |w|
|
||||
if (w.send(from, m) catch |e| ret: {
|
||||
self.logger.err("focus", e);
|
||||
break :ret false;
|
||||
})
|
||||
return;
|
||||
if (self.input_mode) |mode|
|
||||
try mode.handler.send(tp.self_pid(), tp.message.fmtbuf(&buf, .{"F"}) catch |e| return tp.exit_error(e));
|
||||
return false;
|
||||
mode.handler.send(from, m) catch |e| self.logger.err("input handler", e);
|
||||
}
|
||||
|
||||
fn detect_drag(self: *Self, ni: *nc.Input) error{Exit}!bool {
|
||||
return switch (ni.id) {
|
||||
nc.key.BUTTON1...nc.key.BUTTON3, nc.key.BUTTON6...nc.key.BUTTON9 => if (self.drag) self.detect_drag_end(ni) else self.detect_drag_begin(ni),
|
||||
else => false,
|
||||
};
|
||||
fn dispatch_mouse(ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
const m: tp.message = .{ .buf = cbor_msg };
|
||||
const from = tp.self_pid();
|
||||
self.unrendered_input_events_count += 1;
|
||||
self.send_mouse(y, x, from, m) catch |e| self.logger.err("dispatch mouse", e);
|
||||
}
|
||||
|
||||
fn detect_drag_begin(self: *Self, ni: *nc.Input) error{Exit}!bool {
|
||||
if (ni.evtype == nc.event_type.PRESS and self.drag_event.id == ni.id) {
|
||||
self.drag_source = self.find_coord_widget(@intCast(self.drag_event.y), @intCast(self.drag_event.x));
|
||||
self.drag_event = ni.*;
|
||||
self.drag = true;
|
||||
var buf: [256]u8 = undefined;
|
||||
_ = try self.send_mouse_drag(ni.y, ni.x, tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"D",
|
||||
nc.event_type.PRESS,
|
||||
ni.id,
|
||||
nc.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
return true;
|
||||
}
|
||||
if (ni.evtype == nc.event_type.PRESS)
|
||||
self.drag_event = ni.*
|
||||
else
|
||||
self.drag_event = nc.input();
|
||||
return false;
|
||||
}
|
||||
|
||||
fn detect_drag_end(self: *Self, ni: *nc.Input) error{Exit}!bool {
|
||||
var buf: [256]u8 = undefined;
|
||||
if (ni.id == self.drag_event.id and ni.evtype != nc.event_type.PRESS) {
|
||||
_ = try self.send_mouse_drag(ni.y, ni.x, tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"D",
|
||||
nc.event_type.RELEASE,
|
||||
ni.id,
|
||||
nc.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
self.drag = false;
|
||||
self.drag_event = nc.input();
|
||||
fn dispatch_mouse_drag(ctx: *anyopaque, y: c_int, x: c_int, dragging: bool, cbor_msg: []const u8) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
const m: tp.message = .{ .buf = cbor_msg };
|
||||
const from = tp.self_pid();
|
||||
self.unrendered_input_events_count += 1;
|
||||
if (dragging) {
|
||||
if (self.drag_source == null) self.drag_source = self.find_coord_widget(@intCast(y), @intCast(x));
|
||||
} else {
|
||||
self.drag_source = null;
|
||||
return true;
|
||||
}
|
||||
_ = try self.send_mouse_drag(ni.y, ni.x, tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"D",
|
||||
ni.evtype,
|
||||
ni.id,
|
||||
nc.key_string(ni),
|
||||
ni.x,
|
||||
ni.y,
|
||||
ni.xpx,
|
||||
ni.ypx,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
return true;
|
||||
self.send_mouse_drag(y, x, from, m) catch |e| self.logger.err("dispatch mouse", e);
|
||||
}
|
||||
|
||||
fn normalized_evtype(evtype: c_uint) c_uint {
|
||||
return if (evtype == nc.event_type.UNKNOWN) @as(c_uint, @intCast(nc.event_type.PRESS)) else evtype;
|
||||
}
|
||||
|
||||
const EscapeState = enum { none, init, OSC, st, CSI };
|
||||
|
||||
fn handle_escape(self: *Self, ni: *nc.Input) tp.result {
|
||||
switch (self.escape_state) {
|
||||
.none => switch (ni.id) {
|
||||
'\x1B' => {
|
||||
self.escape_state = .init;
|
||||
self.escape_initial = ni.*;
|
||||
},
|
||||
else => unreachable,
|
||||
},
|
||||
.init => switch (ni.id) {
|
||||
']' => self.escape_state = .OSC,
|
||||
'[' => self.escape_state = .CSI,
|
||||
else => {
|
||||
try self.handle_escape_short();
|
||||
_ = try self.dispatch_input_event(ni);
|
||||
},
|
||||
},
|
||||
.OSC => switch (ni.id) {
|
||||
'\x1B' => self.escape_state = .st,
|
||||
'\\' => try self.handle_OSC_escape_code(),
|
||||
' '...'\\' - 1, '\\' + 1...127 => {
|
||||
const p = self.escape_code.addOne() catch |e| return tp.exit_error(e);
|
||||
p.* = @intCast(ni.id);
|
||||
},
|
||||
else => try self.handle_OSC_escape_code(),
|
||||
},
|
||||
.st => switch (ni.id) {
|
||||
'\\' => try self.handle_OSC_escape_code(),
|
||||
else => try self.handle_OSC_escape_code(),
|
||||
},
|
||||
.CSI => switch (ni.id) {
|
||||
'0'...'9', ';', ' ', '-', '?' => {
|
||||
const p = self.escape_code.addOne() catch |e| return tp.exit_error(e);
|
||||
p.* = @intCast(ni.id);
|
||||
},
|
||||
else => {
|
||||
const p = self.escape_code.addOne() catch |e| return tp.exit_error(e);
|
||||
p.* = @intCast(ni.id);
|
||||
try self.handle_CSI_escape_code();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_escape_short(self: *Self) tp.result {
|
||||
self.escape_code.clearAndFree();
|
||||
self.escape_state = .none;
|
||||
defer self.escape_initial = null;
|
||||
if (self.escape_initial) |*ni|
|
||||
_ = try self.dispatch_input_event(ni);
|
||||
fn dispatch_event(ctx: *anyopaque, cbor_msg: []const u8) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
const m: tp.message = .{ .buf = cbor_msg };
|
||||
self.unrendered_input_events_count += 1;
|
||||
self.dispatch_flush_input_event() catch |e| self.logger.err("dispatch event flush", e);
|
||||
tp.self_pid().send_raw(m) catch |e| self.logger.err("dispatch event", e);
|
||||
}
|
||||
|
||||
fn find_coord_widget(self: *Self, y: usize, x: usize) ?*Widget {
|
||||
|
@ -626,11 +445,6 @@ fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
self.mainview.send(from, m);
|
||||
}
|
||||
|
||||
fn dispatch_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) void {
|
||||
self.send_mouse(y, x, from, m) catch |e|
|
||||
self.logger.err("dispatch mouse", e);
|
||||
}
|
||||
|
||||
fn send_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result {
|
||||
tp.trace(tp.channel.input, m);
|
||||
_ = self.input_listeners.send(from, m) catch {};
|
||||
|
@ -656,12 +470,12 @@ fn send_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message)
|
|||
}
|
||||
}
|
||||
|
||||
fn send_mouse_drag(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
fn send_mouse_drag(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result {
|
||||
tp.trace(tp.channel.input, m);
|
||||
_ = self.input_listeners.send(from, m) catch {};
|
||||
if (self.keyboard_focus) |w| {
|
||||
_ = try w.send(from, m);
|
||||
return false;
|
||||
return;
|
||||
} else if (self.find_coord_widget(@intCast(y), @intCast(x))) |w| {
|
||||
if (if (self.hover_focus) |h| h != w else true) {
|
||||
var buf: [256]u8 = undefined;
|
||||
|
@ -679,70 +493,13 @@ fn send_mouse_drag(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.mess
|
|||
}
|
||||
self.hover_focus = null;
|
||||
}
|
||||
return if (self.drag_source) |w|
|
||||
w.send(from, m)
|
||||
else
|
||||
false;
|
||||
}
|
||||
|
||||
fn send_input(self: *Self, from: tp.pid_ref, m: tp.message) void {
|
||||
tp.trace(tp.channel.input, m);
|
||||
if (self.bracketed_paste and self.handle_bracketed_paste_input(m) catch |e| {
|
||||
self.bracketed_paste_buffer.clearAndFree();
|
||||
self.bracketed_paste = false;
|
||||
return self.logger.err("bracketed paste input handler", e);
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
self.input_listeners.send(from, m) catch {};
|
||||
if (self.keyboard_focus) |w|
|
||||
if (w.send(from, m) catch |e| ret: {
|
||||
self.logger.err("focus", e);
|
||||
break :ret false;
|
||||
})
|
||||
return;
|
||||
if (self.input_mode) |mode|
|
||||
mode.handler.send(from, m) catch |e| self.logger.err("input handler", e);
|
||||
if (self.drag_source) |w| _ = try w.send(from, m);
|
||||
}
|
||||
|
||||
pub fn save_config(self: *const Self) !void {
|
||||
try root.write_config(self.config, self.a);
|
||||
}
|
||||
|
||||
fn sync_mod_state(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
||||
if (keypress == nc.key.LCTRL or keypress == nc.key.RCTRL or keypress == nc.key.LALT or keypress == nc.key.RALT or
|
||||
keypress == nc.key.LSHIFT or keypress == nc.key.RSHIFT or keypress == nc.key.LSUPER or keypress == nc.key.RSUPER) return;
|
||||
if (nc.isCtrl(modifiers) and !self.mods.ctrl)
|
||||
try self.send_key(nc.event_type.PRESS, nc.key.LCTRL, "lctrl", modifiers);
|
||||
if (!nc.isCtrl(modifiers) and self.mods.ctrl)
|
||||
try self.send_key(nc.event_type.RELEASE, nc.key.LCTRL, "lctrl", modifiers);
|
||||
if (nc.isAlt(modifiers) and !self.mods.alt)
|
||||
try self.send_key(nc.event_type.PRESS, nc.key.LALT, "lalt", modifiers);
|
||||
if (!nc.isAlt(modifiers) and self.mods.alt)
|
||||
try self.send_key(nc.event_type.RELEASE, nc.key.LALT, "lalt", modifiers);
|
||||
if (nc.isShift(modifiers) and !self.mods.shift)
|
||||
try self.send_key(nc.event_type.PRESS, nc.key.LSHIFT, "lshift", modifiers);
|
||||
if (!nc.isShift(modifiers) and self.mods.shift)
|
||||
try self.send_key(nc.event_type.RELEASE, nc.key.LSHIFT, "lshift", modifiers);
|
||||
self.mods = .{
|
||||
.ctrl = nc.isCtrl(modifiers),
|
||||
.alt = nc.isAlt(modifiers),
|
||||
.shift = nc.isShift(modifiers),
|
||||
};
|
||||
}
|
||||
|
||||
fn send_key(self: *Self, event_type: c_int, keypress: u32, key_string: []const u8, modifiers: u32) tp.result {
|
||||
var buf: [256]u8 = undefined;
|
||||
self.send_input(tp.self_pid(), tp.message.fmtbuf(&buf, .{
|
||||
"I",
|
||||
event_type,
|
||||
keypress,
|
||||
keypress,
|
||||
key_string,
|
||||
modifiers,
|
||||
}) catch |e| return tp.exit_error(e));
|
||||
}
|
||||
|
||||
const cmds = struct {
|
||||
pub const Target = Self;
|
||||
const Ctx = command.Context;
|
||||
|
@ -757,13 +514,13 @@ const cmds = struct {
|
|||
var buf: [256]u8 = undefined;
|
||||
var buf_parent: [256]u8 = undefined;
|
||||
var z: i32 = 0;
|
||||
var n = self.nc.stdplane();
|
||||
var n = self.rdr.stdplane();
|
||||
while (n.below()) |n_| : (n = n_) {
|
||||
z -= 1;
|
||||
l.print("{d} {s} {s}", .{ z, n_.name(&buf), n_.parent().name(&buf_parent) });
|
||||
}
|
||||
z = 0;
|
||||
n = self.nc.stdplane();
|
||||
n = self.rdr.stdplane();
|
||||
while (n.above()) |n_| : (n = n_) {
|
||||
z += 1;
|
||||
l.print("{d} {s} {s}", .{ z, n_.name(&buf), n_.parent().name(&buf_parent) });
|
||||
|
@ -925,238 +682,6 @@ pub fn current() *Self {
|
|||
return if (instance_) |p| p else @panic("tui call out of context");
|
||||
}
|
||||
|
||||
const OSC = "\x1B]"; // Operating System Command
|
||||
const ST = "\x1B\\"; // String Terminator
|
||||
const BEL = "\x07";
|
||||
const OSC0_title = OSC ++ "0;";
|
||||
const OSC52_clipboard = OSC ++ "52;c;";
|
||||
const OSC52_clipboard_paste = OSC ++ "52;p;";
|
||||
const OSC22_cursor = OSC ++ "22;";
|
||||
const OSC22_cursor_reply = OSC ++ "22:";
|
||||
|
||||
pub fn set_terminal_title(text: []const u8) void {
|
||||
var writer = std.io.getStdOut().writer();
|
||||
var buf: [std.posix.PATH_MAX]u8 = undefined;
|
||||
const term_cmd = std.fmt.bufPrint(&buf, OSC0_title ++ "{s}" ++ BEL, .{text}) catch return;
|
||||
_ = writer.write(term_cmd) catch return;
|
||||
}
|
||||
|
||||
pub fn copy_to_system_clipboard(self: *const Self, text: []const u8) void {
|
||||
self.copy_to_system_clipboard_with_errors(text) catch |e| self.logger.err("copy_to_system_clipboard", e);
|
||||
}
|
||||
|
||||
pub fn copy_to_system_clipboard_with_errors(self: *const Self, text: []const u8) !void {
|
||||
var writer = std.io.getStdOut().writer();
|
||||
const encoder = std.base64.standard.Encoder;
|
||||
const size = OSC52_clipboard.len + encoder.calcSize(text.len) + ST.len;
|
||||
const buf = try self.a.alloc(u8, size);
|
||||
defer self.a.free(buf);
|
||||
@memcpy(buf[0..OSC52_clipboard.len], OSC52_clipboard);
|
||||
const b64 = encoder.encode(buf[OSC52_clipboard.len..], text);
|
||||
@memcpy(buf[OSC52_clipboard.len + b64.len ..], ST);
|
||||
_ = try writer.write(buf);
|
||||
}
|
||||
|
||||
pub fn write_stdout(self: *const Self, bytes: []const u8) void {
|
||||
_ = std.io.getStdOut().writer().write(bytes) catch |e| self.logger.err("stdout", e);
|
||||
}
|
||||
|
||||
pub fn request_system_clipboard(self: *const Self) void {
|
||||
self.write_stdout(OSC52_clipboard ++ "?" ++ ST);
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_text(self: *const Self, push_or_pop: bool) void {
|
||||
if (push_or_pop) self.mouse_cursor_push("text") else self.mouse_cursor_pop();
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_pointer(self: *const Self, push_or_pop: bool) void {
|
||||
if (push_or_pop) self.mouse_cursor_push("pointer") else self.mouse_cursor_pop();
|
||||
}
|
||||
|
||||
pub fn request_mouse_cursor_default(self: *const Self, push_or_pop: bool) void {
|
||||
if (push_or_pop) self.mouse_cursor_push("default") else self.mouse_cursor_pop();
|
||||
}
|
||||
|
||||
fn mouse_cursor_push(self: *const Self, comptime name: []const u8) void {
|
||||
self.write_stdout(OSC22_cursor ++ name ++ ST);
|
||||
}
|
||||
|
||||
fn mouse_cursor_pop(self: *const Self) void {
|
||||
self.write_stdout(OSC22_cursor ++ "default" ++ ST);
|
||||
}
|
||||
|
||||
fn match_code(self: *const Self, match: []const u8, skip: usize) bool {
|
||||
const code = self.escape_code.items;
|
||||
if (!(code.len >= match.len - skip)) return false;
|
||||
const code_prefix = code[0 .. match.len - skip];
|
||||
return std.mem.eql(u8, match[skip..], code_prefix);
|
||||
}
|
||||
|
||||
fn handle_OSC_escape_code(self: *Self) tp.result {
|
||||
self.escape_state = .none;
|
||||
self.escape_initial = null;
|
||||
defer self.escape_code.clearAndFree();
|
||||
const code = self.escape_code.items;
|
||||
if (self.match_code(OSC52_clipboard, OSC.len))
|
||||
return self.handle_system_clipboard(code[OSC52_clipboard.len - OSC.len ..]);
|
||||
if (self.match_code(OSC52_clipboard_paste, OSC.len))
|
||||
return self.handle_system_clipboard(code[OSC52_clipboard_paste.len - OSC.len ..]);
|
||||
if (self.match_code(OSC22_cursor_reply, OSC.len))
|
||||
return self.handle_mouse_cursor(code[OSC22_cursor_reply.len - OSC.len ..]);
|
||||
self.logger.print("ignored escape code: OSC {s}", .{std.fmt.fmtSliceEscapeLower(code)});
|
||||
}
|
||||
|
||||
fn handle_system_clipboard(self: *Self, base64: []const u8) tp.result {
|
||||
const decoder = std.base64.standard.Decoder;
|
||||
// try self.logger.print("clipboard: b64 {s}", .{base64});
|
||||
const text = self.a.alloc(
|
||||
u8,
|
||||
decoder.calcSizeForSlice(base64) catch |e| return tp.exit_error(e),
|
||||
) catch |e| return tp.exit_error(e);
|
||||
decoder.decode(text, base64) catch |e| return tp.exit_error(e);
|
||||
// try self.logger.print("clipboard: txt {s}", .{std.fmt.fmtSliceEscapeLower(text)});
|
||||
return tp.self_pid().send(.{ "system_clipboard", text });
|
||||
}
|
||||
|
||||
fn handle_mouse_cursor(self: *Self, text: []const u8) tp.result {
|
||||
self.logger.print("mouse cursor report: {s}", .{text});
|
||||
}
|
||||
|
||||
const CSI = "\x1B["; // Control Sequence Introducer
|
||||
const CSI_bracketed_paste_enable = CSI ++ "?2004h";
|
||||
const CSI_bracketed_paste_disable = CSI ++ "?2004h";
|
||||
const CIS_bracketed_paste_begin = CSI ++ "200~";
|
||||
const CIS_bracketed_paste_end = CSI ++ "201~";
|
||||
|
||||
fn handle_CSI_escape_code(self: *Self) tp.result {
|
||||
self.escape_state = .none;
|
||||
self.escape_initial = null;
|
||||
defer self.escape_code.clearAndFree();
|
||||
const code = self.escape_code.items;
|
||||
if (self.match_code(CIS_bracketed_paste_begin, CSI.len))
|
||||
return self.handle_bracketed_paste_begin();
|
||||
if (self.match_code(CIS_bracketed_paste_end, CSI.len))
|
||||
return self.handle_bracketed_paste_end();
|
||||
self.logger.print("ignored escape code: CSI {s}", .{std.fmt.fmtSliceEscapeLower(code)});
|
||||
}
|
||||
|
||||
fn handle_bracketed_paste_begin(self: *Self) tp.result {
|
||||
_ = try self.dispatch_flush_input_event();
|
||||
self.bracketed_paste_buffer.clearAndFree();
|
||||
self.bracketed_paste = true;
|
||||
}
|
||||
|
||||
fn handle_bracketed_paste_input(self: *Self, m: tp.message) !bool {
|
||||
var keypress: u32 = undefined;
|
||||
var egc: u32 = undefined;
|
||||
if (try m.match(.{ "I", tp.number, tp.extract(&keypress), tp.extract(&egc), tp.string, 0 })) {
|
||||
switch (keypress) {
|
||||
nc.key.ENTER => try self.bracketed_paste_buffer.appendSlice("\n"),
|
||||
else => if (!nc.key.synthesized_p(keypress)) {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = try nc.ucs32_to_utf8(&[_]u32{egc}, &buf);
|
||||
try self.bracketed_paste_buffer.appendSlice(buf[0..bytes]);
|
||||
} else {
|
||||
try self.handle_bracketed_paste_end();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn handle_bracketed_paste_end(self: *Self) tp.result {
|
||||
defer self.bracketed_paste_buffer.clearAndFree();
|
||||
if (!self.bracketed_paste) return;
|
||||
self.bracketed_paste = false;
|
||||
return tp.self_pid().send(.{ "system_clipboard", self.bracketed_paste_buffer.items });
|
||||
}
|
||||
|
||||
fn bracketed_paste_enable(self: *const Self) void {
|
||||
self.write_stdout(CSI_bracketed_paste_enable);
|
||||
}
|
||||
|
||||
fn bracketed_paste_disable(self: *const Self) void {
|
||||
self.write_stdout(CSI_bracketed_paste_disable);
|
||||
}
|
||||
|
||||
pub inline fn fg_channels_from_style(channels: *u64, style: Widget.Theme.Style) void {
|
||||
if (style.fg) |fg| {
|
||||
nc.channels_set_fg_rgb(channels, fg) catch {};
|
||||
nc.channels_set_fg_alpha(channels, nc.ALPHA_OPAQUE) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn bg_channels_from_style(channels: *u64, style: Widget.Theme.Style) void {
|
||||
if (style.bg) |bg| {
|
||||
nc.channels_set_bg_rgb(channels, bg) catch {};
|
||||
nc.channels_set_bg_alpha(channels, nc.ALPHA_OPAQUE) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn channels_from_style(channels: *u64, style: Widget.Theme.Style) void {
|
||||
fg_channels_from_style(channels, style);
|
||||
bg_channels_from_style(channels, style);
|
||||
}
|
||||
|
||||
pub inline fn set_cell_style(cell: *nc.Cell, style: Widget.Theme.Style) void {
|
||||
channels_from_style(&cell.channels, style);
|
||||
if (style.fs) |fs| switch (fs) {
|
||||
.normal => nc.cell_set_styles(cell, nc.style.none),
|
||||
.bold => nc.cell_set_styles(cell, nc.style.bold),
|
||||
.italic => nc.cell_set_styles(cell, nc.style.italic),
|
||||
.underline => nc.cell_set_styles(cell, nc.style.underline),
|
||||
.undercurl => nc.cell_set_styles(cell, nc.style.undercurl),
|
||||
.strikethrough => nc.cell_set_styles(cell, nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn set_cell_style_fg(cell: *nc.Cell, style: Widget.Theme.Style) void {
|
||||
fg_channels_from_style(&cell.channels, style);
|
||||
}
|
||||
|
||||
pub inline fn set_cell_style_bg(cell: *nc.Cell, style: Widget.Theme.Style) void {
|
||||
bg_channels_from_style(&cell.channels, style);
|
||||
}
|
||||
|
||||
pub inline fn set_base_style(plane: *const nc.Plane, egc: [*c]const u8, style: Widget.Theme.Style) void {
|
||||
var channels: u64 = 0;
|
||||
channels_from_style(&channels, style);
|
||||
if (style.fg) |fg| plane.set_fg_rgb(fg) catch {};
|
||||
if (style.bg) |bg| plane.set_bg_rgb(bg) catch {};
|
||||
_ = plane.set_base(egc, 0, channels) catch {};
|
||||
}
|
||||
|
||||
pub fn set_base_style_alpha(plane: nc.Plane, egc: [*:0]const u8, style: Widget.Theme.Style, fg_alpha: c_uint, bg_alpha: c_uint) !void {
|
||||
var channels: u64 = 0;
|
||||
if (style.fg) |fg| {
|
||||
nc.channels_set_fg_rgb(&channels, fg) catch {};
|
||||
nc.channels_set_fg_alpha(&channels, fg_alpha) catch {};
|
||||
}
|
||||
if (style.bg) |bg| {
|
||||
nc.channels_set_bg_rgb(&channels, bg) catch {};
|
||||
nc.channels_set_bg_alpha(&channels, bg_alpha) catch {};
|
||||
}
|
||||
if (style.fg) |fg| plane.set_fg_rgb(fg) catch {};
|
||||
if (style.bg) |bg| plane.set_bg_rgb(bg) catch {};
|
||||
_ = plane.set_base(egc, 0, channels) catch {};
|
||||
}
|
||||
|
||||
pub inline fn set_style(plane: *const nc.Plane, style: Widget.Theme.Style) void {
|
||||
var channels: u64 = 0;
|
||||
channels_from_style(&channels, style);
|
||||
plane.set_channels(channels);
|
||||
if (style.fs) |fs| switch (fs) {
|
||||
.normal => plane.set_styles(nc.style.none),
|
||||
.bold => plane.set_styles(nc.style.bold),
|
||||
.italic => plane.set_styles(nc.style.italic),
|
||||
.underline => plane.set_styles(nc.style.underline),
|
||||
.undercurl => plane.set_styles(nc.style.undercurl),
|
||||
.strikethrough => plane.set_styles(nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_mode() []const u8 {
|
||||
return if (current().input_mode) |m| m.name else "INI";
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue