From c7074a248d5e529d90f7c982806c668dd5efbe00 Mon Sep 17 00:00:00 2001 From: sam <43527203+bytemunch@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:51:57 +0100 Subject: [PATCH] text cursor hover example (#64) * text cursor hover example * move cursor modify fns to src caveats: - only hides cursor when typing if cursor is in app window - only acts on primary window * add config for cursor changes cursor hide does not work on WASM * Update src/cursor.rs Co-authored-by: StaffEngineer <111751109+StaffEngineer@users.noreply.github.com> * fix focus handling in cursor.rs * Update src/lib.rs Co-authored-by: StaffEngineer <111751109+StaffEngineer@users.noreply.github.com> * add none | events | default options for cursor * stop sprites sending hover events per frame * commit missed example --------- Co-authored-by: StaffEngineer <111751109+StaffEngineer@users.noreply.github.com> --- examples/basic_sprite.rs | 5 +- examples/basic_ui.rs | 5 +- examples/bevy_api_testing.rs | 23 +++++++- examples/font_per_widget.rs | 5 +- examples/multiple_sprites.rs | 5 +- examples/readonly.rs | 5 +- src/cursor.rs | 100 +++++++++++++++++++++++++++++++++++ src/lib.rs | 27 ++++++++++ 8 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 src/cursor.rs diff --git a/examples/basic_sprite.rs b/examples/basic_sprite.rs index 35d46cd..e01b445 100644 --- a/examples/basic_sprite.rs +++ b/examples/basic_sprite.rs @@ -51,7 +51,10 @@ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(CosmicEditPlugin { font_config }) + .add_plugins(CosmicEditPlugin { + font_config, + ..default() + }) .add_systems(Startup, setup) .run(); } diff --git a/examples/basic_ui.rs b/examples/basic_ui.rs index 777030f..f02ab72 100644 --- a/examples/basic_ui.rs +++ b/examples/basic_ui.rs @@ -66,7 +66,10 @@ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(CosmicEditPlugin { font_config }) + .add_plugins(CosmicEditPlugin { + font_config, + ..default() + }) .add_systems(Startup, setup) .add_systems(Update, print_text) .run(); diff --git a/examples/bevy_api_testing.rs b/examples/bevy_api_testing.rs index 0b98f53..6f1e9ae 100644 --- a/examples/bevy_api_testing.rs +++ b/examples/bevy_api_testing.rs @@ -17,6 +17,7 @@ fn setup(mut commands: Commands) { cosmic_attrs: CosmicAttrs(AttrsOwned::new( Attrs::new().color(bevy_color_to_cosmic(Color::GREEN)), )), + max_lines: CosmicMaxLines(1), ..default() }); @@ -92,12 +93,32 @@ fn change_active_editor_sprite( } } +fn ev_test( + mut evr_on: EventReader<TextHoverIn>, + mut evr_out: EventReader<TextHoverOut>, + mut evr_type: EventReader<CosmicTextChanged>, +) { + for _ev in evr_on.iter() { + println!("IN"); + } + for _ev in evr_out.iter() { + println!("OUT"); + } + for _ev in evr_type.iter() { + println!("TYPE"); + } +} + fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(CosmicEditPlugin::default()) + .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, ev_test) .run(); } diff --git a/examples/font_per_widget.rs b/examples/font_per_widget.rs index 303862e..6a62805 100644 --- a/examples/font_per_widget.rs +++ b/examples/font_per_widget.rs @@ -293,7 +293,10 @@ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(CosmicEditPlugin { font_config }) + .add_plugins(CosmicEditPlugin { + font_config, + ..default() + }) .add_systems(Startup, setup) .add_systems(Update, change_active_editor_ui) .run(); diff --git a/examples/multiple_sprites.rs b/examples/multiple_sprites.rs index ad24adf..1f1942b 100644 --- a/examples/multiple_sprites.rs +++ b/examples/multiple_sprites.rs @@ -114,7 +114,10 @@ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(CosmicEditPlugin { font_config }) + .add_plugins(CosmicEditPlugin { + font_config, + ..default() + }) .add_systems(Startup, setup) .add_systems(Update, change_active_editor_sprite) .run(); diff --git a/examples/readonly.rs b/examples/readonly.rs index 5247f03..58035b7 100644 --- a/examples/readonly.rs +++ b/examples/readonly.rs @@ -66,7 +66,10 @@ fn main() { App::new() .add_plugins(DefaultPlugins) - .add_plugins(CosmicEditPlugin { font_config }) + .add_plugins(CosmicEditPlugin { + font_config, + ..default() + }) .add_systems(Startup, setup) .run(); } diff --git a/src/cursor.rs b/src/cursor.rs new file mode 100644 index 0000000..d1a8530 --- /dev/null +++ b/src/cursor.rs @@ -0,0 +1,100 @@ +use bevy::{input::mouse::MouseMotion, prelude::*, window::PrimaryWindow}; + +use crate::{CosmicEditor, CosmicTextChanged, ReadOnly}; + +/// For use with custom cursor control; Event is emitted when cursor enters a text widget +#[derive(Event)] +pub struct TextHoverIn; + +/// For use with custom cursor control; Event is emitted when cursor leaves a text widget +#[derive(Event)] +pub struct TextHoverOut; + +pub fn change_cursor( + evr_hover_in: EventReader<TextHoverIn>, + evr_hover_out: EventReader<TextHoverOut>, + evr_text_changed: EventReader<CosmicTextChanged>, + evr_mouse_motion: EventReader<MouseMotion>, + mouse_buttons: Res<Input<MouseButton>>, + mut windows: Query<&mut Window, With<PrimaryWindow>>, +) { + let mut window = windows.single_mut(); + if !evr_hover_in.is_empty() { + window.cursor.icon = CursorIcon::Text; + } + if !evr_hover_out.is_empty() { + window.cursor.icon = CursorIcon::Default; + } + if !evr_text_changed.is_empty() { + window.cursor.visible = false; + } + if mouse_buttons.get_just_pressed().len() != 0 || !evr_mouse_motion.is_empty() { + window.cursor.visible = true; + } +} + +// TODO: Only emit events; If configured to, have a fn to act on the events +pub fn hover_sprites( + windows: Query<&Window, With<PrimaryWindow>>, + mut cosmic_edit_query: Query< + (&mut Sprite, &GlobalTransform), + (With<CosmicEditor>, Without<ReadOnly>), + >, + camera_q: Query<(&Camera, &GlobalTransform)>, + mut hovered: Local<bool>, + mut last_hovered: Local<bool>, + mut evw_hover_in: EventWriter<TextHoverIn>, + mut evw_hover_out: EventWriter<TextHoverOut>, +) { + *hovered = false; + let window = windows.single(); + let (camera, camera_transform) = camera_q.single(); + for (sprite, node_transform) 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 { + *hovered = true; + } + } + } + } + + if *last_hovered != *hovered { + if *hovered { + evw_hover_in.send(TextHoverIn); + } else { + evw_hover_out.send(TextHoverOut); + } + } + + *last_hovered = *hovered; +} + +pub fn hover_ui( + mut interaction_query: Query< + &Interaction, + ( + Changed<Interaction>, + (With<CosmicEditor>, Without<ReadOnly>), + ), + >, + mut evw_hover_in: EventWriter<TextHoverIn>, + mut evw_hover_out: EventWriter<TextHoverOut>, +) { + for interaction in interaction_query.iter_mut() { + match interaction { + Interaction::None => { + evw_hover_out.send(TextHoverOut); + } + Interaction::Hovered => { + evw_hover_in.send(TextHoverIn); + } + _ => {} + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 535de10..d04000f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![allow(clippy::type_complexity)] +mod cursor; + use std::{collections::VecDeque, path::PathBuf, time::Duration}; use bevy::{ @@ -18,6 +20,8 @@ pub use cosmic_text::{ use cosmic_text::{ Affinity, AttrsList, Buffer, BufferLine, Editor, FontSystem, Metrics, Shaping, SwashCache, }; +use cursor::{change_cursor, hover_sprites, hover_ui}; +pub use cursor::{TextHoverIn, TextHoverOut}; use image::{imageops::FilterType, GenericImageView}; #[derive(Clone, Component, PartialEq, Debug)] @@ -369,10 +373,19 @@ pub struct CosmicEditHistory { pub current_edit: usize, } +#[derive(Default)] +pub enum CursorConfig { + #[default] + Default, + Events, + None, +} + /// Plugin struct that adds systems and initializes resources related to cosmic edit functionality. #[derive(Default)] pub struct CosmicEditPlugin { pub font_config: CosmicFontConfig, + pub change_cursor: CursorConfig, } impl Plugin for CosmicEditPlugin { @@ -408,6 +421,20 @@ impl Plugin for CosmicEditPlugin { }) .insert_resource(CosmicFontSystem(font_system)) .add_event::<CosmicTextChanged>(); + + match self.change_cursor { + CursorConfig::Default => { + app.add_systems(Update, (hover_sprites, hover_ui, change_cursor)) + .add_event::<TextHoverIn>() + .add_event::<TextHoverOut>(); + } + CursorConfig::Events => { + app.add_systems(Update, (hover_sprites, hover_ui)) + .add_event::<TextHoverIn>() + .add_event::<TextHoverOut>(); + } + CursorConfig::None => {} + } } } -- GitLab