diff --git a/src/InternalScreen.zig b/src/InternalScreen.zig index ea0e949..02d7f10 100644 --- a/src/InternalScreen.zig +++ b/src/InternalScreen.zig @@ -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); diff --git a/src/Screen.zig b/src/Screen.zig index 32440c8..16920a4 100644 --- a/src/Screen.zig +++ b/src/Screen.zig @@ -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), diff --git a/src/Tty.zig b/src/Tty.zig index 44fd86f..66a6c8d 100644 --- a/src/Tty.zig +++ b/src/Tty.zig @@ -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); diff --git a/src/Window.zig b/src/Window.zig index 05002fa..b6e3d9d 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -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; diff --git a/src/vaxis.zig b/src/vaxis.zig index 563a2f4..2ebd96b 100644 --- a/src/vaxis.zig +++ b/src/vaxis.zig @@ -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); diff --git a/src/widgets/TextInput.zig b/src/widgets/TextInput.zig index 74966d5..b1f15c6 100644 --- a/src/widgets/TextInput.zig +++ b/src/widgets/TextInput.zig @@ -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,