Skip to content
Snippets Groups Projects
lib.rs 6.58 KiB
Newer Older
StarArawn's avatar
StarArawn committed
use bevy::{
MrGVSV's avatar
MrGVSV committed
    input::{
        keyboard::KeyboardInput,
        mouse::{MouseButtonInput, MouseScrollUnit, MouseWheel},
StarToaster's avatar
StarToaster committed
        ButtonState,
MrGVSV's avatar
MrGVSV committed
    },
StarArawn's avatar
StarArawn committed
    math::Vec2,
StarArawn's avatar
StarArawn committed
    prelude::{EventReader, IntoExclusiveSystem, MouseButton, Plugin, Res, World},
StarArawn's avatar
StarArawn committed
    render::color::Color,
StarArawn's avatar
StarArawn committed
    window::{CursorMoved, ReceivedCharacter, WindowCreated, WindowResized, Windows},
StarArawn's avatar
StarArawn committed
};
StarArawn's avatar
StarArawn committed

mod bevy_context;
MrGVSV's avatar
MrGVSV committed
mod cursor;
StarArawn's avatar
StarArawn committed
mod key;
StarArawn's avatar
StarArawn committed
mod render;

MrGVSV's avatar
MrGVSV committed
use crate::cursor::convert_cursor_icon;
StarArawn's avatar
StarArawn committed
pub use bevy_context::BevyContext;
TheRawMeatball's avatar
TheRawMeatball committed
pub use bevy_kayak_renderer::camera::*;
use kayak_core::{bind, Binding, InputEvent, MutableBound};
TheRawMeatball's avatar
TheRawMeatball committed
pub use render::font::FontMapping;
pub use render::image::ImageManager;
StarArawn's avatar
StarArawn committed

#[derive(Default)]
pub struct BevyKayakUIPlugin;

impl Plugin for BevyKayakUIPlugin {
    fn build(&self, app: &mut bevy::prelude::App) {
        app.insert_resource(bind(WindowSize::default()))
TheRawMeatball's avatar
TheRawMeatball committed
            .add_plugin(bevy_kayak_renderer::BevyKayakRendererPlugin)
            .add_plugin(render::BevyKayakUIExtractPlugin)
            .add_system(update_window_size)
StarArawn's avatar
StarArawn committed
            .add_system(process_events.exclusive_system())
            .add_system(update.exclusive_system());
StarArawn's avatar
StarArawn committed
    }
}

pub(crate) fn to_bevy_color(color: &kayak_core::color::Color) -> Color {
    Color::rgba(color.r, color.g, color.b, color.a)
}

pub fn update(world: &mut World) {
    if let Some(bevy_context) = world.remove_resource::<BevyContext>() {
        if let Ok(mut context) = bevy_context.kayak_context.write() {
MrGVSV's avatar
MrGVSV committed
            context.set_global(std::mem::take(world));
            context.render();
MrGVSV's avatar
MrGVSV committed
            *world = context.remove_global::<World>().unwrap();

            if let Some(ref mut windows) = world.get_resource_mut::<Windows>() {
                if let Some(window) = windows.get_primary_mut() {
                    window.set_cursor_icon(convert_cursor_icon(context.cursor_icon()));
                }
            }
        world.insert_resource(bevy_context);
StarArawn's avatar
StarArawn committed
pub fn process_events(world: &mut World) {
    let window_size = if let Some(windows) = world.get_resource::<Windows>() {
        if let Some(window) = windows.get_primary() {
            Vec2::new(window.width(), window.height())
        } else {
            log::warn!("Couldn't find primiary window!");
            return;
StarArawn's avatar
StarArawn committed
        }
StarArawn's avatar
StarArawn committed
    } else {
        log::warn!("Couldn't find primiary window!");
        return;
StarArawn's avatar
StarArawn committed
    if let Some(bevy_context) = world.remove_resource::<BevyContext>() {
        if let Ok(mut context) = bevy_context.kayak_context.write() {
            let mut input_events = Vec::new();
MrGVSV's avatar
MrGVSV committed
            context.set_global(std::mem::take(world));
StarArawn's avatar
StarArawn committed
            context.query_world::<(
                EventReader<CursorMoved>,
                EventReader<MouseButtonInput>,
MrGVSV's avatar
MrGVSV committed
                EventReader<MouseWheel>,
StarArawn's avatar
StarArawn committed
                EventReader<ReceivedCharacter>,
                EventReader<KeyboardInput>,
            ), _, _>(
                |(
MrGVSV's avatar
MrGVSV committed
                    mut cursor_moved_events,
                    mut mouse_button_input_events,
                    mut mouse_wheel_events,
                    mut char_input_events,
                    mut keyboard_input_events,
                )| {
StarArawn's avatar
StarArawn committed
                    if let Some(event) = cursor_moved_events.iter().last() {
                        // Currently, we can only handle a single MouseMoved event at a time so everything but the last needs to be skipped
                        input_events.push(InputEvent::MouseMoved((
                            event.position.x as f32,
                            window_size.y - event.position.y as f32,
                        )));
                    }

                    for event in mouse_button_input_events.iter() {
                        match event.button {
                            MouseButton::Left => {
StarToaster's avatar
StarToaster committed
                                if event.state == ButtonState::Pressed {
StarArawn's avatar
StarArawn committed
                                    input_events.push(InputEvent::MouseLeftPress);
StarToaster's avatar
StarToaster committed
                                } else if event.state == ButtonState::Released {
StarArawn's avatar
StarArawn committed
                                    input_events.push(InputEvent::MouseLeftRelease);
                                }
                            }
                            _ => {}
StarArawn's avatar
StarArawn committed
                    }
MrGVSV's avatar
MrGVSV committed
                    for MouseWheel { x, y, unit } in mouse_wheel_events.iter() {
                        input_events.push(InputEvent::Scroll {
                            dx: *x,
                            dy: *y,
                            is_line: matches!(unit, MouseScrollUnit::Line),
                        })
                    }

StarArawn's avatar
StarArawn committed
                    for event in char_input_events.iter() {
                        input_events.push(InputEvent::CharEvent { c: event.char });
                    }

                    for event in keyboard_input_events.iter() {
                        if let Some(key_code) = event.key_code {
                            let kayak_key_code = key::convert_virtual_key_code(key_code);
                            input_events.push(InputEvent::Keyboard {
                                key: kayak_key_code,
StarToaster's avatar
StarToaster committed
                                is_pressed: matches!(event.state, ButtonState::Pressed),
StarArawn's avatar
StarArawn committed

            context.process_events(input_events);
MrGVSV's avatar
MrGVSV committed
            *world = context.remove_global::<World>().unwrap()
StarArawn's avatar
StarArawn committed

        world.insert_resource(bevy_context);
StarArawn's avatar
StarArawn committed
    }
}

/// Tracks the bevy window size.
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct WindowSize(pub f32, pub f32);

fn update_window_size(
    mut window_resized_events: EventReader<WindowResized>,
    mut window_created_events: EventReader<WindowCreated>,
    windows: Res<Windows>,
    window_size: Res<Binding<WindowSize>>,
) {
    let mut changed_window_ids = Vec::new();
    // handle resize events. latest events are handled first because we only want to resize each
    // window once
    for event in window_resized_events.iter().rev() {
        if changed_window_ids.contains(&event.id) {
            continue;
        }

        changed_window_ids.push(event.id);
    }

    // handle resize events. latest events are handled first because we only want to resize each
    // window once
    for event in window_created_events.iter().rev() {
        if changed_window_ids.contains(&event.id) {
            continue;
        }

        changed_window_ids.push(event.id);
    }

    for window_id in changed_window_ids {
        if let Some(window) = windows.get(window_id) {
            let width = window.width();
            let height = window.height();
            window_size.set(WindowSize(width, height));
        }
    }
}