prevent bindings from triggering when a modifier is held down that matches a different binding
This commit is contained in:
parent
660746f16b
commit
a755678567
4 changed files with 440 additions and 12 deletions
62
src/input.rs
62
src/input.rs
|
@ -54,6 +54,7 @@ impl Default for Input {
|
|||
let mut bindings = [(); ActionId::SIZE].map(|_| Vec::new());
|
||||
let mut bind_key = |action, mods, trigger| {
|
||||
bindings[action as usize].push(Binding {
|
||||
blocking_modifiers: Vec::new(),
|
||||
modifiers: mods,
|
||||
trigger,
|
||||
});
|
||||
|
@ -141,21 +142,26 @@ impl Input {
|
|||
if text_button(d, &globals.mouse, buttons_x + 85, y, 45, "edit") {
|
||||
self.editing_binding = Some((action, binding_index, BindingEdit::Init));
|
||||
}
|
||||
//
|
||||
let trigger = format!("{:?}", binding.trigger);
|
||||
d.draw_text(&trigger, binding_text_x, y + 5, 20, Color::LIMEGREEN);
|
||||
let x = binding_text_x + 10 + d.measure_text(&trigger, 20);
|
||||
let mut x = binding_text_x;
|
||||
d.draw_text(&trigger, x, y + 5, 20, Color::LIMEGREEN);
|
||||
x += 10 + d.measure_text(&trigger, 20);
|
||||
//
|
||||
let modifiers = format!("{:?}", binding.modifiers);
|
||||
d.draw_text(&modifiers, x, y + 5, 20, Color::LIGHTBLUE);
|
||||
x += 10 + d.measure_text(&modifiers, 20);
|
||||
//
|
||||
let conflicts = conflicts(&self.bindings, binding, action);
|
||||
if !conflicts.is_empty() {
|
||||
let x = x + 10 + d.measure_text(&modifiers, 20);
|
||||
d.draw_text(
|
||||
&format!("also used by: {conflicts:?}"),
|
||||
x,
|
||||
y + 5,
|
||||
20,
|
||||
Color::ORANGERED,
|
||||
);
|
||||
let conflict_text = format!("also used by: {conflicts:?}");
|
||||
d.draw_text(&conflict_text, x, y + 5, 20, Color::ORANGERED);
|
||||
x += 10 + d.measure_text(&conflict_text, 20);
|
||||
}
|
||||
//
|
||||
if !binding.blocking_modifiers.is_empty() {
|
||||
let blocking_text = format!("not while: {:?}", binding.blocking_modifiers);
|
||||
d.draw_text(&blocking_text, x, y + 5, 20, Color::GRAY);
|
||||
}
|
||||
y += 32;
|
||||
}
|
||||
|
@ -193,6 +199,7 @@ impl Input {
|
|||
BindingEdit::Init => {
|
||||
if key.just_pressed(d) {
|
||||
*edit_state = BindingEdit::Adding(Binding {
|
||||
blocking_modifiers: Vec::new(),
|
||||
modifiers: Vec::new(),
|
||||
trigger: key,
|
||||
});
|
||||
|
@ -218,6 +225,7 @@ impl Input {
|
|||
globals.mouse.is_over(ok_btn_rect) && key == Button::MouseLeft;
|
||||
if key.just_pressed(d) && !clicking_ok {
|
||||
*edit_state = BindingEdit::Adding(Binding {
|
||||
blocking_modifiers: Vec::new(),
|
||||
modifiers: Vec::new(),
|
||||
trigger: key,
|
||||
});
|
||||
|
@ -255,6 +263,7 @@ impl Input {
|
|||
binding_list.push(binding.clone());
|
||||
}
|
||||
self.editing_binding = None;
|
||||
self.update_modifier_blocks();
|
||||
}
|
||||
}
|
||||
if text_button(d, &globals.mouse, x + 100, y + 40, 80, "cancel") {
|
||||
|
@ -268,7 +277,9 @@ impl Input {
|
|||
let bindings = &self.bindings[i];
|
||||
let mut is_active = false;
|
||||
for binding in bindings {
|
||||
if binding.modifiers.iter().all(|&m| m.is_down(rl)) {
|
||||
if binding.modifiers.iter().all(|&m| m.is_down(rl))
|
||||
&& !binding.blocking_modifiers.iter().any(|&m| m.is_down(rl))
|
||||
{
|
||||
is_active |= binding.trigger.is_down(rl);
|
||||
}
|
||||
}
|
||||
|
@ -299,6 +310,33 @@ impl Input {
|
|||
pub fn is_released(&self, action: ActionId) -> bool {
|
||||
self.states[action as usize] == BindingState::Released
|
||||
}
|
||||
|
||||
/// Must be called after any binding has changed.
|
||||
/// Ensures a binding "S" is not triggered by "Ctrl+S", if "Ctrl+S" is bound to something else.
|
||||
pub fn update_modifier_blocks(&mut self) {
|
||||
for i in 0..ActionId::SIZE {
|
||||
let bindings = &self.bindings[i];
|
||||
for binding_index in 0..bindings.len() {
|
||||
let binding = &self.bindings[i][binding_index];
|
||||
let mut blocking_mods = Vec::new();
|
||||
for i in 0..ActionId::SIZE {
|
||||
let other_bindings = &self.bindings[i];
|
||||
for other_binding in other_bindings {
|
||||
if other_binding.trigger == binding.trigger {
|
||||
for modifier in &other_binding.modifiers {
|
||||
if !blocking_mods.contains(modifier)
|
||||
&& !binding.modifiers.contains(modifier)
|
||||
{
|
||||
blocking_mods.push(*modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.bindings[i][binding_index].blocking_modifiers = blocking_mods;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn conflicts(
|
||||
|
@ -338,6 +376,8 @@ impl ActionId {
|
|||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Binding {
|
||||
#[serde(skip)]
|
||||
blocking_modifiers: Vec<Button>,
|
||||
modifiers: Vec<Button>,
|
||||
trigger: Button,
|
||||
}
|
||||
|
|
245
src/layout.rs
Normal file
245
src/layout.rs
Normal file
|
@ -0,0 +1,245 @@
|
|||
use raylib::{
|
||||
color::Color,
|
||||
drawing::{RaylibDraw, RaylibDrawHandle},
|
||||
RaylibHandle,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Ui {
|
||||
root: Node,
|
||||
width: i32,
|
||||
height: i32,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub enum Axis {
|
||||
#[default]
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, rl: &RaylibHandle) {
|
||||
let w = rl.get_screen_width();
|
||||
let h = rl.get_screen_height();
|
||||
if w != self.width || h != self.height {
|
||||
self.dirty = true;
|
||||
}
|
||||
self.width = w;
|
||||
self.height = h;
|
||||
|
||||
if self.dirty {
|
||||
self.dirty = false;
|
||||
self.layout();
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&mut self) {
|
||||
// fit sizing widths
|
||||
self.root.layout_width();
|
||||
// grow & shrink widths
|
||||
// wrap text
|
||||
self.root.layout_height();
|
||||
// fit sizing heights
|
||||
// grow & shrink heights
|
||||
// positions
|
||||
self.root.layout_position(0, 0);
|
||||
}
|
||||
|
||||
pub fn draw(&self, rl: &mut RaylibDrawHandle) {
|
||||
self.root.draw(rl);
|
||||
}
|
||||
|
||||
pub fn set_root(&mut self, node: Node) {
|
||||
// self.nodes = nodes;
|
||||
self.root = node;
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub enum SizeAxis {
|
||||
#[default]
|
||||
Fit,
|
||||
Fixed(i32),
|
||||
// Grow(),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Node {
|
||||
// id: NodeId,
|
||||
hidden: bool,
|
||||
children: Vec<Node>,
|
||||
computed_width: i32,
|
||||
computed_height: i32,
|
||||
computed_x: i32,
|
||||
computed_y: i32,
|
||||
// min_width: i32,
|
||||
// min_height: i32,
|
||||
// pref_width: i32,
|
||||
// pref_height: i32,
|
||||
// max_width: i32,
|
||||
// max_height: i32,
|
||||
width: SizeAxis,
|
||||
height: SizeAxis,
|
||||
padding: i32,
|
||||
color: Color,
|
||||
name: String, // todo replace with identification enum
|
||||
axis: Axis,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn layout_width(&mut self) -> i32 {
|
||||
self.computed_width = match self.width {
|
||||
SizeAxis::Fixed(val) => val,
|
||||
SizeAxis::Fit => match self.axis {
|
||||
Axis::Horizontal => {
|
||||
let mut width = self.padding;
|
||||
for c in &mut self.children {
|
||||
width += c.layout_width();
|
||||
width += self.padding;
|
||||
}
|
||||
width
|
||||
}
|
||||
Axis::Vertical => {
|
||||
let mut width = self.padding;
|
||||
for c in &mut self.children {
|
||||
width = width.max(c.layout_width());
|
||||
}
|
||||
width + self.padding * 2
|
||||
}
|
||||
},
|
||||
};
|
||||
self.computed_width
|
||||
}
|
||||
|
||||
fn layout_height(&mut self) -> i32 {
|
||||
self.computed_height = match self.height {
|
||||
SizeAxis::Fixed(val) => val,
|
||||
SizeAxis::Fit => match self.axis {
|
||||
Axis::Horizontal => {
|
||||
let mut height = self.padding;
|
||||
for c in &mut self.children {
|
||||
height = height.max(c.layout_height());
|
||||
}
|
||||
height + self.padding * 2
|
||||
}
|
||||
Axis::Vertical => {
|
||||
let mut height = self.padding;
|
||||
for c in &mut self.children {
|
||||
height += c.layout_height();
|
||||
height += self.padding;
|
||||
}
|
||||
height
|
||||
}
|
||||
},
|
||||
};
|
||||
self.computed_height
|
||||
}
|
||||
|
||||
fn layout_position(&mut self, root_x: i32, root_y: i32) {
|
||||
self.computed_x = root_x;
|
||||
self.computed_y = root_y;
|
||||
let mut x = root_x + self.padding;
|
||||
let mut y = root_y + self.padding;
|
||||
for c in &mut self.children {
|
||||
c.layout_position(x, y);
|
||||
match self.axis {
|
||||
Axis::Horizontal => x += c.computed_width + self.padding,
|
||||
Axis::Vertical => y += c.computed_height + self.padding,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, d: &mut RaylibDrawHandle) {
|
||||
d.draw_rectangle(
|
||||
self.computed_x,
|
||||
self.computed_y,
|
||||
self.computed_width,
|
||||
self.computed_height,
|
||||
self.color,
|
||||
);
|
||||
// todo text/image content
|
||||
for c in &self.children {
|
||||
c.draw(d);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(mut self, text: &str) -> Self {
|
||||
self.name = text.to_owned();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: SizeAxis) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn height(mut self, height: SizeAxis) -> Self {
|
||||
self.height = height;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn exact(mut self, width: i32, height: i32) -> Self {
|
||||
self.height = SizeAxis::Fixed(height);
|
||||
self.width = SizeAxis::Fixed(width);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn axis(mut self, axis: Axis) -> Self {
|
||||
self.axis = axis;
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn exact_width(mut self, val: i32) -> Self {
|
||||
// self.min_width = val;
|
||||
// self.pref_width = val;
|
||||
// self.max_width = val;
|
||||
// self
|
||||
// }
|
||||
|
||||
// pub fn shrink_width(mut self, val: i32) -> Self {
|
||||
// self.min_width = val;
|
||||
// self.pref_width = val;
|
||||
// self.max_width = val;
|
||||
// self
|
||||
// }
|
||||
|
||||
// pub fn grow_width(mut self, val: i32) -> Self {
|
||||
// self.min_width = val;
|
||||
// self.pref_width = val;
|
||||
// self.max_width = i32::MAX;
|
||||
// self
|
||||
// }
|
||||
|
||||
// pub fn grow_height(mut self, val: i32) -> Self {
|
||||
// self.min_height = val;
|
||||
// self.pref_height = val;
|
||||
// self.max_height = i32::MAX;
|
||||
// self
|
||||
// }
|
||||
|
||||
pub fn color(mut self, col: Color) -> Self {
|
||||
self.color = col;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_children(mut self, children: Vec<Node>) -> Self {
|
||||
self.children = children;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pad(mut self, padding: i32) -> Self {
|
||||
self.padding = padding;
|
||||
self
|
||||
}
|
||||
}
|
|
@ -36,10 +36,11 @@ impl Globals {
|
|||
textures.load_dir("assets/digits", rl, thread);
|
||||
|
||||
let config_path = userdata_dir().join(CONFIG_FILE_NAME);
|
||||
let config = fs::read_to_string(config_path)
|
||||
let mut config: Config = fs::read_to_string(config_path)
|
||||
.ok()
|
||||
.and_then(|s| serde_json::from_str(&s).ok())
|
||||
.unwrap_or_default();
|
||||
config.input.update_modifier_blocks();
|
||||
|
||||
Self {
|
||||
clipboard: Clipboard::new()
|
||||
|
|
142
src/ui2.rs
Normal file
142
src/ui2.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
use raylib::{
|
||||
color::Color,
|
||||
drawing::{RaylibDraw, RaylibDrawHandle},
|
||||
RaylibHandle,
|
||||
};
|
||||
|
||||
pub type EId = String;
|
||||
// pub enum EId{
|
||||
// }
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct UiCtx {
|
||||
active: Option<EId>,
|
||||
hovered: Option<EId>,
|
||||
kb_selected: Option<EId>,
|
||||
layout: LayoutState,
|
||||
draw_queue: Vec<DrawCommand>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub enum Axis {
|
||||
#[default]
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DrawCommand {
|
||||
Rect(Rect, Color),
|
||||
Text(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Rect {
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LayoutState {
|
||||
pub direction: Axis,
|
||||
cursor_x: i32,
|
||||
cursor_y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
pub padding: i32,
|
||||
}
|
||||
|
||||
type Widget = fn(&mut UiCtx) -> ();
|
||||
// type Widget = FnMut<;
|
||||
|
||||
impl UiCtx {
|
||||
pub fn layout(&mut self) -> &mut LayoutState {
|
||||
&mut self.layout
|
||||
}
|
||||
|
||||
pub fn begin(&mut self, d: &mut RaylibDrawHandle) {
|
||||
self.layout = LayoutState::default();
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, d: &mut RaylibDrawHandle) {
|
||||
while let Some(cmd) = self.draw_queue.pop() {
|
||||
match cmd {
|
||||
DrawCommand::Rect(r, col) => d.draw_rectangle(r.x, r.y, r.width, r.height, col),
|
||||
DrawCommand::Text(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn padded_container(&mut self, color: Color, axis: Axis, widget: Widget) {
|
||||
self.layout.padding = 4;
|
||||
self.layout.direction = axis;
|
||||
self.layout.width = 0;
|
||||
self.layout.height = 0;
|
||||
let old_layout_state = self.layout.clone();
|
||||
widget(self);
|
||||
let width = self.layout.width;
|
||||
let height = self.layout.height;
|
||||
self.layout = old_layout_state;
|
||||
self.layout.update_size_with_child(width, height);
|
||||
// old_layout_state.update_size_with_child(self.layout.width, self.layout.height);
|
||||
// old_layout_state.step(self.layout.width, self.layout.height);
|
||||
self.draw_queue
|
||||
.push(DrawCommand::Rect(self.layout.rect(), color));
|
||||
self.layout.step(width, height);
|
||||
// self.layout.step(width, height);
|
||||
}
|
||||
|
||||
pub fn square(&mut self, color: Color, width: i32) {
|
||||
let height = 50;
|
||||
// self.layout.update_size_with_child(width, height);
|
||||
self.layout.width = width;
|
||||
self.layout.height = height;
|
||||
self.draw_queue.push(DrawCommand::Rect(
|
||||
self.layout.rect(),
|
||||
color,
|
||||
));
|
||||
self.layout.step(width, height)
|
||||
// self.layout.width = width;
|
||||
// self.layout.height = height;
|
||||
// self.layout.step(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutState {
|
||||
fn step(&mut self, width: i32, height: i32) {
|
||||
match self.direction {
|
||||
Axis::Horizontal => self.cursor_x += width + self.padding,
|
||||
Axis::Vertical => self.cursor_y += height + self.padding,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_size_with_child(&mut self, width: i32, height: i32) {
|
||||
match self.direction {
|
||||
Axis::Horizontal => {
|
||||
self.width += width + self.padding;
|
||||
self.height = self.height.max(height + self.padding);
|
||||
}
|
||||
Axis::Vertical => {
|
||||
self.width = self.width.max(width + self.padding);
|
||||
self.height += height + self.padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rect(&self) -> Rect {
|
||||
Rect::new(self.cursor_x, self.cursor_y, self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue