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