From 5f83124db139289ee12cfbabe123adf643f3e9fa Mon Sep 17 00:00:00 2001
From: sam edelsten <samedelsten1@gmail.com>
Date: Wed, 25 Oct 2023 17:47:02 +0100
Subject: [PATCH] basic implementation

adds bullet (U+2022) to font subset

replaces rendered text with bullets in password fields

- selection in password widgets is buggy
---
 examples/login.rs                    | 138 +++++++++++++++++++++++++++
 src/font/FiraMono-Regular-subset.ttf | Bin 18864 -> 18916 bytes
 src/lib.rs                           |   3 +
 src/render.rs                        |  34 +++++--
 4 files changed, 169 insertions(+), 6 deletions(-)
 create mode 100644 examples/login.rs

diff --git a/examples/login.rs b/examples/login.rs
new file mode 100644
index 0000000..ea2e54e
--- /dev/null
+++ b/examples/login.rs
@@ -0,0 +1,138 @@
+use bevy::{prelude::*, window::PrimaryWindow};
+use bevy_cosmic_edit::*;
+
+fn setup(mut commands: Commands) {
+    commands.spawn(Camera2dBundle::default());
+
+    commands
+        .spawn(NodeBundle {
+            style: Style {
+                flex_direction: FlexDirection::Column,
+                width: Val::Px(300.0),
+                ..default()
+            },
+            ..default()
+        })
+        .with_children(|root| {
+            root.spawn(CosmicEditBundle {
+                max_lines: CosmicMaxLines(1),
+                ..default()
+            })
+            .insert(ButtonBundle {
+                style: Style {
+                    // Size and position of text box
+                    width: Val::Px(300.),
+                    height: Val::Px(50.),
+                    margin: UiRect::all(Val::Px(15.0)),
+                    ..default()
+                },
+                background_color: BackgroundColor(Color::WHITE),
+                ..default()
+            })
+            .insert(CosmicEditPlaceholderBundle {
+                text_setter: PlaceholderText(CosmicText::OneStyle("Username".into())),
+                attrs: PlaceholderAttrs(AttrsOwned::new(
+                    Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
+                )),
+            });
+
+            root.spawn(CosmicEditBundle {
+                max_lines: CosmicMaxLines(1),
+                ..default()
+            })
+            .insert(ButtonBundle {
+                style: Style {
+                    // Size and position of text box
+                    width: Val::Px(300.),
+                    height: Val::Px(50.),
+                    margin: UiRect::all(Val::Px(15.0)),
+                    ..default()
+                },
+                background_color: BackgroundColor(Color::WHITE),
+                ..default()
+            })
+            .insert(CosmicEditPlaceholderBundle {
+                text_setter: PlaceholderText(CosmicText::OneStyle("Password".into())),
+                attrs: PlaceholderAttrs(AttrsOwned::new(
+                    Attrs::new().color(bevy_color_to_cosmic(Color::rgb_u8(128, 128, 128))),
+                )),
+            })
+            .insert(PasswordInput);
+        });
+}
+
+fn bevy_color_to_cosmic(color: bevy::prelude::Color) -> CosmicColor {
+    cosmic_text::Color::rgba(
+        (color.r() * 255.) as u8,
+        (color.g() * 255.) as u8,
+        (color.b() * 255.) as u8,
+        (color.a() * 255.) as u8,
+    )
+}
+
+fn change_active_editor_ui(
+    mut commands: Commands,
+    mut interaction_query: Query<
+        (&Interaction, Entity),
+        (
+            Changed<Interaction>,
+            (With<CosmicEditor>, Without<ReadOnly>),
+        ),
+    >,
+) {
+    for (interaction, entity) in interaction_query.iter_mut() {
+        if let Interaction::Pressed = interaction {
+            commands.insert_resource(Focus(Some(entity)));
+        }
+    }
+}
+
+fn change_active_editor_sprite(
+    mut commands: Commands,
+    windows: Query<&Window, With<PrimaryWindow>>,
+    buttons: Res<Input<MouseButton>>,
+    mut cosmic_edit_query: Query<
+        (&mut Sprite, &GlobalTransform, Entity),
+        (With<CosmicEditor>, Without<ReadOnly>),
+    >,
+    camera_q: Query<(&Camera, &GlobalTransform)>,
+) {
+    let window = windows.single();
+    let (camera, camera_transform) = camera_q.single();
+    if buttons.just_pressed(MouseButton::Left) {
+        for (sprite, node_transform, entity) in &mut cosmic_edit_query.iter_mut() {
+            let size = sprite.custom_size.unwrap_or(Vec2::new(1., 1.));
+            let x_min = node_transform.affine().translation.x - size.x / 2.;
+            let y_min = node_transform.affine().translation.y - size.y / 2.;
+            let x_max = node_transform.affine().translation.x + size.x / 2.;
+            let y_max = node_transform.affine().translation.y + size.y / 2.;
+            if let Some(pos) = window.cursor_position() {
+                if let Some(pos) = camera.viewport_to_world_2d(camera_transform, pos) {
+                    if x_min < pos.x && pos.x < x_max && y_min < pos.y && pos.y < y_max {
+                        commands.insert_resource(Focus(Some(entity)))
+                    };
+                }
+            };
+        }
+    }
+}
+
+fn print_changed_input(mut evr_type: EventReader<CosmicTextChanged>) {
+    for ev in evr_type.iter() {
+        println!("Changed: {}", ev.0 .1);
+    }
+}
+
+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_ui)
+        .add_systems(Update, change_active_editor_sprite)
+        .add_systems(Update, print_changed_input)
+        .run();
+}
diff --git a/src/font/FiraMono-Regular-subset.ttf b/src/font/FiraMono-Regular-subset.ttf
index bca868cdd34be48bbd1e23cbe0e91a2ef6c04f0e..c0fee60b0317902b0378bb21537dadf053fcef24 100644
GIT binary patch
delta 533
zcmXX@Pe>F|9RA*nExX93E2~zlG+0QQON(sMAu*(xK}lTPx`dSD?7E9MIph`_f*$SE
z<$-pJyi`O&xgGY<B`Vm0O2R|X5=9<VI&{eJAdcTiJl^}g@6YcK?>(1dU5Ysf01Z59
zpc%&HLHWKq1N2wO#>|Vlp_~?f0kfVsWu}JGWv{$P{XTKROke367%0A>J_;!3hSF!c
z78A`!0P%$SsO?zp$6K$X)V`7%wr>}-+De?c^%?35<8E%w`5yiDhuu4Y+QpnT?NVSf
zGsqasP2L$l^*A#DMBW1NwS3mfG-B0nKtH<kBo6H=>%@M+lX<65Jo+HA%!Ypcr^zYX
zYUyad%96Xp6~`*NVpR;%f0tOhVGTRk=0u+d^m3}eygM~r2+f4H07Ii;!@ZexolPtO
zbsPW>4_-@6zEWmCyu4m_ejCTt4f6OBwU`6^y!J05Q=&)M@}6iBBl4N(QNvoR{4F;7
zS9}N5|2uY23%Eqa|AQERAi)^qQW(RS)mlAH#+&BfylwB-j|;w6HmEPem|RjvN?OfA
zSZUfSl_SgXHh~pX#Ut?<AwEu9vb(FPF&a3rW9SpQezt1lc_#Ie-TO4a0S_DR#JpTH
NbTL*sW=cin?EA<Jh`Im(

delta 484
zcmW+yJxE(&6g~Gn4H1zf5^0A-AGFd|3`&vG!62mOJsN5aO$%=Nj4>H}^-)8K4rXwu
zQz?An&!&qwD70kB(5<a@XepF#Eu}>%T7ozzU5d{WhwnR_d+t5wau1ZeR#JulXyw-c
z!!#ccsaLnk!2KmwBi3Nj)VkypFn^Gzt@LoF>Q&FFPmuRonJ0-<s`L)9x&iI(aHi+>
zO5&dj$QJdntYf>MZ>$%nePb<^eO@%0KgL#p;0X2QNjG11eulrFFx>!}SMv4@hY@5T
z2jI_7%}!ohJnjHORUo!q$k`L&y=a7H48)xR3yuG^1M>Pp*9%UubhQ@RVW6M<)6{g<
zj&`-uGsq0=I(EsGFEYgbMRMb*{m98(=}maR01bRE-07KO(_B*)NY>c!$9<M_og-`j
z0WN^`%WLU<ZvzW`o1=mLGj!;OtmA9g{#(c2o%h;QTH-RUUP@F()V9QR1M{jTM{z)B
zOZ`PIeSkr-`u!uk{!OBfDA3S?d5=304bSt=ynTpj*WajL`UAdDrMqFdMFT4DLp!m`
Y+Q62Nc?t7_K>4kt*p-Kt(sZtJ0d}){Jpcdz

diff --git a/src/lib.rs b/src/lib.rs
index a13ee14..d6d80a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -202,6 +202,9 @@ pub struct PlaceholderText(pub CosmicText);
 #[derive(Component)]
 pub struct PlaceholderAttrs(pub AttrsOwned);
 
+#[derive(Component)]
+pub struct PasswordInput;
+
 impl Default for PlaceholderAttrs {
     fn default() -> Self {
         Self(AttrsOwned::new(Attrs::new()))
diff --git a/src/render.rs b/src/render.rs
index cb585f6..4f0b935 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -12,7 +12,7 @@ use image::{imageops::FilterType, GenericImageView};
 use crate::{
     get_text_size, get_x_offset_center, get_y_offset_center, CosmicAttrs, CosmicBackground,
     CosmicCanvas, CosmicEditor, CosmicFontSystem, CosmicMetrics, CosmicMode, CosmicTextPosition,
-    FillColor, Focus, Placeholder, ReadOnly, XOffset,
+    FillColor, Focus, PasswordInput, Placeholder, ReadOnly, XOffset,
 };
 
 #[derive(Resource)]
@@ -43,6 +43,7 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         &mut XOffset,
         &CosmicMode,
         Option<&mut Placeholder>,
+        Option<&PasswordInput>,
     )>,
     mut font_system: ResMut<CosmicFontSystem>,
 ) {
@@ -50,7 +51,7 @@ pub(crate) fn cosmic_edit_redraw_buffer(
     let scale = primary_window.scale_factor() as f32;
 
     for (
-        mut editor,
+        mut cosmic_editor,
         attrs,
         background_image,
         fill_color,
@@ -62,18 +63,30 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         mut x_offset,
         mode,
         mut placeholder_opt,
+        password_opt,
     ) in &mut cosmic_edit_query.iter_mut()
     {
-        if !editor.0.buffer().redraw() {
+        if !cosmic_editor.0.buffer().redraw() {
             continue;
         }
 
+        let current_text = cosmic_editor.get_text();
+
+        // intercept text for password inputs
+        if password_opt.is_some() && !current_text.is_empty() {
+            cosmic_editor.set_text(
+                crate::CosmicText::OneStyle("•".repeat(current_text.len())),
+                attrs.0.clone(),
+                &mut font_system.0,
+            );
+        }
+
         // Check for placeholder, replace editor if found and buffer is empty
-        let editor = if editor.get_text().is_empty() && placeholder_opt.is_some() {
+        let editor = if current_text.is_empty() && placeholder_opt.is_some() {
             let placeholder = &mut placeholder_opt.as_mut().unwrap().0 .0;
             placeholder.buffer_mut().set_redraw(true);
 
-            editor.0.buffer_mut().set_redraw(true);
+            cosmic_editor.0.buffer_mut().set_redraw(true);
 
             let mut cursor = placeholder.cursor();
             cursor.index = 0;
@@ -81,7 +94,7 @@ pub(crate) fn cosmic_edit_redraw_buffer(
             *x_offset = XOffset(None);
             placeholder
         } else {
-            &mut editor.0
+            &mut cosmic_editor.0
         };
 
         editor.shape_as_needed(&mut font_system.0);
@@ -262,6 +275,15 @@ pub(crate) fn cosmic_edit_redraw_buffer(
         }
 
         editor.buffer_mut().set_redraw(false);
+
+        // reset intercepted text
+        if password_opt.is_some() && !current_text.is_empty() {
+            cosmic_editor.set_text(
+                crate::CosmicText::OneStyle(current_text),
+                attrs.0.clone(),
+                &mut font_system.0,
+            );
+        }
     }
 }
 
-- 
GitLab