Add masked property while rendering text-(like) objects

pull/648/head
sudipghimire533 2 years ago committed by Sudip Ghimire
parent a6b25a4877
commit aeb75a8be7

@ -100,7 +100,7 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.margin(5) .margin(2)
.constraints( .constraints(
[ [
Constraint::Percentage(25), Constraint::Percentage(25),
@ -123,9 +123,14 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &App) {
Style::default().bg(Color::Blue), Style::default().bg(Color::Blue),
)), )),
Spans::from(Span::styled( Spans::from(Span::styled(
"This is a longer line", "Text below this is masked 😃",
Style::default().add_modifier(Modifier::CROSSED_OUT), 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(&long_line, Style::default().bg(Color::Green))),
Spans::from(Span::styled( Spans::from(Span::styled(
"This is a line", "This is a line",

@ -56,6 +56,7 @@ use unicode_width::UnicodeWidthStr;
pub struct StyledGrapheme<'a> { pub struct StyledGrapheme<'a> {
pub symbol: &'a str, pub symbol: &'a str,
pub style: Style, pub style: Style,
pub mask: Option<char>,
} }
/// A string where all graphemes have the same style. /// A string where all graphemes have the same style.
@ -63,6 +64,7 @@ pub struct StyledGrapheme<'a> {
pub struct Span<'a> { pub struct Span<'a> {
pub content: Cow<'a, str>, pub content: Cow<'a, str>,
pub style: Style, pub style: Style,
pub mask: Option<char>,
} }
impl<'a> Span<'a> { impl<'a> Span<'a> {
@ -82,6 +84,7 @@ impl<'a> Span<'a> {
Span { Span {
content: content.into(), content: content.into(),
style: Style::default(), style: Style::default(),
mask: None,
} }
} }
@ -103,6 +106,18 @@ impl<'a> Span<'a> {
Span { Span {
content: content.into(), content: content.into(),
style, style,
mask: None,
}
}
pub fn masked<T>(content: T, style: Style, mask: char) -> Span<'a>
where
T: Into<Cow<'a, str>>,
{
Span {
content: content.into(),
mask: Some(mask),
style,
} }
} }
@ -176,6 +191,7 @@ impl<'a> Span<'a> {
.map(move |g| StyledGrapheme { .map(move |g| StyledGrapheme {
symbol: g, symbol: g,
style: base_style.patch(self.style), style: base_style.patch(self.style),
mask: self.mask,
}) })
.filter(|s| s.symbol != "\n") .filter(|s| s.symbol != "\n")
} }

@ -159,6 +159,7 @@ impl<'a> Widget for Paragraph<'a> {
.chain(iter::once(StyledGrapheme { .chain(iter::once(StyledGrapheme {
symbol: "\n", symbol: "\n",
style: self.style, 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() { while let Some((current_line, current_line_width)) = line_composer.next_line() {
if y >= self.scroll.0 { if y >= self.scroll.0 {
let mut x = get_line_offset(current_line_width, text_area.width, self.alignment); 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) buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll.0)
.set_symbol(if symbol.is_empty() { .set_symbol(to_render_symbol.as_str()).set_style(*style);
// 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);
x += symbol.width() as u16; x += symbol.width() as u16;
} }
} }

@ -55,7 +55,7 @@ impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> {
let mut width_to_last_word_end: u16 = 0; let mut width_to_last_word_end: u16 = 0;
let mut prev_whitespace = false; let mut prev_whitespace = false;
let mut symbols_exhausted = true; 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; symbols_exhausted = false;
let symbol_whitespace = symbol.chars().all(&char::is_whitespace) && symbol != NBSP; 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; 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; current_line_width += symbol.width() as u16;
if current_line_width > self.max_line_width { 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 skip_rest = false;
let mut symbols_exhausted = true; let mut symbols_exhausted = true;
let mut horizontal_offset = self.horizontal_offset as usize; 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; symbols_exhausted = false;
// Ignore characters wider that the total max width. // 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; 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 { if skip_rest {
@ -241,8 +241,9 @@ mod test {
fn run_composer(which: Composer, text: &str, text_area_width: u16) -> (Vec<String>, Vec<u16>) { fn run_composer(which: Composer, text: &str, text_area_width: u16) -> (Vec<String>, Vec<u16>) {
let style = Default::default(); let style = Default::default();
let mask = None;
let mut styled = 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<dyn LineComposer> = match which { let mut composer: Box<dyn LineComposer> = match which {
Composer::WordWrapper { trim } => { Composer::WordWrapper { trim } => {
Box::new(WordWrapper::new(&mut styled, text_area_width, trim)) Box::new(WordWrapper::new(&mut styled, text_area_width, trim))

Loading…
Cancel
Save