fyrox_ui/
message.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Message and events module contains all possible widget messages and OS events. See [`UiMessage`] docs for more info and
22//! examples.
23
24#![warn(missing_docs)]
25
26use crate::{
27    core::{algebra::Vector2, pool::Handle, reflect::prelude::*, visitor::prelude::*},
28    UiNode, UserInterface,
29};
30use fyrox_core::uuid_provider;
31use serde::{Deserialize, Serialize};
32use std::{any::Any, cell::Cell, fmt::Debug};
33use strum_macros::{AsRefStr, EnumString, VariantNames};
34
35/// Defines a new message constructor for a enum variant. It is widely used in this crate to create shortcuts to create
36/// messages. Why is it needed anyway? Just to reduce boilerplate code as much as possible.
37///
38/// ## Examples
39///
40/// The following example shows how to create message constructors for various kinds of enum variants:
41///
42/// ```rust
43/// # use fyrox_ui::{
44/// #     core::pool::Handle, define_constructor, message::MessageDirection, message::UiMessage, UiNode,
45/// #     UserInterface,
46/// # };
47/// #
48/// // Message must be debuggable, comparable, cloneable.
49/// #[derive(Debug, PartialEq, Clone)]
50/// enum MyWidgetMessage {
51///     DoSomething,
52///     Foo(u32),
53///     Bar { foo: u32, baz: u8 },
54/// }
55///
56/// impl MyWidgetMessage {
57///     // The first option is used to create constructors plain enum variants:
58///     //
59///     //                  enum name       variant            name          perform layout?
60///     //                      v              v                 v                  v
61///     define_constructor!(MyWidgetMessage:DoSomething => fn do_something(), layout: false);
62///
63///     // The second option is used to create constructors for single-arg tuple enum variants:
64///     //
65///     //                  enum name     variant    name arg    perform layout?
66///     //                      v            v         v   v           v
67///     define_constructor!(MyWidgetMessage:Foo => fn foo(u32), layout: false);
68///
69///     // The third option is used to create constructors for enum variants with fields:
70///     //
71///     //                  enum name     variant    name arg  type arg type  perform layout?
72///     //                      v            v         v   v     v   v    v          v
73///     define_constructor!(MyWidgetMessage:Bar => fn bar(foo: u32, baz: u8), layout: false);
74/// }
75///
76/// fn using_messages(my_widget: Handle<UiNode>, ui: &UserInterface) {
77///     // Send MyWidgetMessage::DoSomething
78///     ui.send_message(MyWidgetMessage::do_something(
79///         my_widget,
80///         MessageDirection::ToWidget,
81///     ));
82///
83///     // Send MyWidgetMessage::Foo
84///     ui.send_message(MyWidgetMessage::foo(
85///         my_widget,
86///         MessageDirection::ToWidget,
87///         5,
88///     ));
89///
90///     // Send MyWidgetMessage::Bar
91///     ui.send_message(MyWidgetMessage::bar(
92///         my_widget,
93///         MessageDirection::ToWidget,
94///         1,
95///         2,
96///     ));
97/// }
98/// ```
99#[macro_export]
100macro_rules! define_constructor {
101    ($(#[$meta:meta])* $inner:ident : $inner_var:tt => fn $name:ident(), layout: $perform_layout:expr) => {
102        $(#[$meta])*
103        #[must_use = "message does nothing until sent to ui"]
104        pub fn $name(destination: Handle<UiNode>, direction: MessageDirection) -> UiMessage {
105            UiMessage {
106                handled: std::cell::Cell::new(false),
107                data: Box::new($inner::$inner_var),
108                destination,
109                direction,
110                routing_strategy: Default::default(),
111                perform_layout: std::cell::Cell::new($perform_layout),
112                flags: 0
113            }
114        }
115    };
116
117    ($(#[$meta:meta])* $inner:ident : $inner_var:tt => fn $name:ident($typ:ty), layout: $perform_layout:expr) => {
118        $(#[$meta])*
119        #[must_use = "message does nothing until sent to ui"]
120        pub fn $name(destination: Handle<UiNode>, direction: MessageDirection, value:$typ) -> UiMessage {
121            UiMessage {
122                handled: std::cell::Cell::new(false),
123                data: Box::new($inner::$inner_var(value)),
124                destination,
125                direction,
126                routing_strategy: Default::default(),
127                perform_layout: std::cell::Cell::new($perform_layout),
128                flags: 0
129            }
130        }
131    };
132
133    ($(#[$meta:meta])* $inner:ident : $inner_var:tt => fn $name:ident( $($params:ident : $types:ty),+ ), layout: $perform_layout:expr) => {
134        $(#[$meta])*
135        #[must_use = "message does nothing until sent to ui"]
136        pub fn $name(destination: Handle<UiNode>, direction: MessageDirection, $($params : $types),+) -> UiMessage {
137            UiMessage {
138                handled: std::cell::Cell::new(false),
139                data: Box::new($inner::$inner_var { $($params),+ }),
140                destination,
141                direction,
142                routing_strategy: Default::default(),
143                perform_layout: std::cell::Cell::new($perform_layout),
144                flags: 0
145            }
146        }
147    }
148}
149
150/// Message direction allows you to distinguish from where message has came from. Often there is a need to find out who
151/// created a message to respond properly. Imagine that we have a NumericUpDown input field for a property and we using
152/// some data source to feed data into input field. When we change something in the input field by typing, it creates a
153/// message with new value. On other hand we often need to put new value in the input field from some code, in this case
154/// we again creating a message. But how to understand from which "side" message has came from? Was it filled in by user
155/// and we should create a command  to change value in the data source, or it was created from syncing code just to pass
156/// new value to UI? This problem solved by setting a direction to a message. Also it solves another problem: often we
157/// need to respond to a message only if it did some changes. In this case at first we fire a message with ToWidget direction,
158/// widget catches it and checks if changes are needed and if so, it "rethrows" message with direction FromWidget. Listeners
159/// are "subscribed" to FromWidget messages only and won't respond to ToWidget messages.
160#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Eq)]
161pub enum MessageDirection {
162    /// Used to indicate a request for changes in a widget.
163    ToWidget,
164
165    /// Used to indicate response from widget if anything has actually changed.
166    FromWidget,
167}
168
169impl MessageDirection {
170    /// Reverses direction.
171    pub fn reverse(self) -> Self {
172        match self {
173            Self::ToWidget => Self::FromWidget,
174            Self::FromWidget => Self::ToWidget,
175        }
176    }
177}
178
179/// A trait, that is used by every messages used in the user interface. It contains utility methods, that are used
180/// for downcasting and equality comparison.
181pub trait MessageData: 'static + Debug + Any + Send {
182    /// Casts `self` as [`Any`] reference.
183    fn as_any(&self) -> &dyn Any;
184
185    /// Compares this message data with some other.
186    fn compare(&self, other: &dyn MessageData) -> bool;
187
188    /// Clones self as boxed value.
189    fn clone_box(&self) -> Box<dyn MessageData>;
190}
191
192impl<T> MessageData for T
193where
194    T: 'static + Debug + PartialEq + Any + Send + Clone,
195{
196    fn as_any(&self) -> &dyn Any {
197        self
198    }
199
200    fn compare(&self, other: &dyn MessageData) -> bool {
201        other
202            .as_any()
203            .downcast_ref::<T>()
204            .map(|other| other == self)
205            .unwrap_or_default()
206    }
207
208    fn clone_box(&self) -> Box<dyn MessageData> {
209        Box::new(self.clone())
210    }
211}
212
213/// Defines a way of how the message will behave in the widget tree after it was delivered to
214/// the destination node.
215#[derive(Default, Copy, Clone, Debug, PartialEq)]
216pub enum RoutingStrategy {
217    /// A message will be passed to every ancestor widget in the hierarchy until the root.
218    #[default]
219    BubbleUp,
220    /// A message will be passed directly to a widget directly and won't be passed to any other
221    /// widget (message preview mechanism will still be used).
222    Direct,
223}
224
225/// Message is basic communication element that is used to deliver information to widget or to user code.
226///
227/// ## Motivation
228///
229/// This UI library uses message passing mechanism to communicate with widgets. This is very simple and reliable mechanism that
230/// effectively decouples widgets from each other. There is no direct way of modify something during runtime, you have to use
231/// messages to change state of ui elements.
232///
233/// ## Direction
234///
235/// Each message marked with "Direction" field, which means supported routes for message. For example [`crate::button::ButtonMessage::Click`]
236/// has "Direction: To/From UI" which means that it can be sent either from internals of library or from user code. However
237/// [`crate::widget::WidgetMessage::Focus`] has "Direction: From UI" which means that only internal library code can send such messages without
238/// a risk of breaking anything.
239///
240/// ## Threading
241///
242/// UiMessage is nor Send or Sync. User interface is a single-thread thing, as well as its messages.
243///
244/// ## Examples
245///
246/// ```rust
247/// use fyrox_ui::{
248///     core::pool::Handle, define_constructor, message::MessageDirection, message::UiMessage, UiNode,
249///     UserInterface,
250/// };
251///
252/// // Message must be debuggable and comparable.
253/// #[derive(Debug, PartialEq, Clone)]
254/// enum MyWidgetMessage {
255///     DoSomething,
256///     Foo(u32),
257///     Bar { foo: u32, baz: u8 },
258/// }
259///
260/// impl MyWidgetMessage {
261///     define_constructor!(MyWidgetMessage:DoSomething => fn do_something(), layout: false);
262///     define_constructor!(MyWidgetMessage:Foo => fn foo(u32), layout: false);
263///     define_constructor!(MyWidgetMessage:Bar => fn bar(foo: u32, baz: u8), layout: false);
264/// }
265///
266/// fn using_messages(my_widget: Handle<UiNode>, ui: &UserInterface) {
267///     // Send MyWidgetMessage::DoSomething
268///     ui.send_message(MyWidgetMessage::do_something(
269///         my_widget,
270///         MessageDirection::ToWidget,
271///     ));
272///     // Send MyWidgetMessage::Foo
273///     ui.send_message(MyWidgetMessage::foo(
274///         my_widget,
275///         MessageDirection::ToWidget,
276///         5,
277///     ));
278///     // Send MyWidgetMessage::Bar
279///     ui.send_message(MyWidgetMessage::bar(
280///         my_widget,
281///         MessageDirection::ToWidget,
282///         1,
283///         2,
284///     ));
285/// }
286/// ```
287///
288///
289pub struct UiMessage {
290    /// Useful flag to check if a message was already handled. It could be used to mark messages as "handled" to prevent
291    /// any further responses to them. It is especially useful in bubble message routing, when a message is passed through
292    /// the entire chain of parent nodes starting from current. In this, you can mark a message as "handled" and also check
293    /// if it is handled or not. For example, this is used in [`crate::tree::Tree`] implementation, to prevent double-click
294    /// to close all the parent trees from current.
295    pub handled: Cell<bool>,
296
297    /// Actual message data. Use [`UiMessage::data`] method to try to downcast the internal data to a specific type.
298    pub data: Box<dyn MessageData>,
299
300    /// Handle of node that will receive message. Please note that **all** nodes in hierarchy will also receive this message,
301    /// order is "up-on-tree" (so called "bubble" message routing). T
302    pub destination: Handle<UiNode>,
303
304    /// Indicates the direction of the message. See [`MessageDirection`] docs for more info.
305    pub direction: MessageDirection,
306
307    /// Defines a way of how the message will behave in the widget tree after it was delivered to
308    /// the destination node. Default is bubble routing. See [`RoutingStrategy`] for more info.
309    pub routing_strategy: RoutingStrategy,
310
311    /// Whether or not message requires layout to be calculated first.
312    ///
313    /// ## Motivation
314    ///
315    /// Some of message handling routines uses layout info, but message loop performed right after layout pass, but some of messages
316    /// may change layout and this flag tells UI to perform layout before passing message further. In ideal case we'd perform layout
317    /// after **each** message, but since layout pass is super heavy we should do it **only** when it is actually needed.
318    pub perform_layout: Cell<bool>,
319
320    /// A custom user flags. Use it if `handled` flag is not enough.
321    pub flags: u64,
322}
323
324/// Compares the new value with the existing one, and if they do not match, sets the new value to it
325/// and sends the given message back to the message queue with the opposite direction. This function
326/// is useful to reduce boilerplate code when reacting to widget messages.
327pub fn compare_and_set<T>(
328    value: &mut T,
329    new_value: &T,
330    message: &UiMessage,
331    ui: &UserInterface,
332) -> bool
333where
334    T: PartialEq + Clone,
335{
336    if value != new_value {
337        *value = new_value.clone();
338        ui.send_message(message.reverse());
339        true
340    } else {
341        false
342    }
343}
344
345impl Debug for UiMessage {
346    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347        write!(
348            f,
349            "UiMessage({}:({})",
350            match self.direction {
351                MessageDirection::ToWidget => "To",
352                MessageDirection::FromWidget => "From",
353            },
354            self.destination
355        )?;
356        if self.handled.get() {
357            write!(f, ",handled")?;
358        }
359        if self.perform_layout.get() {
360            write!(f, ",layout")?;
361        }
362        if self.flags != 0 {
363            write!(f, ",flags:{}", self.flags)?;
364        }
365        write!(f, "):{:?}", self.data)
366    }
367}
368
369impl Clone for UiMessage {
370    fn clone(&self) -> Self {
371        Self {
372            handled: self.handled.clone(),
373            data: self.data.clone_box(),
374            destination: self.destination,
375            direction: self.direction,
376            routing_strategy: self.routing_strategy,
377            perform_layout: self.perform_layout.clone(),
378            flags: self.flags,
379        }
380    }
381}
382
383impl PartialEq for UiMessage {
384    fn eq(&self, other: &Self) -> bool {
385        self.handled == other.handled
386            && self.data.compare(&*other.data)
387            && self.destination == other.destination
388            && self.routing_strategy == other.routing_strategy
389            && self.direction == other.direction
390            && self.perform_layout == other.perform_layout
391            && self.flags == other.flags
392    }
393}
394
395impl UiMessage {
396    /// Creates new UI message with desired data.
397    pub fn with_data<T: MessageData>(data: T) -> Self {
398        Self {
399            handled: Cell::new(false),
400            data: Box::new(data),
401            destination: Default::default(),
402            direction: MessageDirection::ToWidget,
403            routing_strategy: Default::default(),
404            perform_layout: Cell::new(false),
405            flags: 0,
406        }
407    }
408
409    /// Sets the desired destination of the message.
410    pub fn with_destination(mut self, destination: Handle<UiNode>) -> Self {
411        self.destination = destination;
412        self
413    }
414
415    /// Sets the desired direction of the message.
416    pub fn with_direction(mut self, direction: MessageDirection) -> Self {
417        self.direction = direction;
418        self
419    }
420
421    /// Sets the desired handled flag of the message.
422    pub fn with_handled(self, handled: bool) -> Self {
423        self.handled.set(handled);
424        self
425    }
426
427    /// Sets the desired perform layout flag of the message.
428    pub fn with_perform_layout(self, perform_layout: bool) -> Self {
429        self.perform_layout.set(perform_layout);
430        self
431    }
432
433    /// Sets the desired routing strategy.
434    pub fn with_routing_strategy(mut self, routing_strategy: RoutingStrategy) -> Self {
435        self.routing_strategy = routing_strategy;
436        self
437    }
438
439    /// Sets the desired flags of the message.
440    pub fn with_flags(mut self, flags: u64) -> Self {
441        self.flags = flags;
442        self
443    }
444
445    /// Creates a new copy of the message with reversed direction. Typical use case is to re-send messages to create "response"
446    /// in a widget. For example you have a float input field and it has Value message. When the input field receives Value message
447    /// with [`MessageDirection::ToWidget`] it checks if value needs to be changed and if it does, it re-sends same message, but with
448    /// reversed direction back to message queue so every "listener" can reach properly. The input field won't react at
449    /// [`MessageDirection::FromWidget`] message so there will be no infinite message loop.
450    #[must_use = "method creates new value which must be used"]
451    pub fn reverse(&self) -> Self {
452        Self {
453            handled: self.handled.clone(),
454            data: self.data.clone_box(),
455            destination: self.destination,
456            direction: self.direction.reverse(),
457            routing_strategy: self.routing_strategy,
458            perform_layout: self.perform_layout.clone(),
459            flags: self.flags,
460        }
461    }
462
463    /// Returns destination widget handle of the message.
464    pub fn destination(&self) -> Handle<UiNode> {
465        self.destination
466    }
467
468    /// Tries to downcast current data of the message to a particular type.
469    pub fn data<T: MessageData>(&self) -> Option<&T> {
470        (*self.data).as_any().downcast_ref::<T>()
471    }
472
473    /// Sets handled flag.
474    pub fn set_handled(&self, handled: bool) {
475        self.handled.set(handled);
476    }
477
478    /// Returns handled flag.
479    pub fn handled(&self) -> bool {
480        self.handled.get()
481    }
482
483    /// Returns direction of the message.
484    pub fn direction(&self) -> MessageDirection {
485        self.direction
486    }
487
488    /// Sets perform layout flag.
489    pub fn set_perform_layout(&self, value: bool) {
490        self.perform_layout.set(value);
491    }
492
493    /// Returns perform layout flag.
494    pub fn need_perform_layout(&self) -> bool {
495        self.perform_layout.get()
496    }
497
498    /// Checks if the message has particular flags.
499    pub fn has_flags(&self, flags: u64) -> bool {
500        self.flags & flags != 0
501    }
502}
503
504/// Mouse button state.
505#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Visit, Reflect)]
506pub enum ButtonState {
507    /// Pressed state.
508    Pressed,
509    /// Released state.
510    Released,
511}
512
513/// A set of possible mouse buttons.
514#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Default, Visit, Reflect)]
515pub enum MouseButton {
516    /// Left mouse button.
517    #[default]
518    Left,
519    /// Right mouse button.
520    Right,
521    /// Middle mouse button.
522    Middle,
523    /// Back mouse button.
524    Back,
525    /// Forward mouse button.
526    Forward,
527    /// Any other mouse button.
528    Other(u16),
529}
530
531/// A set of possible touch phases
532#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Visit, Reflect)]
533pub enum TouchPhase {
534    /// Touch started
535    Started,
536    /// Touch and drag
537    Moved,
538    /// Touch ended
539    Ended,
540    /// Touch cancelled
541    Cancelled,
542}
543
544/// Describes the force of a touch event
545#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Visit, Reflect)]
546pub enum Force {
547    /// On iOS, the force is calibrated so that the same number corresponds to
548    /// roughly the same amount of pressure on the screen regardless of the
549    /// device.
550    Calibrated {
551        /// The force of the touch, where a value of 1.0 represents the force of
552        /// an average touch (predetermined by the system, not user-specific).
553        ///
554        /// The force reported by Apple Pencil is measured along the axis of the
555        /// pencil. If you want a force perpendicular to the device, you need to
556        /// calculate this value using the `altitude_angle` value.
557        force: [u8; 8],
558        /// The maximum possible force for a touch.
559        ///
560        /// The value of this field is sufficiently high to provide a wide
561        /// dynamic range for values of the `force` field.
562        max_possible_force: [u8; 8],
563        /// The altitude (in radians) of the stylus.
564        ///
565        /// A value of 0 radians indicates that the stylus is parallel to the
566        /// surface. The value of this property is Pi/2 when the stylus is
567        /// perpendicular to the surface.
568        altitude_angle: Option<[u8; 8]>,
569    },
570    /// If the platform reports the force as normalized, we have no way of
571    /// knowing how much pressure 1.0 corresponds to – we know it's the maximum
572    /// amount of force, but as to how much force, you might either have to
573    /// press really really hard, or not hard at all, depending on the device.
574    Normalized([u8; 8]),
575}
576
577impl Force {
578    /// Returns the force normalized to the range between 0.0 and 1.0 inclusive.
579    ///
580    /// Instead of normalizing the force, you should prefer to handle
581    /// [`Force::Calibrated`] so that the amount of force the user has to apply is
582    /// consistent across devices.
583    pub fn normalized(&self) -> f64 {
584        match self {
585            Force::Calibrated {
586                force,
587                max_possible_force,
588                altitude_angle,
589            } => {
590                let force = match altitude_angle {
591                    Some(altitude_angle) => {
592                        f64::from_be_bytes(*force) / f64::from_be_bytes(*altitude_angle).sin()
593                    }
594                    None => f64::from_be_bytes(*force),
595                };
596                force / f64::from_be_bytes(*max_possible_force)
597            }
598            Force::Normalized(force) => f64::from_be_bytes(*force),
599        }
600    }
601}
602
603/// An event that an OS sends to a window, that is then can be used to "feed" the user interface so it can do some actions.
604#[derive(Debug)]
605pub enum OsEvent {
606    /// Mouse input event.
607    MouseInput {
608        /// Mouse button.
609        button: MouseButton,
610        /// Mouse button state.
611        state: ButtonState,
612    },
613    /// Cursor event.
614    CursorMoved {
615        /// New position of the cursor.
616        position: Vector2<f32>,
617    },
618    /// Keyboard input event.
619    KeyboardInput {
620        /// Code of a key.
621        button: KeyCode,
622        /// Key state.
623        state: ButtonState,
624        /// Text of the key.
625        text: String,
626    },
627    /// Keyboard modifier event (used for key combinations such as Ctrl+A, Ctrl+C, etc).
628    KeyboardModifiers(KeyboardModifiers),
629    /// Mouse wheel event, with a tuple that stores the (x, y) offsets.
630    MouseWheel(f32, f32),
631    /// Touch event.
632    Touch {
633        /// Phase of the touch event
634        phase: TouchPhase,
635        /// Screen location of touch event
636        location: Vector2<f32>,
637        /// Pressure exerted during force event
638        force: Option<Force>,
639        /// Unique touch event identifier to distinguish between fingers, for example
640        id: u64,
641    },
642}
643
644/// A set of possible keyboard modifiers.
645#[derive(
646    Debug,
647    Hash,
648    Ord,
649    PartialOrd,
650    PartialEq,
651    Eq,
652    Clone,
653    Copy,
654    Default,
655    Serialize,
656    Deserialize,
657    Reflect,
658    Visit,
659)]
660pub struct KeyboardModifiers {
661    /// `Alt` key is pressed.
662    pub alt: bool,
663    /// `Shift` key is pressed.
664    pub shift: bool,
665    /// `Ctrl` key is pressed.
666    pub control: bool,
667    /// `System` key is pressed.
668    pub system: bool,
669}
670
671impl KeyboardModifiers {
672    /// Checks if the modifiers is empty (nothing is pressed).
673    pub fn is_none(self) -> bool {
674        !self.shift && !self.control && !self.alt && !self.system
675    }
676}
677
678/// Code of a key on keyboard. Shamelessly taken from `winit` source code to match their key codes with
679/// `fyrox-ui`'s.
680#[derive(
681    Debug,
682    Hash,
683    Ord,
684    PartialOrd,
685    PartialEq,
686    Eq,
687    Clone,
688    Copy,
689    AsRefStr,
690    EnumString,
691    VariantNames,
692    Serialize,
693    Deserialize,
694    Reflect,
695    Visit,
696    Default,
697)]
698#[repr(u32)]
699#[allow(missing_docs)]
700pub enum KeyCode {
701    /// This variant is used when the key cannot be translated to any other variant.
702    #[default]
703    Unknown,
704    /// <kbd>`</kbd> on a US keyboard. This is also called a backtick or grave.
705    /// This is the <kbd>半角</kbd>/<kbd>全角</kbd>/<kbd>漢字</kbd>
706    /// (hankaku/zenkaku/kanji) key on Japanese keyboards
707    Backquote,
708    /// Used for both the US <kbd>\\</kbd> (on the 101-key layout) and also for the key
709    /// located between the <kbd>"</kbd> and <kbd>Enter</kbd> keys on row C of the 102-,
710    /// 104- and 106-key layouts.
711    /// Labeled <kbd>#</kbd> on a UK (102) keyboard.
712    Backslash,
713    /// <kbd>[</kbd> on a US keyboard.
714    BracketLeft,
715    /// <kbd>]</kbd> on a US keyboard.
716    BracketRight,
717    /// <kbd>,</kbd> on a US keyboard.
718    Comma,
719    /// <kbd>0</kbd> on a US keyboard.
720    Digit0,
721    /// <kbd>1</kbd> on a US keyboard.
722    Digit1,
723    /// <kbd>2</kbd> on a US keyboard.
724    Digit2,
725    /// <kbd>3</kbd> on a US keyboard.
726    Digit3,
727    /// <kbd>4</kbd> on a US keyboard.
728    Digit4,
729    /// <kbd>5</kbd> on a US keyboard.
730    Digit5,
731    /// <kbd>6</kbd> on a US keyboard.
732    Digit6,
733    /// <kbd>7</kbd> on a US keyboard.
734    Digit7,
735    /// <kbd>8</kbd> on a US keyboard.
736    Digit8,
737    /// <kbd>9</kbd> on a US keyboard.
738    Digit9,
739    /// <kbd>=</kbd> on a US keyboard.
740    Equal,
741    /// Located between the left <kbd>Shift</kbd> and <kbd>Z</kbd> keys.
742    /// Labeled <kbd>\\</kbd> on a UK keyboard.
743    IntlBackslash,
744    /// Located between the <kbd>/</kbd> and right <kbd>Shift</kbd> keys.
745    /// Labeled <kbd>\\</kbd> (ro) on a Japanese keyboard.
746    IntlRo,
747    /// Located between the <kbd>=</kbd> and <kbd>Backspace</kbd> keys.
748    /// Labeled <kbd>¥</kbd> (yen) on a Japanese keyboard. <kbd>\\</kbd> on a
749    /// Russian keyboard.
750    IntlYen,
751    /// <kbd>a</kbd> on a US keyboard.
752    /// Labeled <kbd>q</kbd> on an AZERTY (e.g., French) keyboard.
753    KeyA,
754    /// <kbd>b</kbd> on a US keyboard.
755    KeyB,
756    /// <kbd>c</kbd> on a US keyboard.
757    KeyC,
758    /// <kbd>d</kbd> on a US keyboard.
759    KeyD,
760    /// <kbd>e</kbd> on a US keyboard.
761    KeyE,
762    /// <kbd>f</kbd> on a US keyboard.
763    KeyF,
764    /// <kbd>g</kbd> on a US keyboard.
765    KeyG,
766    /// <kbd>h</kbd> on a US keyboard.
767    KeyH,
768    /// <kbd>i</kbd> on a US keyboard.
769    KeyI,
770    /// <kbd>j</kbd> on a US keyboard.
771    KeyJ,
772    /// <kbd>k</kbd> on a US keyboard.
773    KeyK,
774    /// <kbd>l</kbd> on a US keyboard.
775    KeyL,
776    /// <kbd>m</kbd> on a US keyboard.
777    KeyM,
778    /// <kbd>n</kbd> on a US keyboard.
779    KeyN,
780    /// <kbd>o</kbd> on a US keyboard.
781    KeyO,
782    /// <kbd>p</kbd> on a US keyboard.
783    KeyP,
784    /// <kbd>q</kbd> on a US keyboard.
785    /// Labeled <kbd>a</kbd> on an AZERTY (e.g., French) keyboard.
786    KeyQ,
787    /// <kbd>r</kbd> on a US keyboard.
788    KeyR,
789    /// <kbd>s</kbd> on a US keyboard.
790    KeyS,
791    /// <kbd>t</kbd> on a US keyboard.
792    KeyT,
793    /// <kbd>u</kbd> on a US keyboard.
794    KeyU,
795    /// <kbd>v</kbd> on a US keyboard.
796    KeyV,
797    /// <kbd>w</kbd> on a US keyboard.
798    /// Labeled <kbd>z</kbd> on an AZERTY (e.g., French) keyboard.
799    KeyW,
800    /// <kbd>x</kbd> on a US keyboard.
801    KeyX,
802    /// <kbd>y</kbd> on a US keyboard.
803    /// Labeled <kbd>z</kbd> on a QWERTZ (e.g., German) keyboard.
804    KeyY,
805    /// <kbd>z</kbd> on a US keyboard.
806    /// Labeled <kbd>w</kbd> on an AZERTY (e.g., French) keyboard, and <kbd>y</kbd> on a
807    /// QWERTZ (e.g., German) keyboard.
808    KeyZ,
809    /// <kbd>-</kbd> on a US keyboard.
810    Minus,
811    /// <kbd>.</kbd> on a US keyboard.
812    Period,
813    /// <kbd>'</kbd> on a US keyboard.
814    Quote,
815    /// <kbd>;</kbd> on a US keyboard.
816    Semicolon,
817    /// <kbd>/</kbd> on a US keyboard.
818    Slash,
819    /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>.
820    AltLeft,
821    /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>.
822    /// This is labeled <kbd>AltGr</kbd> on many keyboard layouts.
823    AltRight,
824    /// <kbd>Backspace</kbd> or <kbd>⌫</kbd>.
825    /// Labeled <kbd>Delete</kbd> on Apple keyboards.
826    Backspace,
827    /// <kbd>CapsLock</kbd> or <kbd>⇪</kbd>
828    CapsLock,
829    /// The application context menu key, which is typically found between the right
830    /// <kbd>Super</kbd> key and the right <kbd>Control</kbd> key.
831    ContextMenu,
832    /// <kbd>Control</kbd> or <kbd>⌃</kbd>
833    ControlLeft,
834    /// <kbd>Control</kbd> or <kbd>⌃</kbd>
835    ControlRight,
836    /// <kbd>Enter</kbd> or <kbd>↵</kbd>. Labeled <kbd>Return</kbd> on Apple keyboards.
837    Enter,
838    /// The Windows, <kbd>⌘</kbd>, <kbd>Command</kbd>, or other OS symbol key.
839    SuperLeft,
840    /// The Windows, <kbd>⌘</kbd>, <kbd>Command</kbd>, or other OS symbol key.
841    SuperRight,
842    /// <kbd>Shift</kbd> or <kbd>⇧</kbd>
843    ShiftLeft,
844    /// <kbd>Shift</kbd> or <kbd>⇧</kbd>
845    ShiftRight,
846    /// <kbd> </kbd> (space)
847    Space,
848    /// <kbd>Tab</kbd> or <kbd>⇥</kbd>
849    Tab,
850    /// Japanese: <kbd>変</kbd> (henkan)
851    Convert,
852    /// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd> (katakana/hiragana/romaji)
853    KanaMode,
854    /// Korean: HangulMode <kbd>한/영</kbd> (han/yeong)
855    ///
856    /// Japanese (Mac keyboard): <kbd>か</kbd> (kana)
857    Lang1,
858    /// Korean: Hanja <kbd>한</kbd> (hanja)
859    ///
860    /// Japanese (Mac keyboard): <kbd>英</kbd> (eisu)
861    Lang2,
862    /// Japanese (word-processing keyboard): Katakana
863    Lang3,
864    /// Japanese (word-processing keyboard): Hiragana
865    Lang4,
866    /// Japanese (word-processing keyboard): Zenkaku/Hankaku
867    Lang5,
868    /// Japanese: <kbd>無変換</kbd> (muhenkan)
869    NonConvert,
870    /// <kbd>⌦</kbd>. The forward delete key.
871    /// Note that on Apple keyboards, the key labelled <kbd>Delete</kbd> on the main part of
872    /// the keyboard is encoded as [`Backspace`].
873    ///
874    /// [`Backspace`]: Self::Backspace
875    Delete,
876    /// <kbd>Page Down</kbd>, <kbd>End</kbd>, or <kbd>↘</kbd>
877    End,
878    /// <kbd>Help</kbd>. Not present on standard PC keyboards.
879    Help,
880    /// <kbd>Home</kbd> or <kbd>↖</kbd>
881    Home,
882    /// <kbd>Insert</kbd> or <kbd>Ins</kbd>. Not present on Apple keyboards.
883    Insert,
884    /// <kbd>Page Down</kbd>, <kbd>PgDn</kbd>, or <kbd>⇟</kbd>
885    PageDown,
886    /// <kbd>Page Up</kbd>, <kbd>PgUp</kbd>, or <kbd>⇞</kbd>
887    PageUp,
888    /// <kbd>↓</kbd>
889    ArrowDown,
890    /// <kbd>←</kbd>
891    ArrowLeft,
892    /// <kbd>→</kbd>
893    ArrowRight,
894    /// <kbd>↑</kbd>
895    ArrowUp,
896    /// On the Mac, this is used for the numpad <kbd>Clear</kbd> key.
897    NumLock,
898    /// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control
899    Numpad0,
900    /// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or remote control
901    Numpad1,
902    /// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control
903    Numpad2,
904    /// <kbd>3 PgDn</kbd> on a keyboard. <kbd>3 DEF</kbd> on a phone or remote control
905    Numpad3,
906    /// <kbd>4 ←</kbd> on a keyboard. <kbd>4 GHI</kbd> on a phone or remote control
907    Numpad4,
908    /// <kbd>5</kbd> on a keyboard. <kbd>5 JKL</kbd> on a phone or remote control
909    Numpad5,
910    /// <kbd>6 →</kbd> on a keyboard. <kbd>6 MNO</kbd> on a phone or remote control
911    Numpad6,
912    /// <kbd>7 Home</kbd> on a keyboard. <kbd>7 PQRS</kbd> or <kbd>7 PRS</kbd> on a phone
913    /// or remote control
914    Numpad7,
915    /// <kbd>8 ↑</kbd> on a keyboard. <kbd>8 TUV</kbd> on a phone or remote control
916    Numpad8,
917    /// <kbd>9 PgUp</kbd> on a keyboard. <kbd>9 WXYZ</kbd> or <kbd>9 WXY</kbd> on a phone
918    /// or remote control
919    Numpad9,
920    /// <kbd>+</kbd>
921    NumpadAdd,
922    /// Found on the Microsoft Natural Keyboard.
923    NumpadBackspace,
924    /// <kbd>C</kbd> or <kbd>A</kbd> (All Clear). Also for use with numpads that have a
925    /// <kbd>Clear</kbd> key that is separate from the <kbd>NumLock</kbd> key. On the Mac, the
926    /// numpad <kbd>Clear</kbd> key is encoded as [`NumLock`].
927    ///
928    /// [`NumLock`]: Self::NumLock
929    NumpadClear,
930    /// <kbd>C</kbd> (Clear Entry)
931    NumpadClearEntry,
932    /// <kbd>,</kbd> (thousands separator). For locales where the thousands separator
933    /// is a "." (e.g., Brazil), this key may generate a <kbd>.</kbd>.
934    NumpadComma,
935    /// <kbd>. Del</kbd>. For locales where the decimal separator is "," (e.g.,
936    /// Brazil), this key may generate a <kbd>,</kbd>.
937    NumpadDecimal,
938    /// <kbd>/</kbd>
939    NumpadDivide,
940    NumpadEnter,
941    /// <kbd>=</kbd>
942    NumpadEqual,
943    /// <kbd>#</kbd> on a phone or remote control device. This key is typically found
944    /// below the <kbd>9</kbd> key and to the right of the <kbd>0</kbd> key.
945    NumpadHash,
946    /// <kbd>M</kbd> Add current entry to the value stored in memory.
947    NumpadMemoryAdd,
948    /// <kbd>M</kbd> Clear the value stored in memory.
949    NumpadMemoryClear,
950    /// <kbd>M</kbd> Replace the current entry with the value stored in memory.
951    NumpadMemoryRecall,
952    /// <kbd>M</kbd> Replace the value stored in memory with the current entry.
953    NumpadMemoryStore,
954    /// <kbd>M</kbd> Subtract current entry from the value stored in memory.
955    NumpadMemorySubtract,
956    /// <kbd>*</kbd> on a keyboard. For use with numpads that provide mathematical
957    /// operations (<kbd>+</kbd>, <kbd>-</kbd> <kbd>*</kbd> and <kbd>/</kbd>).
958    ///
959    /// Use `NumpadStar` for the <kbd>*</kbd> key on phones and remote controls.
960    NumpadMultiply,
961    /// <kbd>(</kbd> Found on the Microsoft Natural Keyboard.
962    NumpadParenLeft,
963    /// <kbd>)</kbd> Found on the Microsoft Natural Keyboard.
964    NumpadParenRight,
965    /// <kbd>*</kbd> on a phone or remote control device.
966    ///
967    /// This key is typically found below the <kbd>7</kbd> key and to the left of
968    /// the <kbd>0</kbd> key.
969    ///
970    /// Use <kbd>"NumpadMultiply"</kbd> for the <kbd>*</kbd> key on
971    /// numeric keypads.
972    NumpadStar,
973    /// <kbd>-</kbd>
974    NumpadSubtract,
975    /// <kbd>Esc</kbd> or <kbd>⎋</kbd>
976    Escape,
977    /// <kbd>Fn</kbd> This is typically a hardware key that does not generate a separate code.
978    Fn,
979    /// <kbd>FLock</kbd> or <kbd>FnLock</kbd>. Function Lock key. Found on the Microsoft
980    /// Natural Keyboard.
981    FnLock,
982    /// <kbd>PrtScr SysRq</kbd> or <kbd>Print Screen</kbd>
983    PrintScreen,
984    /// <kbd>Scroll Lock</kbd>
985    ScrollLock,
986    /// <kbd>Pause Break</kbd>
987    Pause,
988    /// Some laptops place this key to the left of the <kbd>↑</kbd> key.
989    ///
990    /// This also the "back" button (triangle) on Android.
991    BrowserBack,
992    BrowserFavorites,
993    /// Some laptops place this key to the right of the <kbd>↑</kbd> key.
994    BrowserForward,
995    /// The "home" button on Android.
996    BrowserHome,
997    BrowserRefresh,
998    BrowserSearch,
999    BrowserStop,
1000    /// <kbd>Eject</kbd> or <kbd>⏏</kbd>. This key is placed in the function section on some Apple
1001    /// keyboards.
1002    Eject,
1003    /// Sometimes labelled <kbd>My Computer</kbd> on the keyboard
1004    LaunchApp1,
1005    /// Sometimes labelled <kbd>Calculator</kbd> on the keyboard
1006    LaunchApp2,
1007    LaunchMail,
1008    MediaPlayPause,
1009    MediaSelect,
1010    MediaStop,
1011    MediaTrackNext,
1012    MediaTrackPrevious,
1013    /// This key is placed in the function section on some Apple keyboards, replacing the
1014    /// <kbd>Eject</kbd> key.
1015    Power,
1016    Sleep,
1017    AudioVolumeDown,
1018    AudioVolumeMute,
1019    AudioVolumeUp,
1020    WakeUp,
1021    // Legacy modifier key. Also called "Super" in certain places.
1022    Meta,
1023    // Legacy modifier key.
1024    Hyper,
1025    Turbo,
1026    Abort,
1027    Resume,
1028    Suspend,
1029    /// Found on Sun’s USB keyboard.
1030    Again,
1031    /// Found on Sun’s USB keyboard.
1032    Copy,
1033    /// Found on Sun’s USB keyboard.
1034    Cut,
1035    /// Found on Sun’s USB keyboard.
1036    Find,
1037    /// Found on Sun’s USB keyboard.
1038    Open,
1039    /// Found on Sun’s USB keyboard.
1040    Paste,
1041    /// Found on Sun’s USB keyboard.
1042    Props,
1043    /// Found on Sun’s USB keyboard.
1044    Select,
1045    /// Found on Sun’s USB keyboard.
1046    Undo,
1047    /// Use for dedicated <kbd>ひらがな</kbd> key found on some Japanese word processing keyboards.
1048    Hiragana,
1049    /// Use for dedicated <kbd>カタカナ</kbd> key found on some Japanese word processing keyboards.
1050    Katakana,
1051    /// General-purpose function key.
1052    /// Usually found at the top of the keyboard.
1053    F1,
1054    /// General-purpose function key.
1055    /// Usually found at the top of the keyboard.
1056    F2,
1057    /// General-purpose function key.
1058    /// Usually found at the top of the keyboard.
1059    F3,
1060    /// General-purpose function key.
1061    /// Usually found at the top of the keyboard.
1062    F4,
1063    /// General-purpose function key.
1064    /// Usually found at the top of the keyboard.
1065    F5,
1066    /// General-purpose function key.
1067    /// Usually found at the top of the keyboard.
1068    F6,
1069    /// General-purpose function key.
1070    /// Usually found at the top of the keyboard.
1071    F7,
1072    /// General-purpose function key.
1073    /// Usually found at the top of the keyboard.
1074    F8,
1075    /// General-purpose function key.
1076    /// Usually found at the top of the keyboard.
1077    F9,
1078    /// General-purpose function key.
1079    /// Usually found at the top of the keyboard.
1080    F10,
1081    /// General-purpose function key.
1082    /// Usually found at the top of the keyboard.
1083    F11,
1084    /// General-purpose function key.
1085    /// Usually found at the top of the keyboard.
1086    F12,
1087    /// General-purpose function key.
1088    /// Usually found at the top of the keyboard.
1089    F13,
1090    /// General-purpose function key.
1091    /// Usually found at the top of the keyboard.
1092    F14,
1093    /// General-purpose function key.
1094    /// Usually found at the top of the keyboard.
1095    F15,
1096    /// General-purpose function key.
1097    /// Usually found at the top of the keyboard.
1098    F16,
1099    /// General-purpose function key.
1100    /// Usually found at the top of the keyboard.
1101    F17,
1102    /// General-purpose function key.
1103    /// Usually found at the top of the keyboard.
1104    F18,
1105    /// General-purpose function key.
1106    /// Usually found at the top of the keyboard.
1107    F19,
1108    /// General-purpose function key.
1109    /// Usually found at the top of the keyboard.
1110    F20,
1111    /// General-purpose function key.
1112    /// Usually found at the top of the keyboard.
1113    F21,
1114    /// General-purpose function key.
1115    /// Usually found at the top of the keyboard.
1116    F22,
1117    /// General-purpose function key.
1118    /// Usually found at the top of the keyboard.
1119    F23,
1120    /// General-purpose function key.
1121    /// Usually found at the top of the keyboard.
1122    F24,
1123    /// General-purpose function key.
1124    F25,
1125    /// General-purpose function key.
1126    F26,
1127    /// General-purpose function key.
1128    F27,
1129    /// General-purpose function key.
1130    F28,
1131    /// General-purpose function key.
1132    F29,
1133    /// General-purpose function key.
1134    F30,
1135    /// General-purpose function key.
1136    F31,
1137    /// General-purpose function key.
1138    F32,
1139    /// General-purpose function key.
1140    F33,
1141    /// General-purpose function key.
1142    F34,
1143    /// General-purpose function key.
1144    F35,
1145}
1146
1147/// A fixed set of cursor icons that available on most OSes.
1148#[derive(
1149    Debug,
1150    Copy,
1151    Clone,
1152    PartialEq,
1153    Eq,
1154    Hash,
1155    Default,
1156    Visit,
1157    Reflect,
1158    AsRefStr,
1159    EnumString,
1160    VariantNames,
1161)]
1162pub enum CursorIcon {
1163    /// The platform-dependent default cursor. Often rendered as arrow.
1164    #[default]
1165    Default,
1166
1167    /// A context menu is available for the object under the cursor. Often
1168    /// rendered as an arrow with a small menu-like graphic next to it.
1169    ContextMenu,
1170
1171    /// Help is available for the object under the cursor. Often rendered as a
1172    /// question mark or a balloon.
1173    Help,
1174
1175    /// The cursor is a pointer that indicates a link. Often rendered as the
1176    /// backside of a hand with the index finger extended.
1177    Pointer,
1178
1179    /// A progress indicator. The program is performing some processing, but is
1180    /// different from [`CursorIcon::Wait`] in that the user may still interact
1181    /// with the program.
1182    Progress,
1183
1184    /// Indicates that the program is busy and the user should wait. Often
1185    /// rendered as a watch or hourglass.
1186    Wait,
1187
1188    /// Indicates that a cell or set of cells may be selected. Often rendered as
1189    /// a thick plus-sign with a dot in the middle.
1190    Cell,
1191
1192    /// A simple crosshair (e.g., short line segments resembling a "+" sign).
1193    /// Often used to indicate a two dimensional bitmap selection mode.
1194    Crosshair,
1195
1196    /// Indicates text that may be selected. Often rendered as an I-beam.
1197    Text,
1198
1199    /// Indicates vertical-text that may be selected. Often rendered as a
1200    /// horizontal I-beam.
1201    VerticalText,
1202
1203    /// Indicates an alias of/shortcut to something is to be created. Often
1204    /// rendered as an arrow with a small curved arrow next to it.
1205    Alias,
1206
1207    /// Indicates something is to be copied. Often rendered as an arrow with a
1208    /// small plus sign next to it.
1209    Copy,
1210
1211    /// Indicates something is to be moved.
1212    Move,
1213
1214    /// Indicates that the dragged item cannot be dropped at the current cursor
1215    /// location. Often rendered as a hand or pointer with a small circle with a
1216    /// line through it.
1217    NoDrop,
1218
1219    /// Indicates that the requested action will not be carried out. Often
1220    /// rendered as a circle with a line through it.
1221    NotAllowed,
1222
1223    /// Indicates that something can be grabbed (dragged to be moved). Often
1224    /// rendered as the backside of an open hand.
1225    Grab,
1226
1227    /// Indicates that something is being grabbed (dragged to be moved). Often
1228    /// rendered as the backside of a hand with fingers closed mostly out of
1229    /// view.
1230    Grabbing,
1231
1232    /// The east border to be moved.
1233    EResize,
1234
1235    /// The north border to be moved.
1236    NResize,
1237
1238    /// The north-east corner to be moved.
1239    NeResize,
1240
1241    /// The north-west corner to be moved.
1242    NwResize,
1243
1244    /// The south border to be moved.
1245    SResize,
1246
1247    /// The south-east corner to be moved.
1248    SeResize,
1249
1250    /// The south-west corner to be moved.
1251    SwResize,
1252
1253    /// The west border to be moved.
1254    WResize,
1255
1256    /// The east and west borders to be moved.
1257    EwResize,
1258
1259    /// The south and north borders to be moved.
1260    NsResize,
1261
1262    /// The north-east and south-west corners to be moved.
1263    NeswResize,
1264
1265    /// The north-west and south-east corners to be moved.
1266    NwseResize,
1267
1268    /// Indicates that the item/column can be resized horizontally. Often
1269    /// rendered as arrows pointing left and right with a vertical bar
1270    /// separating them.
1271    ColResize,
1272
1273    /// Indicates that the item/row can be resized vertically. Often rendered as
1274    /// arrows pointing up and down with a horizontal bar separating them.
1275    RowResize,
1276
1277    /// Indicates that the something can be scrolled in any direction. Often
1278    /// rendered as arrows pointing up, down, left, and right with a dot in the
1279    /// middle.
1280    AllScroll,
1281
1282    /// Indicates that something can be zoomed in. Often rendered as a
1283    /// magnifying glass with a "+" in the center of the glass.
1284    ZoomIn,
1285
1286    /// Indicates that something can be zoomed in. Often rendered as a
1287    /// magnifying glass with a "-" in the center of the glass.
1288    ZoomOut,
1289}
1290
1291uuid_provider!(CursorIcon = "da7f3a5f-9d26-460a-8e46-38da25f8a8db");