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}