Fix querying resources in fn systems, create CommonActionLabels, more code cleanup of free fly camera

This commit is contained in:
SeanOMik 2024-01-06 21:38:21 -05:00
parent 265752ee76
commit ac24d1f913
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
5 changed files with 215 additions and 125 deletions

View File

@ -1,12 +1,19 @@
use std::ptr::NonNull;
use lyra_engine::{
game::Game,
input::ActionHandler,
input::{ActionHandler, CommonActionLabel},
math::{Quat, Vec3, EulerRot},
plugin::Plugin, ecs::{system::{System, IntoSystem}, world::World, Access, Component}, DeltaTime, scene::CameraComponent,
plugin::Plugin, ecs::{Component, query::{Res, View}}, DeltaTime, scene::CameraComponent,
};
/* enum FreeFlyCameraActions {
MoveForwardBackward,
MoveLeftRight,
MoveUpDown,
LookLeftRight,
LookUpDown,
LookRoll,
} */
#[derive(Clone, Component)]
pub struct FreeFlyCamera {
pub speed: f32,
@ -41,74 +48,58 @@ impl FreeFlyCamera {
}
}
pub struct FreeFlyCameraPlugin;
pub fn free_fly_camera_controller(delta_time: Res<DeltaTime>, handler: Res<ActionHandler>, view: View<(&mut CameraComponent, &FreeFlyCamera)>) -> anyhow::Result<()> {
let delta_time = **delta_time;
for (mut cam, fly) in view.into_iter() {
let forward = cam.transform.forward();
let left = cam.transform.left();
let up = Vec3::Y;
impl System for FreeFlyCameraPlugin {
fn execute(&mut self, mut world: NonNull<World>) -> anyhow::Result<()> {
let world = unsafe { world.as_mut() };
let delta_time = **world.get_resource::<DeltaTime>();
let handler = world.get_resource::<ActionHandler>();
let move_y = handler.get_axis_modifier(CommonActionLabel::MoveUpDown).unwrap_or(0.0);
let move_x = handler.get_axis_modifier(CommonActionLabel::MoveLeftRight).unwrap_or(0.0);
let move_z = handler.get_axis_modifier(CommonActionLabel::MoveForwardBackward).unwrap_or(0.0);
for (mut cam, fly) in world
.view_iter::<(&mut CameraComponent, &FreeFlyCamera)>()
{
let forward = cam.transform.forward();
let left = cam.transform.left();
let up = Vec3::Y;
let mut velocity = Vec3::ZERO;
velocity += move_y * up;
velocity += move_x * left;
velocity += move_z * forward;
let move_y = handler.get_axis_modifier("up_down").unwrap_or(0.0);
let move_x = handler.get_axis_modifier("left_right").unwrap_or(0.0);
let move_z = handler.get_axis_modifier("forward_backward").unwrap_or(0.0);
let mut velocity = Vec3::ZERO;
velocity += move_y * up;
velocity += move_x * left;
velocity += move_z * forward;
if velocity != Vec3::ZERO {
cam.transform.translation += velocity.normalize() * fly.speed * delta_time; // TODO: speeding up
}
let motion_x = handler.get_axis_modifier("look_left_right").unwrap_or(0.0);
let motion_y = handler.get_axis_modifier("look_up_down").unwrap_or(0.0);
let motion_z = handler.get_axis_modifier("look_rotate").unwrap_or(0.0);
let mut camera_rot = Vec3::ZERO;
camera_rot.y -= motion_x * fly.mouse_sensitivity;
camera_rot.x -= motion_y * fly.mouse_sensitivity;
camera_rot.z -= motion_z * fly.mouse_sensitivity;
if camera_rot != Vec3::ZERO {
let look_velocity = camera_rot * fly.look_speed * delta_time;
let (mut y, mut x, _) = cam.transform.rotation.to_euler(EulerRot::YXZ);
x += look_velocity.x;
y += look_velocity.y;
x = x.clamp(-1.54, 1.54);
// rotation is not commutative, keep this order to avoid unintended roll
cam.transform.rotation = Quat::from_axis_angle(Vec3::Y, y)
* Quat::from_axis_angle(Vec3::X, x);
}
if velocity != Vec3::ZERO {
cam.transform.translation += velocity.normalize() * fly.speed * delta_time; // TODO: speeding up
}
Ok(())
let motion_x = handler.get_axis_modifier(CommonActionLabel::LookLeftRight).unwrap_or(0.0);
let motion_y = handler.get_axis_modifier(CommonActionLabel::LookUpDown).unwrap_or(0.0);
let motion_z = handler.get_axis_modifier(CommonActionLabel::LookRoll).unwrap_or(0.0);
let mut camera_rot = Vec3::ZERO;
camera_rot.y -= motion_x * fly.mouse_sensitivity;
camera_rot.x -= motion_y * fly.mouse_sensitivity;
camera_rot.z -= motion_z * fly.mouse_sensitivity;
if camera_rot != Vec3::ZERO {
let look_velocity = camera_rot * fly.look_speed * delta_time;
let (mut y, mut x, _) = cam.transform.rotation.to_euler(EulerRot::YXZ);
x += look_velocity.x;
y += look_velocity.y;
x = x.clamp(-1.54, 1.54);
// rotation is not commutative, keep this order to avoid unintended roll
cam.transform.rotation = Quat::from_axis_angle(Vec3::Y, y)
* Quat::from_axis_angle(Vec3::X, x);
}
}
fn world_access(&self) -> lyra_engine::ecs::Access {
Access::Write
}
Ok(())
}
impl IntoSystem<()> for FreeFlyCameraPlugin {
type System = Self;
fn into_system(self) -> Self::System {
self
}
}
/// A plugin that adds the free fly camera controller system to the world. It is expected that
/// there is a [`FreeFlyCamera`] in the world, if there isn't, the camera would not move.
pub struct FreeFlyCameraPlugin;
impl Plugin for FreeFlyCameraPlugin {
fn setup(&self, game: &mut Game) {
game.with_system("free_fly_camera_system", FreeFlyCameraPlugin, &[]);
game.with_system("free_fly_camera_system", free_fly_camera_controller, &[]);
}
}

View File

@ -1,11 +1,21 @@
use std::ptr::NonNull;
use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}};
use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}};
use lyra_engine::assets::{ResourceManager, Model};
mod free_fly_camera;
use free_fly_camera::{FreeFlyCameraPlugin, FreeFlyCamera};
#[derive(Clone, Copy, Hash, Debug)]
pub enum ActionLabel {
MoveForwardBackward,
MoveLeftRight,
MoveUpDown,
LookLeftRight,
LookUpDown,
LookRoll,
}
struct FixedTimestep {
max_tps: u32,
fixed_time: f32,
@ -233,47 +243,59 @@ async fn main() {
let action_handler = ActionHandler::new()
.add_layout(LayoutId::from(0))
.add_action("forward_backward", Action::new(ActionKind::Axis))
.add_action("left_right", Action::new(ActionKind::Axis))
.add_action("up_down", Action::new(ActionKind::Axis))
.add_action("look_left_right", Action::new(ActionKind::Axis))
.add_action("look_up_down", Action::new(ActionKind::Axis))
.add_action("look_rotate", Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::MoveForwardBackward, Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::MoveLeftRight, Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::MoveUpDown, Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::LookLeftRight, Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::LookUpDown, Action::new(ActionKind::Axis))
.add_action(CommonActionLabel::LookRoll, Action::new(ActionKind::Axis))
.add_mapping(ActionMapping::new(LayoutId::from(0), ActionMappingId::from(0))
.bind("forward_backward", &[
.bind(CommonActionLabel::MoveForwardBackward, &[
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0)
])
.bind("left_right", &[
.bind(CommonActionLabel::MoveLeftRight, &[
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0)
])
.bind("up_down", &[
.bind(CommonActionLabel::MoveUpDown, &[
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0)
])
.bind("look_left_right", &[
.bind(CommonActionLabel::LookLeftRight, &[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickX)).into_binding(),
])
.bind("look_up_down", &[
.bind(CommonActionLabel::LookUpDown, &[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickY)).into_binding(),
])
.bind("look_rotate", &[
.bind(CommonActionLabel::LookRoll, &[
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
])
.finish()
);
/* #[allow(unused_variables)]
let test_system = |world: &mut World| -> anyhow::Result<()> {
let handler = world.get_resource::<ActionHandler>();
if let Some(alpha) = handler.get_axis_modifier("look_rotate") {
debug!("'look_rotate': {alpha}");
}
Ok(())
}; */
game.world().add_resource(action_handler);
game.with_plugin(InputActionPlugin);
//game.with_system("input_test", test_system, &[]);
};
Game::initialize().await

View File

@ -79,7 +79,7 @@ impl<R: ResourceObject> AsQuery for QueryResource<R> {
}
/// A struct used for querying resources from the World.
pub struct Res<'a, T>(Ref<'a, T>);
pub struct Res<'a, T>(pub(crate) Ref<'a, T>);
impl<'a, T: ResourceObject> std::ops::Deref for Res<'a, T> {
type Target = T;
@ -167,7 +167,7 @@ impl<R: ResourceObject> AsQuery for QueryResourceMut<R> {
}
/// A struct used for querying resources from the World.
pub struct ResMut<'a, T>(RefMut<'a, T>);
pub struct ResMut<'a, T>(pub(crate) RefMut<'a, T>);
impl<'a, T: ResourceObject> std::ops::Deref for ResMut<'a, T> {
type Target = T;

View File

@ -1,6 +1,6 @@
use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}};
use std::{ptr::NonNull, marker::PhantomData};
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery}};
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}};
use super::{System, IntoSystem};
@ -184,12 +184,12 @@ pub struct ResourceArgFetcher<R: ResourceObject> {
phantom: PhantomData<fn() -> R>
}
impl<'a, R: ResourceObject> FnArg for Ref<'a, R> {
impl<'a, R: ResourceObject> FnArg for Res<'a, R> {
type Fetcher = ResourceArgFetcher<R>;
}
impl<R: ResourceObject> FnArgFetcher for ResourceArgFetcher<R> {
type Arg<'a> = Ref<'a, R>;
type Arg<'a> = Res<'a, R>;
fn new() -> Self {
ResourceArgFetcher {
@ -199,7 +199,7 @@ impl<R: ResourceObject> FnArgFetcher for ResourceArgFetcher<R> {
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
let world = world.as_ref();
world.get_resource::<R>()
Res(world.get_resource::<R>())
}
}
@ -207,12 +207,12 @@ pub struct ResourceMutArgFetcher<R: ResourceObject> {
phantom: PhantomData<fn() -> R>
}
impl<'a, R: ResourceObject> FnArg for RefMut<'a, R> {
impl<'a, R: ResourceObject> FnArg for ResMut<'a, R> {
type Fetcher = ResourceMutArgFetcher<R>;
}
impl<R: ResourceObject> FnArgFetcher for ResourceMutArgFetcher<R> {
type Arg<'a> = RefMut<'a, R>;
type Arg<'a> = ResMut<'a, R>;
fn new() -> Self {
ResourceMutArgFetcher {
@ -222,15 +222,15 @@ impl<R: ResourceObject> FnArgFetcher for ResourceMutArgFetcher<R> {
unsafe fn get<'a>(&mut self, world: NonNull<World>) -> Self::Arg<'a> {
let world = world.as_ref();
world.get_resource_mut::<R>()
ResMut(world.get_resource_mut::<R>())
}
}
#[cfg(test)]
mod tests {
use std::{ptr::NonNull, cell::RefMut};
use std::ptr::NonNull;
use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View}};
use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View, ResMut}};
use super::{System, IntoSystem};
struct SomeCounter(u32);
@ -355,8 +355,10 @@ mod tests {
world.spawn((Vec2::rand(), Vec3::rand()));
world.add_resource(SomeCounter(0));
let test_system = |mut counter: RefMut<SomeCounter>| -> anyhow::Result<()> {
counter.0 += 10;
let test_system = |mut counter: ResMut<SomeCounter>| -> anyhow::Result<()> {
// .0 is twice here since ResMut's tuple field is pub(crate).
// Users wont need to do this
counter.0.0 += 10;
Ok(())
};
@ -374,10 +376,12 @@ mod tests {
world.spawn((Vec2::rand(), ));
world.add_resource(SomeCounter(0));
let test_system = |mut counter: RefMut<SomeCounter>, view: View<QueryBorrow<Vec2>>| -> anyhow::Result<()> {
let test_system = |mut counter: ResMut<SomeCounter>, view: View<QueryBorrow<Vec2>>| -> anyhow::Result<()> {
for v2 in view.into_iter() {
println!("Got v2 at '{:?}'", v2);
counter.0 += 1;
// .0 is twice here since ResMut's tuple field is pub(crate).
// Users wont need to do this
counter.0.0 += 1;
}
Ok(())

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, ops::Deref};
use std::{collections::HashMap, ops::Deref, hash::{Hash, DefaultHasher, Hasher}, fmt::Debug};
use glam::Vec2;
use lyra_ecs::world::World;
@ -7,6 +7,54 @@ use crate::{plugin::Plugin, game::GameStages, EventQueue};
use super::{Button, KeyCode, InputButtons, MouseMotion};
pub trait ActionLabel: Debug {
/// Returns a unique hash of the label.
fn label_hash(&self) -> u64;
}
impl<T: Hash + Debug> ActionLabel for T {
fn label_hash(&self) -> u64 {
let mut s = DefaultHasher::new();
self.hash(&mut s);
s.finish()
}
}
#[derive(Clone)]
pub struct ActionLabelWrapper(String, u64);
impl Debug for ActionLabelWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl ActionLabel for ActionLabelWrapper {
fn label_hash(&self) -> u64 {
self.1
}
}
impl<A: ActionLabel + Hash> From<A> for ActionLabelWrapper {
fn from(value: A) -> Self {
let lbl = format!("{:?}", value);
Self(lbl, value.label_hash())
}
}
/// Some commonly used action labels.
///
/// The built-in systems uses these labels
#[derive(Clone, Copy, Hash, Debug)]
pub enum CommonActionLabel {
MoveForwardBackward,
MoveLeftRight,
MoveUpDown,
LookLeftRight,
LookUpDown,
LookRoll,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GamepadFormat {
@ -204,7 +252,7 @@ impl From<u32> for ActionMappingId {
pub struct ActionMapping {
layout: LayoutId,
id: ActionMappingId,
action_binds: HashMap<String, Vec<Binding>>,
action_binds: HashMap<u64, Vec<Binding>>,
}
impl ActionMapping {
@ -221,16 +269,17 @@ impl ActionMapping {
/// If the action is not in this layout, this will panic!
///
/// Parameters:
/// * `action_label` - The label corresponding to the action in this Layout.
/// * `action` - The label corresponding to the action in this Layout.
/// * `bind` - The Binding to add to the Action.
pub fn bind(mut self, action_label: &str, bindings: &[Binding]) -> Self {
pub fn bind<L>(mut self, action: L, bindings: &[Binding]) -> Self
where
L: ActionLabel
{
let mut bindings = bindings.to_vec();
let action_binds = self.action_binds.entry(action_label.to_string()).or_default();
let action_binds = self.action_binds.entry(action.label_hash()).or_default();
action_binds.append(&mut bindings);
println!("Creating action label {}", action_label);
self
}
@ -257,7 +306,7 @@ impl ActionMapping {
#[derive(Clone, Default)]
pub struct ActionHandler {
pub actions: HashMap<String, Action>,
pub actions: HashMap<u64, Action>,
pub layouts: HashMap<LayoutId, Layout>,
pub current_layout: LayoutId,
pub current_mapping: ActionMappingId,
@ -274,8 +323,11 @@ impl ActionHandler {
self
}
pub fn add_action(mut self, label: &str, action: Action) -> Self {
self.actions.insert(label.to_string(), action);
pub fn add_action<L>(mut self, label: L, action: Action) -> Self
where
L: ActionLabel
{
self.actions.insert(label.label_hash(), action);
self
}
@ -290,9 +342,12 @@ impl ActionHandler {
/// Returns true if the action is pressed (or was just pressed).
///
/// This will panic if the action name does not correspond to an action.
pub fn is_action_pressed(&self, action_name: &str) -> bool {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn is_action_pressed<L>(&self, action: L) -> bool
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
matches!(action.state, ActionState::Pressed(_) | ActionState::JustPressed(_))
}
@ -300,9 +355,12 @@ impl ActionHandler {
/// Returns true if the action was just pressed.
///
/// This will panic if the action name does not correspond to an action.
pub fn was_action_just_pressed(&self, action_name: &str) -> bool {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn was_action_just_pressed<L>(&self, action: L) -> bool
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
matches!(action.state, ActionState::JustPressed(_))
}
@ -310,9 +368,12 @@ impl ActionHandler {
/// Returns true if the action was just released.
///
/// This will panic if the action name does not correspond to an action.
pub fn was_action_just_released(&self, action_name: &str) -> bool {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn was_action_just_released<L>(&self, action: L) -> bool
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
matches!(action.state, ActionState::JustReleased)
}
@ -320,9 +381,12 @@ impl ActionHandler {
/// Returns an action's state.
///
/// This will panic if the action name does not correspond to an action.
pub fn get_action_state(&self, action_name: &str) -> ActionState {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn get_action_state<L>(&self, action: L) -> ActionState
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
action.state
}
@ -331,9 +395,12 @@ impl ActionHandler {
/// Returns `None` if the action's state is not `ActionState::Pressed` or `ActionState::JustPressed`.
///
/// This will panic if the action name does not correspond to an action.
pub fn get_pressed_modifier(&self, action_name: &str) -> Option<f32> {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn get_pressed_modifier<L>(&self, action: L) -> Option<f32>
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
match action.state {
ActionState::Pressed(v) | ActionState::JustPressed(v) => Some(v),
@ -345,9 +412,12 @@ impl ActionHandler {
/// Returns `None` if the action's state is not `ActionState::JustPressed`.
///
/// This will panic if the action name does not correspond to an action.
pub fn get_just_pressed_modifier(&self, action_name: &str) -> Option<f32> {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn get_just_pressed_modifier<L>(&self, action: L) -> Option<f32>
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
match action.state {
ActionState::JustPressed(v) => Some(v),
@ -359,9 +429,12 @@ impl ActionHandler {
/// Returns `None` if the action's state is not `ActionState::Axis`.
///
/// This will panic if the action name does not correspond to an action.
pub fn get_axis_modifier(&self, action_name: &str) -> Option<f32> {
let action = self.actions.get(action_name)
.unwrap_or_else(|| panic!("Action {action_name} was not found"));
pub fn get_axis_modifier<L>(&self, action: L) -> Option<f32>
where
L: ActionLabel
{
let action = self.actions.get(&action.label_hash())
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
match action.state {
ActionState::Axis(v) => Some(v),
@ -386,8 +459,8 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> {
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
for (action_name, _) in mapping.action_binds.clone().iter() {
let action = handler.actions.get_mut(action_name).expect("Action name for binding is invalid!");
for (action, _) in mapping.action_binds.clone().iter() {
let action = handler.actions.get_mut(action).expect("Action name for binding is invalid!");
if action.kind == ActionKind::Axis {
action.state = ActionState::Axis(0.0);
}
@ -409,8 +482,8 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> {
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
for (action_name, binds) in mapping.action_binds.clone().iter() {
let action = handler.actions.get_mut(action_name).expect("Action name for binding is invalid!");
for (action_lbl, binds) in mapping.action_binds.clone().iter() {
let action = handler.actions.get_mut(action_lbl).expect("Action name for binding is invalid!");
let mut new_state = None;
for bind in binds.iter() {