use crate::plugin::MapStateResource;
use bevy::ecs::message::MessageReader;
use bevy::input::mouse::{MouseMotion, MouseWheel};
use bevy::input::touch::TouchInput;
use bevy::prelude::*;
use rustial_engine::{CameraMode, InputEvent, TouchPhase as EngineTouchPhase};
const ROTATE_SENSITIVITY: f64 = 0.005;
const ZOOM_SENSITIVITY: f64 = 0.1;
const ZOOM_FACTOR_MIN: f64 = 0.1;
const ZOOM_FACTOR_MAX: f64 = 10.0;
#[derive(Resource)]
pub struct MapInputEnabled(pub bool);
impl Default for MapInputEnabled {
fn default() -> Self {
Self(true)
}
}
#[derive(Resource, Default)]
pub(crate) struct PrevCursorPos(pub Option<Vec2>);
pub(crate) fn sync_viewport(windows: Query<&Window>, mut state: ResMut<MapStateResource>) {
if let Ok(window) = windows.single() {
let w = window.resolution.width() as u32;
let h = window.resolution.height() as u32;
if w > 0
&& h > 0
&& (state.0.camera().viewport_width() != w || state.0.camera().viewport_height() != h)
{
state.0.set_viewport(w, h);
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn handle_default_input(
enabled: Res<MapInputEnabled>,
mouse_buttons: Res<ButtonInput<MouseButton>>,
keys: Res<ButtonInput<KeyCode>>,
mut cursor_events: MessageReader<CursorMoved>,
mut mouse_motion: MessageReader<MouseMotion>,
mut mouse_wheel: MessageReader<MouseWheel>,
touch_events: Option<MessageReader<TouchInput>>,
windows: Query<&Window>,
mut state: ResMut<MapStateResource>,
mut prev_cursor: ResMut<PrevCursorPos>,
) {
if !enabled.0 {
for _ in cursor_events.read() {}
for _ in mouse_motion.read() {}
for _ in mouse_wheel.read() {}
if let Some(mut touch) = touch_events {
for _ in touch.read() {}
}
return;
}
let mut pan_delta = Vec2::ZERO;
let mut current_prev = prev_cursor.0;
for event in cursor_events.read() {
let current = event.position;
if mouse_buttons.pressed(MouseButton::Left) {
if let Some(prev) = current_prev {
pan_delta += current - prev;
}
}
current_prev = Some(current);
}
if let Some(pos) = current_prev {
prev_cursor.0 = Some(pos);
}
let mut rotate_delta = Vec2::ZERO;
for motion in mouse_motion.read() {
if mouse_buttons.pressed(MouseButton::Right) {
rotate_delta += motion.delta;
}
}
if pan_delta.length_squared() > 0.0 {
if let Some(pos) = current_prev {
let x = (pos.x - pan_delta.x) as f64;
let y = (pos.y - pan_delta.y) as f64;
state.0.handle_input(InputEvent::pan_at(
pan_delta.x as f64,
pan_delta.y as f64,
x,
y,
));
} else {
state
.0
.handle_input(InputEvent::pan(pan_delta.x as f64, pan_delta.y as f64));
}
}
if rotate_delta.length_squared() > 0.0 {
state.0.handle_input(InputEvent::Rotate {
delta_yaw: -(rotate_delta.x as f64) * ROTATE_SENSITIVITY,
delta_pitch: -(rotate_delta.y as f64) * ROTATE_SENSITIVITY,
});
}
let zoom_cursor = windows
.single()
.ok()
.and_then(Window::cursor_position)
.or(current_prev)
.or(prev_cursor.0);
for wheel in mouse_wheel.read() {
let y = wheel.y as f64;
if y.abs() > f64::EPSILON {
let raw_factor = if y > 0.0 {
1.0 + y * ZOOM_SENSITIVITY
} else {
1.0 / (1.0 + (-y) * ZOOM_SENSITIVITY)
};
if raw_factor.is_finite() && raw_factor > 0.0 {
let factor = raw_factor.clamp(ZOOM_FACTOR_MIN, ZOOM_FACTOR_MAX);
if let Some(pos) = zoom_cursor {
state.0.handle_input(InputEvent::Zoom {
factor,
x: Some(pos.x as f64),
y: Some(pos.y as f64),
});
} else {
state.0.handle_input(InputEvent::Zoom {
factor,
x: None,
y: None,
});
}
}
}
}
if let Some(mut touch_reader) = touch_events {
for touch in touch_reader.read() {
let phase = match touch.phase {
bevy::input::touch::TouchPhase::Started => EngineTouchPhase::Started,
bevy::input::touch::TouchPhase::Moved => EngineTouchPhase::Moved,
bevy::input::touch::TouchPhase::Ended => EngineTouchPhase::Ended,
bevy::input::touch::TouchPhase::Canceled => EngineTouchPhase::Cancelled,
};
state.0.handle_input(InputEvent::touch(
touch.id,
phase,
touch.position.x as f64,
touch.position.y as f64,
));
}
}
if keys.just_pressed(KeyCode::Space) {
let new_mode = match state.0.camera().mode() {
CameraMode::Perspective => CameraMode::Orthographic,
CameraMode::Orthographic => CameraMode::Perspective,
};
state.0.set_camera_mode(new_mode);
}
}