From aeb75a8be7e799b08291593e615236e5d4a3b465 Mon Sep 17 00:00:00 2001 From: sudipghimire533 Date: Thu, 21 Jul 2022 12:12:24 +0545 Subject: [PATCH] Add masked property while rendering text-(like) objects --- examples/paragraph.rs | 9 +++++++-- src/text.rs | 16 ++++++++++++++++ src/widgets/paragraph.rs | 32 +++++++++++++++++++++++--------- src/widgets/reflow.rs | 11 ++++++----- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/examples/paragraph.rs b/examples/paragraph.rs index 1ec8b86..5b60b8e 100644 --- a/examples/paragraph.rs +++ b/examples/paragraph.rs @@ -100,7 +100,7 @@ fn ui(f: &mut Frame, app: &App) { let chunks = Layout::default() .direction(Direction::Vertical) - .margin(5) + .margin(2) .constraints( [ Constraint::Percentage(25), @@ -123,9 +123,14 @@ fn ui(f: &mut Frame, app: &App) { Style::default().bg(Color::Blue), )), Spans::from(Span::styled( - "This is a longer line", + "Text below this is masked 😃", Style::default().add_modifier(Modifier::CROSSED_OUT), )), + Spans::from(Span::masked( + "This is masked text 😃", + Style::default().bg(Color::Black).fg(Color::LightGreen), + '*', + )), Spans::from(Span::styled(&long_line, Style::default().bg(Color::Green))), Spans::from(Span::styled( "This is a line", diff --git a/src/text.rs b/src/text.rs index 7d99343..0ff5816 100644 --- a/src/text.rs +++ b/src/text.rs @@ -56,6 +56,7 @@ use unicode_width::UnicodeWidthStr; pub struct StyledGrapheme<'a> { pub symbol: &'a str, pub style: Style, + pub mask: Option, } /// A string where all graphemes have the same style. @@ -63,6 +64,7 @@ pub struct StyledGrapheme<'a> { pub struct Span<'a> { pub content: Cow<'a, str>, pub style: Style, + pub mask: Option, } impl<'a> Span<'a> { @@ -82,6 +84,7 @@ impl<'a> Span<'a> { Span { content: content.into(), style: Style::default(), + mask: None, } } @@ -103,6 +106,18 @@ impl<'a> Span<'a> { Span { content: content.into(), style, + mask: None, + } + } + + pub fn masked(content: T, style: Style, mask: char) -> Span<'a> + where + T: Into>, + { + Span { + content: content.into(), + mask: Some(mask), + style, } } @@ -176,6 +191,7 @@ impl<'a> Span<'a> { .map(move |g| StyledGrapheme { symbol: g, style: base_style.patch(self.style), + mask: self.mask, }) .filter(|s| s.symbol != "\n") } diff --git a/src/widgets/paragraph.rs b/src/widgets/paragraph.rs index f4ebd8d..d4ded86 100644 --- a/src/widgets/paragraph.rs +++ b/src/widgets/paragraph.rs @@ -159,6 +159,7 @@ impl<'a> Widget for Paragraph<'a> { .chain(iter::once(StyledGrapheme { symbol: "\n", style: self.style, + mask: None, })) }); @@ -175,16 +176,29 @@ impl<'a> Widget for Paragraph<'a> { while let Some((current_line, current_line_width)) = line_composer.next_line() { if y >= self.scroll.0 { let mut x = get_line_offset(current_line_width, text_area.width, self.alignment); - for StyledGrapheme { symbol, style } in current_line { + + for StyledGrapheme { symbol, style, mask } in current_line { + let to_render_symbol = if symbol.is_empty() { + // If the symbol is empty, the last char which rendered last time will + // leave on the line. It's a quick fix. + String::from(" ") + } else { + match mask { + None => symbol.to_string(), + Some(masker) => if false { + // FIXME: + // TODO: Only one of if/else branch is to be left + // should we do: + // - masker.repeat(symbol.len()) or, + // - makser + std::iter::repeat(masker) + .take(symbol.len()) + .collect() + } else { String::from(*masker) }, + } + }; buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll.0) - .set_symbol(if symbol.is_empty() { - // If the symbol is empty, the last char which rendered last time will - // leave on the line. It's a quick fix. - " " - } else { - symbol - }) - .set_style(*style); + .set_symbol(to_render_symbol.as_str()).set_style(*style); x += symbol.width() as u16; } } diff --git a/src/widgets/reflow.rs b/src/widgets/reflow.rs index c611299..c79d80b 100644 --- a/src/widgets/reflow.rs +++ b/src/widgets/reflow.rs @@ -55,7 +55,7 @@ impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> { let mut width_to_last_word_end: u16 = 0; let mut prev_whitespace = false; let mut symbols_exhausted = true; - for StyledGrapheme { symbol, style } in &mut self.symbols { + for StyledGrapheme { symbol, style, mask } in &mut self.symbols { symbols_exhausted = false; let symbol_whitespace = symbol.chars().all(&char::is_whitespace) && symbol != NBSP; @@ -82,7 +82,7 @@ impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> { width_to_last_word_end = current_line_width; } - self.current_line.push(StyledGrapheme { symbol, style }); + self.current_line.push(StyledGrapheme { symbol, style, mask }); current_line_width += symbol.width() as u16; if current_line_width > self.max_line_width { @@ -161,7 +161,7 @@ impl<'a, 'b> LineComposer<'a> for LineTruncator<'a, 'b> { let mut skip_rest = false; let mut symbols_exhausted = true; let mut horizontal_offset = self.horizontal_offset as usize; - for StyledGrapheme { symbol, style } in &mut self.symbols { + for StyledGrapheme { symbol, style, mask } in &mut self.symbols { symbols_exhausted = false; // Ignore characters wider that the total max width. @@ -194,7 +194,7 @@ impl<'a, 'b> LineComposer<'a> for LineTruncator<'a, 'b> { } }; current_line_width += symbol.width() as u16; - self.current_line.push(StyledGrapheme { symbol, style }); + self.current_line.push(StyledGrapheme { symbol, style, mask }); } if skip_rest { @@ -241,8 +241,9 @@ mod test { fn run_composer(which: Composer, text: &str, text_area_width: u16) -> (Vec, Vec) { let style = Default::default(); + let mask = None; let mut styled = - UnicodeSegmentation::graphemes(text, true).map(|g| StyledGrapheme { symbol: g, style }); + UnicodeSegmentation::graphemes(text, true).map(|g| StyledGrapheme { symbol: g, style, mask }); let mut composer: Box = match which { Composer::WordWrapper { trim } => { Box::new(WordWrapper::new(&mut styled, text_area_width, trim))