egui_keybinds/
widget.rs

1use crate::{keycodes::KeyModifier, *};
2use device_query::{DeviceQuery, DeviceState};
3
4use egui::{Align2, FontId, Id, Response, Sense, Ui, Widget};
5use once_cell::sync::Lazy;
6
7pub struct KeyBindWidget<'a> {
8    value: &'a mut KeyBind,
9    id: Id,
10}
11
12#[derive(Clone, Hash)]
13pub struct KeyBindWidgetState {
14    active: bool,                     // is this keybind currently being set
15    modifiers_held: Vec<KeyModifier>, // current held modifier keys
16    last_keys_held: Vec<device_query::Keycode>,
17    keys_held: Vec<device_query::Keycode>,
18}
19
20impl KeyBindWidgetState {
21    fn new() -> Self {
22        Self {
23            active: false,
24            modifiers_held: vec![],
25            last_keys_held: vec![],
26            keys_held: vec![],
27        }
28    }
29}
30
31impl<'a> KeyBindWidget<'a> {
32    pub fn new(value: &'a mut KeyBind) -> Self {
33        let addr = value as *const KeyBind as u64; // get pointer of KeyBind as a u64
34
35        Self {
36            value,
37            id: Id::new(addr), // create id from KeyBind pointer (should be unique)
38        }
39    }
40}
41
42const DEVICE_STATE_ID: Lazy<Id> = Lazy::new(|| Id::new("_keybind_device_state"));
43
44impl KeyBindWidget<'_> {
45    // returns true if the bind was set
46    fn run_input(
47        &mut self,
48        down: Vec<device_query::Keycode>,
49        up: Vec<device_query::Keycode>,
50        state: &mut KeyBindWidgetState,
51    ) -> bool {
52        for device_key in down {
53            let key = KeyCode::from(device_key);
54
55            if let Some(modifier) = key.as_modifier() {
56                // prevent double mods, eg lctrl and rctrl
57                if !state.modifiers_held.contains(&modifier) {
58                    state.modifiers_held.push(modifier);
59                }
60            } else {
61                self.value.key = Some(key);
62                self.value.modifiers = state.modifiers_held.clone();
63
64                return true;
65            }
66        }
67
68        for device_key in up {
69            let key = KeyCode::from(device_key);
70
71            if let Some(modifier) = key.as_modifier() {
72                state.modifiers_held.retain(|m| *m != modifier);
73
74                self.value.key = Some(key);
75                self.value.modifiers = state.modifiers_held.clone();
76
77                return true;
78            }
79        }
80
81        return false;
82    }
83
84    fn check_keys(
85        &mut self,
86        device: &DeviceState,
87        state: &mut KeyBindWidgetState,
88    ) -> (Vec<device_query::Keycode>, Vec<device_query::Keycode>) {
89        state.keys_held = device.get_keys();
90
91        let pressed_keys = helper::vec_intersection(&state.keys_held, &state.last_keys_held);
92
93        let released_keys = helper::vec_intersection(&state.last_keys_held, &state.keys_held);
94
95        state.last_keys_held = state.keys_held.clone();
96
97        (pressed_keys, released_keys)
98    }
99
100    fn read_states(&mut self, ui: &mut Ui) -> (DeviceState, KeyBindWidgetState) {
101        let device_state = ui.data_mut(|d| {
102            d.get_temp(DEVICE_STATE_ID.clone())
103                .unwrap_or(DeviceState::new())
104        });
105
106        let state = ui.data_mut(|d| d.get_temp(self.id).unwrap_or(KeyBindWidgetState::new()));
107
108        (device_state, state)
109    }
110
111    fn save_states(&mut self, ui: &mut Ui, device: DeviceState, state: KeyBindWidgetState) {
112        ui.data_mut(|d| {
113            d.insert_temp(DEVICE_STATE_ID.clone(), device);
114            d.insert_temp(self.id, state);
115        });
116    }
117}
118
119impl Widget for KeyBindWidget<'_> {
120    fn ui(mut self, ui: &mut Ui) -> Response {
121        let (response, painter) = ui.allocate_painter(ui.spacing().interact_size, Sense::click());
122
123        let (device, mut state) = self.read_states(ui);
124
125        let visuals = ui.style().interact_selectable(&response, state.active); // get the current interactable style settings
126
127        painter.rect_filled(response.rect, visuals.rounding, visuals.bg_fill); // draw a lil button :)
128
129        painter.text(
130            response.rect.center(),
131            Align2::CENTER_CENTER,
132            self.value.serialize(),
133            FontId::default(),
134            visuals.text_color(),
135        );
136
137        if response.clicked() {
138            if state.active {
139                state.active = false;
140            } else {
141                state.active = true;
142
143                state.last_keys_held = device.get_keys();
144                state.keys_held = state.last_keys_held.clone();
145            }
146        }
147
148        if response.secondary_clicked() {
149            self.value.key = None;
150            self.value.modifiers = vec![];
151
152            state.active = false;
153        }
154
155        if state.active {
156            let (down, up) = self.check_keys(&device, &mut state);
157
158            if self.run_input(down, up, &mut state) {
159                state.active = false; // key was set
160
161                ui.ctx().request_repaint();
162            }
163        }
164
165        let saved_state = if state.active {
166            state
167        } else {
168            KeyBindWidgetState::new()
169        };
170
171        self.save_states(ui, device, saved_state);
172        return response;
173    }
174}