dtee/model/cursor.rs
1//! # Cursor
2
3/// Cursor shapes.
4#[derive(Debug, Copy, Clone, Eq, PartialEq)]
5pub enum CursorShape {
6 /// Cursor is represented as a vertical caret, similar to `│`.
7 Caret,
8 /// Cursor is represented as a block, similar to `█`.
9 Block,
10 /// Cursor is represented as an underscore, similar to `_`.
11 UnderScore,
12}
13
14/// Cursor position and shape.
15#[derive(Debug, Copy, Clone, Eq, PartialEq)]
16pub struct Cursor {
17 /// Cursor shape.
18 shape: CursorShape,
19 /// Horizontal position of the cursor (column).
20 column: usize,
21 /// Vertical position of the cursor (row).
22 row: usize,
23}
24
25impl Cursor {
26 pub fn new(shape: CursorShape, column: usize, row: usize) -> Self {
27 Self { shape, column, row }
28 }
29
30 pub fn shape(&self) -> CursorShape {
31 self.shape
32 }
33
34 /// Returns the cursor position as a tuple `(column, row)`.
35 pub fn pos(&self) -> (usize, usize) {
36 (self.column, self.row)
37 }
38
39 pub fn set(&mut self, col: usize, row: usize) {
40 self.column = col;
41 self.row = row;
42 }
43
44 pub fn set_col(&mut self, col: usize) {
45 self.column = col;
46 }
47
48 pub fn col(&self) -> usize {
49 self.column
50 }
51
52 pub fn inc_col(&mut self, value: usize) {
53 self.column = self.column.saturating_add(value);
54 }
55
56 pub fn dec_col(&mut self, value: usize) {
57 self.column = self.column.saturating_sub(value);
58 }
59
60 pub fn row(&self) -> usize {
61 self.row
62 }
63
64 pub fn set_row(&mut self, row: usize) {
65 self.row = row;
66 }
67
68 pub fn inc_row(&mut self, value: usize) {
69 self.row = self.row.saturating_add(value);
70 }
71
72 pub fn dec_row(&mut self, value: usize) {
73 self.row = self.row.saturating_sub(value);
74 }
75
76 /// Calculates the cursor position after applying specified offsets.
77 pub fn offset(&self, column_offset: isize, row_offset: isize) -> (usize, usize) {
78 (
79 if column_offset < 0 {
80 self.column.saturating_sub(column_offset.unsigned_abs())
81 } else {
82 self.column.saturating_add(column_offset as usize)
83 },
84 if row_offset < 0 {
85 self.row.saturating_sub(row_offset.unsigned_abs())
86 } else {
87 self.row.saturating_add(row_offset as usize)
88 },
89 )
90 }
91
92 /// Returns `true` when the current cursor shape is a bar (`│`).
93 pub fn is_caret(&self) -> bool {
94 matches!(self.shape, CursorShape::Caret)
95 }
96
97 /// Returns `true` when the current cursor shape is a block (`█`).
98 pub fn is_block(&self) -> bool {
99 matches!(self.shape, CursorShape::Block)
100 }
101
102 /// Returns `true` when the current cursor shape is an underscore (`_`).
103 pub fn is_under_score(&self) -> bool {
104 matches!(self.shape, CursorShape::UnderScore)
105 }
106
107 /// Returns `true` when the cursor is signaling the inserting mode.
108 pub fn insert_mode(&self) -> bool {
109 self.shape == CursorShape::Caret
110 }
111
112 /// Returns `true` when the cursor is signaling the overriding mode.
113 pub fn override_mode(&self) -> bool {
114 self.shape != CursorShape::Caret
115 }
116
117 /// Toggles the cursor shape between caret and block.
118 ///
119 /// Returns the cursor shape after toggling.
120 ///
121 /// # Examples
122 ///
123 /// ```
124 /// use dtee::{Cursor, CursorShape};
125 ///
126 /// let mut cursor = Cursor::new(CursorShape::Caret, 1, 1);
127 /// assert_eq!(true, cursor.is_caret());
128 ///
129 /// assert_eq!(CursorShape::Block, cursor.toggle_caret_block());
130 /// assert_eq!(CursorShape::Caret, cursor.toggle_caret_block());
131 ///
132 /// let mut cursor = Cursor::new(CursorShape::UnderScore, 1, 1);
133 /// assert_eq!(true, cursor.is_under_score());
134 ///
135 /// assert_eq!(CursorShape::Block, cursor.toggle_caret_block());
136 /// assert_eq!(CursorShape::Caret, cursor.toggle_caret_block());
137 /// ```
138 pub fn toggle_caret_block(&mut self) -> CursorShape {
139 match self.shape {
140 CursorShape::Caret | CursorShape::UnderScore => self.shape = CursorShape::Block,
141 _ => self.shape = CursorShape::Caret,
142 }
143 self.shape
144 }
145
146 /// Toggles the cursor shape between caret and underscore.
147 ///
148 /// Returns the cursor shape after toggling.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use dtee::{Cursor, CursorShape};
154 ///
155 /// let mut cursor = Cursor::new(CursorShape::Caret, 1, 1);
156 /// assert_eq!(true, cursor.is_caret());
157 ///
158 /// assert_eq!(CursorShape::UnderScore, cursor.toggle_caret_under_score());
159 /// assert_eq!(CursorShape::Caret, cursor.toggle_caret_under_score());
160 ///
161 /// let mut cursor = Cursor::new(CursorShape::Block, 1, 1);
162 /// assert_eq!(true, cursor.is_block());
163 ///
164 /// assert_eq!(CursorShape::UnderScore, cursor.toggle_caret_under_score());
165 /// assert_eq!(CursorShape::Caret, cursor.toggle_caret_under_score());
166 /// ```
167 pub fn toggle_caret_under_score(&mut self) -> CursorShape {
168 match self.shape {
169 CursorShape::Caret | CursorShape::Block => self.shape = CursorShape::UnderScore,
170 _ => self.shape = CursorShape::Caret,
171 }
172 self.shape
173 }
174}