From e8a853a64ba9bf6a424b59828419764edf97a63a Mon Sep 17 00:00:00 2001
From: Caleb Yates <105958073+ActuallyHappening@users.noreply.github.com>
Date: Fri, 29 Nov 2024 21:32:58 +1000
Subject: [PATCH] Code quality improvements (#164)

* fix: Implement reflect for FocusedWidget

* refactor: using `bevy_math::Rect.contains` implementation to clean up code

* fix: clippy is happy now

* fix: fmt is happy now

* add: Lots of Reflect impls and type registrations

* refactor: moved certain type registrations into their own modules
---
 src/cosmic_edit.rs | 22 +++++++++++-----------
 src/cursor.rs      | 30 ++++++++++++++++--------------
 src/events.rs      |  5 +++--
 src/focus.rs       |  6 ++++--
 src/input.rs       | 14 +++++++-------
 src/lib.rs         | 15 +++++++++++++--
 src/render.rs      |  1 +
 src/util.rs        | 29 ++++++++++++++++++-----------
 src/widget.rs      |  8 +++++---
 9 files changed, 78 insertions(+), 52 deletions(-)

diff --git a/src/cosmic_edit.rs b/src/cosmic_edit.rs
index 953b28d..392bb8e 100644
--- a/src/cosmic_edit.rs
+++ b/src/cosmic_edit.rs
@@ -3,7 +3,7 @@ use bevy::prelude::*;
 use cosmic_text::{Attrs, AttrsOwned, Editor, FontSystem};
 
 /// Enum representing text wrapping in a cosmic [`Buffer`]
-#[derive(Clone, Component, PartialEq, Default)]
+#[derive(Component, Reflect, Clone, PartialEq, Default)]
 pub enum CosmicWrap {
     InfiniteLine,
     #[default]
@@ -11,7 +11,7 @@ pub enum CosmicWrap {
 }
 
 /// Enum representing the text alignment in a cosmic [`Buffer`]
-#[derive(Clone, Component)]
+#[derive(Component, Reflect, Clone)]
 pub enum CosmicTextAlign {
     Center { padding: i32 },
     TopLeft { padding: i32 },
@@ -30,7 +30,7 @@ impl Default for CosmicTextAlign {
 pub struct ReadOnly; // tag component
 
 /// Internal value used to decide what section of a [`Buffer`] to render
-#[derive(Component, Debug, Default)]
+#[derive(Component, Reflect, Debug, Default)]
 pub struct XOffset {
     pub left: f32,
     pub width: f32,
@@ -47,32 +47,32 @@ impl Default for DefaultAttrs {
 }
 
 /// Image to be used as a buffer's background
-#[derive(Component, Default)]
+#[derive(Component, Reflect, Default)]
 pub struct CosmicBackgroundImage(pub Option<Handle<Image>>);
 
 /// Color to be used as a buffer's background
-#[derive(Component, Default, Deref)]
+#[derive(Component, Reflect, Default, Deref)]
 pub struct CosmicBackgroundColor(pub Color);
 
 /// Color to be used for the text cursor
-#[derive(Component, Default, Deref)]
+#[derive(Component, Reflect, Default, Deref)]
 pub struct CursorColor(pub Color);
 
 /// Color to be used as the selected text background
-#[derive(Component, Default, Deref)]
+#[derive(Component, Reflect, Default, Deref)]
 pub struct SelectionColor(pub Color);
 
 /// Color to be used for the selected text
-#[derive(Component, Default, Deref)]
+#[derive(Component, Reflect, Default, Deref)]
 pub struct SelectedTextColor(pub Color);
 
 /// Maximum number of lines allowed in a buffer
-#[derive(Component, Default)]
+#[derive(Component, Reflect, Default)]
 pub struct MaxLines(pub usize);
 
 /// Maximum number of characters allowed in a buffer
 // TODO: Check this functionality with widechars; Use graphemes to test?
-#[derive(Component, Default)]
+#[derive(Component, Reflect, Default)]
 pub struct MaxChars(pub usize);
 
 /// Buffer does not respond to scroll events
@@ -111,7 +111,7 @@ pub struct ScrollDisabled;
 /// #         .add_plugins(CosmicEditPlugin::default())
 /// #         .add_systems(Startup, setup);
 /// # }
-#[derive(Component)]
+#[derive(Component, Reflect)]
 pub struct CosmicSource(pub Entity);
 
 /// A bundle containing all the required components for [`CosmicBuffer`] functionality.
diff --git a/src/cursor.rs b/src/cursor.rs
index 27354b7..ae3b250 100644
--- a/src/cursor.rs
+++ b/src/cursor.rs
@@ -23,11 +23,12 @@ impl Plugin for CursorPlugin {
                 .run_if(not(resource_exists::<CursorPluginDisabled>)),
         )
         .add_event::<TextHoverIn>()
+        .register_type::<TextHoverIn>()
         .add_event::<TextHoverOut>();
     }
 }
 
-#[derive(Component, Deref)]
+#[derive(Component, Reflect, Deref)]
 pub struct HoverCursor(pub CursorIcon);
 
 impl Default for HoverCursor {
@@ -39,12 +40,12 @@ impl Default for HoverCursor {
 /// For use with custom cursor control
 /// Event is emitted when cursor enters a text widget
 /// Event contains the cursor from the buffer's [`HoverCursor`]
-#[derive(Event, Deref)]
+#[derive(Event, Reflect, Deref, Debug)]
 pub struct TextHoverIn(pub CursorIcon);
 
 /// For use with custom cursor control
 /// Event is emitted when cursor leaves a text widget
-#[derive(Event)]
+#[derive(Event, Debug)]
 pub struct TextHoverOut;
 
 pub(crate) fn change_cursor(
@@ -109,17 +110,18 @@ pub(crate) fn hover_sprites(
         }
 
         let size = sprite.custom_size.unwrap_or(Vec2::ONE);
-        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;
-                    icon = hover.0;
-                }
-            }
+        if get_node_cursor_pos(
+            window,
+            node_transform,
+            size,
+            false,
+            camera,
+            camera_transform,
+        )
+        .is_some()
+        {
+            *hovered = true;
+            icon = hover.0;
         }
     }
 
diff --git a/src/events.rs b/src/events.rs
index e9ecea5..1f542b3 100644
--- a/src/events.rs
+++ b/src/events.rs
@@ -7,12 +7,13 @@ pub(crate) struct EventsPlugin;
 
 impl Plugin for EventsPlugin {
     fn build(&self, app: &mut App) {
-        app.add_event::<CosmicTextChanged>();
+        app.add_event::<CosmicTextChanged>()
+            .register_type::<CosmicTextChanged>();
     }
 }
 
 /// Text change events
 /// Sent when text is changed in a cosmic buffer
 /// Contains the entity on which the text was changed, and the new text as a [`String`]
-#[derive(Event, Debug)]
+#[derive(Event, Reflect, Debug)]
 pub struct CosmicTextChanged(pub (Entity, String));
diff --git a/src/focus.rs b/src/focus.rs
index 522b18e..88cf6e5 100644
--- a/src/focus.rs
+++ b/src/focus.rs
@@ -17,12 +17,14 @@ impl Plugin for FocusPlugin {
                 .in_set(FocusSet)
                 .after(WidgetSet),
         )
-        .init_resource::<FocusedWidget>();
+        .init_resource::<FocusedWidget>()
+        .register_type::<FocusedWidget>();
     }
 }
 
 /// Resource struct that keeps track of the currently active editor entity.
-#[derive(Resource, Default, Deref, DerefMut)]
+#[derive(Resource, Reflect, Default, Deref, DerefMut)]
+#[reflect(Resource)]
 pub struct FocusedWidget(pub Option<Entity>);
 
 pub(crate) fn add_editor_to_focused(
diff --git a/src/input.rs b/src/input.rs
index 0b1df13..931028b 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -125,8 +125,8 @@ pub(crate) fn input_mouse(
 
         let mut is_ui_node = false;
         let mut transform = sprite_transform;
-        let (mut width, mut height) =
-            (sprite.custom_size.unwrap().x, sprite.custom_size.unwrap().y);
+        let sprite_size = sprite.custom_size.expect("Must specify Sprite.custom_size");
+        let (mut width, mut height) = (sprite_size.x, sprite_size.y);
 
         // TODO: this is bad loop nesting, rethink system with relationships in mind
         for (node, node_transform, source) in node_q.iter() {
@@ -159,10 +159,10 @@ pub(crate) fn input_mouse(
                 get_y_offset_center(height * scale_factor, &buffer),
             ),
         };
-        let point = |node_cursor_pos: (f32, f32)| {
+        let point = |node_cursor_pos: Vec2| {
             (
-                (node_cursor_pos.0 * scale_factor) as i32 - padding_x,
-                (node_cursor_pos.1 * scale_factor) as i32 - padding_y,
+                (node_cursor_pos.x * scale_factor) as i32 - padding_x,
+                (node_cursor_pos.y * scale_factor) as i32 - padding_y,
             )
         };
 
@@ -173,7 +173,7 @@ pub(crate) fn input_mouse(
             if let Some(node_cursor_pos) = get_node_cursor_pos(
                 primary_window,
                 transform,
-                (width, height),
+                Vec2::new(width, height),
                 is_ui_node,
                 camera,
                 camera_transform,
@@ -213,7 +213,7 @@ pub(crate) fn input_mouse(
             if let Some(node_cursor_pos) = get_node_cursor_pos(
                 primary_window,
                 transform,
-                (width, height),
+                Vec2::new(width, height),
                 is_ui_node,
                 camera,
                 camera_transform,
diff --git a/src/lib.rs b/src/lib.rs
index 18eeb5f..1568a32 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -138,6 +138,18 @@ impl Plugin for CosmicEditPlugin {
         ))
         .insert_resource(CosmicFontSystem(font_system));
 
+        app.register_type::<CosmicWrap>()
+            .register_type::<CosmicTextAlign>()
+            .register_type::<XOffset>()
+            .register_type::<CosmicBackgroundImage>()
+            .register_type::<CosmicBackgroundColor>()
+            .register_type::<CursorColor>()
+            .register_type::<SelectionColor>()
+            .register_type::<MaxLines>()
+            .register_type::<MaxChars>()
+            .register_type::<CosmicSource>()
+            .register_type::<HoverCursor>();
+
         #[cfg(target_arch = "wasm32")]
         {
             let (tx, rx) = crossbeam_channel::bounded::<WasmPaste>(1);
@@ -148,7 +160,7 @@ impl Plugin for CosmicEditPlugin {
 }
 
 /// Attach to primary camera, and enable the `multicam` feature to use multiple cameras.
-/// Will panic if no `Camera`s without this component exist and the `multicam` feature is enabled.
+/// Will panic if no Camera's without this component exist and the `multicam` feature is enabled.
 ///
 /// A very basic example which doesn't panic:
 /// ```rust
@@ -176,7 +188,6 @@ impl Plugin for CosmicEditPlugin {
 ///   });
 /// }
 /// ```
-#[cfg(feature = "multicam")]
 #[derive(Component, Debug, Default)]
 pub struct CosmicPrimaryCamera;
 
diff --git a/src/render.rs b/src/render.rs
index 4466a3c..0fa8a80 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -218,6 +218,7 @@ fn render_texture(
 
         if let Some(prev_image) = images.get_mut(canvas) {
             prev_image.data.clear();
+            // Updates the stored asset image with the computed pixels
             prev_image.data.extend_from_slice(pixels.as_slice());
             prev_image.resize(Extent3d {
                 width: size.0.x as u32,
diff --git a/src/util.rs b/src/util.rs
index c4439f3..7c5233d 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -36,22 +36,26 @@ pub fn deselect_editor_on_esc(i: Res<ButtonInput<KeyCode>>, mut focus: ResMut<Fo
 pub fn get_node_cursor_pos(
     window: &Window,
     node_transform: &GlobalTransform,
-    size: (f32, f32),
+    size: Vec2,
     is_ui_node: bool,
     camera: &Camera,
     camera_transform: &GlobalTransform,
-) -> Option<(f32, f32)> {
-    let (x_min, y_min, x_max, y_max) = (
-        node_transform.affine().translation.x - size.0 / 2.,
-        node_transform.affine().translation.y - size.1 / 2.,
-        node_transform.affine().translation.x + size.0 / 2.,
-        node_transform.affine().translation.y + size.1 / 2.,
+) -> Option<Vec2> {
+    let node_translation = node_transform.affine().translation;
+    let node_bounds = Rect::new(
+        node_translation.x - size.x / 2.,
+        node_translation.y - size.y / 2.,
+        node_translation.x + size.x / 2.,
+        node_translation.y + size.y / 2.,
     );
 
     window.cursor_position().and_then(|pos| {
         if is_ui_node {
-            if x_min < pos.x && pos.x < x_max && y_min < pos.y && pos.y < y_max {
-                Some((pos.x - x_min, pos.y - y_min))
+            if node_bounds.contains(pos) {
+                Some(Vec2::new(
+                    pos.x - node_bounds.min.x,
+                    pos.y - node_bounds.min.y,
+                ))
             } else {
                 None
             }
@@ -59,8 +63,11 @@ pub fn get_node_cursor_pos(
             camera
                 .viewport_to_world_2d(camera_transform, pos)
                 .and_then(|pos| {
-                    if x_min < pos.x && pos.x < x_max && y_min < pos.y && pos.y < y_max {
-                        Some((pos.x - x_min, y_max - pos.y))
+                    if node_bounds.contains(pos) {
+                        Some(Vec2::new(
+                            pos.x - node_bounds.min.x,
+                            node_bounds.max.y - pos.y,
+                        ))
                     } else {
                         None
                     }
diff --git a/src/widget.rs b/src/widget.rs
index 6e4b2f8..c6410af 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -24,21 +24,23 @@ impl Plugin for WidgetPlugin {
                     .chain()
                     .in_set(WidgetSet)
                     .after(TransformSystem::TransformPropagate),
-            );
+            )
+            .register_type::<CosmicPadding>()
+            .register_type::<CosmicWidgetSize>();
     }
 }
 
 /// Wrapper for a [`Vec2`] describing the horizontal and vertical padding of a widget.
 /// This is set programatically, not for user modification.
 /// To set a widget's padding, use [`CosmicTextAlign`]
-#[derive(Component, Default, Deref, DerefMut, Debug)]
+#[derive(Component, Reflect, Default, Deref, DerefMut, Debug)]
 pub struct CosmicPadding(pub Vec2);
 
 /// Wrapper for a [`Vec2`] describing the horizontal and vertical size of a widget.
 /// This is set programatically, not for user modification.
 /// To set a widget's size, use either it's [`Sprite`] dimensions or modify the target UI element's
 /// size.
-#[derive(Component, Default, Deref, DerefMut)]
+#[derive(Component, Reflect, Default, Deref, DerefMut)]
 pub struct CosmicWidgetSize(pub Vec2);
 
 /// Reshapes text in a [`CosmicEditor`]
-- 
GitLab