Skip to main content

appcui/ui/password/
password.rs

1use crate::prelude::*;
2use crate::ui::password::events::EventData;
3
4#[CustomControl(overwrite=OnPaint+OnKeyPressed+OnMouseEvent, internal=true)]
5pub struct Password {
6    pass: String,
7    visible: bool,
8}
9impl Password {
10    /// Creates a new Password control with the specified layout.
11    /// The Password control is a single line text input control that hides the input characters with asterisks.
12    /// 
13    /// # Example
14    /// ```rust, no_run
15    /// use appcui::prelude::*;
16    /// 
17    /// let mut password = Password::new(layout!("x:1,y:1,w:20,h:1"));
18    /// ```
19    pub fn new(layout: Layout) -> Self {
20        let mut p = Self {
21            base: ControlBase::with_status_flags(layout, StatusFlags::Visible | StatusFlags::Enabled | StatusFlags::AcceptInput),
22            pass: String::new(),
23            visible: false,
24        };
25        p.set_size_bounds(4, 1, u16::MAX, 1);
26        p
27    }
28
29    /// Manually sets the password string.
30    pub fn set_password(&mut self, password: &str) {
31        self.pass.clear();
32        self.pass.push_str(password);
33    }
34
35    /// Returns the current password string.
36    #[inline(always)]
37    pub fn password(&self) -> &str {
38        &self.pass
39    }
40}
41impl OnPaint for Password {
42    fn on_paint(&self, surface: &mut Surface, theme: &Theme) {
43        let attr = match () {
44            _ if !self.is_enabled() => theme.editor.inactive,
45            _ if self.has_focus() => theme.editor.focused,
46            _ if self.is_mouse_over() => theme.editor.hovered,
47            _ => theme.editor.normal,
48        };
49        let mut sz = self.pass.len() as u32;
50        let w = self.size().width;
51        if (sz + 3) >= w {
52            sz = w - 3;
53        }
54        surface.fill_horizontal_line(0, 0, w as i32, Character::with_attributes(' ', attr));
55        if self.visible {
56            if sz > 0 {
57                let format = TextFormatBuilder::new()
58                    .position(1, 0)
59                    .attribute(attr)
60                    .align(TextAlignment::Left)
61                    .wrap_type(WrapType::SingleLineWrap(sz as u16))
62                    .build();
63                surface.write_text(&self.pass, &format);
64            }
65            surface.write_char(
66                w as i32 - 1,
67                0,
68                Character::with_attributes(SpecialChar::CircleFilled, theme.symbol.checked),
69            );
70        } else {
71            surface.fill_horizontal_line_with_size(1, 0, sz, Character::with_attributes('*', attr));
72            surface.write_char(
73                w as i32 - 1,
74                0,
75                Character::with_attributes(SpecialChar::CircleEmpty, theme.symbol.unchecked),
76            );
77        }
78        if self.has_focus() {
79            surface.set_cursor(1 + sz as i32, 0);
80        }
81    }
82}
83impl OnKeyPressed for Password {
84    fn on_key_pressed(&mut self, key: Key, character: char) -> EventProcessStatus {
85        if character as u32 > 0 {
86            self.pass.push(character);
87            return EventProcessStatus::Processed;
88        }
89        match key.value() {
90            key!("Back") => {
91                self.pass.pop();
92                return EventProcessStatus::Processed;
93            }
94            key!("Enter") => {
95                self.raise_event(ControlEvent {
96                    emitter: self.handle,
97                    receiver: self.event_processor,
98                    data: ControlEventData::Password(EventData { accept: true }),
99                });
100                return EventProcessStatus::Processed;
101            }
102            key!("Esc") => {
103                self.raise_event(ControlEvent {
104                    emitter: self.handle,
105                    receiver: self.event_processor,
106                    data: ControlEventData::Password(EventData { accept: false }),
107                });
108                return EventProcessStatus::Processed;
109            }
110            _ => {}
111        }
112        EventProcessStatus::Ignored
113    }
114}
115impl OnMouseEvent for Password {
116    fn on_mouse_event(&mut self, event: &MouseEvent) -> EventProcessStatus {
117        match event {
118            MouseEvent::Enter => {
119                self.show_tooltip("Click and hold to see the password");
120                EventProcessStatus::Processed
121            }
122            MouseEvent::Leave => {
123                self.hide_tooltip();
124                EventProcessStatus::Processed
125            }
126            MouseEvent::Over(_) => EventProcessStatus::Ignored,
127            MouseEvent::Pressed(_) => {
128                self.visible = true;
129                EventProcessStatus::Processed
130            }
131            MouseEvent::Released(_) => {
132                self.visible = false;
133                EventProcessStatus::Processed
134            }
135            MouseEvent::DoubleClick(_) => EventProcessStatus::Ignored,
136            MouseEvent::Drag(_) => EventProcessStatus::Ignored,
137            MouseEvent::Wheel(_) => EventProcessStatus::Ignored,
138        }
139    }
140}