#![warn(missing_docs)]
use crate::{
core::{algebra::Vector2, pool::Handle, reflect::prelude::*, visitor::prelude::*},
UiNode, UserInterface,
};
use fyrox_core::uuid_provider;
use serde::{Deserialize, Serialize};
use std::{any::Any, cell::Cell, fmt::Debug};
use strum_macros::{AsRefStr, EnumString, VariantNames};
#[macro_export]
macro_rules! define_constructor {
($(#[$meta:meta])* $inner:ident : $inner_var:tt => fn $name:ident(), layout: $perform_layout:expr) => {
$(#[$meta])*
#[must_use = "message does nothing until sent to ui"]
pub fn $name(destination: Handle<UiNode>, direction: MessageDirection) -> UiMessage {
UiMessage {
handled: std::cell::Cell::new(false),
data: Box::new($inner::$inner_var),
destination,
direction,
routing_strategy: Default::default(),
perform_layout: std::cell::Cell::new($perform_layout),
flags: 0
}
}
};
($(#[$meta:meta])* $inner:ident : $inner_var:tt => fn $name:ident($typ:ty), layout: $perform_layout:expr) => {
$(#[$meta])*
#[must_use = "message does nothing until sent to ui"]
pub fn $name(destination: Handle<UiNode>, direction: MessageDirection, value:$typ) -> UiMessage {
UiMessage {
handled: std::cell::Cell::new(false),
data: Box::new($inner::$inner_var(value)),
destination,
direction,
routing_strategy: Default::default(),
perform_layout: std::cell::Cell::new($perform_layout),
flags: 0
}
}
};
($(#[$meta:meta])* $inner:ident : $inner_var:tt => fn $name:ident( $($params:ident : $types:ty),+ ), layout: $perform_layout:expr) => {
$(#[$meta])*
#[must_use = "message does nothing until sent to ui"]
pub fn $name(destination: Handle<UiNode>, direction: MessageDirection, $($params : $types),+) -> UiMessage {
UiMessage {
handled: std::cell::Cell::new(false),
data: Box::new($inner::$inner_var { $($params),+ }),
destination,
direction,
routing_strategy: Default::default(),
perform_layout: std::cell::Cell::new($perform_layout),
flags: 0
}
}
}
}
#[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 MessageData: 'static + Debug + Any + Send {
fn as_any(&self) -> &dyn Any;
fn compare(&self, other: &dyn MessageData) -> bool;
fn clone_box(&self) -> Box<dyn MessageData>;
}
impl<T> MessageData for T
where
T: 'static + Debug + PartialEq + Any + Send + Clone,
{
fn as_any(&self) -> &dyn Any {
self
}
fn compare(&self, other: &dyn MessageData) -> 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())
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
pub enum RoutingStrategy {
#[default]
BubbleUp,
Direct,
}
pub struct UiMessage {
pub handled: Cell<bool>,
pub data: Box<dyn MessageData>,
pub destination: Handle<UiNode>,
pub direction: MessageDirection,
pub routing_strategy: RoutingStrategy,
pub perform_layout: Cell<bool>,
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.send_message(message.reverse());
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.perform_layout.get() {
write!(f, ",layout")?;
}
if self.flags != 0 {
write!(f, ",flags:{}", self.flags)?;
}
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,
perform_layout: self.perform_layout.clone(),
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.perform_layout == other.perform_layout
&& 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(),
perform_layout: Cell::new(false),
flags: 0,
}
}
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_perform_layout(self, perform_layout: bool) -> Self {
self.perform_layout.set(perform_layout);
self
}
pub fn with_routing_strategy(mut self, routing_strategy: RoutingStrategy) -> Self {
self.routing_strategy = routing_strategy;
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,
perform_layout: self.perform_layout.clone(),
flags: self.flags,
}
}
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 set_perform_layout(&self, value: bool) {
self.perform_layout.set(value);
}
pub fn need_perform_layout(&self) -> bool {
self.perform_layout.get()
}
pub fn has_flags(&self, flags: u64) -> bool {
self.flags & flags != 0
}
}
#[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,
}
#[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");