Files
ghostty/src/renderer/cursor.zig
Qwerasd 2384bd69cc style: use decl literals
This commit changes a LOT of areas of the code to use decl literals
instead of redundantly referring to the type.

These changes were mostly driven by some regex searches and then manual
adjustment on a case-by-case basis.

I almost certainly missed quite a few places where decl literals could
be used, but this is a good first step in converting things, and other
instances can be addressed when they're discovered.

I tested GLFW+Metal and building the framework on macOS and tested a GTK
build on Linux, so I'm 99% sure I didn't introduce any syntax errors or
other problems with this. (fingers crossed)
2025-05-26 21:50:14 -06:00

160 lines
5.4 KiB
Zig

const 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);
}