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