Skip to main content

ferroui_native/
cursor.rs

1//! Mouse cursors.
2
3use crate::helpers::{to_frn_cursor_type, ComResultExt};
4use crate::interop::*;
5use ferroui_base::input::StandardCursorType;
6use ferroui_base::media::imaging::{Bitmap, BitmapEncoderOptions, PngBitmapEncoderOptions};
7use ferroui_base::platform::{ICursorFactory, ICursorImpl};
8use ferroui_base::PixelPoint;
9use ferroui_microcom::ComPtr;
10use std::any::Any;
11use std::cell::RefCell;
12use std::ffi::c_void;
13use std::rc::Rc;
14
15/// A native cursor.
16pub struct FerroNativeCursor {
17    cursor: RefCell<Option<ComPtr<IFrnCursor>>>,
18}
19
20impl FerroNativeCursor {
21    pub fn new(cursor: Option<ComPtr<IFrnCursor>>) -> Rc<FerroNativeCursor> {
22        Rc::new(FerroNativeCursor { cursor: RefCell::new(cursor) })
23    }
24
25    /// The native cursor; `None` once disposed.
26    pub fn cursor(&self) -> Option<ComPtr<IFrnCursor>> {
27        self.cursor.borrow().clone()
28    }
29
30    pub fn handle(&self) -> isize {
31        0
32    }
33
34    pub fn handle_descriptor(&self) -> &'static str {
35        "<none>"
36    }
37
38    /// The cursor behind a cursor implementation handle, if it is a cursor
39    /// of this backend.
40    pub(crate) fn from_cursor_impl(cursor: &dyn ICursorImpl) -> Option<&FerroNativeCursor> {
41        cursor.as_any().downcast_ref::<FerroNativeCursor>()
42    }
43}
44
45impl ICursorImpl for FerroNativeCursor {
46    fn dispose(&self) {
47        let cursor = self.cursor.borrow_mut().take();
48        drop(cursor);
49    }
50
51    fn as_any(&self) -> &dyn Any {
52        self
53    }
54}
55
56/// Creates the cursors of the macOS backend.
57pub struct CursorFactory {
58    native: ComPtr<IFrnCursorFactory>,
59}
60
61impl CursorFactory {
62    pub fn new(native: ComPtr<IFrnCursorFactory>) -> Self {
63        Self { native }
64    }
65
66    /// Creates a cursor from an encoded image (PNG); `hot_spot` is the
67    /// pixel of the image that is the pointer position.
68    fn create_cursor_from_image_data(&self, mut image_data: Vec<u8>, hot_spot: PixelPoint) -> Rc<dyn ICursorImpl> {
69        // SAFETY: the native side copies the image data during the call.
70        let native_cursor = unsafe {
71            self.native.create_custom_cursor(
72                image_data.as_mut_ptr() as *mut c_void,
73                image_data.len(),
74                FrnPixelSize { width: hot_spot.x, height: hot_spot.y },
75            )
76        }
77        .check();
78
79        FerroNativeCursor::new(native_cursor)
80    }
81}
82
83impl ICursorFactory for CursorFactory {
84    fn get_cursor(&self, cursor_type: StandardCursorType) -> Rc<dyn ICursorImpl> {
85        let cursor = self.native.get_cursor(to_frn_cursor_type(cursor_type)).check();
86        FerroNativeCursor::new(cursor)
87    }
88
89    /// # Panics
90    /// Panics when the bitmap cannot be encoded, where the reference
91    /// implementation throws.
92    fn create_cursor(&self, cursor: &Bitmap, hot_spot: PixelPoint) -> Rc<dyn ICursorImpl> {
93        let mut image_data = Vec::new();
94        if let Err(error) = cursor.save(&mut image_data, &BitmapEncoderOptions::Png(PngBitmapEncoderOptions::DEFAULT))
95        {
96            panic!("Unable to save the cursor bitmap: {error}");
97        }
98
99        self.create_cursor_from_image_data(image_data, hot_spot)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    struct OtherCursor;
108    impl ICursorImpl for OtherCursor {
109        fn dispose(&self) {}
110        fn as_any(&self) -> &dyn Any {
111            self
112        }
113    }
114
115    #[test]
116    fn cursors_of_this_backend_are_recovered_from_their_contract_handle() {
117        let cursor = FerroNativeCursor::new(None);
118        let handle: Rc<dyn ICursorImpl> = cursor.clone();
119        let found = FerroNativeCursor::from_cursor_impl(&*handle).expect("a cursor of this backend");
120        assert!(std::ptr::eq(found, &*cursor));
121        assert_eq!(cursor.handle(), 0);
122        assert_eq!(cursor.handle_descriptor(), "<none>");
123
124        let other: Rc<dyn ICursorImpl> = Rc::new(OtherCursor);
125        assert!(FerroNativeCursor::from_cursor_impl(&*other).is_none());
126    }
127}