diff --git a/Cargo.toml b/Cargo.toml index e4a1314..d98250b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ [workspace.package] authors = ["IDEDARY"] - version = "0.2.1" + version = "0.2.2" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/bytestring-net/bevy-lunex" @@ -26,8 +26,8 @@ [workspace.dependencies] - bevy_lunex = { path = "crates/bevy_lunex", version = "0.2.1" } - lunex_engine = { path = "crates/lunex_engine", version = "0.2.1" } + bevy_lunex = { path = "crates/bevy_lunex", version = "0.2.2" } + lunex_engine = { path = "crates/lunex_engine", version = "0.2.2" } colored = { version = "^2.1" } indexmap = { version = "^2.1" } diff --git a/README.md b/README.md index 631d7a6..280d704 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ For production ready example/template check out [`Bevypunk source code`](https:/ | Bevy | Bevy Lunex | |--------|-----------------| -| 0.14.0 | 0.2.0 - 0.2.1 | +| 0.14.0 | 0.2.0 - 0.2.2 | | 0.13.2 | 0.1.0 | | 0.12.1 | 0.0.10 - 0.0.11 | | 0.12.0 | 0.0.7 - 0.0.9 | diff --git a/crates/bevy_lunex/README.md b/crates/bevy_lunex/README.md index 631d7a6..280d704 100644 --- a/crates/bevy_lunex/README.md +++ b/crates/bevy_lunex/README.md @@ -89,7 +89,7 @@ For production ready example/template check out [`Bevypunk source code`](https:/ | Bevy | Bevy Lunex | |--------|-----------------| -| 0.14.0 | 0.2.0 - 0.2.1 | +| 0.14.0 | 0.2.0 - 0.2.2 | | 0.13.2 | 0.1.0 | | 0.12.1 | 0.0.10 - 0.0.11 | | 0.12.0 | 0.0.7 - 0.0.9 | diff --git a/crates/bevy_lunex/src/logic/actions.rs b/crates/bevy_lunex/src/logic/actions.rs index e88b794..5f472fd 100644 --- a/crates/bevy_lunex/src/logic/actions.rs +++ b/crates/bevy_lunex/src/logic/actions.rs @@ -159,7 +159,7 @@ fn apply_event_hide_cursor_2d(mut events: EventReader, mut query: for mut cursor in &mut query { #[cfg(feature = "verbose")] info!("{} - Set cursor to hidden: {}", "EVENT".purple().bold(), event.0); - cursor.hidden = event.0; + cursor.visible = !event.0; } } } diff --git a/crates/bevy_lunex/src/logic/cursor.rs b/crates/bevy_lunex/src/logic/cursor.rs index 92ff2da..e08e982 100644 --- a/crates/bevy_lunex/src/logic/cursor.rs +++ b/crates/bevy_lunex/src/logic/cursor.rs @@ -1,10 +1,13 @@ use crate::*; -use bevy::{utils::HashMap, window::{CursorGrabMode, PrimaryWindow}}; - +use bevy::{input::{gamepad::GamepadButtonChangedEvent, mouse::MouseButtonInput, ButtonState}, render::camera::RenderTarget, utils::HashMap, window::{CursorGrabMode, PrimaryWindow, WindowRef}}; +use picking_core::PickSet; +use pointer::{InputMove, InputPress, Location}; // #===================# // #=== CURSOR TYPE ===# +/// Component for easy cursor control. +/// Read more about it in the [docs](https://bytestring-net.github.io/bevy_lunex/advanced/3_cursor.html) #[derive(Component, Default)] pub struct Cursor2d { /// Indicates which cursor is being requested. @@ -13,12 +16,12 @@ pub struct Cursor2d { cursor_request_priority: f32, /// Map which cursor has which atlas index and offset cursor_atlas_map: HashMap, - /// A toggle if this cursor should replace the native cursor - native_cursor: bool, - /// If the cursor is allowed to leave window + /// Location of the cursor (same as [`Transform`] without sprite offset). + pub location: Vec2, + /// If the cursor is allowed to leave window. Does nothing is cursor is controlled by gamepad. pub confined: bool, - /// A toggle if the cursor should be hidden - pub hidden: bool, + /// A toggle if the cursor should be visible + pub visible: bool, } impl Cursor2d { /// Creates new default Cursor2d. @@ -27,21 +30,11 @@ impl Cursor2d { cursor_request: CursorIcon::Default, cursor_request_priority: 0.0, cursor_atlas_map: HashMap::new(), - native_cursor: true, + location: Vec2::ZERO, confined: false, - hidden: false, + visible: true, } } - /// If the cursor is allowed to leave window - pub fn confined(mut self, confined: bool) -> Self { - self.confined = confined; - self - } - /// A toggle if this cursor should be native - pub fn native_cursor(mut self, enable: bool) -> Self { - self.native_cursor = enable; - self - } /// A method to request a new cursor icon. Works only if priority is higher than already set priority this tick. pub fn request_cursor(&mut self, request: CursorIcon, priority: f32) { if priority > self.cursor_request_priority { @@ -49,58 +42,292 @@ impl Cursor2d { self.cursor_request_priority = priority; } } - /// Adds a new index and offset to the cursor. - pub fn register_cursor(mut self, icon: CursorIcon, index: usize, offset: impl Into) -> Self { + /// This function binds the specific cursor icon to an image index that is used if the entity has texture atlas attached to it. + pub fn set_index(mut self, icon: CursorIcon, index: usize, offset: impl Into) -> Self { self.cursor_atlas_map.insert(icon, (index, offset.into())); self } } -fn cursor_update( +/// This will make the [`Cursor2d`] controllable by specific gamepad. +#[derive(Component, Debug, Clone, PartialEq)] +pub struct GamepadCursor { + /// Gamepad index + pub id: usize, + /// This struct defines how should the cursor movement behave. + pub mode: GamepadCursorMode, + /// Cursor speed scale + pub speed: f32, +} +impl GamepadCursor { + /// Creates a new instance from gamepad id. + pub fn new(id: usize) -> Self { + Self { id, ..Default::default() } + } +} +impl Default for GamepadCursor { + fn default() -> Self { + Self { id: 0, mode: Default::default(), speed: 1.0 } + } +} + + +/// This struct defines how should the cursor movement behave. +#[derive(Debug, Clone, Default, PartialEq)] +pub enum GamepadCursorMode { + /// Cursor will freely move on input. + #[default] + Free, + // /// Will try to snap to nearby nodes on input. + //Snap, +} + + +// #========================# +// #=== CURSOR FUNCTIONS ===# + +/// This function controls the visibility of the cursor +fn cursor_set_visibility( mut windows: Query<&mut Window, With>, - cameras: Query<&OrthographicProjection>, - mut query: Query<(&Cursor2d, &Parent, &mut Transform, &mut Visibility)> + mut query: Query<(&Cursor2d, Option<&mut Visibility>, Has, Has>)> ) { if let Ok(mut window) = windows.get_single_mut() { - for (cursor, parent, mut transform, mut visibility) in &mut query { + for (cursor, optional_visibility, has_gamepad, has_image) in &mut query { + // If we have visibility then change it + if let Some(mut visibility) = optional_visibility { + *visibility = if cursor.visible { Visibility::Visible } else { Visibility::Hidden }; + if window.cursor_position().is_none() && !has_gamepad { *visibility = Visibility::Hidden } + } + + // If it is not a gamepad + if !has_gamepad { + // Set native cursor to invisible if image is attached to the cursor + window.cursor.visible = if has_image { false } else { cursor.visible }; + } + } + } +} - window.cursor.visible = if cursor.native_cursor { !cursor.hidden } else { false }; +/// This function controls the native mouse cursor settings +fn cursor_change_native( + mut windows: Query<&mut Window, With>, + mut query: Query<&Cursor2d, Without> +) { + if let Ok(mut window) = windows.get_single_mut() { + for cursor in &mut query { + // Change native cursor if window.cursor.visible { window.cursor.icon = cursor.cursor_request; } - if cursor.confined { - window.cursor.grab_mode = CursorGrabMode::Confined; - } else { - window.cursor.grab_mode = CursorGrabMode::None; + // Change grab mode + window.cursor.grab_mode = if cursor.confined { CursorGrabMode::Confined } else { CursorGrabMode::None } + } + } +} + + +/// This function controls the location of the cursor based on gamepad input +fn gamepad_move_cursor( + axis: Res>, + time: Res