Skip to content
Snippets Groups Projects
cursor.rs 4.53 KiB
Newer Older
// This will all be rewritten soon, looking toward per-widget cursor control
// Rewrite should address issue #93 too

use crate::*;
sam's avatar
sam committed
use bevy::{input::mouse::MouseMotion, prelude::*, window::PrimaryWindow};

/// System set for mouse cursor systems. Runs in [`Update`]
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub struct CursorSet;

pub struct CursorPlugin;

impl Plugin for CursorPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, ((hover_sprites, hover_ui), change_cursor).chain())
            .add_event::<TextHoverIn>()
            .add_event::<TextHoverOut>();
#[derive(Component, Deref)]
pub struct HoverCursor(pub CursorIcon);

impl Default for HoverCursor {
    fn default() -> Self {
        Self(CursorIcon::Text)
    }
}
/// 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)]
pub struct TextHoverIn(pub CursorIcon);

/// For use with custom cursor control
/// Event is emitted when cursor leaves a text widget
sam's avatar
sam committed
#[derive(Event)]
pub struct TextHoverOut;

pub(crate) fn change_cursor(
    mut evr_hover_in: EventReader<TextHoverIn>,
sam's avatar
sam committed
    evr_hover_out: EventReader<TextHoverOut>,
    evr_text_changed: EventReader<CosmicTextChanged>,
    evr_mouse_motion: EventReader<MouseMotion>,
sam edelsten's avatar
sam edelsten committed
    mouse_buttons: Res<ButtonInput<MouseButton>>,
sam's avatar
sam committed
    mut windows: Query<&mut Window, With<PrimaryWindow>>,
) {
    if windows.iter().len() == 0 {
        return;
    }
sam's avatar
sam committed
    let mut window = windows.single_mut();

    if let Some(ev) = evr_hover_in.read().last() {
        window.cursor.icon = ev.0;
    } else if !evr_hover_out.is_empty() {
sam's avatar
sam committed
        window.cursor.icon = CursorIcon::Default;
    }
sam's avatar
sam committed
    if !evr_text_changed.is_empty() {
        window.cursor.visible = false;
    }
sam's avatar
sam committed
    if mouse_buttons.get_just_pressed().len() != 0 || !evr_mouse_motion.is_empty() {
        window.cursor.visible = true;
    }
}

#[cfg(feature = "multicam")]
type CameraQuery<'a, 'b, 'c, 'd> =
    Query<'a, 'b, (&'c Camera, &'d GlobalTransform), With<CosmicPrimaryCamera>>;

#[cfg(not(feature = "multicam"))]
type CameraQuery<'a, 'b, 'c, 'd> = Query<'a, 'b, (&'c Camera, &'d GlobalTransform)>;

pub(crate) fn hover_sprites(
sam's avatar
sam committed
    windows: Query<&Window, With<PrimaryWindow>>,
    mut cosmic_edit_query: Query<
        (&mut Sprite, &Visibility, &GlobalTransform, &HoverCursor),
        With<CosmicBuffer>,
    >,
    camera_q: CameraQuery,
sam's avatar
sam committed
    mut hovered: Local<bool>,
    mut last_hovered: Local<bool>,
    mut evw_hover_in: EventWriter<TextHoverIn>,
    mut evw_hover_out: EventWriter<TextHoverOut>,
) {
    *hovered = false;
    if windows.iter().len() == 0 {
        return;
    }
sam's avatar
sam committed
    let window = windows.single();
    let (camera, camera_transform) = camera_q.single();

    let mut icon = CursorIcon::Default;

    for (sprite, visibility, node_transform, hover) in &mut cosmic_edit_query.iter_mut() {
        if visibility == Visibility::Hidden {
            continue;
        }
        let size = sprite.custom_size.unwrap_or(Vec2::ONE);
sam's avatar
sam committed
        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;
sam's avatar
sam committed
                }
            }
        }
    }

    if *last_hovered != *hovered {
        if *hovered {
            evw_hover_in.send(TextHoverIn(icon));
sam's avatar
sam committed
        } else {
            evw_hover_out.send(TextHoverOut);
        }
    }

    *last_hovered = *hovered;
}

pub(crate) fn hover_ui(
    interaction_query: Query<(&Interaction, &CosmicSource), Changed<Interaction>>,
    cosmic_query: Query<&HoverCursor, With<CosmicBuffer>>,
sam's avatar
sam committed
    mut evw_hover_in: EventWriter<TextHoverIn>,
    mut evw_hover_out: EventWriter<TextHoverOut>,
) {
    for (interaction, source) in interaction_query.iter() {
sam's avatar
sam committed
        match interaction {
            Interaction::None => {
                evw_hover_out.send(TextHoverOut);
            }
            Interaction::Hovered => {
                if let Ok(hover) = cosmic_query.get(source.0) {
                    evw_hover_in.send(TextHoverIn(hover.0));
                }