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, modifiers_held: Vec<KeyModifier>, 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; Self {
36 value,
37 id: Id::new(addr), }
39 }
40}
41
42const DEVICE_STATE_ID: Lazy<Id> = Lazy::new(|| Id::new("_keybind_device_state"));
43
44impl KeyBindWidget<'_> {
45 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 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); painter.rect_filled(response.rect, visuals.rounding, visuals.bg_fill); 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; 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}