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