diff --git a/Cargo.toml b/Cargo.toml index 7f44d8b6e4cf357ceb5e42cad7ca577074da1a06..7c85c9e48d06c674a7e42e3ebc33dac827a77b2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ exclude = ["assets/*"] [features] multicam = [] placeholder = [] +password = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/examples/password.rs b/examples/password.rs new file mode 100644 index 0000000000000000000000000000000000000000..28f92dcc71a508045e42abcb3a57238167152ae2 --- /dev/null +++ b/examples/password.rs @@ -0,0 +1,46 @@ +use bevy::prelude::*; +use bevy_cosmic_edit::{password::Password, *}; +use util::{change_active_editor_sprite, deselect_editor_on_esc, print_editor_text}; + +fn setup(mut commands: Commands) { + commands.spawn(Camera2dBundle::default()); + + // Sprite editor + commands.spawn(( + CosmicEditBundle { + max_lines: CosmicMaxLines(1), + mode: CosmicMode::InfiniteLine, + sprite_bundle: SpriteBundle { + // Sets size of text box + sprite: Sprite { + custom_size: Some(Vec2::new(300., 100.)), + ..default() + }, + // Position of text box + transform: Transform::from_xyz(0., 100., 0.), + ..default() + }, + ..default() + }, + Password::default(), + )); +} + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugins(CosmicEditPlugin { + change_cursor: CursorConfig::Default, + ..default() + }) + .add_systems(Startup, setup) + .add_systems( + Update, + ( + change_active_editor_sprite, + deselect_editor_on_esc, + print_editor_text, + ), + ) + .run(); +} diff --git a/src/lib.rs b/src/lib.rs index 726f5a242712ffeace3c930240f7ae0691c553c0..3028487eeae09fbf8f135bcae981e4021366b797 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -203,6 +203,9 @@ impl Default for CosmicFontConfig { #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct KbInput; +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +pub struct Render; + /// Plugin struct that adds systems and initializes resources related to cosmic edit functionality. #[derive(Default)] pub struct CosmicEditPlugin { @@ -248,6 +251,7 @@ impl Plugin for CosmicEditPlugin { render_texture, ) .chain() + .in_set(Render) .after(TransformSystem::TransformPropagate), ) .init_resource::<FocusedWidget>() @@ -287,6 +291,9 @@ fn add_feature_plugins(app: &mut App) -> &mut App { #[cfg(feature = "placeholder")] app.add_plugins(plugins::placeholder::PlaceholderPlugin); + #[cfg(feature = "password")] + app.add_plugins(plugins::password::PasswordPlugin); + app } diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 2c07c75b01ca1a0c47da5a42c846fcb4bd87a1d0..8355c2fb4089375d89fad9d153c68621f4b4028a 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,2 +1,4 @@ +#[cfg(feature = "password")] +pub mod password; #[cfg(feature = "placeholder")] pub mod placeholder; diff --git a/src/plugins/password/mod.rs b/src/plugins/password/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..739cb809b82b3485eb0c0567c640b8a190982484 --- /dev/null +++ b/src/plugins/password/mod.rs @@ -0,0 +1,113 @@ +use bevy::prelude::*; +use cosmic_text::{Buffer, Edit, Shaping}; + +use crate::{CosmicBuffer, CosmicEditor, CosmicFontSystem, DefaultAttrs, Render}; + +pub struct PasswordPlugin; + +impl Plugin for PasswordPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + 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(), + glyph: '*', + } + } +} + +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 { + editor.with_buffer_mut(|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 + } + + let text = get_text(buffer); + buffer.set_text( + &mut font_system, + password.glyph.to_string().repeat(text.len()).as_str(), + attrs.as_attrs(), + Shaping::Advanced, + ); + password.real_text = text; + }); + + continue; + } + + let text = buffer.get_text(); + 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 { + editor.with_buffer_mut(|buffer| { + buffer.set_text( + &mut font_system, + password.real_text.as_str(), + attrs.as_attrs(), + Shaping::Advanced, + ) + }); + continue; + } + + buffer.set_text( + &mut font_system, + password.real_text.as_str(), + attrs.as_attrs(), + ); + } +}