vaxis: use vt caps to measure grapheme widths
Implement our own grapheme measuring function which switches on whether the terminal supports mode 2027 Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
parent
04f6117cfe
commit
9f89ed06f3
6 changed files with 30 additions and 9 deletions
|
@ -10,6 +10,8 @@ const InternalScreen = @This();
|
|||
pub const InternalCell = struct {
|
||||
char: std.ArrayList(u8) = undefined,
|
||||
style: Style = .{},
|
||||
// if we got skipped because of a wide character
|
||||
skipped: bool = false,
|
||||
|
||||
pub fn eql(self: InternalCell, cell: Cell) bool {
|
||||
return std.mem.eql(u8, self.char.items, cell.char.grapheme) and std.meta.eql(self.style, cell.style);
|
||||
|
|
|
@ -16,6 +16,8 @@ cursor_row: usize = 0,
|
|||
cursor_col: usize = 0,
|
||||
cursor_vis: bool = false,
|
||||
|
||||
unicode: bool = false,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !Screen {
|
||||
var self = Screen{
|
||||
.buf = try alloc.alloc(Cell, w * h),
|
||||
|
|
|
@ -178,13 +178,17 @@ pub fn run(
|
|||
}
|
||||
},
|
||||
.cap_kitty_keyboard => {
|
||||
log.info("kitty capability detected", .{});
|
||||
vx.caps.kitty_keyboard = true;
|
||||
},
|
||||
.cap_rgb => {
|
||||
log.info("rgb capability detected", .{});
|
||||
vx.caps.rgb = true;
|
||||
},
|
||||
.cap_unicode => {
|
||||
log.info("unicode capability detected", .{});
|
||||
vx.caps.unicode = true;
|
||||
vx.screen.unicode = true;
|
||||
},
|
||||
.cap_da1 => {
|
||||
std.Thread.Futex.wake(&vx.query_futex, 10);
|
||||
|
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
|
||||
const Screen = @import("Screen.zig");
|
||||
const Cell = @import("cell.zig").Cell;
|
||||
const gw = @import("gwidth.zig");
|
||||
|
||||
const log = std.log.scoped(.window);
|
||||
|
||||
|
@ -72,6 +73,13 @@ pub fn clear(self: Window) void {
|
|||
self.fill(.{});
|
||||
}
|
||||
|
||||
/// returns the width of the grapheme. This depends on the terminal capabilities
|
||||
pub fn gwidth(self: Window, str: []const u8) usize {
|
||||
const m: gw.Method = if (self.screen.unicode) .unicode else .wcwidth;
|
||||
log.info("using method {any}", .{m});
|
||||
return gw.gwidth(str, m) catch 1;
|
||||
}
|
||||
|
||||
/// fills the window with the provided cell
|
||||
pub fn fill(self: Window, cell: Cell) void {
|
||||
var row: usize = self.y_off;
|
||||
|
|
|
@ -12,6 +12,7 @@ const Window = @import("Window.zig");
|
|||
const Options = @import("Options.zig");
|
||||
const Style = @import("cell.zig").Style;
|
||||
const strWidth = @import("ziglyph").display_width.strWidth;
|
||||
const gwidth = @import("gwidth.zig");
|
||||
|
||||
/// Vaxis is the entrypoint for a Vaxis application. The provided type T should
|
||||
/// be a tagged union which contains all of the events the application will
|
||||
|
@ -141,6 +142,7 @@ pub fn Vaxis(comptime T: type) type {
|
|||
log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
|
||||
self.screen.deinit(alloc);
|
||||
self.screen = try Screen.init(alloc, winsize.cols, winsize.rows);
|
||||
self.screen.unicode = self.caps.unicode;
|
||||
// try self.screen.int(alloc, winsize.cols, winsize.rows);
|
||||
// we only init our current screen. This has the effect of redrawing
|
||||
// every cell
|
||||
|
@ -270,12 +272,14 @@ pub fn Vaxis(comptime T: type) type {
|
|||
const cell = self.screen.buf[i];
|
||||
defer {
|
||||
// advance by the width of this char mod 1
|
||||
const width = blk: {
|
||||
if (cell.char.width > 0) break :blk cell.char.width;
|
||||
break :blk strWidth(cell.char.grapheme, .half) catch 1;
|
||||
};
|
||||
col += width;
|
||||
i += width;
|
||||
const method: gwidth.Method = if (self.caps.unicode) .unicode else .wcwidth;
|
||||
const w = gwidth.gwidth(cell.char.grapheme, method) catch 1;
|
||||
var j = i + 1;
|
||||
while (j < i + w) : (j += 1) {
|
||||
self.screen_last.buf[j].skipped = true;
|
||||
}
|
||||
col += w;
|
||||
i += w;
|
||||
}
|
||||
if (col >= self.screen.width) {
|
||||
row += 1;
|
||||
|
@ -283,7 +287,8 @@ pub fn Vaxis(comptime T: type) type {
|
|||
}
|
||||
// If cell is the same as our last frame, we don't need to do
|
||||
// anything
|
||||
if (!self.refresh and self.screen_last.buf[i].eql(cell)) {
|
||||
const last = self.screen_last.buf[i];
|
||||
if (!self.refresh and last.eql(cell) and !last.skipped) {
|
||||
reposition = true;
|
||||
// Close any osc8 sequence we might be in before
|
||||
// repositioning
|
||||
|
@ -292,6 +297,7 @@ pub fn Vaxis(comptime T: type) type {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
self.screen_last.buf[i].skipped = false;
|
||||
defer cursor = cell.style;
|
||||
// Set this cell in the last frame
|
||||
self.screen_last.writeCell(col, row, cell.char.grapheme, cell.style);
|
||||
|
|
|
@ -3,7 +3,6 @@ const Cell = @import("../cell.zig").Cell;
|
|||
const Key = @import("../Key.zig");
|
||||
const Window = @import("../Window.zig");
|
||||
const GraphemeIterator = @import("ziglyph").GraphemeIterator;
|
||||
const strWidth = @import("ziglyph").display_width.strWidth;
|
||||
|
||||
const log = std.log.scoped(.text_input);
|
||||
|
||||
|
@ -67,7 +66,7 @@ pub fn draw(self: *TextInput, win: Window) void {
|
|||
var cursor_idx: usize = 0;
|
||||
while (iter.next()) |grapheme| {
|
||||
const g = grapheme.slice(self.buf.items);
|
||||
const w = strWidth(g, .half) catch 1;
|
||||
const w = win.gwidth(g);
|
||||
win.writeCell(col, 0, .{
|
||||
.char = .{
|
||||
.grapheme = g,
|
||||
|
|
Loading…
Reference in a new issue