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 key_with_modifiers(key: KeyCode, modifiers: KeyboardModifiers) -> Self {
74 Self::Some {
75 code: key,
76 modifiers,
77 }
78 }
79
80 pub fn ctrl_key(key: KeyCode) -> Self {
82 Self::Some {
83 code: key,
84 modifiers: KeyboardModifiers {
85 control: true,
86 ..Default::default()
87 },
88 }
89 }
90
91 pub fn shift_key(key: KeyCode) -> Self {
93 Self::Some {
94 code: key,
95 modifiers: KeyboardModifiers {
96 shift: true,
97 ..Default::default()
98 },
99 }
100 }
101
102 pub fn alt_key(key: KeyCode) -> Self {
104 Self::Some {
105 code: key,
106 modifiers: KeyboardModifiers {
107 shift: true,
108 ..Default::default()
109 },
110 }
111 }
112
113 pub fn sys_key(key: KeyCode) -> Self {
116 Self::Some {
117 code: key,
118 modifiers: KeyboardModifiers {
119 system: true,
120 ..Default::default()
121 },
122 }
123 }
124}
125
126impl Display for HotKey {
127 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
128 match self {
129 HotKey::NotSet => f.write_str("Not Set"),
130 HotKey::Some { code, modifiers } => {
131 if modifiers.control {
132 f.write_str("Ctrl+")?;
133 }
134 if modifiers.alt {
135 f.write_str("Alt+")?;
136 }
137 if modifiers.shift {
138 f.write_str("Shift+")?;
139 }
140 if modifiers.system {
141 f.write_str("Sys+")?;
142 }
143 write!(f, "{}", code.as_ref())
144 }
145 }
146 }
147}
148
149#[derive(Debug, Clone, PartialEq)]
151pub enum HotKeyEditorMessage {
152 Value(HotKey),
155}
156impl MessageData for HotKeyEditorMessage {}
157
158#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
194#[reflect(derived_type = "UiNode")]
195pub struct HotKeyEditor {
196 widget: Widget,
197 text: InheritableVariable<Handle<Text>>,
198 value: InheritableVariable<HotKey>,
199 editing: InheritableVariable<bool>,
200}
201
202impl ConstructorProvider<UiNode, UserInterface> for HotKeyEditor {
203 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
204 GraphNodeConstructor::new::<Self>()
205 .with_variant("Hot Key Editor", |ui| {
206 HotKeyEditorBuilder::new(WidgetBuilder::new().with_name("Hot Key Editor"))
207 .build(&mut ui.build_ctx())
208 .to_base()
209 .into()
210 })
211 .with_group("Input")
212 }
213}
214
215define_widget_deref!(HotKeyEditor);
216
217impl HotKeyEditor {
218 fn set_editing(&mut self, editing: bool, ui: &UserInterface) {
219 self.editing.set_value_and_mark_modified(editing);
220 let text = if *self.editing {
221 "[WAITING INPUT]".to_string()
222 } else {
223 format!("{}", *self.value)
224 };
225 ui.send(*self.text, TextMessage::Text(text));
226 }
227}
228
229uuid_provider!(HotKeyEditor = "7bc49843-1302-4e36-b901-63af5cea6c60");
230
231impl Control for HotKeyEditor {
232 fn draw(&self, drawing_context: &mut DrawingContext) {
233 drawing_context.push_rect_filled(&self.bounding_rect(), None);
235 drawing_context.commit(
236 self.clip_bounds(),
237 Brush::Solid(Color::TRANSPARENT),
238 CommandTexture::None,
239 &self.material,
240 None,
241 );
242 }
243
244 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
245 self.widget.handle_routed_message(ui, message);
246
247 if let Some(msg) = message.data::<WidgetMessage>() {
248 match msg {
249 WidgetMessage::KeyDown(key) => {
250 if *self.editing
251 && !matches!(
252 *key,
253 KeyCode::ControlLeft
254 | KeyCode::ControlRight
255 | KeyCode::ShiftLeft
256 | KeyCode::ShiftRight
257 | KeyCode::AltLeft
258 | KeyCode::AltRight
259 )
260 {
261 ui.send(
262 self.handle,
263 HotKeyEditorMessage::Value(HotKey::Some {
264 code: *key,
265 modifiers: ui.keyboard_modifiers,
266 }),
267 );
268
269 message.set_handled(true);
270 }
271 }
272 WidgetMessage::MouseDown { button, .. } => {
273 if *button == MouseButton::Left {
274 if *self.editing {
275 self.set_editing(false, ui);
276 } else {
277 self.set_editing(true, ui);
278 }
279 }
280 }
281 WidgetMessage::Unfocus => {
282 if *self.editing {
283 self.set_editing(false, ui);
284 }
285 }
286 _ => (),
287 }
288 }
289
290 if let Some(HotKeyEditorMessage::Value(value)) = message.data_for(self.handle) {
291 if value != &*self.value {
292 self.value.set_value_and_mark_modified(value.clone());
293 ui.send(*self.text, TextMessage::Text(format!("{}", *self.value)));
294 ui.try_send_response(message);
295 }
296 }
297 }
298}
299
300pub struct HotKeyEditorBuilder {
302 widget_builder: WidgetBuilder,
303 value: HotKey,
304}
305
306impl HotKeyEditorBuilder {
307 pub fn new(widget_builder: WidgetBuilder) -> Self {
309 Self {
310 widget_builder,
311 value: HotKey::NotSet,
312 }
313 }
314
315 pub fn with_value(mut self, hot_key: HotKey) -> Self {
317 self.value = hot_key;
318 self
319 }
320
321 pub fn build(self, ctx: &mut BuildContext) -> Handle<HotKeyEditor> {
323 let text = TextBuilder::new(WidgetBuilder::new())
324 .with_text(format!("{}", self.value))
325 .build(ctx);
326
327 let editor = HotKeyEditor {
328 widget: self.widget_builder.with_child(text).build(ctx),
329 text: text.into(),
330 editing: false.into(),
331 value: self.value.into(),
332 };
333
334 ctx.add(editor)
335 }
336}
337
338#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Reflect, Visit, Default)]
341pub enum KeyBinding {
342 #[default]
344 NotSet,
345 Some(KeyCode),
347}
348
349impl PartialEq<KeyCode> for KeyBinding {
350 fn eq(&self, other: &KeyCode) -> bool {
351 match self {
352 KeyBinding::NotSet => false,
353 KeyBinding::Some(code) => code == other,
354 }
355 }
356}
357
358impl KeyBinding {
359 pub fn from_key_code(key: KeyCode) -> Self {
361 Self::Some(key)
362 }
363}
364
365impl Display for KeyBinding {
366 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
367 match self {
368 Self::NotSet => f.write_str("Not Set"),
369 Self::Some(code) => write!(f, "{}", code.as_ref()),
370 }
371 }
372}
373
374#[derive(Debug, Clone, PartialEq)]
376pub enum KeyBindingEditorMessage {
377 Value(KeyBinding),
379}
380impl MessageData for KeyBindingEditorMessage {}
381
382#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
408#[reflect(derived_type = "UiNode")]
409pub struct KeyBindingEditor {
410 widget: Widget,
411 text: InheritableVariable<Handle<Text>>,
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 .to_base()
423 .into()
424 })
425 .with_group("Input")
426 }
427}
428
429define_widget_deref!(KeyBindingEditor);
430
431impl KeyBindingEditor {
432 fn set_editing(&mut self, editing: bool, ui: &UserInterface) {
433 self.editing.set_value_and_mark_modified(editing);
434 ui.send(
435 *self.text,
436 TextMessage::Text(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 &self.material,
456 None,
457 );
458 }
459
460 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
461 self.widget.handle_routed_message(ui, message);
462
463 if let Some(msg) = message.data::<WidgetMessage>() {
464 match msg {
465 WidgetMessage::KeyDown(key) => {
466 ui.send(
467 self.handle,
468 KeyBindingEditorMessage::Value(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 let Some(KeyBindingEditorMessage::Value(value)) = message.data_for(self.handle) {
492 if value != &*self.value {
493 self.value.set_value_and_mark_modified(value.clone());
494 ui.send(*self.text, TextMessage::Text(format!("{}", *self.value)));
495 ui.try_send_response(message);
496 }
497 }
498 }
499}
500
501pub struct KeyBindingEditorBuilder {
503 widget_builder: WidgetBuilder,
504 value: KeyBinding,
505}
506
507impl KeyBindingEditorBuilder {
508 pub fn new(widget_builder: WidgetBuilder) -> Self {
510 Self {
511 widget_builder,
512 value: KeyBinding::NotSet,
513 }
514 }
515
516 pub fn with_value(mut self, key_binding: KeyBinding) -> Self {
518 self.value = key_binding;
519 self
520 }
521
522 pub fn build(self, ctx: &mut BuildContext) -> Handle<KeyBindingEditor> {
524 let text = TextBuilder::new(WidgetBuilder::new())
525 .with_text(format!("{}", self.value))
526 .build(ctx);
527
528 let editor = KeyBindingEditor {
529 widget: self.widget_builder.with_child(text).build(ctx),
530 text: text.into(),
531 editing: false.into(),
532 value: self.value.into(),
533 };
534
535 ctx.add(editor)
536 }
537}
538
539#[cfg(test)]
540mod test {
541 use crate::key::{HotKeyEditorBuilder, KeyBindingEditorBuilder};
542 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
543
544 #[test]
545 fn test_deletion() {
546 test_widget_deletion(|ctx| KeyBindingEditorBuilder::new(WidgetBuilder::new()).build(ctx));
547 test_widget_deletion(|ctx| HotKeyEditorBuilder::new(WidgetBuilder::new()).build(ctx));
548 }
549}