Add TextEdit::password to hide input characters
This commit is contained in:
parent
33a4058381
commit
d848b2a664
6 changed files with 60 additions and 24 deletions
|
@ -19,6 +19,7 @@ NOTE: `eframe`, `egui_web` and `egui_glium` has their own changelogs!
|
|||
* Add `DebugOptions::show_widgets` to debug layouting by hovering widgets.
|
||||
* Add `ComboBox` to more easily customize combo boxes.
|
||||
* Add `Slider::new` and `DragValue::new` to replace old type-specific constructors.
|
||||
* Add `TextEdit::password` to hide input characters.
|
||||
|
||||
### Changed 🔧
|
||||
* `kb_focus` is now just called `focus`.
|
||||
|
|
|
@ -313,12 +313,14 @@ impl Painter {
|
|||
color: Color32,
|
||||
fake_italics: bool,
|
||||
) {
|
||||
self.add(Shape::Text {
|
||||
pos,
|
||||
galley,
|
||||
color,
|
||||
fake_italics,
|
||||
});
|
||||
if !galley.is_empty() {
|
||||
self.add(Shape::Text {
|
||||
pos,
|
||||
galley,
|
||||
color,
|
||||
fake_italics,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,7 @@ pub struct TextEdit<'t> {
|
|||
id_source: Option<Id>,
|
||||
text_style: Option<TextStyle>,
|
||||
text_color: Option<Color32>,
|
||||
password: bool,
|
||||
frame: bool,
|
||||
multiline: bool,
|
||||
enabled: bool,
|
||||
|
@ -160,6 +161,7 @@ impl<'t> TextEdit<'t> {
|
|||
id_source: None,
|
||||
text_style: None,
|
||||
text_color: None,
|
||||
password: false,
|
||||
frame: true,
|
||||
multiline: false,
|
||||
enabled: true,
|
||||
|
@ -176,8 +178,9 @@ impl<'t> TextEdit<'t> {
|
|||
id: None,
|
||||
id_source: None,
|
||||
text_style: None,
|
||||
frame: true,
|
||||
text_color: None,
|
||||
password: false,
|
||||
frame: true,
|
||||
multiline: true,
|
||||
enabled: true,
|
||||
desired_width: None,
|
||||
|
@ -202,6 +205,12 @@ impl<'t> TextEdit<'t> {
|
|||
self
|
||||
}
|
||||
|
||||
/// If true, hide the letters from view and prevent copying from the field.
|
||||
pub fn password(mut self, password: bool) -> Self {
|
||||
self.password = password;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text_style(mut self, text_style: TextStyle) -> Self {
|
||||
self.text_style = Some(text_style);
|
||||
self
|
||||
|
@ -292,6 +301,7 @@ impl<'t> TextEdit<'t> {
|
|||
id_source,
|
||||
text_style,
|
||||
text_color,
|
||||
password,
|
||||
frame: _,
|
||||
multiline,
|
||||
enabled,
|
||||
|
@ -302,13 +312,31 @@ impl<'t> TextEdit<'t> {
|
|||
let text_style = text_style.unwrap_or_else(|| ui.style().body_text_style);
|
||||
let line_spacing = ui.fonts().row_height(text_style);
|
||||
let available_width = ui.available_width();
|
||||
let mut galley = if multiline {
|
||||
ui.fonts()
|
||||
.layout_multiline(text_style, text.clone(), available_width)
|
||||
} else {
|
||||
ui.fonts().layout_single_line(text_style, text.clone())
|
||||
|
||||
let make_galley = |ui: &Ui, text: &str| {
|
||||
let text = if password {
|
||||
std::iter::repeat(epaint::text::PASSWORD_REPLACEMENT_CHAR)
|
||||
.take(text.chars().count())
|
||||
.collect::<String>()
|
||||
} else {
|
||||
text.to_owned()
|
||||
};
|
||||
if multiline {
|
||||
ui.fonts()
|
||||
.layout_multiline(text_style, text, available_width)
|
||||
} else {
|
||||
ui.fonts().layout_single_line(text_style, text)
|
||||
}
|
||||
};
|
||||
|
||||
let copy_if_not_password = |ui: &Ui, text: String| {
|
||||
if !password {
|
||||
ui.ctx().output().copied_text = text;
|
||||
}
|
||||
};
|
||||
|
||||
let mut galley = make_galley(ui, text);
|
||||
|
||||
let desired_width = desired_width.unwrap_or_else(|| ui.spacing().text_edit_width);
|
||||
let desired_height = (desired_height_rows.at_least(1) as f32) * line_spacing;
|
||||
let desired_size = vec2(
|
||||
|
@ -411,18 +439,18 @@ impl<'t> TextEdit<'t> {
|
|||
let did_mutate_text = match event {
|
||||
Event::Copy => {
|
||||
if cursorp.is_empty() {
|
||||
ui.ctx().output().copied_text = text.clone();
|
||||
copy_if_not_password(ui, text.clone());
|
||||
} else {
|
||||
ui.ctx().output().copied_text = selected_str(text, &cursorp).to_owned();
|
||||
copy_if_not_password(ui, selected_str(text, &cursorp).to_owned());
|
||||
}
|
||||
None
|
||||
}
|
||||
Event::Cut => {
|
||||
if cursorp.is_empty() {
|
||||
ui.ctx().output().copied_text = std::mem::take(text);
|
||||
copy_if_not_password(ui, std::mem::take(text));
|
||||
Some(CCursorPair::default())
|
||||
} else {
|
||||
ui.ctx().output().copied_text = selected_str(text, &cursorp).to_owned();
|
||||
copy_if_not_password(ui, selected_str(text, &cursorp).to_owned());
|
||||
Some(CCursorPair::one(delete_selected(text, &cursorp)))
|
||||
}
|
||||
}
|
||||
|
@ -482,12 +510,7 @@ impl<'t> TextEdit<'t> {
|
|||
response.mark_changed();
|
||||
|
||||
// Layout again to avoid frame delay, and to keep `text` and `galley` in sync.
|
||||
galley = if multiline {
|
||||
ui.fonts()
|
||||
.layout_multiline(text_style, text.clone(), available_width)
|
||||
} else {
|
||||
ui.fonts().layout_single_line(text_style, text.clone())
|
||||
};
|
||||
galley = make_galley(ui, text);
|
||||
|
||||
// Set cursorp using new galley:
|
||||
cursorp = CursorPair {
|
||||
|
|
|
@ -24,6 +24,7 @@ pub struct Widgets {
|
|||
color: Color32,
|
||||
single_line_text_input: String,
|
||||
multiline_text_input: String,
|
||||
show_password: bool,
|
||||
}
|
||||
|
||||
impl Default for Widgets {
|
||||
|
@ -36,6 +37,7 @@ impl Default for Widgets {
|
|||
color: (Rgba::from_rgb(0.0, 1.0, 0.5) * 0.75).into(),
|
||||
single_line_text_input: "Hello World!".to_owned(),
|
||||
multiline_text_input: "Text can both be so wide that it needs a line break, but you can also add manual line break by pressing enter, creating new paragraphs.\nThis is the start of the next paragraph.\n\nClick me to edit me!".to_owned(),
|
||||
show_password: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,11 +130,15 @@ impl Widgets {
|
|||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Single line text input:");
|
||||
let response = ui.text_edit_singleline(&mut self.single_line_text_input);
|
||||
ui.label("Password:");
|
||||
let response = ui.add(
|
||||
egui::TextEdit::singleline(&mut self.single_line_text_input)
|
||||
.password(!self.show_password),
|
||||
);
|
||||
if response.lost_focus() && ui.input().key_pressed(egui::Key::Enter) {
|
||||
// …
|
||||
}
|
||||
ui.checkbox(&mut self.show_password, "Show password");
|
||||
});
|
||||
|
||||
ui.label("Multiline text input:");
|
||||
|
|
|
@ -211,6 +211,7 @@ impl Font {
|
|||
slf.glyph_info(c);
|
||||
}
|
||||
slf.glyph_info('°');
|
||||
slf.glyph_info(crate::text::PASSWORD_REPLACEMENT_CHAR); // password replacement character
|
||||
|
||||
slf
|
||||
}
|
||||
|
|
|
@ -9,3 +9,6 @@ pub use {
|
|||
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
|
||||
galley::{Galley, Row},
|
||||
};
|
||||
|
||||
/// Suggested character to use to replace those in password text fields.
|
||||
pub const PASSWORD_REPLACEMENT_CHAR: char = '•';
|
||||
|
|
Loading…
Reference in a new issue