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}