Release Notes: - N/A
Unified · Split
| @@ -16,13 +16,13 @@ use crate::{ · 15 unmodified lines | ||
| 16 | 16 | current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle, |
| 17 | 17 | AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, |
| 18 | 18 | Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, |
| 19 | − Pixels, Platform, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, | |
| 20 | − TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, | |
| 21 | − WindowId, | |
| 19 | + PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString, | |
| 20 | + SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, | |
| 21 | + View, Window, WindowContext, WindowHandle, WindowId, | |
| 22 | 22 | }; |
| 23 | 23 | use anyhow::{anyhow, Result}; |
| 24 | 24 | use collections::{HashMap, HashSet, VecDeque}; |
| 25 | −use futures::{future::LocalBoxFuture, Future}; | |
| 25 | +use futures::{channel::oneshot, future::LocalBoxFuture, Future}; | |
| 26 | 26 | use parking_lot::Mutex; |
| 27 | 27 | use slotmap::SlotMap; |
| 28 | 28 | use std::{ |
| @@ -31,7 +31,7 @@ use std::{ · 2 unmodified lines | ||
| 31 | 31 | marker::PhantomData, |
| 32 | 32 | mem, |
| 33 | 33 | ops::{Deref, DerefMut}, |
| 34 | − path::PathBuf, | |
| 34 | + path::{Path, PathBuf}, | |
| 35 | 35 | rc::{Rc, Weak}, |
| 36 | 36 | sync::{atomic::Ordering::SeqCst, Arc}, |
| 37 | 37 | time::Duration, |
| @@ -262,38 +262,13 @@ impl AppContext { · 224 unmodified lines | ||
| 262 | 262 | .collect() |
| 263 | 263 | } |
| 264 | 264 | |
| 265 | − pub(crate) fn update_window<R>( | |
| 266 | − &mut self, | |
| 267 | − handle: AnyWindowHandle, | |
| 268 | − update: impl FnOnce(AnyView, &mut WindowContext) -> R, | |
| 269 | − ) -> Result<R> { | |
| 270 | − self.update(|cx| { | |
| 271 | − let mut window = cx | |
| 272 | − .windows | |
| 273 | − .get_mut(handle.id) | |
| 274 | − .ok_or_else(|| anyhow!("window not found"))? | |
| 275 | − .take() | |
| 276 | − .unwrap(); | |
| 277 | − | |
| 278 | − let root_view = window.root_view.clone().unwrap(); | |
| 279 | − let result = update(root_view, &mut WindowContext::new(cx, &mut window)); | |
| 280 | − | |
| 281 | − cx.windows | |
| 282 | − .get_mut(handle.id) | |
| 283 | − .ok_or_else(|| anyhow!("window not found"))? | |
| 284 | − .replace(window); | |
| 285 | − | |
| 286 | − Ok(result) | |
| 287 | − }) | |
| 288 | − } | |
| 289 | − | |
| 290 | 265 | /// Opens a new window with the given option and the root view returned by the given function. |
| 291 | 266 | /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific |
| 292 | 267 | /// functionality. |
| 293 | 268 | pub fn open_window<V: Render>( |
| 294 | 269 | &mut self, |
| 295 | 270 | options: crate::WindowOptions, |
| 296 | − build_root_view: impl FnOnce(&mut WindowContext) -> View<V> + 'static, | |
| 271 | + build_root_view: impl FnOnce(&mut WindowContext) -> View<V>, | |
| 297 | 272 | ) -> WindowHandle<V> { |
| 298 | 273 | self.update(|cx| { |
| 299 | 274 | let id = cx.windows.insert(None); |
| @@ -306,47 +281,63 @@ impl AppContext { · 6 unmodified lines | ||
| 306 | 281 | }) |
| 307 | 282 | } |
| 308 | 283 | |
| 309 | − pub(crate) fn platform(&self) -> &Rc<dyn Platform> { | |
| 310 | − &self.platform | |
| 311 | − } | |
| 312 | − | |
| 313 | 284 | /// Instructs the platform to activate the application by bringing it to the foreground. |
| 314 | 285 | pub fn activate(&self, ignoring_other_apps: bool) { |
| 315 | − self.platform().activate(ignoring_other_apps); | |
| 286 | + self.platform.activate(ignoring_other_apps); | |
| 287 | + } | |
| 288 | + | |
| 289 | + /// Returns the list of currently active displays. | |
| 290 | + pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> { | |
| 291 | + self.platform.displays() | |
| 316 | 292 | } |
| 317 | 293 | |
| 318 | 294 | /// Writes data to the platform clipboard. |
| 319 | 295 | pub fn write_to_clipboard(&self, item: ClipboardItem) { |
| 320 | − self.platform().write_to_clipboard(item) | |
| 296 | + self.platform.write_to_clipboard(item) | |
| 321 | 297 | } |
| 322 | 298 | |
| 323 | 299 | /// Reads data from the platform clipboard. |
| 324 | 300 | pub fn read_from_clipboard(&self) -> Option<ClipboardItem> { |
| 325 | − self.platform().read_from_clipboard() | |
| 301 | + self.platform.read_from_clipboard() | |
| 326 | 302 | } |
| 327 | 303 | |
| 328 | 304 | /// Writes credentials to the platform keychain. |
| 329 | 305 | pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> { |
| 330 | − self.platform().write_credentials(url, username, password) | |
| 306 | + self.platform.write_credentials(url, username, password) | |
| 331 | 307 | } |
| 332 | 308 | |
| 333 | 309 | /// Reads credentials from the platform keychain. |
| 334 | 310 | pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> { |
| 335 | − self.platform().read_credentials(url) | |
| 311 | + self.platform.read_credentials(url) | |
| 336 | 312 | } |
| 337 | 313 | |
| 338 | 314 | /// Deletes credentials from the platform keychain. |
| 339 | 315 | pub fn delete_credentials(&self, url: &str) -> Result<()> { |
| 340 | − self.platform().delete_credentials(url) | |
| 316 | + self.platform.delete_credentials(url) | |
| 341 | 317 | } |
| 342 | 318 | |
| 343 | 319 | /// Directs the platform's default browser to open the given URL. |
| 344 | 320 | pub fn open_url(&self, url: &str) { |
| 345 | − self.platform().open_url(url); | |
| 321 | + self.platform.open_url(url); | |
| 346 | 322 | } |
| 347 | 323 | |
| 348 | 324 | pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> { |
| 349 | − self.platform().path_for_auxiliary_executable(name) | |
| 325 | + self.platform.path_for_auxiliary_executable(name) | |
| 326 | + } | |
| 327 | + | |
| 328 | + pub fn prompt_for_paths( | |
| 329 | + &self, | |
| 330 | + options: PathPromptOptions, | |
| 331 | + ) -> oneshot::Receiver<Option<Vec<PathBuf>>> { | |
| 332 | + self.platform.prompt_for_paths(options) | |
| 333 | + } | |
| 334 | + | |
| 335 | + pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> { | |
| 336 | + self.platform.prompt_for_new_path(directory) | |
| 337 | + } | |
| 338 | + | |
| 339 | + pub fn reveal_path(&self, path: &Path) { | |
| 340 | + self.platform.reveal_path(path) | |
| 350 | 341 | } |
| 351 | 342 | |
| 352 | 343 | pub(crate) fn push_effect(&mut self, effect: Effect) { |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | use crate::{ |
| 2 | 2 | AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model, |
| 3 | 3 | ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext, |
| 4 | + WindowHandle, | |
| 4 | 5 | }; |
| 5 | 6 | use anyhow::{anyhow, Context as _}; |
| 6 | 7 | use derive_more::{Deref, DerefMut}; |
| @@ -82,17 +83,20 @@ impl AsyncAppContext { · 75 unmodified lines | ||
| 82 | 83 | Ok(f(&mut *lock)) |
| 83 | 84 | } |
| 84 | 85 | |
| 85 | − pub fn update_window<R>( | |
| 86 | + pub fn open_window<V>( | |
| 86 | 87 | &self, |
| 87 | − handle: AnyWindowHandle, | |
| 88 | − update: impl FnOnce(AnyView, &mut WindowContext) -> R, | |
| 89 | − ) -> Result<R> { | |
| 88 | + options: crate::WindowOptions, | |
| 89 | + build_root_view: impl FnOnce(&mut WindowContext) -> View<V>, | |
| 90 | + ) -> Result<WindowHandle<V>> | |
| 91 | + where | |
| 92 | + V: Render, | |
| 93 | + { | |
| 90 | 94 | let app = self |
| 91 | 95 | .app |
| 92 | 96 | .upgrade() |
| 93 | 97 | .ok_or_else(|| anyhow!("app was released"))?; |
| 94 | − let mut app_context = app.borrow_mut(); | |
| 95 | − app_context.update_window(handle, update) | |
| 98 | + let mut lock = app.borrow_mut(); | |
| 99 | + Ok(lock.open_window(options, build_root_view)) | |
| 96 | 100 | } |
| 97 | 101 | |
| 98 | 102 | pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R> |
| @@ -87,15 +87,6 @@ impl TestAppContext { · 86 unmodified lines | ||
| 87 | 87 | cx.update(f) |
| 88 | 88 | } |
| 89 | 89 | |
| 90 | − pub fn update_window<R>( | |
| 91 | − &self, | |
| 92 | − handle: AnyWindowHandle, | |
| 93 | − update: impl FnOnce(AnyView, &mut WindowContext) -> R, | |
| 94 | − ) -> R { | |
| 95 | − let mut app = self.app.borrow_mut(); | |
| 96 | − app.update_window(handle, update).unwrap() | |
| 97 | − } | |
| 98 | − | |
| 99 | 90 | pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R> |
| 100 | 91 | where |
| 101 | 92 | Fut: Future<Output = R> + 'static, |
| @@ -138,12 +138,7 @@ pub(crate) trait PlatformWindow { · 137 unmodified lines | ||
| 138 | 138 | fn mouse_position(&self) -> Point<Pixels>; |
| 139 | 139 | fn as_any_mut(&mut self) -> &mut dyn Any; |
| 140 | 140 | fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>); |
| 141 | − fn prompt( | |
| 142 | − &self, | |
| 143 | − level: WindowPromptLevel, | |
| 144 | − msg: &str, | |
| 145 | − answers: &[&str], | |
| 146 | − ) -> oneshot::Receiver<usize>; | |
| 141 | + fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>; | |
| 147 | 142 | fn activate(&self); |
| 148 | 143 | fn set_title(&mut self, title: &str); |
| 149 | 144 | fn set_edited(&mut self, edited: bool); |
| @@ -454,14 +449,6 @@ impl Default for WindowAppearance { · 304 unmodified lines | ||
| 454 | 449 | } |
| 455 | 450 | } |
| 456 | 451 | |
| 457 | −#[derive(Copy, Clone, Debug, PartialEq, Default)] | |
| 458 | −pub enum WindowPromptLevel { | |
| 459 | − #[default] | |
| 460 | − Info, | |
| 461 | − Warning, | |
| 462 | − Critical, | |
| 463 | −} | |
| 464 | − | |
| 465 | 452 | #[derive(Copy, Clone, Debug)] |
| 466 | 453 | pub struct PathPromptOptions { |
| 467 | 454 | pub files: bool, |
| @@ -3,8 +3,8 @@ use crate::{ · 2 unmodified lines | ||
| 3 | 3 | display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths, |
| 4 | 4 | FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke, |
| 5 | 5 | Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, |
| 6 | − Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, | |
| 7 | − Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, | |
| 6 | + Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, | |
| 7 | + PromptLevel, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, | |
| 8 | 8 | }; |
| 9 | 9 | use block::ConcreteBlock; |
| 10 | 10 | use cocoa::{ |
| @@ -742,12 +742,7 @@ impl PlatformWindow for MacWindow { · 731 unmodified lines | ||
| 742 | 742 | self.0.as_ref().lock().input_handler = Some(input_handler); |
| 743 | 743 | } |
| 744 | 744 | |
| 745 | − fn prompt( | |
| 746 | − &self, | |
| 747 | − level: WindowPromptLevel, | |
| 748 | − msg: &str, | |
| 749 | − answers: &[&str], | |
| 750 | − ) -> oneshot::Receiver<usize> { | |
| 745 | + fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> { | |
| 751 | 746 | // macOs applies overrides to modal window buttons after they are added. |
| 752 | 747 | // Two most important for this logic are: |
| 753 | 748 | // * Buttons with "Cancel" title will be displayed as the last buttons in the modal |
| @@ -777,9 +772,9 @@ impl PlatformWindow for MacWindow { · 23 unmodified lines | ||
| 777 | 772 | let alert: id = msg_send![class!(NSAlert), alloc]; |
| 778 | 773 | let alert: id = msg_send![alert, init]; |
| 779 | 774 | let alert_style = match level { |
| 780 | − WindowPromptLevel::Info => 1, | |
| 781 | − WindowPromptLevel::Warning => 0, | |
| 782 | − WindowPromptLevel::Critical => 2, | |
| 775 | + PromptLevel::Info => 1, | |
| 776 | + PromptLevel::Warning => 0, | |
| 777 | + PromptLevel::Critical => 2, | |
| 783 | 778 | }; |
| 784 | 779 | let _: () = msg_send![alert, setAlertStyle: alert_style]; |
| 785 | 780 | let _: () = msg_send![alert, setMessageText: ns_string(msg)]; |
| @@ -98,6 +98,10 @@ pub struct WeakView<V> { · 97 unmodified lines | ||
| 98 | 98 | } |
| 99 | 99 | |
| 100 | 100 | impl<V: 'static> WeakView<V> { |
| 101 | + pub fn entity_id(&self) -> EntityId { | |
| 102 | + self.model.entity_id | |
| 103 | + } | |
| 104 | + | |
| 101 | 105 | pub fn upgrade(&self) -> Option<View<V>> { |
| 102 | 106 | Entity::upgrade_from(self) |
| 103 | 107 | } |
| @@ -4,14 +4,15 @@ use crate::{ · 3 unmodified lines | ||
| 4 | 4 | Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, |
| 5 | 5 | Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, |
| 6 | 6 | Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, |
| 7 | − MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, | |
| 8 | − Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, | |
| 9 | − Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, | |
| 10 | − UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, | |
| 7 | + MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, | |
| 8 | + PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, | |
| 9 | + SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, | |
| 10 | + Underline, UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, | |
| 11 | 11 | }; |
| 12 | 12 | use anyhow::{anyhow, Result}; |
| 13 | 13 | use collections::HashMap; |
| 14 | 14 | use derive_more::{Deref, DerefMut}; |
| 15 | +use futures::channel::oneshot; | |
| 15 | 16 | use parking_lot::RwLock; |
| 16 | 17 | use slotmap::SlotMap; |
| 17 | 18 | use smallvec::SmallVec; |
| @@ -195,7 +196,7 @@ impl Window { · 177 unmodified lines | ||
| 195 | 196 | options: WindowOptions, |
| 196 | 197 | cx: &mut AppContext, |
| 197 | 198 | ) -> Self { |
| 198 | − let platform_window = cx.platform().open_window(handle, options); | |
| 199 | + let platform_window = cx.platform.open_window(handle, options); | |
| 199 | 200 | let display_id = platform_window.display().id(); |
| 200 | 201 | let sprite_atlas = platform_window.sprite_atlas(); |
| 201 | 202 | let mouse_position = platform_window.mouse_position(); |
| @@ -419,7 +420,7 @@ impl<'a> WindowContext<'a> { · 217 unmodified lines | ||
| 419 | 420 | } else { |
| 420 | 421 | let mut async_cx = self.to_async(); |
| 421 | 422 | self.next_frame_callbacks.insert(display_id, vec![f]); |
| 422 | − self.platform().set_display_link_output_callback( | |
| 423 | + self.platform.set_display_link_output_callback( | |
| 423 | 424 | display_id, |
| 424 | 425 | Box::new(move |_current_time, _output_time| { |
| 425 | 426 | let _ = async_cx.update(|_, cx| { |
| @@ -434,32 +435,26 @@ impl<'a> WindowContext<'a> { · 8 unmodified lines | ||
| 434 | 435 | } |
| 435 | 436 | |
| 436 | 437 | if cx.next_frame_callbacks.get(&display_id).unwrap().is_empty() { |
| 437 | − cx.platform().stop_display_link(display_id); | |
| 438 | + cx.platform.stop_display_link(display_id); | |
| 438 | 439 | } |
| 439 | 440 | }); |
| 440 | 441 | }), |
| 441 | 442 | ); |
| 442 | 443 | } |
| 443 | 444 | |
| 444 | − self.platform().start_display_link(display_id); | |
| 445 | + self.platform.start_display_link(display_id); | |
| 445 | 446 | } |
| 446 | 447 | |
| 447 | 448 | /// Spawn the future returned by the given closure on the application thread pool. |
| 448 | 449 | /// The closure is provided a handle to the current window and an `AsyncWindowContext` for |
| 449 | 450 | /// use within your future. |
| 450 | − pub fn spawn<Fut, R>( | |
| 451 | − &mut self, | |
| 452 | − f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut, | |
| 453 | − ) -> Task<R> | |
| 451 | + pub fn spawn<Fut, R>(&mut self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R> | |
| 454 | 452 | where |
| 455 | 453 | R: 'static, |
| 456 | 454 | Fut: Future<Output = R> + 'static, |
| 457 | 455 | { |
| 458 | − let window = self.window.handle; | |
| 459 | − self.app.spawn(move |app| { | |
| 460 | − let cx = AsyncWindowContext::new(app, window); | |
| 461 | − f(window, cx) | |
| 462 | − }) | |
| 456 | + self.app | |
| 457 | + .spawn(|app| f(AsyncWindowContext::new(app, self.window.handle))) | |
| 463 | 458 | } |
| 464 | 459 | |
| 465 | 460 | /// Update the global of the given type. The given closure is given simultaneous mutable |
| @@ -1153,6 +1148,19 @@ impl<'a> WindowContext<'a> { · 687 unmodified lines | ||
| 1153 | 1148 | ) |
| 1154 | 1149 | } |
| 1155 | 1150 | |
| 1151 | + pub fn activate_window(&self) { | |
| 1152 | + self.window.platform_window.activate(); | |
| 1153 | + } | |
| 1154 | + | |
| 1155 | + pub fn prompt( | |
| 1156 | + &self, | |
| 1157 | + level: PromptLevel, | |
| 1158 | + msg: &str, | |
| 1159 | + answers: &[&str], | |
| 1160 | + ) -> oneshot::Receiver<usize> { | |
| 1161 | + self.window.platform_window.prompt(level, msg, answers) | |
| 1162 | + } | |
| 1163 | + | |
| 1156 | 1164 | fn dispatch_action( |
| 1157 | 1165 | &mut self, |
| 1158 | 1166 | action: Box<dyn Action>, |
| @@ -1809,7 +1817,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { · 650 unmodified lines | ||
| 1809 | 1817 | Fut: Future<Output = R> + 'static, |
| 1810 | 1818 | { |
| 1811 | 1819 | let view = self.view().downgrade(); |
| 1812 | − self.window_cx.spawn(move |_, cx| f(view, cx)) | |
| 1820 | + self.window_cx.spawn(|cx| f(view, cx)) | |
| 1813 | 1821 | } |
| 1814 | 1822 | |
| 1815 | 1823 | pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R |