implement keybinding editing
This commit is contained in:
parent
7a4c9014c8
commit
8d81f94b70
2 changed files with 109 additions and 15 deletions
|
@ -3,7 +3,7 @@ Game store page: https://crispypin.itch.io/marble-machinations
|
|||
|
||||
## [Unreleased]
|
||||
### added
|
||||
- configurable hotkeys (via file only, only some actions)
|
||||
- configurable key bindings for the basic editor actions
|
||||
- OS clipboard copy/paste, with fallback to old behavior when copying
|
||||
- in-grid text comments (not yet editable in-game)
|
||||
- changelog file
|
||||
|
|
122
src/input.rs
122
src/input.rs
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::BTreeMap, mem::transmute};
|
||||
use std::{collections::BTreeMap, mem::transmute, vec};
|
||||
|
||||
use raylib::{
|
||||
color::Color,
|
||||
|
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::{
|
||||
theme::{BG_DARK, BG_LIGHT},
|
||||
ui::text_button,
|
||||
util::screen_centered_rect,
|
||||
util::{rect, screen_centered_rect},
|
||||
Globals,
|
||||
};
|
||||
|
||||
|
@ -75,7 +75,14 @@ pub struct Input {
|
|||
bindings: [Vec<Binding>; ActionId::SIZE],
|
||||
states: [BindingState; ActionId::SIZE],
|
||||
#[serde(skip)]
|
||||
editing_input: Option<(ActionId, usize)>,
|
||||
editing_input: Option<(ActionId, usize, BindingEdit)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum BindingEdit {
|
||||
Init,
|
||||
Adding(Binding),
|
||||
Releasing(Binding),
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
@ -98,7 +105,7 @@ impl Input {
|
|||
return;
|
||||
}
|
||||
if text_button(d, &globals.mouse, 245, y, 45, "edit") {
|
||||
self.editing_input = Some((action, binding_index));
|
||||
self.editing_input = Some((action, binding_index, BindingEdit::Init));
|
||||
}
|
||||
let trigger = format!("{:?}", binding.trigger);
|
||||
d.draw_text(&trigger, 300, y, 20, Color::LIMEGREEN);
|
||||
|
@ -107,21 +114,93 @@ impl Input {
|
|||
d.draw_text(&modifiers, x, y, 20, Color::LIGHTBLUE);
|
||||
y += 32;
|
||||
}
|
||||
y += 8;
|
||||
if text_button(d, &globals.mouse, 160, y, 130, "add binding") {
|
||||
self.editing_input =
|
||||
Some((action, self.bindings[action_index].len(), BindingEdit::Init));
|
||||
}
|
||||
y += 45;
|
||||
}
|
||||
|
||||
if let Some((action, binding_index)) = &self.editing_input {
|
||||
globals.mouse.update(d); // todo less scuffed
|
||||
let border = screen_centered_rect(d, 208, 88);
|
||||
if let Some((action, binding_index, edit_state)) = &mut self.editing_input {
|
||||
globals.mouse.update(d);
|
||||
let border = screen_centered_rect(d, 368, 128);
|
||||
d.draw_rectangle_rec(border, BG_LIGHT);
|
||||
let bounds = screen_centered_rect(d, 200, 80);
|
||||
let bounds = screen_centered_rect(d, 360, 120);
|
||||
d.draw_rectangle_rec(bounds, BG_DARK);
|
||||
|
||||
// TODO capture and display current input
|
||||
|
||||
let x = bounds.x as i32;
|
||||
let y = bounds.y as i32;
|
||||
if text_button(d, &globals.mouse, x + 10, y + 40, 80, "ok") {}
|
||||
d.draw_text(
|
||||
&format!("editing binding for {action:?}"),
|
||||
x + 5,
|
||||
y + 5,
|
||||
20,
|
||||
Color::WHITE,
|
||||
);
|
||||
let y = y + 30;
|
||||
let ok_btn_x = x + 10;
|
||||
let ok_btn_y = y + 40;
|
||||
let ok_btn_width = 80;
|
||||
let ok_btn_rect = rect(ok_btn_x, ok_btn_y, ok_btn_width, 30);
|
||||
|
||||
for key_index in 0..Button::SIZE {
|
||||
let key = Button::from_usize(key_index).unwrap();
|
||||
match edit_state {
|
||||
BindingEdit::Init => {
|
||||
if key.just_pressed(d) {
|
||||
*edit_state = BindingEdit::Adding(Binding {
|
||||
modifiers: Vec::new(),
|
||||
trigger: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
BindingEdit::Adding(binding) => {
|
||||
if key.just_pressed(d) {
|
||||
if key != binding.trigger && !binding.modifiers.contains(&key) {
|
||||
binding.modifiers.push(binding.trigger);
|
||||
binding.trigger = key;
|
||||
}
|
||||
} else if key.released(d) {
|
||||
if let Some(i) = binding.modifiers.iter().position(|&k| k == key) {
|
||||
binding.modifiers.remove(i);
|
||||
binding.modifiers.push(binding.trigger);
|
||||
binding.trigger = key;
|
||||
}
|
||||
*edit_state = BindingEdit::Releasing(binding.clone());
|
||||
}
|
||||
}
|
||||
BindingEdit::Releasing(_binding) => {
|
||||
let clicking_ok =
|
||||
globals.mouse.is_over(ok_btn_rect) && key == Button::MouseLeft;
|
||||
if key.just_pressed(d) && !clicking_ok {
|
||||
*edit_state = BindingEdit::Adding(Binding {
|
||||
modifiers: Vec::new(),
|
||||
trigger: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let BindingEdit::Adding(b) | BindingEdit::Releasing(b) = &edit_state {
|
||||
let colour = if matches!(edit_state, BindingEdit::Releasing(_)) {
|
||||
Color::GREEN
|
||||
} else {
|
||||
Color::ORANGE
|
||||
};
|
||||
let text = format!("{:?} + {:?}", b.modifiers, b.trigger);
|
||||
d.draw_text(&text, x + 5, y + 5, 20, colour);
|
||||
}
|
||||
if text_button(d, &globals.mouse, ok_btn_x, ok_btn_y, ok_btn_width, "ok") {
|
||||
if let BindingEdit::Releasing(binding) = edit_state {
|
||||
let binding_list = &mut self.bindings[*action as usize];
|
||||
if *binding_index < binding_list.len() {
|
||||
binding_list[*binding_index] = binding.clone();
|
||||
} else {
|
||||
binding_list.push(binding.clone());
|
||||
}
|
||||
self.editing_input = None;
|
||||
}
|
||||
}
|
||||
if text_button(d, &globals.mouse, x + 100, y + 40, 80, "cancel") {
|
||||
self.editing_input = None;
|
||||
}
|
||||
|
@ -206,7 +285,8 @@ impl From<Input> for InputMap {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
#[repr(u8)]
|
||||
enum Button {
|
||||
MouseLeft,
|
||||
MouseRight,
|
||||
|
@ -350,6 +430,20 @@ impl Button {
|
|||
}
|
||||
}
|
||||
|
||||
fn just_pressed(self, rl: &RaylibHandle) -> bool {
|
||||
match self.to_raylib() {
|
||||
RlInput::Key(key) => rl.is_key_pressed(key),
|
||||
RlInput::Mouse(btn) => rl.is_mouse_button_pressed(btn),
|
||||
}
|
||||
}
|
||||
|
||||
fn released(self, rl: &RaylibHandle) -> bool {
|
||||
match self.to_raylib() {
|
||||
RlInput::Key(key) => rl.is_key_released(key),
|
||||
RlInput::Mouse(btn) => rl.is_mouse_button_released(btn),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_raylib(self) -> RlInput {
|
||||
use KeyboardKey::*;
|
||||
use RlInput::*;
|
||||
|
|
Loading…
Add table
Reference in a new issue