#![warn(missing_docs)]
use crate::{
core::{algebra::Vector2, pool::Handle, reflect::prelude::*, visitor::prelude::*},
UiNode, UserInterface,
};
use fyrox_core::pool::ObjectOrVariant;
use fyrox_core::uuid_provider;
use serde::{Deserialize, Serialize};
use std::{any::Any, cell::Cell, fmt::Debug};
use strum_macros::{AsRefStr, EnumString, VariantNames};
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Eq)]
pub enum MessageDirection {
ToWidget,
FromWidget,
}
impl MessageDirection {
pub fn reverse(self) -> Self {
match self {
Self::ToWidget => Self::FromWidget,
Self::FromWidget => Self::ToWidget,
}
}
}
pub trait BaseMessageData: 'static + Debug + Any + Send {
fn as_any(&self) -> &dyn Any;
fn compare(&self, other: &dyn BaseMessageData) -> bool;
fn clone_box(&self) -> Box<dyn MessageData>;
}
impl<T> BaseMessageData for T
where
T: 'static + Debug + PartialEq + Any + Send + Clone + MessageData,
{
fn as_any(&self) -> &dyn Any {
self
}
fn compare(&self, other: &dyn BaseMessageData) -> bool {
other
.as_any()
.downcast_ref::<T>()
.map(|other| other == self)
.unwrap_or_default()
}
fn clone_box(&self) -> Box<dyn MessageData> {
Box::new(self.clone())
}
}
pub trait MessageData: BaseMessageData {
fn need_perform_layout(&self) -> bool {
false
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
pub enum RoutingStrategy {
#[default]
BubbleUp,
Direct,
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
pub enum DeliveryMode {
#[default]
FullCycle,
SyncOnly,
}
pub struct UiMessage {
pub handled: Cell<bool>,
pub data: Box<dyn MessageData>,
pub destination: Handle<UiNode>,
pub direction: MessageDirection,
pub routing_strategy: RoutingStrategy,
pub delivery_mode: DeliveryMode,
pub flags: u64,
}
pub fn compare_and_set<T>(
value: &mut T,
new_value: &T,
message: &UiMessage,
ui: &UserInterface,
) -> bool
where
T: PartialEq + Clone,
{
if value != new_value {
*value = new_value.clone();
ui.try_send_response(message);
true
} else {
false
}
}
impl Debug for UiMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"UiMessage({}:({})",
match self.direction {
MessageDirection::ToWidget => "To",
MessageDirection::FromWidget => "From",
},
self.destination
)?;
if self.handled.get() {
write!(f, ",handled")?;
}
if self.flags != 0 {
write!(f, ",flags:{}", self.flags)?;
}
match self.delivery_mode {
DeliveryMode::FullCycle => {
write!(f, ",full cycle")?;
}
DeliveryMode::SyncOnly => {
write!(f, ",sync only")?;
}
}
write!(f, "):{:?}", self.data)
}
}
impl Clone for UiMessage {
fn clone(&self) -> Self {
Self {
handled: self.handled.clone(),
data: self.data.clone_box(),
destination: self.destination,
direction: self.direction,
routing_strategy: self.routing_strategy,
delivery_mode: Default::default(),
flags: self.flags,
}
}
}
impl PartialEq for UiMessage {
fn eq(&self, other: &Self) -> bool {
self.handled == other.handled
&& self.data.compare(&*other.data)
&& self.destination == other.destination
&& self.routing_strategy == other.routing_strategy
&& self.direction == other.direction
&& self.flags == other.flags
}
}
impl UiMessage {
pub fn with_data<T: MessageData>(data: T) -> Self {
Self {
handled: Cell::new(false),
data: Box::new(data),
destination: Default::default(),
direction: MessageDirection::ToWidget,
routing_strategy: Default::default(),
delivery_mode: Default::default(),
flags: 0,
}
}
pub fn for_widget(
handle: Handle<impl ObjectOrVariant<UiNode>>,
data: impl MessageData,
) -> Self {
Self::with_data(data)
.with_destination(handle.transmute())
.with_direction(MessageDirection::ToWidget)
}
pub fn from_widget(
handle: Handle<impl ObjectOrVariant<UiNode>>,
data: impl MessageData,
) -> Self {
Self::with_data(data)
.with_destination(handle.transmute())
.with_direction(MessageDirection::FromWidget)
}
pub fn with_destination(mut self, destination: Handle<UiNode>) -> Self {
self.destination = destination;
self
}
pub fn with_direction(mut self, direction: MessageDirection) -> Self {
self.direction = direction;
self
}
pub fn with_handled(self, handled: bool) -> Self {
self.handled.set(handled);
self
}
pub fn with_routing_strategy(mut self, routing_strategy: RoutingStrategy) -> Self {
self.routing_strategy = routing_strategy;
self
}
pub fn with_delivery_mode(mut self, delivery_mode: DeliveryMode) -> Self {
self.delivery_mode = delivery_mode;
self
}
pub fn with_flags(mut self, flags: u64) -> Self {
self.flags = flags;
self
}
#[must_use = "method creates new value which must be used"]
pub fn reverse(&self) -> Self {
Self {
handled: self.handled.clone(),
data: self.data.clone_box(),
destination: self.destination,
direction: self.direction.reverse(),
routing_strategy: self.routing_strategy,
delivery_mode: self.delivery_mode,
flags: self.flags,
}
}
pub fn data_from<T: MessageData>(
&self,
handle: Handle<impl ObjectOrVariant<UiNode>>,
) -> Option<&T> {
if self.is_from(handle) {
self.data()
} else {
None
}
}
pub fn data_for<T: MessageData>(
&self,
handle: Handle<impl ObjectOrVariant<UiNode>>,
) -> Option<&T> {
if self.is_for(handle) {
self.data()
} else {
None
}
}
pub fn is_from(&self, handle: Handle<impl ObjectOrVariant<UiNode>>) -> bool {
self.destination == handle && self.direction == MessageDirection::FromWidget
}
pub fn is_for(&self, handle: Handle<impl ObjectOrVariant<UiNode>>) -> bool {
self.destination == handle && self.direction == MessageDirection::ToWidget
}
pub fn destination(&self) -> Handle<UiNode> {
self.destination
}
pub fn data<T: MessageData>(&self) -> Option<&T> {
(*self.data).as_any().downcast_ref::<T>()
}
pub fn set_handled(&self, handled: bool) {
self.handled.set(handled);
}
pub fn handled(&self) -> bool {
self.handled.get()
}
pub fn direction(&self) -> MessageDirection {
self.direction
}
pub fn need_perform_layout(&self) -> bool {
self.data.need_perform_layout()
}
pub fn has_flags(&self, flags: u64) -> bool {
self.flags & flags != 0
}
pub fn is_sync(&self) -> bool {
self.delivery_mode == DeliveryMode::SyncOnly
}
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Visit, Reflect)]
pub enum ButtonState {
Pressed,
Released,
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Default, Visit, Reflect)]
pub enum MouseButton {
#[default]
Left,
Right,
Middle,
Back,
Forward,
Other(u16),
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Visit, Reflect)]
pub enum TouchPhase {
Started,
Moved,
Ended,
Cancelled,
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Visit, Reflect)]
pub enum Force {
Calibrated {
force: [u8; 8],
max_possible_force: [u8; 8],
altitude_angle: Option<[u8; 8]>,
},
Normalized([u8; 8]),
}
impl Force {
pub fn normalized(&self) -> f64 {
match self {
Force::Calibrated {
force,
max_possible_force,
altitude_angle,
} => {
let force = match altitude_angle {
Some(altitude_angle) => {
f64::from_be_bytes(*force) / f64::from_be_bytes(*altitude_angle).sin()
}
None => f64::from_be_bytes(*force),
};
force / f64::from_be_bytes(*max_possible_force)
}
Force::Normalized(force) => f64::from_be_bytes(*force),
}
}
}
#[derive(Debug)]
pub enum OsEvent {
MouseInput {
button: MouseButton,
state: ButtonState,
},
CursorMoved {
position: Vector2<f32>,
},
KeyboardInput {
button: KeyCode,
state: ButtonState,
text: String,
},
KeyboardModifiers(KeyboardModifiers),
MouseWheel(f32, f32),
Touch {
phase: TouchPhase,
location: Vector2<f32>,
force: Option<Force>,
id: u64,
},
}
#[derive(
Debug,
Hash,
Ord,
PartialOrd,
PartialEq,
Eq,
Clone,
Copy,
Default,
Serialize,
Deserialize,
Reflect,
Visit,
)]
pub struct KeyboardModifiers {
pub alt: bool,
pub shift: bool,
pub control: bool,
pub system: bool,
}
impl KeyboardModifiers {
pub fn is_none(self) -> bool {
!self.shift && !self.control && !self.alt && !self.system
}
}
#[derive(
Debug,
Hash,
Ord,
PartialOrd,
PartialEq,
Eq,
Clone,
Copy,
AsRefStr,
EnumString,
VariantNames,
Serialize,
Deserialize,
Reflect,
Visit,
Default,
)]
#[repr(u32)]
#[allow(missing_docs)]
pub enum KeyCode {
#[default]
Unknown,
Backquote,
Backslash,
BracketLeft,
BracketRight,
Comma,
Digit0,
Digit1,
Digit2,
Digit3,
Digit4,
Digit5,
Digit6,
Digit7,
Digit8,
Digit9,
Equal,
IntlBackslash,
IntlRo,
IntlYen,
KeyA,
KeyB,
KeyC,
KeyD,
KeyE,
KeyF,
KeyG,
KeyH,
KeyI,
KeyJ,
KeyK,
KeyL,
KeyM,
KeyN,
KeyO,
KeyP,
KeyQ,
KeyR,
KeyS,
KeyT,
KeyU,
KeyV,
KeyW,
KeyX,
KeyY,
KeyZ,
Minus,
Period,
Quote,
Semicolon,
Slash,
AltLeft,
AltRight,
Backspace,
CapsLock,
ContextMenu,
ControlLeft,
ControlRight,
Enter,
SuperLeft,
SuperRight,
ShiftLeft,
ShiftRight,
Space,
Tab,
Convert,
KanaMode,
Lang1,
Lang2,
Lang3,
Lang4,
Lang5,
NonConvert,
Delete,
End,
Help,
Home,
Insert,
PageDown,
PageUp,
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
NumLock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
NumpadAdd,
NumpadBackspace,
NumpadClear,
NumpadClearEntry,
NumpadComma,
NumpadDecimal,
NumpadDivide,
NumpadEnter,
NumpadEqual,
NumpadHash,
NumpadMemoryAdd,
NumpadMemoryClear,
NumpadMemoryRecall,
NumpadMemoryStore,
NumpadMemorySubtract,
NumpadMultiply,
NumpadParenLeft,
NumpadParenRight,
NumpadStar,
NumpadSubtract,
Escape,
Fn,
FnLock,
PrintScreen,
ScrollLock,
Pause,
BrowserBack,
BrowserFavorites,
BrowserForward,
BrowserHome,
BrowserRefresh,
BrowserSearch,
BrowserStop,
Eject,
LaunchApp1,
LaunchApp2,
LaunchMail,
MediaPlayPause,
MediaSelect,
MediaStop,
MediaTrackNext,
MediaTrackPrevious,
Power,
Sleep,
AudioVolumeDown,
AudioVolumeMute,
AudioVolumeUp,
WakeUp,
Meta,
Hyper,
Turbo,
Abort,
Resume,
Suspend,
Again,
Copy,
Cut,
Find,
Open,
Paste,
Props,
Select,
Undo,
Hiragana,
Katakana,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
F25,
F26,
F27,
F28,
F29,
F30,
F31,
F32,
F33,
F34,
F35,
}
impl KeyCode {
pub fn to_char(self) -> Option<char> {
match self {
KeyCode::Backquote => Some('`'),
KeyCode::Backslash => Some('\\'),
KeyCode::BracketLeft => Some('['),
KeyCode::BracketRight => Some(']'),
KeyCode::Comma => Some(','),
KeyCode::Digit0 => Some('0'),
KeyCode::Digit1 => Some('1'),
KeyCode::Digit2 => Some('2'),
KeyCode::Digit3 => Some('3'),
KeyCode::Digit4 => Some('4'),
KeyCode::Digit5 => Some('5'),
KeyCode::Digit6 => Some('6'),
KeyCode::Digit7 => Some('7'),
KeyCode::Digit8 => Some('8'),
KeyCode::Digit9 => Some('9'),
KeyCode::Equal => Some('='),
KeyCode::KeyA => Some('A'),
KeyCode::KeyB => Some('B'),
KeyCode::KeyC => Some('C'),
KeyCode::KeyD => Some('D'),
KeyCode::KeyE => Some('E'),
KeyCode::KeyF => Some('F'),
KeyCode::KeyG => Some('G'),
KeyCode::KeyH => Some('H'),
KeyCode::KeyI => Some('I'),
KeyCode::KeyJ => Some('J'),
KeyCode::KeyK => Some('K'),
KeyCode::KeyL => Some('L'),
KeyCode::KeyM => Some('M'),
KeyCode::KeyN => Some('N'),
KeyCode::KeyO => Some('O'),
KeyCode::KeyP => Some('P'),
KeyCode::KeyQ => Some('Q'),
KeyCode::KeyR => Some('R'),
KeyCode::KeyS => Some('S'),
KeyCode::KeyT => Some('T'),
KeyCode::KeyU => Some('U'),
KeyCode::KeyV => Some('V'),
KeyCode::KeyW => Some('W'),
KeyCode::KeyX => Some('X'),
KeyCode::KeyY => Some('Y'),
KeyCode::KeyZ => Some('Z'),
KeyCode::Minus => Some('-'),
KeyCode::Period => Some('.'),
KeyCode::Quote => Some('\''),
KeyCode::Semicolon => Some(';'),
KeyCode::Slash => Some('/'),
KeyCode::Enter => Some('\n'),
KeyCode::Space => Some(' '),
KeyCode::Tab => Some('\t'),
KeyCode::Numpad0 => Some('0'),
KeyCode::Numpad1 => Some('1'),
KeyCode::Numpad2 => Some('2'),
KeyCode::Numpad3 => Some('3'),
KeyCode::Numpad4 => Some('4'),
KeyCode::Numpad5 => Some('5'),
KeyCode::Numpad6 => Some('6'),
KeyCode::Numpad7 => Some('7'),
KeyCode::Numpad8 => Some('8'),
KeyCode::Numpad9 => Some('9'),
KeyCode::NumpadAdd => Some('+'),
KeyCode::NumpadComma => Some('.'),
KeyCode::NumpadDecimal => Some(','),
KeyCode::NumpadDivide => Some('/'),
KeyCode::NumpadEnter => Some('\n'),
KeyCode::NumpadEqual => Some('='),
KeyCode::NumpadHash => Some('#'),
KeyCode::NumpadMultiply => Some('*'),
KeyCode::NumpadParenLeft => Some('('),
KeyCode::NumpadParenRight => Some(')'),
KeyCode::NumpadStar => Some('*'),
KeyCode::NumpadSubtract => Some('-'),
_ => None,
}
}
}
impl TryFrom<char> for KeyCode {
type Error = &'static str;
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'-' | '_' => Ok(Self::Minus),
'+' | '=' => Ok(Self::NumpadAdd),
' ' => Ok(Self::Space),
'\\' | '|' => Ok(Self::Backslash),
'/' | '?' => Ok(Self::Slash),
'.' | '>' => Ok(Self::Period),
',' | '<' => Ok(Self::Comma),
'\'' | '"' => Ok(Self::Quote),
';' | ':' => Ok(Self::Semicolon),
'`' | '~' => Ok(Self::Backquote),
'0' | ')' => Ok(Self::Digit0),
'1' | '!' => Ok(Self::Digit1),
'2' | '@' => Ok(Self::Digit2),
'3' | '#' => Ok(Self::Digit3),
'4' | '$' => Ok(Self::Digit4),
'5' | '%' => Ok(Self::Digit5),
'6' | '^' => Ok(Self::Digit6),
'7' | '&' => Ok(Self::Digit7),
'8' | '*' => Ok(Self::Digit8),
'9' | '(' => Ok(Self::Digit9),
'a' | 'A' => Ok(Self::KeyA),
'b' | 'B' => Ok(Self::KeyB),
'c' | 'C' => Ok(Self::KeyC),
'd' | 'D' => Ok(Self::KeyD),
'e' | 'E' => Ok(Self::KeyE),
'f' | 'F' => Ok(Self::KeyF),
'g' | 'G' => Ok(Self::KeyG),
'h' | 'H' => Ok(Self::KeyH),
'i' | 'I' => Ok(Self::KeyI),
'j' | 'J' => Ok(Self::KeyJ),
'k' | 'K' => Ok(Self::KeyK),
'l' | 'L' => Ok(Self::KeyL),
'm' | 'M' => Ok(Self::KeyM),
'n' | 'N' => Ok(Self::KeyN),
'o' | 'O' => Ok(Self::KeyO),
'p' | 'P' => Ok(Self::KeyP),
'q' | 'Q' => Ok(Self::KeyQ),
'r' | 'R' => Ok(Self::KeyR),
's' | 'S' => Ok(Self::KeyS),
't' | 'T' => Ok(Self::KeyT),
'u' | 'U' => Ok(Self::KeyU),
'v' | 'V' => Ok(Self::KeyV),
'w' | 'W' => Ok(Self::KeyW),
'x' | 'X' => Ok(Self::KeyX),
'y' | 'Y' => Ok(Self::KeyY),
'z' | 'Z' => Ok(Self::KeyZ),
'[' | '{' => Ok(Self::BracketLeft),
']' | '}' => Ok(Self::BracketRight),
_ => Err("unsupported"),
}
}
}
#[derive(
Debug,
Copy,
Clone,
PartialEq,
Eq,
Hash,
Default,
Visit,
Reflect,
AsRefStr,
EnumString,
VariantNames,
)]
pub enum CursorIcon {
#[default]
Default,
ContextMenu,
Help,
Pointer,
Progress,
Wait,
Cell,
Crosshair,
Text,
VerticalText,
Alias,
Copy,
Move,
NoDrop,
NotAllowed,
Grab,
Grabbing,
EResize,
NResize,
NeResize,
NwResize,
SResize,
SeResize,
SwResize,
WResize,
EwResize,
NsResize,
NeswResize,
NwseResize,
ColResize,
RowResize,
AllScroll,
ZoomIn,
ZoomOut,
}
uuid_provider!(CursorIcon = "da7f3a5f-9d26-460a-8e46-38da25f8a8db");