1use crate::media::imaging::Bitmap;
2use crate::platform::{ICursorFactory, ICursorImpl};
3use crate::{FerroLocator, LocatorExtensions, PixelPoint};
4use std::cell::RefCell;
5use std::fmt;
6use std::rc::Rc;
7
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
10pub enum StandardCursorType {
11 #[default]
12 Arrow,
13 Ibeam,
14 Wait,
15 Cross,
16 UpArrow,
17 SizeWestEast,
18 SizeNorthSouth,
19 SizeAll,
20 No,
21 Hand,
22 AppStarting,
23 Help,
24 TopSide,
25 BottomSide,
26 LeftSide,
27 RightSide,
28 TopLeftCorner,
29 TopRightCorner,
30 BottomLeftCorner,
31 BottomRightCorner,
32 DragMove,
33 DragCopy,
34 DragLink,
35 None,
36}
37
38impl StandardCursorType {
39 const ALL: [StandardCursorType; 24] = [
40 StandardCursorType::Arrow,
41 StandardCursorType::Ibeam,
42 StandardCursorType::Wait,
43 StandardCursorType::Cross,
44 StandardCursorType::UpArrow,
45 StandardCursorType::SizeWestEast,
46 StandardCursorType::SizeNorthSouth,
47 StandardCursorType::SizeAll,
48 StandardCursorType::No,
49 StandardCursorType::Hand,
50 StandardCursorType::AppStarting,
51 StandardCursorType::Help,
52 StandardCursorType::TopSide,
53 StandardCursorType::BottomSide,
54 StandardCursorType::LeftSide,
55 StandardCursorType::RightSide,
56 StandardCursorType::TopLeftCorner,
57 StandardCursorType::TopRightCorner,
58 StandardCursorType::BottomLeftCorner,
59 StandardCursorType::BottomRightCorner,
60 StandardCursorType::DragMove,
61 StandardCursorType::DragCopy,
62 StandardCursorType::DragLink,
63 StandardCursorType::None,
64 ];
65
66 pub fn name(self) -> &'static str {
68 match self {
69 StandardCursorType::Arrow => "Arrow",
70 StandardCursorType::Ibeam => "Ibeam",
71 StandardCursorType::Wait => "Wait",
72 StandardCursorType::Cross => "Cross",
73 StandardCursorType::UpArrow => "UpArrow",
74 StandardCursorType::SizeWestEast => "SizeWestEast",
75 StandardCursorType::SizeNorthSouth => "SizeNorthSouth",
76 StandardCursorType::SizeAll => "SizeAll",
77 StandardCursorType::No => "No",
78 StandardCursorType::Hand => "Hand",
79 StandardCursorType::AppStarting => "AppStarting",
80 StandardCursorType::Help => "Help",
81 StandardCursorType::TopSide => "TopSide",
82 StandardCursorType::BottomSide => "BottomSide",
83 StandardCursorType::LeftSide => "LeftSide",
84 StandardCursorType::RightSide => "RightSide",
85 StandardCursorType::TopLeftCorner => "TopLeftCorner",
86 StandardCursorType::TopRightCorner => "TopRightCorner",
87 StandardCursorType::BottomLeftCorner => "BottomLeftCorner",
88 StandardCursorType::BottomRightCorner => "BottomRightCorner",
89 StandardCursorType::DragMove => "DragMove",
90 StandardCursorType::DragCopy => "DragCopy",
91 StandardCursorType::DragLink => "DragLink",
92 StandardCursorType::None => "None",
93 }
94 }
95
96 pub fn parse(s: &str) -> Option<StandardCursorType> {
98 let s = s.trim();
99 Self::ALL.iter().copied().find(|t| t.name().eq_ignore_ascii_case(s))
100 }
101}
102
103impl fmt::Display for StandardCursorType {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 f.write_str(self.name())
106 }
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct ParseCursorError(pub String);
112
113impl fmt::Display for ParseCursorError {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(f, "Unrecognized cursor type '{}'.", self.0)
116 }
117}
118
119impl std::error::Error for ParseCursorError {}
120
121crate::ferro_thread_local! {
122 static DEFAULT_CURSOR: RefCell<Option<Rc<Cursor>>> = const { RefCell::new(None) };
123}
124
125pub struct Cursor {
130 platform_impl: Rc<dyn ICursorImpl>,
131 name: String,
132}
133
134impl Cursor {
135 fn get_cursor_factory() -> Rc<dyn ICursorFactory> {
136 FerroLocator::current().get_required_service::<dyn ICursorFactory>()
137 }
138
139 pub fn default_cursor() -> Rc<Cursor> {
143 if let Some(cursor) = DEFAULT_CURSOR.with(|cursor| cursor.borrow().clone()) {
144 return cursor;
145 }
146 let cursor = Cursor::new(StandardCursorType::Arrow);
147 DEFAULT_CURSOR.with(|slot| *slot.borrow_mut() = Some(cursor.clone()));
148 cursor
149 }
150
151 pub fn new(cursor_type: StandardCursorType) -> Rc<Cursor> {
155 Rc::new(Cursor {
156 platform_impl: Self::get_cursor_factory().get_cursor(cursor_type),
157 name: cursor_type.name().to_string(),
158 })
159 }
160
161 pub fn from_bitmap(cursor: &Bitmap, hot_spot: PixelPoint) -> Rc<Cursor> {
166 Rc::new(Cursor {
167 platform_impl: Self::get_cursor_factory().create_cursor(cursor, hot_spot),
168 name: "BitmapCursor".to_string(),
169 })
170 }
171
172 pub fn platform_impl(&self) -> &Rc<dyn ICursorImpl> {
174 &self.platform_impl
175 }
176
177 pub fn dispose(&self) {
179 self.platform_impl.dispose()
180 }
181
182 pub fn parse(s: &str) -> Result<Rc<Cursor>, ParseCursorError> {
186 match StandardCursorType::parse(s) {
187 Some(cursor_type) => Ok(Cursor::new(cursor_type)),
188 None => Err(ParseCursorError(s.to_string())),
189 }
190 }
191}
192
193impl PartialEq for Cursor {
194 #[inline]
195 fn eq(&self, other: &Self) -> bool {
196 std::ptr::eq(self, other)
197 }
198}
199
200impl Eq for Cursor {}
201
202impl fmt::Display for Cursor {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 f.write_str(&self.name)
205 }
206}
207
208impl fmt::Debug for Cursor {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.write_str(&self.name)
211 }
212}