use bevy::prelude::*;
use bevy_enhanced_input::prelude::*;
#[macro_export]
macro_rules! action {
($(#[$meta:meta])* $action:ident) => {
$(#[$meta])*
#[derive(InputAction)]
#[action_output(bool)]
pub struct $action;
};
}
/// Generates a Bevy `Event` struct with `Reflect` support.
#[macro_export]
macro_rules! event {
($(#[$meta:meta])* $event:ident) => {
$(#[$meta])*
#[derive(Event, Reflect, Default)]
#[reflect(Event)]
pub struct $event;
};
($(#[$meta:meta])* $event:ident { $($field:ident : $ty:ty),+ $(,)? }) => {
$(#[$meta])*
#[derive(Event, Reflect)]
#[reflect(Event)]
pub struct $event {
$(pub $field: $ty,)+
}
};
}
#[macro_export]
macro_rules! bind_action_system {
($app:expr, $action:ty, $event:ty, $command:path) => {
$app.add_observer(
|_: On<bevy_enhanced_input::action::events::Start<$action>>, mut commands: Commands| {
commands.trigger(<$event>::default());
},
)
.add_observer(|_: On<$event>, mut commands: Commands| {
commands.run_system_cached($command);
})
};
}
#[derive(InputAction)]
#[action_output(bool)]
struct PrimaryShortcutsModifier;
#[derive(InputAction)]
#[action_output(bool)]
struct AltModifier;
#[derive(InputAction)]
#[action_output(bool)]
struct ControlModifier;
pub struct Keybindings<C: Component> {
all_modifiers: Vec<Entity>,
non_shift_modifiers: Vec<Entity>,
settings: ActionSettings,
_marker: std::marker::PhantomData<C>,
}
impl<C: Component> Keybindings<C> {
pub fn new<S: InputAction>(spawner: &mut ActionSpawner<C>, settings: ActionSettings) -> Self {
let non_consuming_modifier = ActionSettings {
consume_input: false,
require_reset: true,
..default()
};
let primary_modifier_bindings = if cfg!(target_os = "macos") {
bindings![KeyCode::SuperLeft, KeyCode::SuperRight]
} else {
bindings![KeyCode::ControlLeft, KeyCode::ControlRight]
};
let shift = spawner
.spawn((
Action::<S>::new(),
non_consuming_modifier,
bindings![KeyCode::ShiftLeft, KeyCode::ShiftRight],
))
.id();
let primary = spawner
.spawn((
Action::<PrimaryShortcutsModifier>::new(),
non_consuming_modifier,
primary_modifier_bindings,
))
.id();
let alt = spawner
.spawn((
Action::<AltModifier>::new(),
non_consuming_modifier,
bindings![KeyCode::AltLeft, KeyCode::AltRight],
))
.id();
let mut all_modifiers = vec![shift, primary, alt];
let mut non_shift_modifiers = vec![primary, alt];
if cfg!(target_os = "macos") {
let ctrl = spawner
.spawn((
Action::<ControlModifier>::new(),
non_consuming_modifier,
bindings![KeyCode::ControlLeft, KeyCode::ControlRight],
))
.id();
all_modifiers.push(ctrl);
non_shift_modifiers.push(ctrl);
}
Self {
all_modifiers,
non_shift_modifiers,
settings,
_marker: std::marker::PhantomData,
}
}
pub fn spawn_key<A: InputAction>(&self, spawner: &mut ActionSpawner<C>, key: KeyCode) {
spawner.spawn((
Action::<A>::new(),
self.settings,
BlockBy::new(self.all_modifiers.clone()),
bindings![key],
));
}
pub fn spawn_shift_key<A: InputAction>(&self, spawner: &mut ActionSpawner<C>, key: KeyCode) {
spawner.spawn((
Action::<A>::new(),
self.settings,
BlockBy::new(self.non_shift_modifiers.clone()),
bindings![key.with_mod_keys(ModKeys::SHIFT)],
));
}
pub fn spawn_binding<A: InputAction, B: Bundle>(
&self,
spawner: &mut ActionSpawner<C>,
bindings: B,
) {
spawner.spawn((
Action::<A>::new(),
self.settings,
BlockBy::new(self.all_modifiers.clone()),
bindings,
));
}
pub fn spawn_platform_key<A: InputAction>(&self, spawner: &mut ActionSpawner<C>, key: KeyCode) {
let platform_bindings = if cfg!(target_os = "macos") {
bindings![key.with_mod_keys(ModKeys::SUPER)]
} else {
bindings![key.with_mod_keys(ModKeys::CONTROL)]
};
spawner.spawn((Action::<A>::new(), self.settings, platform_bindings));
}
}
#[cfg(test)]
mod tests {
use super::*;
event!(TestEvent);
event!(TestPayloadEvent { value: u32 });
#[test]
fn unit_event_defaults() {
let event = TestEvent;
assert_eq!(std::mem::size_of_val(&event), 0);
}
#[test]
fn payload_event_fields() {
let event = TestPayloadEvent { value: 42 };
assert_eq!(event.value, 42);
}
}