1use super::{CursorStyle, WindowBounds};
2use crate::{
3 geometry::vector::{vec2f, Vector2F},
4 AnyAction, ClipboardItem,
5};
6use anyhow::{anyhow, Result};
7use parking_lot::Mutex;
8use std::{
9 any::Any,
10 cell::RefCell,
11 path::{Path, PathBuf},
12 rc::Rc,
13 sync::Arc,
14};
15use time::UtcOffset;
16
17pub struct Platform {
18 dispatcher: Arc<dyn super::Dispatcher>,
19 fonts: Arc<dyn super::FontSystem>,
20 current_clipboard_item: Mutex<Option<ClipboardItem>>,
21 cursor: Mutex<CursorStyle>,
22}
23
24#[derive(Default)]
25pub struct ForegroundPlatform {
26 last_prompt_for_new_path_args: RefCell<Option<(PathBuf, Box<dyn FnOnce(Option<PathBuf>)>)>>,
27}
28
29struct Dispatcher;
30
31pub struct Window {
32 size: Vector2F,
33 scale_factor: f32,
34 current_scene: Option<crate::Scene>,
35 event_handlers: Vec<Box<dyn FnMut(super::Event)>>,
36 resize_handlers: Vec<Box<dyn FnMut()>>,
37 close_handlers: Vec<Box<dyn FnOnce()>>,
38 pub(crate) last_prompt: RefCell<Option<Box<dyn FnOnce(usize)>>>,
39}
40
41impl ForegroundPlatform {
42 pub(crate) fn simulate_new_path_selection(
43 &self,
44 result: impl FnOnce(PathBuf) -> Option<PathBuf>,
45 ) {
46 let (dir_path, callback) = self
47 .last_prompt_for_new_path_args
48 .take()
49 .expect("prompt_for_new_path was not called");
50 callback(result(dir_path));
51 }
52
53 pub(crate) fn did_prompt_for_new_path(&self) -> bool {
54 self.last_prompt_for_new_path_args.borrow().is_some()
55 }
56}
57
58impl super::ForegroundPlatform for ForegroundPlatform {
59 fn on_become_active(&self, _: Box<dyn FnMut()>) {}
60
61 fn on_resign_active(&self, _: Box<dyn FnMut()>) {}
62
63 fn on_quit(&self, _: Box<dyn FnMut()>) {}
64
65 fn on_event(&self, _: Box<dyn FnMut(crate::Event) -> bool>) {}
66
67 fn on_open_files(&self, _: Box<dyn FnMut(Vec<std::path::PathBuf>)>) {}
68
69 fn run(&self, _on_finish_launching: Box<dyn FnOnce() -> ()>) {
70 unimplemented!()
71 }
72
73 fn on_menu_command(&self, _: Box<dyn FnMut(&dyn AnyAction)>) {}
74
75 fn set_menus(&self, _: Vec<crate::Menu>) {}
76
77 fn prompt_for_paths(
78 &self,
79 _: super::PathPromptOptions,
80 _: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
81 ) {
82 }
83
84 fn prompt_for_new_path(&self, path: &Path, f: Box<dyn FnOnce(Option<std::path::PathBuf>)>) {
85 *self.last_prompt_for_new_path_args.borrow_mut() = Some((path.to_path_buf(), f));
86 }
87}
88
89impl Platform {
90 fn new() -> Self {
91 Self {
92 dispatcher: Arc::new(Dispatcher),
93 fonts: Arc::new(super::current::FontSystem::new()),
94 current_clipboard_item: Default::default(),
95 cursor: Mutex::new(CursorStyle::Arrow),
96 }
97 }
98}
99
100impl super::Platform for Platform {
101 fn dispatcher(&self) -> Arc<dyn super::Dispatcher> {
102 self.dispatcher.clone()
103 }
104
105 fn fonts(&self) -> std::sync::Arc<dyn super::FontSystem> {
106 self.fonts.clone()
107 }
108
109 fn activate(&self, _ignoring_other_apps: bool) {}
110
111 fn open_window(
112 &self,
113 _: usize,
114 options: super::WindowOptions,
115 _executor: Rc<super::executor::Foreground>,
116 ) -> Box<dyn super::Window> {
117 Box::new(Window::new(match options.bounds {
118 WindowBounds::Maximized => vec2f(1024., 768.),
119 WindowBounds::Fixed(rect) => rect.size(),
120 }))
121 }
122
123 fn key_window_id(&self) -> Option<usize> {
124 None
125 }
126
127 fn quit(&self) {}
128
129 fn write_to_clipboard(&self, item: ClipboardItem) {
130 *self.current_clipboard_item.lock() = Some(item);
131 }
132
133 fn read_from_clipboard(&self) -> Option<ClipboardItem> {
134 self.current_clipboard_item.lock().clone()
135 }
136
137 fn open_url(&self, _: &str) {}
138
139 fn write_credentials(&self, _: &str, _: &str, _: &[u8]) -> Result<()> {
140 Ok(())
141 }
142
143 fn read_credentials(&self, _: &str) -> Result<Option<(String, Vec<u8>)>> {
144 Ok(None)
145 }
146
147 fn delete_credentials(&self, _: &str) -> Result<()> {
148 Ok(())
149 }
150
151 fn set_cursor_style(&self, style: CursorStyle) {
152 *self.cursor.lock() = style;
153 }
154
155 fn local_timezone(&self) -> UtcOffset {
156 UtcOffset::UTC
157 }
158
159 fn path_for_resource(&self, _name: Option<&str>, _extension: Option<&str>) -> Result<PathBuf> {
160 Err(anyhow!("app not running inside a bundle"))
161 }
162}
163
164impl Window {
165 fn new(size: Vector2F) -> Self {
166 Self {
167 size,
168 event_handlers: Vec::new(),
169 resize_handlers: Vec::new(),
170 close_handlers: Vec::new(),
171 scale_factor: 1.0,
172 current_scene: None,
173 last_prompt: RefCell::new(None),
174 }
175 }
176}
177
178impl super::Dispatcher for Dispatcher {
179 fn is_main_thread(&self) -> bool {
180 true
181 }
182
183 fn run_on_main_thread(&self, task: async_task::Runnable) {
184 task.run();
185 }
186}
187
188impl super::WindowContext for Window {
189 fn size(&self) -> Vector2F {
190 self.size
191 }
192
193 fn scale_factor(&self) -> f32 {
194 self.scale_factor
195 }
196
197 fn titlebar_height(&self) -> f32 {
198 24.
199 }
200
201 fn present_scene(&mut self, scene: crate::Scene) {
202 self.current_scene = Some(scene);
203 }
204}
205
206impl super::Window for Window {
207 fn as_any_mut(&mut self) -> &mut dyn Any {
208 self
209 }
210
211 fn on_event(&mut self, callback: Box<dyn FnMut(crate::Event)>) {
212 self.event_handlers.push(callback);
213 }
214
215 fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
216 self.resize_handlers.push(callback);
217 }
218
219 fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
220 self.close_handlers.push(callback);
221 }
222
223 fn prompt(&self, _: crate::PromptLevel, _: &str, _: &[&str], f: Box<dyn FnOnce(usize)>) {
224 self.last_prompt.replace(Some(f));
225 }
226}
227
228pub fn platform() -> Platform {
229 Platform::new()
230}
231
232pub fn foreground_platform() -> ForegroundPlatform {
233 ForegroundPlatform::default()
234}