Skip to content
Snippets Groups Projects
mod.rs 4.46 KiB
Newer Older
sam edelsten's avatar
sam edelsten committed
use bevy::prelude::*;
use cosmic_text::{Buffer, Edit, Shaping};
use unicode_segmentation::UnicodeSegmentation;
sam edelsten's avatar
sam edelsten committed
use crate::{
    input::{input_mouse, kb_input_text, kb_move_cursor},
    CosmicBuffer, CosmicEditor, CosmicFontSystem, DefaultAttrs, Render,
sam edelsten's avatar
sam edelsten committed
};
sam edelsten's avatar
sam edelsten committed

pub struct PasswordPlugin;

impl Plugin for PasswordPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
sam edelsten's avatar
sam edelsten committed
            PreUpdate,
            (
                hide_password_text.before(input_mouse),
                restore_password_text.after(input_mouse),
            ),
        )
        .add_systems(
            Update,
            (
                hide_password_text.before(kb_move_cursor),
                restore_password_text
                    .before(kb_input_text)
                    .after(kb_move_cursor),
            ),
        )
sam edelsten's avatar
sam edelsten committed
        .add_systems(
sam edelsten's avatar
sam edelsten committed
            PostUpdate,
            (
                hide_password_text.before(Render),
                restore_password_text.after(Render),
            ),
        );
    }
}

#[derive(Component)]
pub struct Password {
    real_text: String,
    glyph: char,
}

impl Default for Password {
    fn default() -> Self {
        Self {
            real_text: Default::default(),
// TODO: impl this on buffer
fn get_text(buffer: &mut Buffer) -> String {
    let mut text = String::new();
    let line_count = buffer.lines.len();

    for (i, line) in buffer.lines.iter().enumerate() {
        text.push_str(line.text());

        if i < line_count - 1 {
            text.push('\n');
        }
    }

    text
}

sam edelsten's avatar
sam edelsten committed
fn hide_password_text(
    mut q: Query<(
        &mut Password,
        &mut CosmicBuffer,
        &DefaultAttrs,
        Option<&mut CosmicEditor>,
    )>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    for (mut password, mut buffer, attrs, editor_opt) in q.iter_mut() {
        if let Some(mut editor) = editor_opt {
            let mut cursor = editor.cursor();

sam edelsten's avatar
sam edelsten committed
            editor.with_buffer_mut(|buffer| {
                let text = get_text(buffer);
                let (pre, _post) = text.split_at(cursor.index);
                let graphemes = pre.graphemes(true).count();
                cursor.index = graphemes * password.glyph.len_utf8();
sam edelsten's avatar
sam edelsten committed

                buffer.set_text(
                    &mut font_system,
                    password
                        .glyph
                        .to_string()
                        .repeat(text.graphemes(true).count())
                        .as_str(),
sam edelsten's avatar
sam edelsten committed
                    attrs.as_attrs(),
                    Shaping::Advanced,
                );
sam edelsten's avatar
sam edelsten committed
                password.real_text = text;
            });

            editor.set_cursor(cursor);

sam edelsten's avatar
sam edelsten committed
            continue;
        }

        let text = buffer.get_text();
sam edelsten's avatar
sam edelsten committed
        buffer.set_text(
            &mut font_system,
            password.glyph.to_string().repeat(text.len()).as_str(),
            attrs.as_attrs(),
        );
        password.real_text = text;
    }
}

fn restore_password_text(
    mut q: Query<(
        &Password,
        &mut CosmicBuffer,
        &DefaultAttrs,
        Option<&mut CosmicEditor>,
    )>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    for (password, mut buffer, attrs, editor_opt) in q.iter_mut() {
        if let Some(mut editor) = editor_opt {
            let mut cursor = editor.cursor();
            let mut index = 0;

sam edelsten's avatar
sam edelsten committed
            editor.with_buffer_mut(|buffer| {
                let text = get_text(buffer);
                let (pre, _post) = text.split_at(cursor.index);

                let grapheme_count = pre.graphemes(true).count();

                let mut g_idx = 0;
                for (i, _c) in password.real_text.grapheme_indices(true) {
                    if g_idx == grapheme_count {
                        index = i;
                    }
                    g_idx += 1;
                }

                // TODO: save/restore with selection bounds

                if cursor.index > 0 && index == 0 {
                    index = password.real_text.len();
                }

sam edelsten's avatar
sam edelsten committed
                buffer.set_text(
                    &mut font_system,
                    password.real_text.as_str(),
                    attrs.as_attrs(),
                    Shaping::Advanced,
sam edelsten's avatar
sam edelsten committed
            });
            editor.set_cursor(cursor);

sam edelsten's avatar
sam edelsten committed
            continue;
        }

        buffer.set_text(
            &mut font_system,
            password.real_text.as_str(),
            attrs.as_attrs(),
        );
    }
}