Skip to content

Commit

Permalink
Initial pass at adding support for "password" fields.
Browse files Browse the repository at this point in the history
This works, but may definitely want to be designed and implemented in a different way, as adding this in does seem a bit hacky.
  • Loading branch information
daboross committed May 21, 2017
1 parent 15077a3 commit 7bcb6f0
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 24 deletions.
6 changes: 6 additions & 0 deletions src/widget/text_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ widget_style!{
- justify: text::Justify { text::Justify::Left }
/// The font used for the `Text`.
- font_id: Option<text::font::Id> { theme.font_id }
/// Display text with all characters replaced by this
- char_replace: Option<char> { None }
}
}

Expand Down Expand Up @@ -91,6 +93,7 @@ impl<'a> TextBox<'a> {
pub font_size { style.font_size = Some(FontSize) }
pub justify { style.justify = Some(text::Justify) }
pub pad_text { style.text_padding = Some(Scalar) }
pub hide_with_char { style.char_replace = Some(Option<char>) }
}

}
Expand Down Expand Up @@ -156,6 +159,8 @@ impl<'a> Widget for TextBox<'a> {
.border_color(border_color)
.set(state.ids.rectangle, ui);

let char_replace = style.char_replace(ui.theme());

let mut events = Vec::new();

let text_color = style.text_color(ui.theme());
Expand All @@ -167,6 +172,7 @@ impl<'a> Widget for TextBox<'a> {
.font_size(font_size)
.color(text_color)
.justify(justify)
.hide_with_char(char_replace)
.parent(id)
.set(state.ids.text_edit, ui)
{
Expand Down
72 changes: 48 additions & 24 deletions src/widget/text_edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ widget_style!{
- restrict_to_height: bool { true }
/// The font used for the `Text`.
- font_id: Option<text::font::Id> { theme.font_id }
/// Display text with all characters replaced by this
- char_replace: Option<char> { None }
}
}

Expand Down Expand Up @@ -164,6 +166,7 @@ impl<'a> TextEdit<'a> {
pub line_wrap { style.line_wrap = Some(Wrap) }
pub line_spacing { style.line_spacing = Some(Scalar) }
pub restrict_to_height { style.restrict_to_height = Some(bool) }
pub hide_with_char { style.char_replace = Some(Option<char>) }
}

}
Expand Down Expand Up @@ -275,6 +278,10 @@ impl<'a> Widget for TextEdit<'a> {
}
}

let mut replacement_string = style.char_replace
.and_then(|opt| opt)
.map(|c: char| text.chars().map(|_| c).collect::<String>().into());

// Check to see if the given text has changed since the last time the widget was updated.
{
let maybe_new_line_infos = {
Expand Down Expand Up @@ -441,7 +448,8 @@ impl<'a> Widget for TextEdit<'a> {
let abs_xy = utils::vec2_add(rel_xy, rect.xy());
let infos = &state.line_infos;
let font = ui.fonts.get(font_id).unwrap();
let closest = closest_cursor_index_and_xy(abs_xy, &text, infos, font);
let closest = closest_cursor_index_and_xy(abs_xy, &replacement_string.as_ref().unwrap_or(&text),
infos, font);
if let Some((closest_cursor, _)) = closest {
cursor = Cursor::Idx(closest_cursor);
}
Expand Down Expand Up @@ -506,6 +514,8 @@ impl<'a> Widget for TextEdit<'a> {
*text.to_mut() = text.chars().take(start_idx)
.chain(text.chars().skip(end_idx))
.collect();
replacement_string = style.char_replace.and_then(|opt| opt)
.map(|c: char| text.chars().map(|_| c).collect::<String>().into());
state.update(|state| {
let font = ui.fonts.get(font_id).unwrap();
let w = rect.w();
Expand All @@ -526,28 +536,30 @@ impl<'a> Widget for TextEdit<'a> {
Cursor::Selection { start, end } => (start, end),
};

let displayed_text = replacement_string.as_ref().unwrap_or(&text);

let new_cursor_idx = {
let line_infos = state.line_infos.iter().cloned();
match (key, move_word) {
(input::Key::Left, true) => cursor_idx
.previous_word_start(&text, line_infos),
.previous_word_start(&displayed_text, line_infos),
(input::Key::Right, true) => cursor_idx
.next_word_end(&text, line_infos),
.next_word_end(&displayed_text, line_infos),
(input::Key::Left, false) => cursor_idx
.previous(line_infos),
(input::Key::Right, false) => cursor_idx
.next(line_infos),

// Up/Down movement
_ => cursor_xy_at(cursor_idx, &text, &state.line_infos, font)
_ => cursor_xy_at(cursor_idx, &displayed_text, &state.line_infos, font)
.and_then(|(x_pos, _)| {
let text::cursor::Index { line, .. } = cursor_idx;
let next_line = match key {
input::Key::Up => line.saturating_sub(1),
input::Key::Down => line + 1,
_ => unreachable!(),
};
closest_cursor_index_on_line(x_pos, next_line, &text, &state.line_infos, font)
closest_cursor_index_on_line(x_pos, next_line, &displayed_text, &state.line_infos, font)
})
}.unwrap_or(cursor_idx)
};
Expand Down Expand Up @@ -581,10 +593,10 @@ impl<'a> Widget for TextEdit<'a> {
let line_infos = state.line_infos.iter().cloned();
match key {
input::Key::Left | input::Key::Up => {
cursor_idx.previous_word_start(&text, line_infos)
cursor_idx.previous_word_start(&displayed_text, line_infos)
},
input::Key::Right | input::Key::Down => {
cursor_idx.next_word_end(&text, line_infos)
cursor_idx.next_word_end(&displayed_text, line_infos)
}
_ => unreachable!(),
}.unwrap_or(cursor_idx)
Expand Down Expand Up @@ -656,6 +668,8 @@ impl<'a> Widget for TextEdit<'a> {
match insert_text("\n", cursor, &text, &state.line_infos, font) {
Some((new_text, new_cursor, new_line_infos)) => {
*text.to_mut() = new_text;
replacement_string = style.char_replace.and_then(|opt| opt)
.map(|c: char| text.chars().map(|_| c).collect::<String>().into());
cursor = new_cursor;
state.update(|state| state.line_infos = new_line_infos);
}, _ => ()
Expand Down Expand Up @@ -698,6 +712,8 @@ impl<'a> Widget for TextEdit<'a> {
match insert_text(&string, cursor, &text, &state.line_infos, font) {
Some((new_text, new_cursor, new_line_infos)) => {
*text.to_mut() = new_text;
replacement_string = style.char_replace.and_then(|opt| opt)
.map(|c: char| text.chars().map(|_| c).collect::<String>().into());
cursor = new_cursor;
state.update(|state| state.line_infos = new_line_infos);
}, _ => ()
Expand All @@ -706,8 +722,9 @@ impl<'a> Widget for TextEdit<'a> {

// Check whether or not we need to extend a text selection or drag some text.
event::Widget::Drag(drag_event) if drag_event.button == input::MouseButton::Left => {
match drag {
let displayed_text = replacement_string.as_ref().unwrap_or(&text);

match drag {
Some(Drag::Selecting) => {
let start_cursor_idx = match cursor {
Cursor::Idx(idx) => idx,
Expand All @@ -716,7 +733,7 @@ impl<'a> Widget for TextEdit<'a> {
let abs_xy = utils::vec2_add(drag_event.to, rect.xy());
let infos = &state.line_infos;
let font = ui.fonts.get(font_id).unwrap();
match closest_cursor_index_and_xy(abs_xy, &text, infos, font) {
match closest_cursor_index_and_xy(abs_xy, &displayed_text, infos, font) {
Some((end_cursor_idx, _)) =>
cursor = Cursor::Selection {
start: start_cursor_idx,
Expand Down Expand Up @@ -763,20 +780,26 @@ impl<'a> Widget for TextEdit<'a> {
let text_y_range = Range::new(0.0, text_height).align_to(y_align, rect.y);
let text_rect = Rect { x: rect.x, y: text_y_range };

match line_wrap {
Wrap::Whitespace => widget::Text::new(&text).wrap_by_word(),
Wrap::Character => widget::Text::new(&text).wrap_by_character(),
{
let display_text = match replacement_string {
Some(ref s) => s,
None => &text,
};
match line_wrap {
Wrap::Whitespace => widget::Text::new(display_text).wrap_by_word(),
Wrap::Character => widget::Text::new(display_text).wrap_by_character(),
}
.font_id(font_id)
.wh(text_rect.dim())
.xy(text_rect.xy())
.justify(justify)
.parent(id)
.graphics_for(id)
.color(color)
.line_spacing(line_spacing)
.font_size(font_size)
.set(state.ids.text, ui);
}
.font_id(font_id)
.wh(text_rect.dim())
.xy(text_rect.xy())
.justify(justify)
.parent(id)
.graphics_for(id)
.color(color)
.line_spacing(line_spacing)
.font_size(font_size)
.set(state.ids.text, ui);

// Draw the line for the cursor.
let cursor_idx = match cursor {
Expand All @@ -791,7 +814,7 @@ impl<'a> Widget for TextEdit<'a> {

let (cursor_x, cursor_y_range) = {
let font = ui.fonts.get(font_id).unwrap();
cursor_xy_at(cursor_idx, &text, &state.line_infos, font)
cursor_xy_at(cursor_idx, replacement_string.as_ref().unwrap_or(&text), &state.line_infos, font)
.unwrap_or_else(|| {
let x = rect.left();
let y = Range::new(0.0, font_size as Scalar).align_to(y_align, rect.y);
Expand Down Expand Up @@ -839,7 +862,7 @@ impl<'a> Widget for TextEdit<'a> {

let selected_rects: Vec<Rect> = {
let line_infos = state.line_infos.iter().cloned();
let lines = line_infos.clone().map(|info| &text[info.byte_range()]);
let lines = line_infos.clone().map(|info| &replacement_string.as_ref().unwrap_or(&text)[info.byte_range()]);
let line_rects = text::line::rects(line_infos.clone(), font_size, rect,
justify, y_align, line_spacing);
let lines_with_rects = lines.zip(line_rects.clone());
Expand Down Expand Up @@ -867,6 +890,7 @@ impl<'a> Widget for TextEdit<'a> {
}
}


take_if_owned(text)
}

Expand Down

0 comments on commit 7bcb6f0

Please sign in to comment.