This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160const std = @import("std");const terminal = @import("../terminal/main.zig");const State = @import("State.zig");
/// Available cursor styles for drawing that renderers must support./// This is a superset of terminal cursor styles since the renderer supports/// some additional cursor states such as the hollow block.pub const Style = enum { // Typical cursor input styles block, block_hollow, bar, underline,
// Special cursor styles lock,
/// Create a cursor style from the terminal style request. pub fn fromTerminal(term: terminal.CursorStyle) ?Style { return switch (term) { .bar => .bar, .block => .block, .block_hollow => .block_hollow, .underline => .underline, }; }};
/// Returns the cursor style to use for the current render state or null/// if a cursor should not be rendered at all.pub fn style( state: *State, focused: bool, blink_visible: bool,) ?Style { // Note the order of conditionals below is important. It represents // a priority system of how we determine what state overrides cursor // visibility and style.
// The cursor is only at the bottom of the viewport. If we aren't // at the bottom, we never render the cursor. The cursor x/y is by // viewport so if we are above the viewport, we'll end up rendering // the cursor in some random part of the screen. if (!state.terminal.screen.viewportIsBottom()) return null;
// If we are in preedit, then we always show the block cursor. We do // this even if the cursor is explicitly not visible because it shows // an important editing state to the user. if (state.preedit != null) return .block;
// If the cursor is explicitly not visible by terminal mode, we don't render. if (!state.terminal.modes.get(.cursor_visible)) return null;
// If we're not focused, our cursor is always visible so that // we can show the hollow box. if (!focused) return .block_hollow;
// If the cursor is blinking and our blink state is not visible, // then we don't show the cursor. if (state.terminal.modes.get(.cursor_blinking) and !blink_visible) { return null; }
// Otherwise, we use whatever style the terminal wants. return .fromTerminal(state.terminal.screen.cursor.cursor_style);}
test "cursor: default uses configured style" { const testing = std.testing; const alloc = testing.allocator; var term = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 10 }); defer term.deinit(alloc);
term.screen.cursor.cursor_style = .bar; term.modes.set(.cursor_blinking, true);
var state: State = .{ .mutex = undefined, .terminal = &term, .preedit = null, };
try testing.expect(style(&state, true, true) == .bar); try testing.expect(style(&state, false, true) == .block_hollow); try testing.expect(style(&state, false, false) == .block_hollow); try testing.expect(style(&state, true, false) == null);}
test "cursor: blinking disabled" { const testing = std.testing; const alloc = testing.allocator; var term = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 10 }); defer term.deinit(alloc);
term.screen.cursor.cursor_style = .bar; term.modes.set(.cursor_blinking, false);
var state: State = .{ .mutex = undefined, .terminal = &term, .preedit = null, };
try testing.expect(style(&state, true, true) == .bar); try testing.expect(style(&state, true, false) == .bar); try testing.expect(style(&state, false, true) == .block_hollow); try testing.expect(style(&state, false, false) == .block_hollow);}
test "cursor: explicitly not visible" { const testing = std.testing; const alloc = testing.allocator; var term = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 10 }); defer term.deinit(alloc);
term.screen.cursor.cursor_style = .bar; term.modes.set(.cursor_visible, false); term.modes.set(.cursor_blinking, false);
var state: State = .{ .mutex = undefined, .terminal = &term, .preedit = null, };
try testing.expect(style(&state, true, true) == null); try testing.expect(style(&state, true, false) == null); try testing.expect(style(&state, false, true) == null); try testing.expect(style(&state, false, false) == null);}
test "cursor: always block with preedit" { const testing = std.testing; const alloc = testing.allocator; var term = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 10 }); defer term.deinit(alloc);
var state: State = .{ .mutex = undefined, .terminal = &term, .preedit = .{}, };
// In any bool state try testing.expect(style(&state, false, false) == .block); try testing.expect(style(&state, true, false) == .block); try testing.expect(style(&state, true, true) == .block); try testing.expect(style(&state, false, true) == .block);
// If we're scrolled though, then we don't show the cursor. for (0..100) |_| try term.index(); try term.scrollViewport(.{ .top = {} });
// In any bool state try testing.expect(style(&state, false, false) == null); try testing.expect(style(&state, true, false) == null); try testing.expect(style(&state, true, true) == null); try testing.expect(style(&state, false, true) == null);}