ambient_input 0.2.0-rc9

Ambient input functionality. Host-only.
Documentation
use std::collections::HashSet;

use ambient_ecs::{components, generated::messages, world_events, Debuggable, Entity, Resource, System, SystemGroup, WorldEventsExt};
use glam::{vec2, Vec2};
use serde::{Deserialize, Serialize};
use winit::event::ModifiersState;
pub use winit::event::{DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent};

pub mod picking;

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct PlayerRawInput {
    pub keys: HashSet<ambient_shared_types::VirtualKeyCode>,
    pub mouse_position: Vec2,
    pub mouse_delta: Vec2,
    pub mouse_wheel: f32,
    pub mouse_buttons: HashSet<ambient_shared_types::MouseButton>,
}

components!("input", {
    event_modifiers_change: ModifiersState,

    @[Debuggable, Resource]
    player_raw_input: PlayerRawInput,
    @[Debuggable, Resource]
    player_prev_raw_input: PlayerRawInput,
});

pub fn init_all_components() {
    picking::init_components();
    init_components();
}

pub fn event_systems() -> SystemGroup<Event<'static, ()>> {
    SystemGroup::new("inputs", vec![Box::new(InputSystem::new())])
}

pub fn resources() -> Entity {
    Entity::new().with_default(player_raw_input()).with_default(player_prev_raw_input())
}

#[derive(Debug)]
pub struct InputSystem {
    modifiers: ModifiersState,
    is_focused: bool,
}

impl InputSystem {
    pub fn new() -> Self {
        Self { modifiers: ModifiersState::empty(), is_focused: true }
    }
}

impl System<Event<'static, ()>> for InputSystem {
    fn run(&mut self, world: &mut ambient_ecs::World, event: &Event<'static, ()>) {
        match event {
            Event::WindowEvent { event, .. } => match event {
                &WindowEvent::Focused(focused) => {
                    self.is_focused = focused;
                    world.resource_mut(world_events()).add_message(messages::WindowFocusChange::new(focused));
                }
                WindowEvent::ReceivedCharacter(c) => {
                    world.resource_mut(world_events()).add_message(messages::WindowKeyboardCharacter::new(c.to_string()));
                }

                WindowEvent::ModifiersChanged(mods) => {
                    self.modifiers = *mods;
                    world.resource_mut(world_events()).add_message(messages::WindowKeyboardModifiersChange::new(mods.bits()));
                }

                WindowEvent::CloseRequested => {
                    world.resource_mut(world_events()).add_message(messages::WindowClose::new());
                }

                WindowEvent::KeyboardInput { input, .. } => {
                    let keycode = input.virtual_keycode.map(|key| ambient_shared_types::VirtualKeyCode::from(key).to_string());
                    let modifiers = self.modifiers.bits();
                    let pressed = match input.state {
                        ElementState::Pressed => true,
                        ElementState::Released => false,
                    };
                    world.resource_mut(world_events()).add_message(messages::WindowKeyboardInput::new(keycode, modifiers, pressed));
                }

                WindowEvent::MouseInput { state, button, .. } => {
                    world.resource_mut(world_events()).add_message(messages::WindowMouseInput::new(
                        ambient_shared_types::MouseButton::from(*button),
                        match state {
                            ElementState::Pressed => true,
                            ElementState::Released => false,
                        },
                    ));
                }

                WindowEvent::MouseWheel { delta, .. } => {
                    world.resource_mut(world_events()).add_message(messages::WindowMouseWheel::new(
                        match *delta {
                            MouseScrollDelta::LineDelta(x, y) => vec2(x, y),
                            MouseScrollDelta::PixelDelta(p) => vec2(p.x as f32, p.y as f32),
                        },
                        matches!(delta, MouseScrollDelta::PixelDelta(..)),
                    ));
                }

                _ => {}
            },

            Event::DeviceEvent { event: DeviceEvent::MouseMotion { delta }, .. } => {
                world.resource_mut(world_events()).add_message(messages::WindowMouseMotion::new(vec2(delta.0 as f32, delta.1 as f32)));
            }
            _ => {}
        }
    }
}

#[derive(Clone)]
pub struct MouseInput {
    pub state: ElementState,
    pub button: MouseButton,
}