From b0d32f35816737d36fb52d0073b4d49cbd4bd9fa Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 7 Apr 2026 11:30:37 +0200 Subject: [PATCH] refactor(tui): centralize native cursor check in has_native_cursor() --- src/tui/InputBox.zig | 2 +- src/tui/editor.zig | 10 +++++----- src/tui/mode/overlay/completion_dropdown.zig | 2 +- src/tui/status/filestate.zig | 2 +- src/tui/terminal_view.zig | 2 +- src/tui/tui.zig | 8 ++++++-- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/tui/InputBox.zig b/src/tui/InputBox.zig index 8a06b094..3a4cd8af 100644 --- a/src/tui/InputBox.zig +++ b/src/tui/InputBox.zig @@ -51,7 +51,7 @@ pub fn Options(context: type) type { } if (self.cursor) |cursor| { const pos: c_int = @intCast(cursor); - if (tui.config().enable_terminal_cursor) { + if (tui.has_native_cursor()) { const y, const x = self.plane.rel_yx_to_abs(0, pos + self.opts.padding + self.icon_width); tui.rdr().cursor_enable(y, x, tui.get_cursor_shape()) catch {}; } else { diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 9f49c1a9..23bd9f60 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -376,7 +376,7 @@ pub const Editor = struct { animation_lag: f64, animation_last_time: i64, - enable_terminal_cursor: bool, + software_rendered_cursor: bool, render_whitespace: WhitespaceMode, indent_size: usize, tab_width: usize, @@ -639,7 +639,7 @@ pub const Editor = struct { .animation_frame_rate = frame_rate, .animation_last_time = time.microTimestamp(), .enable_format_on_save = tui.config().enable_format_on_save, - .enable_terminal_cursor = tui.config().enable_terminal_cursor, + .software_rendered_cursor = !tui.has_native_cursor(), .render_whitespace = tui.config().whitespace_mode, }; self.add_default_symbol_triggers(); @@ -1307,7 +1307,7 @@ pub const Editor = struct { fn render_cursors(self: *Self, theme: *const Widget.Theme, cell_map: CellMap, focused: bool) !void { const frame = tracy.initZone(@src(), .{ .name = "editor render cursors" }); defer frame.deinit(); - if (focused and tui.config().enable_terminal_cursor and tui.rdr().vx.caps.multi_cursor) + if (focused and !self.software_rendered_cursor and tui.rdr().vx.caps.multi_cursor) tui.rdr().clear_all_multi_cursors() catch {}; for (self.cursels.items[0 .. self.cursels.items.len - 1]) |*cursel_| if (cursel_.*) |*cursel| { const cursor = cursel.cursor; @@ -1332,7 +1332,7 @@ pub const Editor = struct { const focused = focused_ or self.cursor_focus_override; - if (focused and self.enable_terminal_cursor) { + if (focused and !self.software_rendered_cursor) { if (screen_pos) |pos| { self.render_term_cursor(pos, cursor_shape); } else if (tui.is_mainview_focused() and tui.rdr().vx.caps.multi_cursor and self.has_secondary_cursors()) { @@ -1345,7 +1345,7 @@ pub const Editor = struct { fn render_cursor_secondary(self: *Self, cursor: *const Cursor, theme: *const Widget.Theme, cell_map: CellMap, focused: bool) !void { const pos = self.screen_cursor(cursor) orelse return; set_cell_map_cursor(cell_map, pos.row, pos.col); - if (focused and self.enable_terminal_cursor and tui.rdr().vx.caps.multi_cursor) + if (focused and !self.software_rendered_cursor and tui.rdr().vx.caps.multi_cursor) self.render_term_cursor_secondary(pos) else self.render_soft_cursor(pos, theme.editor_cursor_secondary); diff --git a/src/tui/mode/overlay/completion_dropdown.zig b/src/tui/mode/overlay/completion_dropdown.zig index a9a20786..94fc7e05 100644 --- a/src/tui/mode/overlay/completion_dropdown.zig +++ b/src/tui/mode/overlay/completion_dropdown.zig @@ -200,7 +200,7 @@ pub fn on_render_menu(self: *Type, button: *Type.ButtonType, theme: *const Widge const icon_: []const u8 = values.kind.icon(); const color: u24 = 0x0; - if (tui.config().enable_terminal_cursor) blk: { + if (tui.has_native_cursor()) blk: { const cursor = self.value.editor.get_primary_abs() orelse break :blk; tui.rdr().cursor_enable(@intCast(cursor.row), @intCast(cursor.col), tui.get_cursor_shape()) catch {}; } diff --git a/src/tui/status/filestate.zig b/src/tui/status/filestate.zig index 3425d59a..337e5cac 100644 --- a/src/tui/status/filestate.zig +++ b/src/tui/status/filestate.zig @@ -117,7 +117,7 @@ fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void { _ = plane.putstr_unicode(mini_mode.text) catch {}; if (mini_mode.cursor) |cursor| { const pos: c_int = @intCast(cursor); - if (tui.config().enable_terminal_cursor) { + if (tui.has_native_cursor()) { const y, const x = plane.rel_yx_to_abs(0, pos + 1); tui.rdr().cursor_enable(y, x, tui.get_cursor_shape()) catch {}; } else { diff --git a/src/tui/terminal_view.zig b/src/tui/terminal_view.zig index 8b80de76..7d79aace 100644 --- a/src/tui/terminal_view.zig +++ b/src/tui/terminal_view.zig @@ -379,7 +379,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { } // Blit the terminal's front screen into our vaxis.Window. - const software_cursor = build_options.gui or !tui.config().enable_terminal_cursor; + const software_cursor = !tui.has_native_cursor(); const focused_cursor_color: ?[3]u8 = if (theme.editor_cursor.bg) |bg| RGB.to_u8s(RGB.from_u24(bg.color)) else null; const unfocused_cursor_color: ?[3]u8 = if (theme.editor_cursor_secondary.bg) |bg| RGB.to_u8s(RGB.from_u24(bg.color)) else focused_cursor_color; self.vt.vt.draw(self.allocator, self.plane.window, self.focused and tui.terminal_has_focus(), software_cursor, focused_cursor_color, unfocused_cursor_color) catch |e| { diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 6626cf4d..502ae979 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -164,7 +164,6 @@ fn init(allocator: Allocator) InitError!*Self { conf.theme = dark_theme.name; const light_theme = Widget.get_theme_by_name(allocator, conf.light_theme) orelse Widget.get_theme_by_name(allocator, "default-light") orelse return error.UnknownTheme; conf.light_theme = light_theme.name; - if (build_options.gui) conf.enable_terminal_cursor = false; const frame_rate: usize = @intCast(tp.env.get().num("frame-rate")); if (frame_rate != 0) @@ -659,7 +658,7 @@ fn render(self: *Self) void { defer frame.deinit(); self.rdr_.stdplane().erase(); const theme_ = self.current_theme(); - if (self.config_.enable_terminal_cursor) { + if (has_native_cursor()) { self.rdr_.cursor_disable(); if (self.rdr_.vx.caps.multi_cursor) self.rdr_.clear_all_multi_cursors() catch {}; self.rdr_.set_terminal_cursor_color(theme_.editor_cursor.bg.?); @@ -2190,6 +2189,11 @@ pub fn is_cursor_beam() bool { }; } +pub fn has_native_cursor() bool { + if (build_options.gui) return false; + return current().config_.enable_terminal_cursor; +} + pub fn get_selection_style() @import("Buffer").Selection.Style { return if (current().input_mode_) |mode| mode.selection_style else .normal; }