1use 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
15pub 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 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 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
56pub struct CursorFactory {
58 native: ComPtr<IFrnCursorFactory>,
59}
60
61impl CursorFactory {
62 pub fn new(native: ComPtr<IFrnCursorFactory>) -> Self {
63 Self { native }
64 }
65
66 fn create_cursor_from_image_data(&self, mut image_data: Vec<u8>, hot_spot: PixelPoint) -> Rc<dyn ICursorImpl> {
69 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 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}