1
0
зеркало из https://github.com/glebtv/tui-textarea.git synced 2026-09-08 00:15:50 +03:00

change 'password' to more generic 'mask'. Fix tab behavior

Этот коммит содержится в:
paul moore
2023-10-15 09:55:16 -07:00
родитель 84985fa04c
Коммит 45445e0554
3 изменённых файлов: 51 добавлений и 33 удалений

Просмотреть файл

@@ -21,8 +21,8 @@ fn main() -> io::Result<()> {
let mut textarea = TextArea::default(); let mut textarea = TextArea::default();
textarea.set_cursor_line_style(Style::default()); textarea.set_cursor_line_style(Style::default());
textarea.set_password_mode(true); textarea.set_mask_char('\u{2022}'); //U+2022 BULLET (•)
textarea.set_placeholder_text("please enter your password"); textarea.set_placeholder_text("Please enter your password");
let layout = let layout =
Layout::default().constraints([Constraint::Length(3), Constraint::Min(1)].as_slice()); Layout::default().constraints([Constraint::Length(3), Constraint::Min(1)].as_slice());
textarea.set_style(Style::default().fg(Color::LightGreen)); textarea.set_style(Style::default().fg(Color::LightGreen));

Просмотреть файл

@@ -48,28 +48,39 @@ impl Boundary {
} }
} }
fn replace_tabs(s: &str, tab_len: u8, mask: bool) -> Cow<'_, str> { fn prepare_line(s: &str, tab_len: u8, mask: Option<char>) -> Cow<'_, str> {
let tab = spaces(tab_len); // two tasks performed here
let mut buf = String::new(); // - mask out chars if mask is Some
for (i, c) in s.char_indices() { // - replace hard tabs by the correct number of spaces
if buf.is_empty() { // (soft tabs were done earlier in 'insert_tab')
if c == '\t' { match mask {
buf.reserve(s.len()); Some(ch) => {
buf.push_str(&s[..i]); // no tab processing in the mask case
buf.push_str(tab); return Cow::Owned(s.chars().map(|_| ch).collect());
}
} else if c == '\t' {
buf.push_str(tab);
} else {
buf.push(c);
} }
} None => {
match (buf.is_empty(), mask) { let tab = spaces(tab_len);
(true, true) => Cow::Owned(s.chars().map(|_| '*').collect()), let mut buf = String::new();
(true, false) => Cow::Borrowed(s), for (i, c) in s.char_indices() {
(false, true) => Cow::Owned(buf.chars().map(|_| '*').collect()), if buf.is_empty() {
(false, false) => Cow::Owned(buf), if c == '\t' {
} buf.reserve(s.len());
buf.push_str(&s[..i]);
buf.push_str(tab);
}
} else if c == '\t' {
buf.push_str(tab);
} else {
buf.push(c);
}
}
if !buf.is_empty() {
return Cow::Owned(buf);
}
}
};
// drop through in the case of no mask and no tabs
return Cow::Borrowed(s);
} }
pub struct LineHighlighter<'a> { pub struct LineHighlighter<'a> {
@@ -122,7 +133,7 @@ impl<'a> LineHighlighter<'a> {
} }
} }
pub fn into_spans(self, mask: bool) -> Spans<'a> { pub fn into_spans(self, mask: Option<char>) -> Spans<'a> {
let Self { let Self {
line, line,
mut spans, mut spans,
@@ -134,7 +145,7 @@ impl<'a> LineHighlighter<'a> {
} = self; } = self;
if boundaries.is_empty() { if boundaries.is_empty() {
spans.push(Span::styled(replace_tabs(line, tab_len, mask), style_begin)); spans.push(Span::styled(prepare_line(line, tab_len, mask), style_begin));
if cursor_at_end { if cursor_at_end {
spans.push(Span::styled(" ", cursor_style)); spans.push(Span::styled(" ", cursor_style));
} }
@@ -155,7 +166,7 @@ impl<'a> LineHighlighter<'a> {
if let Some((next_boundary, end)) = boundaries.next() { if let Some((next_boundary, end)) = boundaries.next() {
if start < end { if start < end {
spans.push(Span::styled( spans.push(Span::styled(
replace_tabs(&line[start..end], tab_len, mask), prepare_line(&line[start..end], tab_len, mask),
style, style,
)); ));
} }
@@ -170,7 +181,7 @@ impl<'a> LineHighlighter<'a> {
} else { } else {
if start != line.len() { if start != line.len() {
spans.push(Span::styled( spans.push(Span::styled(
replace_tabs(&line[start..], tab_len, mask), prepare_line(&line[start..], tab_len, mask),
style, style,
)); ));
} }

Просмотреть файл

@@ -63,7 +63,7 @@ pub struct TextArea<'a> {
alignment: Alignment, alignment: Alignment,
pub(crate) placeholder: String, pub(crate) placeholder: String,
pub(crate) placeholder_style: Style, pub(crate) placeholder_style: Style,
password_mode: bool, mask: Option<char>,
} }
/// Convert any iterator whose elements can be converted into [`String`] into [`TextArea`]. Each [`String`] element is /// Convert any iterator whose elements can be converted into [`String`] into [`TextArea`]. Each [`String`] element is
@@ -163,7 +163,7 @@ impl<'a> TextArea<'a> {
alignment: Alignment::Left, alignment: Alignment::Left,
placeholder: String::new(), placeholder: String::new(),
placeholder_style: Style::default().fg(Color::DarkGray), placeholder_style: Style::default().fg(Color::DarkGray),
password_mode: false, mask: None,
} }
} }
@@ -1007,7 +1007,7 @@ impl<'a> TextArea<'a> {
hl.search(matches, self.search.style); hl.search(matches, self.search.style);
} }
hl.into_spans(self.password_mode) hl.into_spans(self.mask)
} }
/// Build a tui-rs widget to render the current state of the textarea. The widget instance returned from this /// Build a tui-rs widget to render the current state of the textarea. The widget instance returned from this
@@ -1306,11 +1306,18 @@ impl<'a> TextArea<'a> {
} }
} }
/// Sets a flag indicating if the text should be masked with * characters /// Specifies that the text should be masked with the specified character
/// ///
pub fn set_password_mode(&mut self, value: bool) { pub fn set_mask_char(&mut self, mask: char) {
self.password_mode = value; self.mask = Some(mask);
} }
/// Clear the previously set masking character
///
pub fn clear_mask_char(&mut self) {
self.mask = None;
}
/// Set the style of cursor. By default, a cursor is rendered in the reversed color. Setting the same style as /// Set the style of cursor. By default, a cursor is rendered in the reversed color. Setting the same style as
/// cursor line hides a cursor. /// cursor line hides a cursor.
/// ``` /// ```