1#![warn(missing_docs)]
25
26use crate::message::MessageData;
27use crate::text::Text;
28use crate::{
29 brush::Brush,
30 core::{
31 color::Color, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
32 visitor::prelude::*,
33 },
34 define_widget_deref,
35 draw::{CommandTexture, Draw, DrawingContext},
36 message::{KeyCode, KeyboardModifiers, MouseButton, UiMessage},
37 text::{TextBuilder, TextMessage},
38 widget::{Widget, WidgetBuilder, WidgetMessage},
39 BuildContext, Control, UiNode, UserInterface,
40};
41use fyrox_core::uuid_provider;
42use fyrox_core::variable::InheritableVariable;
43use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
44use serde::{Deserialize, Serialize};
45use std::fmt::{Display, Formatter};
46
47#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Reflect, Default, Visit)]
49pub enum HotKey {
50 #[default]
52 NotSet,
53 Some {
55 code: KeyCode,
57 modifiers: KeyboardModifiers,
59 },
60}
61
62impl HotKey {
63 pub fn from_key_code(key: KeyCode) -> Self {
65 Self::Some {
66 code: key,
67 modifiers: Default::default(),
68 }
69 }
70
71 pub fn ctrl_key(key: KeyCode) -> Self {
73 Self::Some {
74 code: key,
75 modifiers: KeyboardModifiers {
76 control: true,
77 ..Default::default()
78 },
79 }
80 }
81
82 pub fn shift_key(key: KeyCode) -> Self {
84 Self::Some {
85 code: key,
86 modifiers: KeyboardModifiers {
87 shift: true,
88 ..Default::default()
89 },
90 }
91 }
92
93 pub fn alt_key(key: KeyCode) -> Self {
95 Self::Some {
96 code: key,
97 modifiers: KeyboardModifiers {
98 shift: true,
99 ..Default::default()
100 },
101 }
102 }
103}
104
105impl Display for HotKey {
106 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107 match self {
108 HotKey::NotSet => f.write_str("Not Set"),
109 HotKey::Some { code, modifiers } => {
110 if modifiers.control {
111 f.write_str("Ctrl+")?;
112 }
113 if modifiers.alt {
114 f.write_str("Alt+")?;
115 }
116 if modifiers.shift {
117 f.write_str("Shift+")?;
118 }
119 if modifiers.system {
120 f.write_str("Sys+")?;
121 }
122 write!(f, "{}", code.as_ref())
123 }
124 }
125 }
126}
127
128#[derive(Debug, Clone, PartialEq)]
130pub enum HotKeyEditorMessage {
131 Value(HotKey),
134}
135impl MessageData for HotKeyEditorMessage {}
136
137#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
173#[reflect(derived_type = "UiNode")]
174pub struct HotKeyEditor {
175 widget: Widget,
176 text: InheritableVariable<Handle<Text>>,
177 value: InheritableVariable<HotKey>,
178 editing: InheritableVariable<bool>,
179}
180
181impl ConstructorProvider<UiNode, UserInterface> for HotKeyEditor {
182 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
183 GraphNodeConstructor::new::<Self>()
184 .with_variant("Hot Key Editor", |ui| {
185 HotKeyEditorBuilder::new(WidgetBuilder::new().with_name("Hot Key Editor"))
186 .build(&mut ui.build_ctx())
187 .to_base()
188 .into()
189 })
190 .with_group("Input")
191 }
192}
193
194define_widget_deref!(HotKeyEditor);
195
196impl HotKeyEditor {
197 fn set_editing(&mut self, editing: bool, ui: &UserInterface) {
198 self.editing.set_value_and_mark_modified(editing);
199 let text = if *self.editing {
200 "[WAITING INPUT]".to_string()
201 } else {
202 format!("{}", *self.value)
203 };
204 ui.send(*self.text, TextMessage::Text(text));
205 }
206}
207
208uuid_provider!(HotKeyEditor = "7bc49843-1302-4e36-b901-63af5cea6c60");
209
210impl Control for HotKeyEditor {
211 fn draw(&self, drawing_context: &mut DrawingContext) {
212 drawing_context.push_rect_filled(&self.bounding_rect(), None);
214 drawing_context.commit(
215 self.clip_bounds(),
216 Brush::Solid(Color::TRANSPARENT),
217 CommandTexture::None,
218 &self.material,
219 None,
220 );
221 }
222
223 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
224 self.widget.handle_routed_message(ui, message);
225
226 if let Some(msg) = message.data::<WidgetMessage>() {
227 match msg {
228 WidgetMessage::KeyDown(key) => {
229 if *self.editing
230 && !matches!(
231 *key,
232 KeyCode::ControlLeft
233 | KeyCode::ControlRight
234 | KeyCode::ShiftLeft
235 | KeyCode::ShiftRight
236 | KeyCode::AltLeft
237 | KeyCode::AltRight
238 )
239 {
240 ui.send(
241 self.handle,
242 HotKeyEditorMessage::Value(HotKey::Some {
243 code: *key,
244 modifiers: ui.keyboard_modifiers,
245 }),
246 );
247
248 message.set_handled(true);
249 }
250 }
251 WidgetMessage::MouseDown { button, .. } => {
252 if *button == MouseButton::Left {
253 if *self.editing {
254 self.set_editing(false, ui);
255 } else {
256 self.set_editing(true, ui);
257 }
258 }
259 }
260 WidgetMessage::Unfocus => {
261 if *self.editing {
262 self.set_editing(false, ui);
263 }
264 }
265 _ => (),
266 }
267 }
268
269 if let Some(HotKeyEditorMessage::Value(value)) = message.data_for(self.handle) {
270 if value != &*self.value {
271 self.value.set_value_and_mark_modified(value.clone());
272 ui.send(*self.text, TextMessage::Text(format!("{}", *self.value)));
273 ui.send_message(message.reverse());
274 }
275 }
276 }
277}
278
279pub struct HotKeyEditorBuilder {
281 widget_builder: WidgetBuilder,
282 value: HotKey,
283}
284
285impl HotKeyEditorBuilder {
286 pub fn new(widget_builder: WidgetBuilder) -> Self {
288 Self {
289 widget_builder,
290 value: HotKey::NotSet,
291 }
292 }
293
294 pub fn with_value(mut self, hot_key: HotKey) -> Self {
296 self.value = hot_key;
297 self
298 }
299
300 pub fn build(self, ctx: &mut BuildContext) -> Handle<HotKeyEditor> {
302 let text = TextBuilder::new(WidgetBuilder::new())
303 .with_text(format!("{}", self.value))
304 .build(ctx);
305
306 let editor = HotKeyEditor {
307 widget: self.widget_builder.with_child(text).build(ctx),
308 text: text.into(),
309 editing: false.into(),
310 value: self.value.into(),
311 };
312
313 ctx.add(editor)
314 }
315}
316
317#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Reflect, Visit, Default)]
320pub enum KeyBinding {
321 #[default]
323 NotSet,
324 Some(KeyCode),
326}
327
328impl PartialEq<KeyCode> for KeyBinding {
329 fn eq(&self, other: &KeyCode) -> bool {
330 match self {
331 KeyBinding::NotSet => false,
332 KeyBinding::Some(code) => code == other,
333 }
334 }
335}
336
337impl KeyBinding {
338 pub fn from_key_code(key: KeyCode) -> Self {
340 Self::Some(key)
341 }
342}
343
344impl Display for KeyBinding {
345 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
346 match self {
347 Self::NotSet => f.write_str("Not Set"),
348 Self::Some(code) => write!(f, "{}", code.as_ref()),
349 }
350 }
351}
352
353#[derive(Debug, Clone, PartialEq)]
355pub enum KeyBindingEditorMessage {
356 Value(KeyBinding),
358}
359impl MessageData for KeyBindingEditorMessage {}
360
361#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
387#[reflect(derived_type = "UiNode")]
388pub struct KeyBindingEditor {
389 widget: Widget,
390 text: InheritableVariable<Handle<Text>>,
391 value: InheritableVariable<KeyBinding>,
392 editing: InheritableVariable<bool>,
393}
394
395impl ConstructorProvider<UiNode, UserInterface> for KeyBindingEditor {
396 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
397 GraphNodeConstructor::new::<Self>()
398 .with_variant("Key Binding Editor", |ui| {
399 KeyBindingEditorBuilder::new(WidgetBuilder::new().with_name("Key Binding Editor"))
400 .build(&mut ui.build_ctx())
401 .to_base()
402 .into()
403 })
404 .with_group("Input")
405 }
406}
407
408define_widget_deref!(KeyBindingEditor);
409
410impl KeyBindingEditor {
411 fn set_editing(&mut self, editing: bool, ui: &UserInterface) {
412 self.editing.set_value_and_mark_modified(editing);
413 ui.send(
414 *self.text,
415 TextMessage::Text(if *self.editing {
416 "[WAITING INPUT]".to_string()
417 } else {
418 format!("{}", *self.value)
419 }),
420 );
421 }
422}
423
424uuid_provider!(KeyBindingEditor = "150113ce-f95e-4c76-9ac9-4503e78b960f");
425
426impl Control for KeyBindingEditor {
427 fn draw(&self, drawing_context: &mut DrawingContext) {
428 drawing_context.push_rect_filled(&self.bounding_rect(), None);
430 drawing_context.commit(
431 self.clip_bounds(),
432 Brush::Solid(Color::TRANSPARENT),
433 CommandTexture::None,
434 &self.material,
435 None,
436 );
437 }
438
439 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
440 self.widget.handle_routed_message(ui, message);
441
442 if let Some(msg) = message.data::<WidgetMessage>() {
443 match msg {
444 WidgetMessage::KeyDown(key) => {
445 ui.send(
446 self.handle,
447 KeyBindingEditorMessage::Value(KeyBinding::Some(*key)),
448 );
449
450 message.set_handled(true);
451 }
452 WidgetMessage::MouseDown { button, .. } => {
453 if *button == MouseButton::Left {
454 if *self.editing {
455 self.set_editing(false, ui);
456 } else {
457 self.set_editing(true, ui);
458 }
459 }
460 }
461 WidgetMessage::Unfocus => {
462 if *self.editing {
463 self.set_editing(false, ui);
464 }
465 }
466 _ => (),
467 }
468 }
469
470 if let Some(KeyBindingEditorMessage::Value(value)) = message.data_for(self.handle) {
471 if value != &*self.value {
472 self.value.set_value_and_mark_modified(value.clone());
473 ui.send(*self.text, TextMessage::Text(format!("{}", *self.value)));
474 ui.send_message(message.reverse());
475 }
476 }
477 }
478}
479
480pub struct KeyBindingEditorBuilder {
482 widget_builder: WidgetBuilder,
483 value: KeyBinding,
484}
485
486impl KeyBindingEditorBuilder {
487 pub fn new(widget_builder: WidgetBuilder) -> Self {
489 Self {
490 widget_builder,
491 value: KeyBinding::NotSet,
492 }
493 }
494
495 pub fn with_value(mut self, key_binding: KeyBinding) -> Self {
497 self.value = key_binding;
498 self
499 }
500
501 pub fn build(self, ctx: &mut BuildContext) -> Handle<KeyBindingEditor> {
503 let text = TextBuilder::new(WidgetBuilder::new())
504 .with_text(format!("{}", self.value))
505 .build(ctx);
506
507 let editor = KeyBindingEditor {
508 widget: self.widget_builder.with_child(text).build(ctx),
509 text: text.into(),
510 editing: false.into(),
511 value: self.value.into(),
512 };
513
514 ctx.add(editor)
515 }
516}
517
518#[cfg(test)]
519mod test {
520 use crate::key::{HotKeyEditorBuilder, KeyBindingEditorBuilder};
521 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
522
523 #[test]
524 fn test_deletion() {
525 test_widget_deletion(|ctx| KeyBindingEditorBuilder::new(WidgetBuilder::new()).build(ctx));
526 test_widget_deletion(|ctx| HotKeyEditorBuilder::new(WidgetBuilder::new()).build(ctx));
527 }
528}