From 5b2ecf33811459a6796967feb00ecc4da457833d Mon Sep 17 00:00:00 2001 From: Timon Date: Fri, 29 Nov 2019 17:46:50 +0100 Subject: [PATCH] Introduced more cursor commands. (#327) --- CHANGELOG.md | 1 + src/cursor.rs | 102 ++++++++++++++++++++++++++++++-------- src/cursor/ansi.rs | 12 +++++ src/cursor/sys.rs | 4 +- src/cursor/sys/windows.rs | 49 +++++++++++++++++- src/lib.rs | 3 +- 6 files changed, 146 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c670101..e048dea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ documentation * Replace `docs/UPGRADE.md` with the [Upgrade Paths](https://github.com/crossterm-rs/crossterm/wiki#upgrade-paths) documentation + - Add `MoveToColumn`, `MoveToPreviousLine`, `MoveToNextLine` commands # Version 0.13.3 diff --git a/src/cursor.rs b/src/cursor.rs index 02e89e2..bf985f2 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -72,6 +72,68 @@ impl Command for MoveTo { } } +/// A command that moves the terminal cursor up the given number of lines, +/// and moves it to the first column. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +pub struct MoveToNextLine(u16); + +impl Command for MoveToNextLine { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::move_to_next_line_csi_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_to_next_line(self.0) + } +} + +/// A command that moves the terminal cursor down the given number of lines, +/// and moves it to the first column. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +pub struct MoveToPreviousLine(u16); + +impl Command for MoveToPreviousLine { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::move_to_previous_line_csi_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_to_previous_line(self.0) + } +} + +/// A command that moves the terminal cursor to the given column on the current row. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +pub struct MoveToColumn(u16); + +impl Command for MoveToColumn { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::move_to_column_csi_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_to_column(self.0) + } +} + /// A command that moves the terminal cursor a given number of rows up. /// /// # Notes @@ -92,6 +154,26 @@ impl Command for MoveUp { } } +/// A command that moves the terminal cursor a given number of columns to the right. +/// +/// # Notes +/// +/// Commands must be executed/queued for execution otherwise they do nothing. +pub struct MoveRight(pub u16); + +impl Command for MoveRight { + type AnsiType = String; + + fn ansi_code(&self) -> Self::AnsiType { + ansi::move_right_csi_sequence(self.0) + } + + #[cfg(windows)] + fn execute_winapi(&self) -> Result<()> { + sys::move_right(self.0) + } +} + /// A command that moves the terminal cursor a given number of rows down. /// /// # Notes @@ -132,13 +214,6 @@ impl Command for MoveLeft { } } -/// A command that moves the terminal cursor a given number of columns to the right. -/// -/// # Notes -/// -/// Commands must be executed/queued for execution otherwise they do nothing. -pub struct MoveRight(pub u16); - /// A command that saves the current terminal cursor position. /// /// See the [RestorePosition](./struct.RestorePosition.html) command. @@ -149,19 +224,6 @@ pub struct MoveRight(pub u16); /// - Commands must be executed/queued for execution otherwise they do nothing. pub struct SavePosition; -impl Command for MoveRight { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi::move_right_csi_sequence(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - sys::move_right(self.0) - } -} - impl Command for SavePosition { type AnsiType = &'static str; diff --git a/src/cursor/ansi.rs b/src/cursor/ansi.rs index 071c1cb..d256569 100644 --- a/src/cursor/ansi.rs +++ b/src/cursor/ansi.rs @@ -22,6 +22,18 @@ pub(crate) fn move_left_csi_sequence(count: u16) -> String { format!(csi!("{}D"), count) } +pub(crate) fn move_to_column_csi_sequence(count: u16) -> String { + format!(csi!("{}G"), count) +} + +pub(crate) fn move_to_previous_line_csi_sequence(count: u16) -> String { + format!(csi!("{}F"), count) +} + +pub(crate) fn move_to_next_line_csi_sequence(count: u16) -> String { + format!(csi!("{}E"), count) +} + pub(crate) const SAVE_POSITION_CSI_SEQUENCE: &str = "\x1B7"; pub(crate) const RESTORE_POSITION_CSI_SEQUENCE: &str = "\x1B8"; pub(crate) const HIDE_CSI_SEQUENCE: &str = csi!("?25l"); diff --git a/src/cursor/sys.rs b/src/cursor/sys.rs index a013e50..149375d 100644 --- a/src/cursor/sys.rs +++ b/src/cursor/sys.rs @@ -6,8 +6,8 @@ pub use self::unix::position; pub use self::windows::position; #[cfg(windows)] pub(crate) use self::windows::{ - move_down, move_left, move_right, move_to, move_up, restore_position, save_position, - show_cursor, + move_down, move_left, move_right, move_to, move_to_column, move_to_next_line, + move_to_previous_line, move_up, restore_position, save_position, show_cursor, }; #[cfg(windows)] diff --git a/src/cursor/sys/windows.rs b/src/cursor/sys/windows.rs index d27167a..bd965ab 100644 --- a/src/cursor/sys/windows.rs +++ b/src/cursor/sys/windows.rs @@ -58,6 +58,24 @@ pub(crate) fn move_left(count: u16) -> Result<()> { Ok(()) } +pub(crate) fn move_to_column(new_column: u16) -> Result<()> { + let (_, row) = position()?; + move_to(new_column, row)?; + Ok(()) +} + +pub(crate) fn move_to_next_line(count: u16) -> Result<()> { + let (_, row) = position()?; + move_to(0, row + count)?; + Ok(()) +} + +pub(crate) fn move_to_previous_line(count: u16) -> Result<()> { + let (_, row) = position()?; + move_to(0, row - count)?; + Ok(()) +} + pub(crate) fn save_position() -> Result<()> { ScreenBufferCursor::output()?.save_position()?; Ok(()) @@ -166,8 +184,8 @@ impl From for ScreenBufferCursor { #[cfg(test)] mod tests { use super::{ - move_down, move_left, move_right, move_to, move_up, position, restore_position, - save_position, + move_down, move_left, move_right, move_to, move_to_column, move_to_next_line, + move_to_previous_line, move_up, position, restore_position, save_position, }; #[test] @@ -206,6 +224,33 @@ mod tests { assert_eq!(position().unwrap(), (0, 0)); } + #[test] + fn test_move_to_next_line_winapi() { + move_to(0, 2).unwrap(); + + move_to_next_line(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 4)); + } + + #[test] + fn test_move_to_previous_line_winapi() { + move_to(0, 2).unwrap(); + + move_to_previous_line(2).unwrap(); + + assert_eq!(position().unwrap(), (0, 0)); + } + + #[test] + fn test_move_to_column_winapi() { + move_to(0, 2).unwrap(); + + move_to_column(12).unwrap(); + + assert_eq!(position().unwrap(), (12, 2)); + } + #[test] fn test_move_down_winapi() { move_to(0, 0).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 487c85e..9e8abb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,8 @@ //! [`SavePosition`](cursor/struct.SavePosition.html), [`RestorePosition`](cursor/struct.RestorePosition.html), //! [`MoveUp`](cursor/struct.MoveUp.html), [`MoveDown`](cursor/struct.MoveDown.html), //! [`MoveLeft`](cursor/struct.MoveLeft.html), [`MoveRight`](cursor/struct.MoveRight.html), -//! [`MoveTo`](cursor/struct.MoveTo.html) +//! [`MoveTo`](cursor/struct.MoveTo.html), [`MoveToColumn`](cursor/struct.MoveToColumn.html), +//! [`MoveToNextLine`](cursor/struct.MoveToNextLine.html), [`MoveToPreviousLine`](cursor/struct.MoveToPreviousLine.html), //! - Module `event` //! - Mouse events - [`EnableMouseCapture`](event/struct.EnableMouseCapture.html), //! [`DisableMouseCapture`](event/struct.DisableMouseCapture.html)