1#![warn(missing_docs)]
25
26use crate::{
27    brush::Brush,
28    core::{
29        color::Color, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
30        visitor::prelude::*,
31    },
32    define_constructor, define_widget_deref,
33    draw::{CommandTexture, Draw, DrawingContext},
34    message::{KeyCode, KeyboardModifiers, MessageDirection, MouseButton, UiMessage},
35    text::{TextBuilder, TextMessage},
36    widget::{Widget, WidgetBuilder, WidgetMessage},
37    BuildContext, Control, UiNode, UserInterface,
38};
39use fyrox_core::uuid_provider;
40use fyrox_core::variable::InheritableVariable;
41use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
42use serde::{Deserialize, Serialize};
43use std::{
44    fmt::{Display, Formatter},
45    ops::{Deref, DerefMut},
46};
47
48#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Reflect, Default, Visit)]
50pub enum HotKey {
51    #[default]
53    NotSet,
54    Some {
56        code: KeyCode,
58        modifiers: KeyboardModifiers,
60    },
61}
62
63impl HotKey {
64    pub fn from_key_code(key: KeyCode) -> Self {
66        Self::Some {
67            code: key,
68            modifiers: Default::default(),
69        }
70    }
71
72    pub fn ctrl_key(key: KeyCode) -> Self {
74        Self::Some {
75            code: key,
76            modifiers: KeyboardModifiers {
77                control: true,
78                ..Default::default()
79            },
80        }
81    }
82
83    pub fn shift_key(key: KeyCode) -> Self {
85        Self::Some {
86            code: key,
87            modifiers: KeyboardModifiers {
88                shift: true,
89                ..Default::default()
90            },
91        }
92    }
93
94    pub fn alt_key(key: KeyCode) -> Self {
96        Self::Some {
97            code: key,
98            modifiers: KeyboardModifiers {
99                shift: true,
100                ..Default::default()
101            },
102        }
103    }
104}
105
106impl Display for HotKey {
107    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
108        match self {
109            HotKey::NotSet => f.write_str("Not Set"),
110            HotKey::Some { code, modifiers } => {
111                if modifiers.control {
112                    f.write_str("Ctrl+")?;
113                }
114                if modifiers.alt {
115                    f.write_str("Alt+")?;
116                }
117                if modifiers.shift {
118                    f.write_str("Shift+")?;
119                }
120                if modifiers.system {
121                    f.write_str("Sys+")?;
122                }
123                write!(f, "{}", code.as_ref())
124            }
125        }
126    }
127}
128
129#[derive(Debug, Clone, PartialEq)]
131pub enum HotKeyEditorMessage {
132    Value(HotKey),
135}
136
137impl HotKeyEditorMessage {
138    define_constructor!(
139        HotKeyEditorMessage:Value => fn value(HotKey), layout: false
141    );
142}
143
144#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
180pub struct HotKeyEditor {
181    widget: Widget,
182    text: InheritableVariable<Handle<UiNode>>,
183    value: InheritableVariable<HotKey>,
184    editing: InheritableVariable<bool>,
185}
186
187impl ConstructorProvider<UiNode, UserInterface> for HotKeyEditor {
188    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
189        GraphNodeConstructor::new::<Self>()
190            .with_variant("Hot Key Editor", |ui| {
191                HotKeyEditorBuilder::new(WidgetBuilder::new().with_name("Hot Key Editor"))
192                    .build(&mut ui.build_ctx())
193                    .into()
194            })
195            .with_group("Input")
196    }
197}
198
199define_widget_deref!(HotKeyEditor);
200
201impl HotKeyEditor {
202    fn set_editing(&mut self, editing: bool, ui: &UserInterface) {
203        self.editing.set_value_and_mark_modified(editing);
204        ui.send_message(TextMessage::text(
205            *self.text,
206            MessageDirection::ToWidget,
207            if *self.editing {
208                "[WAITING INPUT]".to_string()
209            } else {
210                format!("{}", *self.value)
211            },
212        ));
213    }
214}
215
216uuid_provider!(HotKeyEditor = "7bc49843-1302-4e36-b901-63af5cea6c60");
217
218impl Control for HotKeyEditor {
219    fn draw(&self, drawing_context: &mut DrawingContext) {
220        drawing_context.push_rect_filled(&self.bounding_rect(), None);
222        drawing_context.commit(
223            self.clip_bounds(),
224            Brush::Solid(Color::TRANSPARENT),
225            CommandTexture::None,
226            None,
227        );
228    }
229
230    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
231        self.widget.handle_routed_message(ui, message);
232
233        if let Some(msg) = message.data::<WidgetMessage>() {
234            match msg {
235                WidgetMessage::KeyDown(key) => {
236                    if *self.editing
237                        && !matches!(
238                            *key,
239                            KeyCode::ControlLeft
240                                | KeyCode::ControlRight
241                                | KeyCode::ShiftLeft
242                                | KeyCode::ShiftRight
243                                | KeyCode::AltLeft
244                                | KeyCode::AltRight
245                        )
246                    {
247                        ui.send_message(HotKeyEditorMessage::value(
248                            self.handle,
249                            MessageDirection::ToWidget,
250                            HotKey::Some {
251                                code: *key,
252                                modifiers: ui.keyboard_modifiers,
253                            },
254                        ));
255
256                        message.set_handled(true);
257                    }
258                }
259                WidgetMessage::MouseDown { button, .. } => {
260                    if *button == MouseButton::Left {
261                        if *self.editing {
262                            self.set_editing(false, ui);
263                        } else {
264                            self.set_editing(true, ui);
265                        }
266                    }
267                }
268                WidgetMessage::Unfocus => {
269                    if *self.editing {
270                        self.set_editing(false, ui);
271                    }
272                }
273                _ => (),
274            }
275        }
276
277        if message.destination() == self.handle && message.direction() == MessageDirection::ToWidget
278        {
279            if let Some(HotKeyEditorMessage::Value(value)) = message.data() {
280                if value != &*self.value {
281                    self.value.set_value_and_mark_modified(value.clone());
282
283                    ui.send_message(TextMessage::text(
284                        *self.text,
285                        MessageDirection::ToWidget,
286                        format!("{}", *self.value),
287                    ));
288
289                    ui.send_message(message.reverse());
290                }
291            }
292        }
293    }
294}
295
296pub struct HotKeyEditorBuilder {
298    widget_builder: WidgetBuilder,
299    value: HotKey,
300}
301
302impl HotKeyEditorBuilder {
303    pub fn new(widget_builder: WidgetBuilder) -> Self {
305        Self {
306            widget_builder,
307            value: HotKey::NotSet,
308        }
309    }
310
311    pub fn with_value(mut self, hot_key: HotKey) -> Self {
313        self.value = hot_key;
314        self
315    }
316
317    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
319        let text = TextBuilder::new(WidgetBuilder::new())
320            .with_text(format!("{}", self.value))
321            .build(ctx);
322
323        let editor = HotKeyEditor {
324            widget: self.widget_builder.with_child(text).build(ctx),
325            text: text.into(),
326            editing: false.into(),
327            value: self.value.into(),
328        };
329
330        ctx.add_node(UiNode::new(editor))
331    }
332}
333
334#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Reflect, Visit, Default)]
337pub enum KeyBinding {
338    #[default]
340    NotSet,
341    Some(KeyCode),
343}
344
345impl PartialEq<KeyCode> for KeyBinding {
346    fn eq(&self, other: &KeyCode) -> bool {
347        match self {
348            KeyBinding::NotSet => false,
349            KeyBinding::Some(code) => code == other,
350        }
351    }
352}
353
354impl KeyBinding {
355    pub fn from_key_code(key: KeyCode) -> Self {
357        Self::Some(key)
358    }
359}
360
361impl Display for KeyBinding {
362    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
363        match self {
364            Self::NotSet => f.write_str("Not Set"),
365            Self::Some(code) => write!(f, "{}", code.as_ref()),
366        }
367    }
368}
369
370#[derive(Debug, Clone, PartialEq)]
372pub enum KeyBindingEditorMessage {
373    Value(KeyBinding),
375}
376
377impl KeyBindingEditorMessage {
378    define_constructor!(
379        KeyBindingEditorMessage:Value => fn value(KeyBinding), layout: false);
381}
382
383#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
409pub struct KeyBindingEditor {
410    widget: Widget,
411    text: InheritableVariable<Handle<UiNode>>,
412    value: InheritableVariable<KeyBinding>,
413    editing: InheritableVariable<bool>,
414}
415
416impl ConstructorProvider<UiNode, UserInterface> for KeyBindingEditor {
417    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
418        GraphNodeConstructor::new::<Self>()
419            .with_variant("Key Binding Editor", |ui| {
420                KeyBindingEditorBuilder::new(WidgetBuilder::new().with_name("Key Binding Editor"))
421                    .build(&mut ui.build_ctx())
422                    .into()
423            })
424            .with_group("Input")
425    }
426}
427
428define_widget_deref!(KeyBindingEditor);
429
430impl KeyBindingEditor {
431    fn set_editing(&mut self, editing: bool, ui: &UserInterface) {
432        self.editing.set_value_and_mark_modified(editing);
433        ui.send_message(TextMessage::text(
434            *self.text,
435            MessageDirection::ToWidget,
436            if *self.editing {
437                "[WAITING INPUT]".to_string()
438            } else {
439                format!("{}", *self.value)
440            },
441        ));
442    }
443}
444
445uuid_provider!(KeyBindingEditor = "150113ce-f95e-4c76-9ac9-4503e78b960f");
446
447impl Control for KeyBindingEditor {
448    fn draw(&self, drawing_context: &mut DrawingContext) {
449        drawing_context.push_rect_filled(&self.bounding_rect(), None);
451        drawing_context.commit(
452            self.clip_bounds(),
453            Brush::Solid(Color::TRANSPARENT),
454            CommandTexture::None,
455            None,
456        );
457    }
458
459    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
460        self.widget.handle_routed_message(ui, message);
461
462        if let Some(msg) = message.data::<WidgetMessage>() {
463            match msg {
464                WidgetMessage::KeyDown(key) => {
465                    ui.send_message(KeyBindingEditorMessage::value(
466                        self.handle,
467                        MessageDirection::ToWidget,
468                        KeyBinding::Some(*key),
469                    ));
470
471                    message.set_handled(true);
472                }
473                WidgetMessage::MouseDown { button, .. } => {
474                    if *button == MouseButton::Left {
475                        if *self.editing {
476                            self.set_editing(false, ui);
477                        } else {
478                            self.set_editing(true, ui);
479                        }
480                    }
481                }
482                WidgetMessage::Unfocus => {
483                    if *self.editing {
484                        self.set_editing(false, ui);
485                    }
486                }
487                _ => (),
488            }
489        }
490
491        if message.destination() == self.handle && message.direction() == MessageDirection::ToWidget
492        {
493            if let Some(KeyBindingEditorMessage::Value(value)) = message.data() {
494                if value != &*self.value {
495                    self.value.set_value_and_mark_modified(value.clone());
496
497                    ui.send_message(TextMessage::text(
498                        *self.text,
499                        MessageDirection::ToWidget,
500                        format!("{}", *self.value),
501                    ));
502
503                    ui.send_message(message.reverse());
504                }
505            }
506        }
507    }
508}
509
510pub struct KeyBindingEditorBuilder {
512    widget_builder: WidgetBuilder,
513    value: KeyBinding,
514}
515
516impl KeyBindingEditorBuilder {
517    pub fn new(widget_builder: WidgetBuilder) -> Self {
519        Self {
520            widget_builder,
521            value: KeyBinding::NotSet,
522        }
523    }
524
525    pub fn with_value(mut self, key_binding: KeyBinding) -> Self {
527        self.value = key_binding;
528        self
529    }
530
531    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
533        let text = TextBuilder::new(WidgetBuilder::new())
534            .with_text(format!("{}", self.value))
535            .build(ctx);
536
537        let editor = KeyBindingEditor {
538            widget: self.widget_builder.with_child(text).build(ctx),
539            text: text.into(),
540            editing: false.into(),
541            value: self.value.into(),
542        };
543
544        ctx.add_node(UiNode::new(editor))
545    }
546}
547
548#[cfg(test)]
549mod test {
550    use crate::key::{HotKeyEditorBuilder, KeyBindingEditorBuilder};
551    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
552
553    #[test]
554    fn test_deletion() {
555        test_widget_deletion(|ctx| KeyBindingEditorBuilder::new(WidgetBuilder::new()).build(ctx));
556        test_widget_deletion(|ctx| HotKeyEditorBuilder::new(WidgetBuilder::new()).build(ctx));
557    }
558}