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 {
|
pub const InternalCell = struct {
|
||||||
char: std.ArrayList(u8) = undefined,
|
char: std.ArrayList(u8) = undefined,
|
||||||
style: Style = .{},
|
style: Style = .{},
|
||||||
|
// if we got skipped because of a wide character
|
||||||
|
skipped: bool = false,
|
||||||
|
|
||||||
pub fn eql(self: InternalCell, cell: Cell) bool {
|
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);
|
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_col: usize = 0,
|
||||||
cursor_vis: bool = false,
|
cursor_vis: bool = false,
|
||||||
|
|
||||||
|
unicode: bool = false,
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !Screen {
|
pub fn init(alloc: std.mem.Allocator, w: usize, h: usize) !Screen {
|
||||||
var self = Screen{
|
var self = Screen{
|
||||||
.buf = try alloc.alloc(Cell, w * h),
|
.buf = try alloc.alloc(Cell, w * h),
|
||||||
|
|
|
@ -178,13 +178,17 @@ pub fn run(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.cap_kitty_keyboard => {
|
.cap_kitty_keyboard => {
|
||||||
|
log.info("kitty capability detected", .{});
|
||||||
vx.caps.kitty_keyboard = true;
|
vx.caps.kitty_keyboard = true;
|
||||||
},
|
},
|
||||||
.cap_rgb => {
|
.cap_rgb => {
|
||||||
|
log.info("rgb capability detected", .{});
|
||||||
vx.caps.rgb = true;
|
vx.caps.rgb = true;
|
||||||
},
|
},
|
||||||
.cap_unicode => {
|
.cap_unicode => {
|
||||||
|
log.info("unicode capability detected", .{});
|
||||||
vx.caps.unicode = true;
|
vx.caps.unicode = true;
|
||||||
|
vx.screen.unicode = true;
|
||||||
},
|
},
|
||||||
.cap_da1 => {
|
.cap_da1 => {
|
||||||
std.Thread.Futex.wake(&vx.query_futex, 10);
|
std.Thread.Futex.wake(&vx.query_futex, 10);
|
||||||
|
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||||
|
|
||||||
const Screen = @import("Screen.zig");
|
const Screen = @import("Screen.zig");
|
||||||
const Cell = @import("cell.zig").Cell;
|
const Cell = @import("cell.zig").Cell;
|
||||||
|
const gw = @import("gwidth.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.window);
|
const log = std.log.scoped(.window);
|
||||||
|
|
||||||
|
@ -72,6 +73,13 @@ pub fn clear(self: Window) void {
|
||||||
self.fill(.{});
|
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
|
/// fills the window with the provided cell
|
||||||
pub fn fill(self: Window, cell: Cell) void {
|
pub fn fill(self: Window, cell: Cell) void {
|
||||||
var row: usize = self.y_off;
|
var row: usize = self.y_off;
|
||||||
|
|
|
@ -12,6 +12,7 @@ const Window = @import("Window.zig");
|
||||||
const Options = @import("Options.zig");
|
const Options = @import("Options.zig");
|
||||||
const Style = @import("cell.zig").Style;
|
const Style = @import("cell.zig").Style;
|
||||||
const strWidth = @import("ziglyph").display_width.strWidth;
|
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
|
/// 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
|
/// 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 });
|
log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
|
||||||
self.screen.deinit(alloc);
|
self.screen.deinit(alloc);
|
||||||
self.screen = try Screen.init(alloc, winsize.cols, winsize.rows);
|
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);
|
// try self.screen.int(alloc, winsize.cols, winsize.rows);
|
||||||
// we only init our current screen. This has the effect of redrawing
|
// we only init our current screen. This has the effect of redrawing
|
||||||
// every cell
|
// every cell
|
||||||
|
@ -270,12 +272,14 @@ pub fn Vaxis(comptime T: type) type {
|
||||||
const cell = self.screen.buf[i];
|
const cell = self.screen.buf[i];
|
||||||
defer {
|
defer {
|
||||||
// advance by the width of this char mod 1
|
// advance by the width of this char mod 1
|
||||||
const width = blk: {
|
const method: gwidth.Method = if (self.caps.unicode) .unicode else .wcwidth;
|
||||||
if (cell.char.width > 0) break :blk cell.char.width;
|
const w = gwidth.gwidth(cell.char.grapheme, method) catch 1;
|
||||||
break :blk strWidth(cell.char.grapheme, .half) catch 1;
|
var j = i + 1;
|
||||||
};
|
while (j < i + w) : (j += 1) {
|
||||||
col += width;
|
self.screen_last.buf[j].skipped = true;
|
||||||
i += width;
|
}
|
||||||
|
col += w;
|
||||||
|
i += w;
|
||||||
}
|
}
|
||||||
if (col >= self.screen.width) {
|
if (col >= self.screen.width) {
|
||||||
row += 1;
|
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
|
// If cell is the same as our last frame, we don't need to do
|
||||||
// anything
|
// 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;
|
reposition = true;
|
||||||
// Close any osc8 sequence we might be in before
|
// Close any osc8 sequence we might be in before
|
||||||
// repositioning
|
// repositioning
|
||||||
|
@ -292,6 +297,7 @@ pub fn Vaxis(comptime T: type) type {
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
self.screen_last.buf[i].skipped = false;
|
||||||
defer cursor = cell.style;
|
defer cursor = cell.style;
|
||||||
// Set this cell in the last frame
|
// Set this cell in the last frame
|
||||||
self.screen_last.writeCell(col, row, cell.char.grapheme, cell.style);
|
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 Key = @import("../Key.zig");
|
||||||
const Window = @import("../Window.zig");
|
const Window = @import("../Window.zig");
|
||||||
const GraphemeIterator = @import("ziglyph").GraphemeIterator;
|
const GraphemeIterator = @import("ziglyph").GraphemeIterator;
|
||||||
const strWidth = @import("ziglyph").display_width.strWidth;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.text_input);
|
const log = std.log.scoped(.text_input);
|
||||||
|
|
||||||
|
@ -67,7 +66,7 @@ pub fn draw(self: *TextInput, win: Window) void {
|
||||||
var cursor_idx: usize = 0;
|
var cursor_idx: usize = 0;
|
||||||
while (iter.next()) |grapheme| {
|
while (iter.next()) |grapheme| {
|
||||||
const g = grapheme.slice(self.buf.items);
|
const g = grapheme.slice(self.buf.items);
|
||||||
const w = strWidth(g, .half) catch 1;
|
const w = win.gwidth(g);
|
||||||
win.writeCell(col, 0, .{
|
win.writeCell(col, 0, .{
|
||||||
.char = .{
|
.char = .{
|
||||||
.grapheme = g,
|
.grapheme = g,
|
||||||
|
|
Loading…
Reference in a new issue