1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::backend::Backend;
use crate::layout::Position;
use crate::terminal::Terminal;
impl<B: Backend> Terminal<B> {
/// Hides the cursor.
///
/// When using [`Terminal::draw`] / [`Terminal::try_draw`], prefer controlling the cursor with
/// [`Frame::set_cursor_position`]. A later successful [`Terminal::draw`] /
/// [`Terminal::try_draw`] call may overwrite this change.
///
/// [`Frame::set_cursor_position`]: crate::terminal::Frame::set_cursor_position
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
/// [`Terminal::try_draw`]: crate::terminal::Terminal::try_draw
pub fn hide_cursor(&mut self) -> Result<(), B::Error> {
self.backend.hide_cursor()?;
self.hidden_cursor = true;
Ok(())
}
/// Shows the cursor.
///
/// When using [`Terminal::draw`] / [`Terminal::try_draw`], prefer controlling the cursor with
/// [`Frame::set_cursor_position`]. A later successful [`Terminal::draw`] /
/// [`Terminal::try_draw`] call may overwrite this change.
///
/// [`Frame::set_cursor_position`]: crate::terminal::Frame::set_cursor_position
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
/// [`Terminal::try_draw`]: crate::terminal::Terminal::try_draw
pub fn show_cursor(&mut self) -> Result<(), B::Error> {
self.backend.show_cursor()?;
self.hidden_cursor = false;
Ok(())
}
/// Gets the current cursor position.
///
/// This queries the backend for the current cursor position and returns it as an `(x, y)`
/// tuple.
#[deprecated = "use `get_cursor_position()` instead which returns `Result<Position>`"]
pub fn get_cursor(&mut self) -> Result<(u16, u16), B::Error> {
let Position { x, y } = self.get_cursor_position()?;
Ok((x, y))
}
/// Sets the cursor position.
#[deprecated = "use `set_cursor_position((x, y))` instead which takes `impl Into<Position>`"]
pub fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), B::Error> {
self.set_cursor_position(Position { x, y })
}
/// Gets the current cursor position.
///
/// This queries the backend for the current cursor position. It is not limited to Ratatui's
/// last render pass, so direct backend mutations may also affect the returned value.
///
/// When using [`Terminal::draw`] / [`Terminal::try_draw`], prefer controlling the cursor with
/// [`Frame::set_cursor_position`]. For direct control, see [`Terminal::set_cursor_position`].
///
/// [`Frame::set_cursor_position`]: crate::terminal::Frame::set_cursor_position
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
/// [`Terminal::try_draw`]: crate::terminal::Terminal::try_draw
pub fn get_cursor_position(&mut self) -> Result<Position, B::Error> {
self.backend.get_cursor_position()
}
/// Sets the cursor position.
///
/// This updates the backend cursor and Ratatui's internal cursor tracking. Inline viewports
/// use that tracking when recomputing the viewport on resize.
///
/// When using [`Terminal::draw`] / [`Terminal::try_draw`], consider using
/// [`Frame::set_cursor_position`] instead so the cursor is updated as part of the normal
/// rendering flow. A later successful
/// [`Terminal::draw`] / [`Terminal::try_draw`] call may overwrite a direct cursor move.
///
/// [`Frame::set_cursor_position`]: crate::terminal::Frame::set_cursor_position
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
/// [`Terminal::try_draw`]: crate::terminal::Terminal::try_draw
pub fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> Result<(), B::Error> {
let position = position.into();
self.backend.set_cursor_position(position)?;
self.last_known_cursor_pos = position;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::backend::{Backend, TestBackend};
use crate::layout::Position;
use crate::terminal::Terminal;
#[test]
fn hide_cursor_updates_terminal_state() {
let backend = TestBackend::new(10, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal.hide_cursor().unwrap();
assert!(terminal.hidden_cursor);
assert!(!terminal.backend().cursor_visible());
}
#[test]
fn show_cursor_updates_terminal_state() {
let backend = TestBackend::new(10, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal.hide_cursor().unwrap();
terminal.show_cursor().unwrap();
assert!(!terminal.hidden_cursor);
assert!(terminal.backend().cursor_visible());
}
#[test]
fn set_cursor_position_updates_backend_and_tracking() {
let backend = TestBackend::new(10, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal.set_cursor_position((3, 4)).unwrap();
assert_eq!(terminal.last_known_cursor_pos, Position { x: 3, y: 4 });
terminal
.backend_mut()
.assert_cursor_position(Position { x: 3, y: 4 });
}
#[test]
fn get_cursor_position_queries_backend() {
let backend = TestBackend::new(10, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.backend_mut()
.set_cursor_position(Position { x: 7, y: 2 })
.unwrap();
assert_eq!(
terminal.get_cursor_position().unwrap(),
Position { x: 7, y: 2 }
);
}
#[test]
#[allow(deprecated)]
fn deprecated_cursor_wrappers_delegate_to_position_apis() {
let backend = TestBackend::new(10, 5);
let mut terminal = Terminal::new(backend).unwrap();
terminal.set_cursor(4, 1).unwrap();
assert_eq!(terminal.get_cursor().unwrap(), (4, 1));
assert_eq!(terminal.last_known_cursor_pos, Position { x: 4, y: 1 });
terminal
.backend_mut()
.assert_cursor_position(Position { x: 4, y: 1 });
}
}