#![allow(unsafe_op_in_unsafe_fn)]
use core::marker::PhantomData;
use core::ptr::NonNull;
use std::ffi::{CString, c_void};
use crate::ffi::{
noesis_element_datacontext_get_u64, noesis_event_args_kind, noesis_key_args_key,
noesis_mouse_args_position, noesis_mouse_button_args_button, noesis_mouse_wheel_args_delta,
noesis_routed_args_source, noesis_routed_events_add_copying_handler,
noesis_routed_events_add_pasting_handler, noesis_routed_events_do_drag_drop,
noesis_routed_events_drag_data, noesis_routed_events_drag_effects,
noesis_routed_events_drag_position, noesis_routed_events_drag_set_effects,
noesis_routed_events_focus_new, noesis_routed_events_focus_old,
noesis_routed_events_manip_cumulative, noesis_routed_events_manip_delta,
noesis_routed_events_manip_is_inertial, noesis_routed_events_manip_origin,
noesis_routed_events_manip_velocities, noesis_routed_events_remove_data_object_handler,
noesis_size_changed_args_new_size, noesis_subscribe_click, noesis_subscribe_event,
noesis_subscribe_keydown, noesis_subscribe_lifecycle, noesis_subscribe_selection_changed,
noesis_text_args_ch, noesis_unsubscribe_click, noesis_unsubscribe_event,
noesis_unsubscribe_keydown, noesis_unsubscribe_lifecycle, noesis_unsubscribe_selection_changed,
};
use crate::view::{FrameworkElement, Key, MouseButton};
unsafe extern "C" fn free_donated<T>(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
if userdata.is_null() {
return;
}
drop(unsafe { Box::from_raw(userdata.cast::<T>()) });
})
}
mod arg_kind {
pub const MOUSE_WHEEL: i32 = 3;
}
pub trait ClickHandler: Send + 'static {
fn on_click(&self);
}
impl<F: Fn() + Send + 'static> ClickHandler for F {
fn on_click(&self) {
self();
}
}
unsafe extern "C" fn click_trampoline(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn ClickHandler>>();
handler.on_click();
})
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct ClickSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for ClickSubscription {}
impl Drop for ClickSubscription {
fn drop(&mut self) {
unsafe { noesis_unsubscribe_click(self.token.as_ptr()) }
}
}
pub fn subscribe_click<H: ClickHandler>(
element: &FrameworkElement,
handler: H,
) -> Option<ClickSubscription> {
let outer: Box<Box<dyn ClickHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
noesis_subscribe_click(
element.raw(),
click_trampoline,
userdata.cast(),
free_donated::<Box<dyn ClickHandler>>,
)
};
if let Some(token) = NonNull::new(token) {
Some(ClickSubscription { token })
} else {
unsafe { drop(Box::from_raw(userdata)) };
None
}
}
pub trait SelectionChangedHandler: Send + 'static {
fn on_selection_changed(&self);
}
impl<F: Fn() + Send + 'static> SelectionChangedHandler for F {
fn on_selection_changed(&self) {
self();
}
}
unsafe extern "C" fn selection_changed_trampoline(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn SelectionChangedHandler>>();
handler.on_selection_changed();
})
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct SelectionChangedSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for SelectionChangedSubscription {}
impl Drop for SelectionChangedSubscription {
fn drop(&mut self) {
unsafe { noesis_unsubscribe_selection_changed(self.token.as_ptr()) }
}
}
pub fn subscribe_selection_changed<H: SelectionChangedHandler>(
element: &FrameworkElement,
handler: H,
) -> Option<SelectionChangedSubscription> {
let outer: Box<Box<dyn SelectionChangedHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
noesis_subscribe_selection_changed(
element.raw(),
selection_changed_trampoline,
userdata.cast(),
free_donated::<Box<dyn SelectionChangedHandler>>,
)
};
if let Some(token) = NonNull::new(token) {
Some(SelectionChangedSubscription { token })
} else {
unsafe { drop(Box::from_raw(userdata)) };
None
}
}
pub trait KeyDownHandler: Send + 'static {
fn on_keydown(&self, key: Key) -> bool;
}
impl<F: Fn(Key) -> bool + Send + 'static> KeyDownHandler for F {
fn on_keydown(&self, key: Key) -> bool {
self(key)
}
}
unsafe extern "C" fn keydown_trampoline(userdata: *mut c_void, key: i32, out_handled: *mut bool) {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn KeyDownHandler>>();
let mapped = key_from_raw(key);
let handled = handler.on_keydown(mapped);
if !out_handled.is_null() {
*out_handled = handled;
}
})
}
fn key_from_raw(raw: i32) -> Key {
match raw {
0 => Key::None,
2 => Key::Back,
3 => Key::Tab,
6 => Key::Return,
7 => Key::Pause,
8 => Key::CapsLock,
13 => Key::Escape,
18 => Key::Space,
19 => Key::PageUp,
20 => Key::PageDown,
21 => Key::End,
22 => Key::Home,
23 => Key::Left,
24 => Key::Up,
25 => Key::Right,
26 => Key::Down,
30 => Key::PrintScreen,
31 => Key::Insert,
32 => Key::Delete,
33 => Key::Help,
34..=43 => match raw {
34 => Key::D0,
35 => Key::D1,
36 => Key::D2,
37 => Key::D3,
38 => Key::D4,
39 => Key::D5,
40 => Key::D6,
41 => Key::D7,
42 => Key::D8,
43 => Key::D9,
_ => Key::None,
},
44..=69 => match raw {
44 => Key::A,
45 => Key::B,
46 => Key::C,
47 => Key::D,
48 => Key::E,
49 => Key::F,
50 => Key::G,
51 => Key::H,
52 => Key::I,
53 => Key::J,
54 => Key::K,
55 => Key::L,
56 => Key::M,
57 => Key::N,
58 => Key::O,
59 => Key::P,
60 => Key::Q,
61 => Key::R,
62 => Key::S,
63 => Key::T,
64 => Key::U,
65 => Key::V,
66 => Key::W,
67 => Key::X,
68 => Key::Y,
69 => Key::Z,
_ => Key::None,
},
70 => Key::LWin,
71 => Key::RWin,
72 => Key::Apps,
74..=83 => match raw {
74 => Key::NumPad0,
75 => Key::NumPad1,
76 => Key::NumPad2,
77 => Key::NumPad3,
78 => Key::NumPad4,
79 => Key::NumPad5,
80 => Key::NumPad6,
81 => Key::NumPad7,
82 => Key::NumPad8,
83 => Key::NumPad9,
_ => Key::None,
},
84 => Key::Multiply,
85 => Key::Add,
87 => Key::Subtract,
88 => Key::Decimal,
89 => Key::Divide,
90..=113 => match raw {
90 => Key::F1,
91 => Key::F2,
92 => Key::F3,
93 => Key::F4,
94 => Key::F5,
95 => Key::F6,
96 => Key::F7,
97 => Key::F8,
98 => Key::F9,
99 => Key::F10,
100 => Key::F11,
101 => Key::F12,
102 => Key::F13,
103 => Key::F14,
104 => Key::F15,
105 => Key::F16,
106 => Key::F17,
107 => Key::F18,
108 => Key::F19,
109 => Key::F20,
110 => Key::F21,
111 => Key::F22,
112 => Key::F23,
113 => Key::F24,
_ => Key::None,
},
114 => Key::NumLock,
115 => Key::ScrollLock,
116 => Key::LeftShift,
117 => Key::RightShift,
118 => Key::LeftCtrl,
119 => Key::RightCtrl,
120 => Key::LeftAlt,
121 => Key::RightAlt,
140 => Key::OemSemicolon,
141 => Key::OemPlus,
142 => Key::OemComma,
143 => Key::OemMinus,
144 => Key::OemPeriod,
145 => Key::OemSlash,
146 => Key::OemTilde,
149 => Key::OemOpenBrackets,
150 => Key::OemPipe,
151 => Key::OemCloseBrackets,
152 => Key::OemQuotes,
175 => Key::GamepadLeft,
176 => Key::GamepadUp,
177 => Key::GamepadRight,
178 => Key::GamepadDown,
179 => Key::GamepadAccept,
180 => Key::GamepadCancel,
181 => Key::GamepadMenu,
182 => Key::GamepadView,
183 => Key::GamepadPageUp,
184 => Key::GamepadPageDown,
185 => Key::GamepadPageLeft,
186 => Key::GamepadPageRight,
187 => Key::GamepadContext1,
188 => Key::GamepadContext2,
189 => Key::GamepadContext3,
190 => Key::GamepadContext4,
_ => Key::None,
}
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct KeyDownSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for KeyDownSubscription {}
impl Drop for KeyDownSubscription {
fn drop(&mut self) {
unsafe { noesis_unsubscribe_keydown(self.token.as_ptr()) }
}
}
pub fn subscribe_keydown<H: KeyDownHandler>(
element: &FrameworkElement,
handler: H,
) -> Option<KeyDownSubscription> {
let outer: Box<Box<dyn KeyDownHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
noesis_subscribe_keydown(
element.raw(),
keydown_trampoline,
userdata.cast(),
free_donated::<Box<dyn KeyDownHandler>>,
)
};
if let Some(token) = NonNull::new(token) {
Some(KeyDownSubscription { token })
} else {
unsafe { drop(Box::from_raw(userdata)) };
None
}
}
pub struct EventArgs {
raw: *const c_void,
_not_send: PhantomData<*const c_void>,
}
impl EventArgs {
#[doc(hidden)]
pub unsafe fn from_raw(raw: *const c_void) -> Self {
EventArgs {
raw,
_not_send: PhantomData,
}
}
pub fn position(&self) -> Option<(f32, f32)> {
let mut x = 0.0f32;
let mut y = 0.0f32;
let ok = unsafe { noesis_mouse_args_position(self.raw, &mut x, &mut y) };
ok.then_some((x, y))
}
pub fn mouse_button(&self) -> Option<MouseButton> {
let raw = unsafe { noesis_mouse_button_args_button(self.raw) };
match raw {
0 => Some(MouseButton::Left),
1 => Some(MouseButton::Right),
2 => Some(MouseButton::Middle),
3 => Some(MouseButton::XButton1),
4 => Some(MouseButton::XButton2),
_ => None,
}
}
pub fn wheel_delta(&self) -> Option<i32> {
if !self.is_wheel() {
return None;
}
Some(unsafe { noesis_mouse_wheel_args_delta(self.raw) })
}
fn kind(&self) -> i32 {
unsafe { noesis_event_args_kind(self.raw) }
}
fn is_wheel(&self) -> bool {
self.kind() == arg_kind::MOUSE_WHEEL
}
pub fn key(&self) -> Option<Key> {
let raw = unsafe { noesis_key_args_key(self.raw) };
(raw >= 0).then(|| key_from_raw(raw))
}
pub fn text_char(&self) -> Option<char> {
let raw = unsafe { noesis_text_args_ch(self.raw) };
if raw < 0 {
return None;
}
char::from_u32(raw as u32)
}
pub fn new_size(&self) -> Option<(f32, f32)> {
let mut w = 0.0f32;
let mut h = 0.0f32;
let ok = unsafe { noesis_size_changed_args_new_size(self.raw, &mut w, &mut h) };
ok.then_some((w, h))
}
pub fn source_ptr(&self) -> Option<*mut c_void> {
let p = unsafe { noesis_routed_args_source(self.raw) };
(!p.is_null()).then_some(p)
}
#[must_use]
pub fn source_data_context_u64(&self, prop_name: &str) -> Option<u64> {
let source = self.source_ptr()?;
let c = CString::new(prop_name).expect("property name contained interior NUL");
let mut out: u64 = 0;
let ok = unsafe { noesis_element_datacontext_get_u64(source, c.as_ptr(), &mut out) };
ok.then_some(out)
}
pub fn focus_old_ptr(&self) -> Option<*mut c_void> {
let p = unsafe { noesis_routed_events_focus_old(self.raw) };
(!p.is_null()).then_some(p)
}
pub fn focus_new_ptr(&self) -> Option<*mut c_void> {
let p = unsafe { noesis_routed_events_focus_new(self.raw) };
(!p.is_null()).then_some(p)
}
pub fn drag(&self) -> Option<DragInfo> {
let mut effects = 0u32;
let mut allowed = 0u32;
let mut key_states = 0u32;
let ok = unsafe {
noesis_routed_events_drag_effects(self.raw, &mut effects, &mut allowed, &mut key_states)
};
ok.then_some(DragInfo {
effects: DragEffects(effects),
allowed_effects: DragEffects(allowed),
key_states: DragKeyStates(key_states),
})
}
#[must_use = "a false return means the effect was not set because the live args are not a drag event"]
pub fn set_drag_effects(&self, effects: DragEffects) -> bool {
unsafe { noesis_routed_events_drag_set_effects(self.raw, effects.bits()) }
}
pub fn drag_data_ptr(&self) -> Option<*mut c_void> {
let p = unsafe { noesis_routed_events_drag_data(self.raw) };
(!p.is_null()).then_some(p)
}
pub fn drag_position(&self, relative_to: &FrameworkElement) -> Option<(f32, f32)> {
let mut x = 0.0f32;
let mut y = 0.0f32;
let ok = unsafe {
noesis_routed_events_drag_position(self.raw, relative_to.raw(), &mut x, &mut y)
};
ok.then_some((x, y))
}
pub fn manip_origin(&self) -> Option<(f32, f32)> {
let mut x = 0.0f32;
let mut y = 0.0f32;
let ok = unsafe { noesis_routed_events_manip_origin(self.raw, &mut x, &mut y) };
ok.then_some((x, y))
}
pub fn manip_delta(&self) -> Option<ManipulationDelta> {
let mut d = ManipulationDelta::default();
let ok = unsafe {
noesis_routed_events_manip_delta(
self.raw,
&mut d.translation.0,
&mut d.translation.1,
&mut d.scale,
&mut d.rotation,
&mut d.expansion.0,
&mut d.expansion.1,
)
};
ok.then_some(d)
}
pub fn manip_cumulative(&self) -> Option<ManipulationDelta> {
let mut d = ManipulationDelta::default();
let ok = unsafe {
noesis_routed_events_manip_cumulative(
self.raw,
&mut d.translation.0,
&mut d.translation.1,
&mut d.scale,
&mut d.rotation,
&mut d.expansion.0,
&mut d.expansion.1,
)
};
ok.then_some(d)
}
pub fn manip_velocities(&self) -> Option<ManipulationVelocities> {
let mut v = ManipulationVelocities::default();
let ok = unsafe {
noesis_routed_events_manip_velocities(
self.raw,
&mut v.angular,
&mut v.linear.0,
&mut v.linear.1,
&mut v.expansion.0,
&mut v.expansion.1,
)
};
ok.then_some(v)
}
pub fn manip_is_inertial(&self) -> Option<bool> {
match unsafe { noesis_routed_events_manip_is_inertial(self.raw) } {
0 => Some(false),
1 => Some(true),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DragEffects(pub u32);
impl DragEffects {
pub const NONE: Self = Self(0);
pub const COPY: Self = Self(1);
pub const MOVE: Self = Self(2);
pub const LINK: Self = Self(4);
pub const SCROLL: Self = Self(0x8000_0000);
pub const ALL: Self = Self(Self::COPY.0 | Self::MOVE.0 | Self::SCROLL.0);
#[must_use]
pub const fn from_bits(bits: u32) -> Self {
Self(bits)
}
#[must_use]
pub const fn bits(self) -> u32 {
self.0
}
#[must_use]
pub const fn with(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl core::ops::BitOr for DragEffects {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl FromIterator<DragEffects> for DragEffects {
fn from_iter<I: IntoIterator<Item = DragEffects>>(iter: I) -> Self {
let mut acc = 0;
for e in iter {
acc |= e.0;
}
Self(acc)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DragKeyStates(pub u32);
impl DragKeyStates {
pub const NONE: Self = Self(0);
pub const LEFT_MOUSE_BUTTON: Self = Self(1);
pub const RIGHT_MOUSE_BUTTON: Self = Self(2);
pub const SHIFT_KEY: Self = Self(4);
pub const CONTROL_KEY: Self = Self(8);
pub const MIDDLE_MOUSE_BUTTON: Self = Self(16);
pub const ALT_KEY: Self = Self(32);
#[must_use]
pub const fn from_bits(bits: u32) -> Self {
Self(bits)
}
#[must_use]
pub const fn bits(self) -> u32 {
self.0
}
#[must_use]
pub const fn with(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[must_use]
pub const fn contains(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl core::ops::BitOr for DragKeyStates {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl FromIterator<DragKeyStates> for DragKeyStates {
fn from_iter<I: IntoIterator<Item = DragKeyStates>>(iter: I) -> Self {
let mut acc = 0;
for k in iter {
acc |= k.0;
}
Self(acc)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DragInfo {
pub effects: DragEffects,
pub allowed_effects: DragEffects,
pub key_states: DragKeyStates,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct ManipulationDelta {
pub translation: (f32, f32),
pub scale: f32,
pub rotation: f32,
pub expansion: (f32, f32),
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct ManipulationVelocities {
pub angular: f32,
pub linear: (f32, f32),
pub expansion: (f32, f32),
}
pub trait RoutedEventHandler: Send + 'static {
fn on_event(&self, args: &EventArgs) -> bool;
}
impl<F: Fn(&EventArgs) -> bool + Send + 'static> RoutedEventHandler for F {
fn on_event(&self, args: &EventArgs) -> bool {
self(args)
}
}
unsafe extern "C" fn event_trampoline(
userdata: *mut c_void,
args: *const c_void,
out_handled: *mut bool,
) {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn RoutedEventHandler>>();
let ev = EventArgs {
raw: args,
_not_send: PhantomData,
};
let handled = handler.on_event(&ev);
if !out_handled.is_null() {
*out_handled = handled;
}
})
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct EventSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for EventSubscription {}
impl Drop for EventSubscription {
fn drop(&mut self) {
unsafe { noesis_unsubscribe_event(self.token.as_ptr()) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum RoutedEvent {
MouseEnter,
MouseLeave,
MouseMove,
PreviewMouseMove,
GotMouseCapture,
LostMouseCapture,
MouseDown,
MouseUp,
MouseLeftButtonDown,
MouseLeftButtonUp,
MouseRightButtonDown,
MouseRightButtonUp,
PreviewMouseDown,
PreviewMouseUp,
PreviewMouseLeftButtonDown,
PreviewMouseLeftButtonUp,
PreviewMouseRightButtonDown,
PreviewMouseRightButtonUp,
MouseWheel,
PreviewMouseWheel,
KeyDown,
KeyUp,
PreviewKeyDown,
PreviewKeyUp,
TextInput,
PreviewTextInput,
GotFocus,
LostFocus,
GotKeyboardFocus,
LostKeyboardFocus,
PreviewGotKeyboardFocus,
PreviewLostKeyboardFocus,
Loaded,
Unloaded,
SizeChanged,
TouchDown,
TouchMove,
TouchUp,
TouchEnter,
TouchLeave,
Tapped,
DoubleTapped,
Holding,
RightTapped,
ManipulationStarting,
ManipulationStarted,
ManipulationDelta,
ManipulationInertiaStarting,
ManipulationCompleted,
DragEnter,
DragOver,
DragLeave,
Drop,
PreviewDragEnter,
PreviewDragOver,
PreviewDragLeave,
PreviewDrop,
}
impl RoutedEvent {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::MouseEnter => "MouseEnter",
Self::MouseLeave => "MouseLeave",
Self::MouseMove => "MouseMove",
Self::PreviewMouseMove => "PreviewMouseMove",
Self::GotMouseCapture => "GotMouseCapture",
Self::LostMouseCapture => "LostMouseCapture",
Self::MouseDown => "MouseDown",
Self::MouseUp => "MouseUp",
Self::MouseLeftButtonDown => "MouseLeftButtonDown",
Self::MouseLeftButtonUp => "MouseLeftButtonUp",
Self::MouseRightButtonDown => "MouseRightButtonDown",
Self::MouseRightButtonUp => "MouseRightButtonUp",
Self::PreviewMouseDown => "PreviewMouseDown",
Self::PreviewMouseUp => "PreviewMouseUp",
Self::PreviewMouseLeftButtonDown => "PreviewMouseLeftButtonDown",
Self::PreviewMouseLeftButtonUp => "PreviewMouseLeftButtonUp",
Self::PreviewMouseRightButtonDown => "PreviewMouseRightButtonDown",
Self::PreviewMouseRightButtonUp => "PreviewMouseRightButtonUp",
Self::MouseWheel => "MouseWheel",
Self::PreviewMouseWheel => "PreviewMouseWheel",
Self::KeyDown => "KeyDown",
Self::KeyUp => "KeyUp",
Self::PreviewKeyDown => "PreviewKeyDown",
Self::PreviewKeyUp => "PreviewKeyUp",
Self::TextInput => "TextInput",
Self::PreviewTextInput => "PreviewTextInput",
Self::GotFocus => "GotFocus",
Self::LostFocus => "LostFocus",
Self::GotKeyboardFocus => "GotKeyboardFocus",
Self::LostKeyboardFocus => "LostKeyboardFocus",
Self::PreviewGotKeyboardFocus => "PreviewGotKeyboardFocus",
Self::PreviewLostKeyboardFocus => "PreviewLostKeyboardFocus",
Self::Loaded => "Loaded",
Self::Unloaded => "Unloaded",
Self::SizeChanged => "SizeChanged",
Self::TouchDown => "TouchDown",
Self::TouchMove => "TouchMove",
Self::TouchUp => "TouchUp",
Self::TouchEnter => "TouchEnter",
Self::TouchLeave => "TouchLeave",
Self::Tapped => "Tapped",
Self::DoubleTapped => "DoubleTapped",
Self::Holding => "Holding",
Self::RightTapped => "RightTapped",
Self::ManipulationStarting => "ManipulationStarting",
Self::ManipulationStarted => "ManipulationStarted",
Self::ManipulationDelta => "ManipulationDelta",
Self::ManipulationInertiaStarting => "ManipulationInertiaStarting",
Self::ManipulationCompleted => "ManipulationCompleted",
Self::DragEnter => "DragEnter",
Self::DragOver => "DragOver",
Self::DragLeave => "DragLeave",
Self::Drop => "Drop",
Self::PreviewDragEnter => "PreviewDragEnter",
Self::PreviewDragOver => "PreviewDragOver",
Self::PreviewDragLeave => "PreviewDragLeave",
Self::PreviewDrop => "PreviewDrop",
}
}
}
pub fn subscribe_event<H: RoutedEventHandler>(
element: &FrameworkElement,
event: RoutedEvent,
handled_too: bool,
handler: H,
) -> Option<EventSubscription> {
subscribe_event_by_name(element, event.as_str(), handled_too, handler)
}
pub fn subscribe_event_by_name<H: RoutedEventHandler>(
element: &FrameworkElement,
event_name: &str,
handled_too: bool,
handler: H,
) -> Option<EventSubscription> {
let cname = CString::new(event_name).ok()?;
let outer: Box<Box<dyn RoutedEventHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
noesis_subscribe_event(
element.raw(),
cname.as_ptr(),
handled_too,
event_trampoline,
userdata.cast(),
free_donated::<Box<dyn RoutedEventHandler>>,
)
};
if let Some(token) = NonNull::new(token) {
Some(EventSubscription { token })
} else {
unsafe { drop(Box::from_raw(userdata)) };
None
}
}
pub trait LifecycleHandler: Send + 'static {
fn on_event(&self);
}
impl<F: Fn() + Send + 'static> LifecycleHandler for F {
fn on_event(&self) {
self();
}
}
unsafe extern "C" fn lifecycle_trampoline(userdata: *mut c_void) {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn LifecycleHandler>>();
handler.on_event();
})
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct LifecycleSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for LifecycleSubscription {}
impl Drop for LifecycleSubscription {
fn drop(&mut self) {
unsafe { noesis_unsubscribe_lifecycle(self.token.as_ptr()) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LifecycleEvent {
Initialized,
LayoutUpdated,
DataContextChanged,
IsEnabledChanged,
IsVisibleChanged,
IsHitTestVisibleChanged,
IsKeyboardFocusedChanged,
IsKeyboardFocusWithinChanged,
IsMouseCapturedChanged,
IsMouseCaptureWithinChanged,
IsMouseDirectlyOverChanged,
FocusableChanged,
}
impl LifecycleEvent {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Initialized => "Initialized",
Self::LayoutUpdated => "LayoutUpdated",
Self::DataContextChanged => "DataContextChanged",
Self::IsEnabledChanged => "IsEnabledChanged",
Self::IsVisibleChanged => "IsVisibleChanged",
Self::IsHitTestVisibleChanged => "IsHitTestVisibleChanged",
Self::IsKeyboardFocusedChanged => "IsKeyboardFocusedChanged",
Self::IsKeyboardFocusWithinChanged => "IsKeyboardFocusWithinChanged",
Self::IsMouseCapturedChanged => "IsMouseCapturedChanged",
Self::IsMouseCaptureWithinChanged => "IsMouseCaptureWithinChanged",
Self::IsMouseDirectlyOverChanged => "IsMouseDirectlyOverChanged",
Self::FocusableChanged => "FocusableChanged",
}
}
}
pub fn subscribe_lifecycle<H: LifecycleHandler>(
element: &FrameworkElement,
event: LifecycleEvent,
handler: H,
) -> Option<LifecycleSubscription> {
subscribe_lifecycle_by_name(element, event.as_str(), handler)
}
pub fn subscribe_lifecycle_by_name<H: LifecycleHandler>(
element: &FrameworkElement,
name: &str,
handler: H,
) -> Option<LifecycleSubscription> {
let cname = CString::new(name).ok()?;
let outer: Box<Box<dyn LifecycleHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
noesis_subscribe_lifecycle(
element.raw(),
cname.as_ptr(),
lifecycle_trampoline,
userdata.cast(),
free_donated::<Box<dyn LifecycleHandler>>,
)
};
if let Some(token) = NonNull::new(token) {
Some(LifecycleSubscription { token })
} else {
unsafe { drop(Box::from_raw(userdata)) };
None
}
}
pub fn do_drag_drop(
source: &FrameworkElement,
data: &FrameworkElement,
allowed_effects: DragEffects,
) -> bool {
unsafe { noesis_routed_events_do_drag_drop(source.raw(), data.raw(), allowed_effects.bits()) }
}
pub trait DataObjectHandler: Send + 'static {
fn on_data_object(&self, data_object: Option<*mut c_void>, is_drag_drop: bool) -> bool;
}
impl<F: Fn(Option<*mut c_void>, bool) -> bool + Send + 'static> DataObjectHandler for F {
fn on_data_object(&self, data_object: Option<*mut c_void>, is_drag_drop: bool) -> bool {
self(data_object, is_drag_drop)
}
}
unsafe extern "C" fn data_object_trampoline(
userdata: *mut c_void,
data_object: *mut c_void,
is_drag_drop: bool,
out_cancel: *mut bool,
) {
crate::panic_guard::guard(|| {
let handler = &*userdata.cast::<Box<dyn DataObjectHandler>>();
let data = (!data_object.is_null()).then_some(data_object);
let cancel = handler.on_data_object(data, is_drag_drop);
if !out_cancel.is_null() {
*out_cancel = cancel;
}
})
}
#[must_use = "dropping the subscription immediately unsubscribes the handler"]
pub struct DataObjectSubscription {
token: NonNull<c_void>,
}
unsafe impl Send for DataObjectSubscription {}
impl Drop for DataObjectSubscription {
fn drop(&mut self) {
unsafe { noesis_routed_events_remove_data_object_handler(self.token.as_ptr()) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DataObjectEvent {
Copying,
Pasting,
}
pub fn subscribe_data_object<H: DataObjectHandler>(
element: &FrameworkElement,
event: DataObjectEvent,
handler: H,
) -> Option<DataObjectSubscription> {
let outer: Box<Box<dyn DataObjectHandler>> = Box::new(Box::new(handler));
let userdata = Box::into_raw(outer);
let token = unsafe {
match event {
DataObjectEvent::Copying => noesis_routed_events_add_copying_handler(
element.raw(),
data_object_trampoline,
userdata.cast(),
free_donated::<Box<dyn DataObjectHandler>>,
),
DataObjectEvent::Pasting => noesis_routed_events_add_pasting_handler(
element.raw(),
data_object_trampoline,
userdata.cast(),
free_donated::<Box<dyn DataObjectHandler>>,
),
}
};
if let Some(token) = NonNull::new(token) {
Some(DataObjectSubscription { token })
} else {
unsafe { drop(Box::from_raw(userdata)) };
None
}
}