ailloli_ui_terminal_core/cursor.rs
1//! Terminal cursor position, visibility, and presentation shape.
2
3use serde::{Deserialize, Serialize};
4
5use crate::size::TerminalSize;
6
7/// Requested cursor rendering shape.
8///
9/// # Examples
10///
11/// ```
12/// use ailloli_ui_terminal_core::TerminalCursorShape;
13/// assert_ne!(TerminalCursorShape::Block, TerminalCursorShape::Bar);
14/// ```
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16pub enum TerminalCursorShape {
17 /// Full-cell block.
18 Block,
19 /// Horizontal underline.
20 Underline,
21 /// Vertical insertion bar.
22 Bar,
23}
24
25/// Zero-based cursor position and presentation state.
26///
27/// Public fields and deserialization may place the cursor out of bounds; call
28/// [`Self::clamp_to`] before indexing a screen.
29///
30/// # Examples
31///
32/// ```
33/// use ailloli_ui_terminal_core::{TerminalCursor, TerminalCursorShape};
34/// let cursor = TerminalCursor::new();
35/// assert_eq!((cursor.row, cursor.col, cursor.visible, cursor.shape), (0, 0, true, TerminalCursorShape::Block));
36/// ```
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38pub struct TerminalCursor {
39 /// Zero-based screen row.
40 pub row: usize,
41 /// Zero-based screen column.
42 pub col: usize,
43 /// Whether the renderer should show the cursor.
44 pub visible: bool,
45 /// Requested renderer shape.
46 pub shape: TerminalCursorShape,
47}
48
49impl TerminalCursor {
50 /// Creates a visible block cursor at row zero, column zero.
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// use ailloli_ui_terminal_core::TerminalCursor;
56 /// assert_eq!((TerminalCursor::new().row, TerminalCursor::new().col), (0, 0));
57 /// ```
58 pub const fn new() -> Self {
59 Self {
60 row: 0,
61 col: 0,
62 visible: true,
63 shape: TerminalCursorShape::Block,
64 }
65 }
66
67 /// Clamps row and column into a terminal size.
68 ///
69 /// Zero dimensions are first clamped to one, so the result is always a
70 /// valid `(row, col)` for [`TerminalSize::clamped`]. Visibility and shape
71 /// are unchanged.
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use ailloli_ui_terminal_core::{TerminalCursor, TerminalSize};
77 /// let mut cursor = TerminalCursor { row: 9, col: 9, ..TerminalCursor::new() };
78 /// cursor.clamp_to(TerminalSize { rows: 2, cols: 3 });
79 /// assert_eq!((cursor.row, cursor.col), (1, 2));
80 /// ```
81 pub fn clamp_to(&mut self, size: TerminalSize) {
82 let size = size.clamped();
83 self.row = self.row.min(size.rows - 1);
84 self.col = self.col.min(size.cols - 1);
85 }
86}
87
88impl Default for TerminalCursor {
89 fn default() -> Self {
90 Self::new()
91 }
92}