From 32c31c7b9b75975ba7e81d6c3884d66a79d44429 Mon Sep 17 00:00:00 2001 From: rhysd Date: Tue, 14 Jun 2022 13:45:04 +0900 Subject: [PATCH] return if `TextArea::undo` and `TextArea::redo` modified contents --- src/history.rs | 5 +---- src/input.rs | 1 + src/textarea.rs | 46 +++++++++++++++++++++++++++++++++++----------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/history.rs b/src/history.rs index ab554d7..b0a0587 100644 --- a/src/history.rs +++ b/src/history.rs @@ -132,10 +132,7 @@ impl History { } pub fn undo(&mut self, lines: &mut Vec) -> Option<(usize, usize)> { - if self.index == 0 { - return None; - } - self.index -= 1; + self.index = self.index.checked_sub(1)?; let edit = &self.edits[self.index]; edit.undo(lines); Some(edit.cursor_before()) diff --git a/src/input.rs b/src/input.rs index 9a5b509..906c406 100644 --- a/src/input.rs +++ b/src/input.rs @@ -64,6 +64,7 @@ pub struct Input { } impl Default for Input { + /// The default input is [`Key::Null`] without pressing Ctrl nor Alt, which means invalid input. fn default() -> Self { Input { key: Key::Null, diff --git a/src/textarea.rs b/src/textarea.rs index 8bf7cf6..d8c0ac3 100644 --- a/src/textarea.rs +++ b/src/textarea.rs @@ -53,12 +53,19 @@ pub struct TextArea<'a> { /// ``` /// use tui_textarea::TextArea; /// +/// // From `String` /// let text = "hello\nworld"; /// let textarea = TextArea::from(text.lines()); /// assert_eq!(textarea.lines(), ["hello", "world"]); /// +/// // From array of `&str` /// let textarea = TextArea::from(["hello", "world"]); /// assert_eq!(textarea.lines(), ["hello", "world"]); +/// +/// // From slice of `&str` +/// let slice = &["hello", "world"]; +/// let textarea = TextArea::from(slice.iter().copied()); +/// assert_eq!(textarea.lines(), ["hello", "world"]); /// ``` impl<'a, I> From for TextArea<'a> where @@ -347,12 +354,16 @@ impl<'a> TextArea<'a> { key: Key::Char('u'), ctrl: true, alt: false, - } => self.undo(), + } => { + self.undo(); + } Input { key: Key::Char('r'), ctrl: true, alt: false, - } => self.redo(), + } => { + self.redo(); + } Input { key: Key::Char('y' | 'v'), ctrl: true, @@ -749,7 +760,8 @@ impl<'a> TextArea<'a> { } } - /// Undo the last modification. When no modification is added to the text, this method does nothing. + /// Undo the last modification. When no modification is added to the text, this method does not modify contents and + /// returns `false`. /// ``` /// use tui_textarea::{TextArea, CursorMove}; /// @@ -760,13 +772,16 @@ impl<'a> TextArea<'a> { /// textarea.undo(); /// assert_eq!(textarea.lines(), ["abc def"]); /// ``` - pub fn undo(&mut self) { + pub fn undo(&mut self) -> bool { if let Some(cursor) = self.history.undo(&mut self.lines) { self.cursor = cursor; + true + } else { + false } } - /// Redo the last undo change. When no undo change remain, this method does nothing. + /// Redo the last undo change. When no undo change remain, this method does not modify contents and returns `false`. /// ``` /// use tui_textarea::{TextArea, CursorMove}; /// @@ -779,9 +794,12 @@ impl<'a> TextArea<'a> { /// textarea.redo(); /// assert_eq!(textarea.lines(), [" def"]); /// ``` - pub fn redo(&mut self) { + pub fn redo(&mut self) -> bool { if let Some(cursor) = self.history.redo(&mut self.lines) { self.cursor = cursor; + true + } else { + false } } @@ -850,7 +868,7 @@ impl<'a> TextArea<'a> { } let inner = Paragraph::new(Text::from(lines)).style(self.style); - TextAreaWidget { + Renderer { scroll_top: &self.scroll_top, cursor: (self.cursor.0 as u16, self.cursor.1 as u16), block: self.block.clone(), @@ -1055,16 +1073,22 @@ impl<'a> TextArea<'a> { } } -struct TextAreaWidget<'a> { - // &mut 'a (u16, u16) is not available since TextAreaWidget instance takes over the ownership of TextArea instance. - // In the case the TextArea instance cannot be accessed from any other objects since it is mutablly borrowed. +struct Renderer<'a> { + // &mut 'a (u16, u16) is not available since TextAreaWidget instance totally takes over the ownership of TextArea + // instance. In the case, the TextArea instance cannot be accessed from any other objects since it is mutablly + // borrowed. + // + // `tui::terminal::Frame::render_stateful_widget` would be an assumed way to render a stateful widget. But at this + // point we stick with using `tui::terminal::Frame::render_widget` because it is simpler API. Users don't need to + // manage states of textarea instances separately. + // https://docs.rs/tui/latest/tui/terminal/struct.Frame.html#method.render_stateful_widget scroll_top: &'a (AtomicU16, AtomicU16), cursor: (u16, u16), block: Option>, inner: Paragraph<'a>, } -impl<'a> Widget for TextAreaWidget<'a> { +impl<'a> Widget for Renderer<'a> { fn render(mut self, area: Rect, buf: &mut Buffer) { let inner_area = if let Some(b) = self.block.take() { let area = b.inner(area);