appcui 0.4.8

A feature-rich and cross-platform TUI/CUI framework for Rust, enabling modern terminal-based applications on Windows, Linux, and macOS. Includes built-in UI components like buttons, menus, list views, tree views, checkboxes, and more. Perfect for building fast and interactive CLI tools and text-based interfaces.
Documentation
use crate::prelude::*;
use crate::ui::password::events::EventData;

#[CustomControl(overwrite=OnPaint+OnKeyPressed+OnMouseEvent, internal=true)]
pub struct Password {
    pass: String,
    visible: bool,
}
impl Password {
    /// Creates a new Password control with the specified layout.
    /// The Password control is a single line text input control that hides the input characters with asterisks.
    /// 
    /// # Example
    /// ```rust, no_run
    /// use appcui::prelude::*;
    /// 
    /// let mut password = Password::new(layout!("x:1,y:1,w:20,h:1"));
    /// ```
    pub fn new(layout: Layout) -> Self {
        let mut p = Self {
            base: ControlBase::with_status_flags(layout, StatusFlags::Visible | StatusFlags::Enabled | StatusFlags::AcceptInput),
            pass: String::new(),
            visible: false,
        };
        p.set_size_bounds(4, 1, u16::MAX, 1);
        p
    }

    /// Manually sets the password string.
    pub fn set_password(&mut self, password: &str) {
        self.pass.clear();
        self.pass.push_str(password);
    }

    /// Returns the current password string.
    #[inline(always)]
    pub fn password(&self) -> &str {
        &self.pass
    }
}
impl OnPaint for Password {
    fn on_paint(&self, surface: &mut Surface, theme: &Theme) {
        let attr = match () {
            _ if !self.is_enabled() => theme.editor.inactive,
            _ if self.has_focus() => theme.editor.focused,
            _ if self.is_mouse_over() => theme.editor.hovered,
            _ => theme.editor.normal,
        };
        let mut sz = self.pass.len() as u32;
        let w = self.size().width;
        if (sz + 3) >= w {
            sz = w - 3;
        }
        surface.fill_horizontal_line(0, 0, w as i32, Character::with_attributes(' ', attr));
        if self.visible {
            if sz > 0 {
                let format = TextFormatBuilder::new()
                    .position(1, 0)
                    .attribute(attr)
                    .align(TextAlignment::Left)
                    .wrap_type(WrapType::SingleLineWrap(sz as u16))
                    .build();
                surface.write_text(&self.pass, &format);
            }
            surface.write_char(
                w as i32 - 1,
                0,
                Character::with_attributes(SpecialChar::CircleFilled, theme.symbol.checked),
            );
        } else {
            surface.fill_horizontal_line_with_size(1, 0, sz, Character::with_attributes('*', attr));
            surface.write_char(
                w as i32 - 1,
                0,
                Character::with_attributes(SpecialChar::CircleEmpty, theme.symbol.unchecked),
            );
        }
        if self.has_focus() {
            surface.set_cursor(1 + sz as i32, 0);
        }
    }
}
impl OnKeyPressed for Password {
    fn on_key_pressed(&mut self, key: Key, character: char) -> EventProcessStatus {
        if character as u32 > 0 {
            self.pass.push(character);
            return EventProcessStatus::Processed;
        }
        match key.value() {
            key!("Back") => {
                self.pass.pop();
                return EventProcessStatus::Processed;
            }
            key!("Enter") => {
                self.raise_event(ControlEvent {
                    emitter: self.handle,
                    receiver: self.event_processor,
                    data: ControlEventData::Password(EventData { accept: true }),
                });
                return EventProcessStatus::Processed;
            }
            key!("Esc") => {
                self.raise_event(ControlEvent {
                    emitter: self.handle,
                    receiver: self.event_processor,
                    data: ControlEventData::Password(EventData { accept: false }),
                });
                return EventProcessStatus::Processed;
            }
            _ => {}
        }
        EventProcessStatus::Ignored
    }
}
impl OnMouseEvent for Password {
    fn on_mouse_event(&mut self, event: &MouseEvent) -> EventProcessStatus {
        match event {
            MouseEvent::Enter => {
                self.show_tooltip("Click and hold to see the password");
                EventProcessStatus::Processed
            }
            MouseEvent::Leave => {
                self.hide_tooltip();
                EventProcessStatus::Processed
            }
            MouseEvent::Over(_) => EventProcessStatus::Ignored,
            MouseEvent::Pressed(_) => {
                self.visible = true;
                EventProcessStatus::Processed
            }
            MouseEvent::Released(_) => {
                self.visible = false;
                EventProcessStatus::Processed
            }
            MouseEvent::DoubleClick(_) => EventProcessStatus::Ignored,
            MouseEvent::Drag(_) => EventProcessStatus::Ignored,
            MouseEvent::Wheel(_) => EventProcessStatus::Ignored,
        }
    }
}