diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 48a9324b05..5a6e360802 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -16,13 +16,13 @@ use crate::{ current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, - Pixels, Platform, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, - TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, - WindowId, + PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString, + SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, + View, Window, WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; -use futures::{future::LocalBoxFuture, Future}; +use futures::{channel::oneshot, future::LocalBoxFuture, Future}; use parking_lot::Mutex; use slotmap::SlotMap; use std::{ @@ -31,7 +31,7 @@ use std::{ marker::PhantomData, mem, ops::{Deref, DerefMut}, - path::PathBuf, + path::{Path, PathBuf}, rc::{Rc, Weak}, sync::{atomic::Ordering::SeqCst, Arc}, time::Duration, @@ -262,38 +262,13 @@ impl AppContext { .collect() } - pub(crate) fn update_window( - &mut self, - handle: AnyWindowHandle, - update: impl FnOnce(AnyView, &mut WindowContext) -> R, - ) -> Result { - self.update(|cx| { - let mut window = cx - .windows - .get_mut(handle.id) - .ok_or_else(|| anyhow!("window not found"))? - .take() - .unwrap(); - - let root_view = window.root_view.clone().unwrap(); - let result = update(root_view, &mut WindowContext::new(cx, &mut window)); - - cx.windows - .get_mut(handle.id) - .ok_or_else(|| anyhow!("window not found"))? - .replace(window); - - Ok(result) - }) - } - /// Opens a new window with the given option and the root view returned by the given function. /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific /// functionality. pub fn open_window( &mut self, options: crate::WindowOptions, - build_root_view: impl FnOnce(&mut WindowContext) -> View + 'static, + build_root_view: impl FnOnce(&mut WindowContext) -> View, ) -> WindowHandle { self.update(|cx| { let id = cx.windows.insert(None); @@ -306,47 +281,63 @@ impl AppContext { }) } - pub(crate) fn platform(&self) -> &Rc { - &self.platform - } - /// Instructs the platform to activate the application by bringing it to the foreground. pub fn activate(&self, ignoring_other_apps: bool) { - self.platform().activate(ignoring_other_apps); + self.platform.activate(ignoring_other_apps); + } + + /// Returns the list of currently active displays. + pub fn displays(&self) -> Vec> { + self.platform.displays() } /// Writes data to the platform clipboard. pub fn write_to_clipboard(&self, item: ClipboardItem) { - self.platform().write_to_clipboard(item) + self.platform.write_to_clipboard(item) } /// Reads data from the platform clipboard. pub fn read_from_clipboard(&self) -> Option { - self.platform().read_from_clipboard() + self.platform.read_from_clipboard() } /// Writes credentials to the platform keychain. pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> { - self.platform().write_credentials(url, username, password) + self.platform.write_credentials(url, username, password) } /// Reads credentials from the platform keychain. pub fn read_credentials(&self, url: &str) -> Result)>> { - self.platform().read_credentials(url) + self.platform.read_credentials(url) } /// Deletes credentials from the platform keychain. pub fn delete_credentials(&self, url: &str) -> Result<()> { - self.platform().delete_credentials(url) + self.platform.delete_credentials(url) } /// Directs the platform's default browser to open the given URL. pub fn open_url(&self, url: &str) { - self.platform().open_url(url); + self.platform.open_url(url); } pub fn path_for_auxiliary_executable(&self, name: &str) -> Result { - self.platform().path_for_auxiliary_executable(name) + self.platform.path_for_auxiliary_executable(name) + } + + pub fn prompt_for_paths( + &self, + options: PathPromptOptions, + ) -> oneshot::Receiver>> { + self.platform.prompt_for_paths(options) + } + + pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver> { + self.platform.prompt_for_new_path(directory) + } + + pub fn reveal_path(&self, path: &Path) { + self.platform.reveal_path(path) } pub(crate) fn push_effect(&mut self, effect: Effect) { diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index f08c7ed0a9..4bbab43446 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -1,6 +1,7 @@ use crate::{ AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext, + WindowHandle, }; use anyhow::{anyhow, Context as _}; use derive_more::{Deref, DerefMut}; @@ -82,17 +83,20 @@ impl AsyncAppContext { Ok(f(&mut *lock)) } - pub fn update_window( + pub fn open_window( &self, - handle: AnyWindowHandle, - update: impl FnOnce(AnyView, &mut WindowContext) -> R, - ) -> Result { + options: crate::WindowOptions, + build_root_view: impl FnOnce(&mut WindowContext) -> View, + ) -> Result> + where + V: Render, + { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; - let mut app_context = app.borrow_mut(); - app_context.update_window(handle, update) + let mut lock = app.borrow_mut(); + Ok(lock.open_window(options, build_root_view)) } pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task diff --git a/crates/gpui2/src/app/test_context.rs b/crates/gpui2/src/app/test_context.rs index 27275d3a04..aaf42dd4a2 100644 --- a/crates/gpui2/src/app/test_context.rs +++ b/crates/gpui2/src/app/test_context.rs @@ -87,15 +87,6 @@ impl TestAppContext { cx.update(f) } - pub fn update_window( - &self, - handle: AnyWindowHandle, - update: impl FnOnce(AnyView, &mut WindowContext) -> R, - ) -> R { - let mut app = self.app.borrow_mut(); - app.update_window(handle, update).unwrap() - } - pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task where Fut: Future + 'static, diff --git a/crates/gpui2/src/platform.rs b/crates/gpui2/src/platform.rs index 9a6768342f..cdce67d8c1 100644 --- a/crates/gpui2/src/platform.rs +++ b/crates/gpui2/src/platform.rs @@ -138,12 +138,7 @@ pub(crate) trait PlatformWindow { fn mouse_position(&self) -> Point; fn as_any_mut(&mut self) -> &mut dyn Any; fn set_input_handler(&mut self, input_handler: Box); - fn prompt( - &self, - level: WindowPromptLevel, - msg: &str, - answers: &[&str], - ) -> oneshot::Receiver; + fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver; fn activate(&self); fn set_title(&mut self, title: &str); fn set_edited(&mut self, edited: bool); @@ -454,14 +449,6 @@ impl Default for WindowAppearance { } } -#[derive(Copy, Clone, Debug, PartialEq, Default)] -pub enum WindowPromptLevel { - #[default] - Info, - Warning, - Critical, -} - #[derive(Copy, Clone, Debug)] pub struct PathPromptOptions { pub files: bool, diff --git a/crates/gpui2/src/platform/mac/window.rs b/crates/gpui2/src/platform/mac/window.rs index bf62e2e0dc..77675e3c27 100644 --- a/crates/gpui2/src/platform/mac/window.rs +++ b/crates/gpui2/src/platform/mac/window.rs @@ -4,7 +4,7 @@ use crate::{ FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, - Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, + Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, PromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -742,12 +742,7 @@ impl PlatformWindow for MacWindow { self.0.as_ref().lock().input_handler = Some(input_handler); } - fn prompt( - &self, - level: WindowPromptLevel, - msg: &str, - answers: &[&str], - ) -> oneshot::Receiver { + fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver { // macOs applies overrides to modal window buttons after they are added. // Two most important for this logic are: // * Buttons with "Cancel" title will be displayed as the last buttons in the modal @@ -777,9 +772,9 @@ impl PlatformWindow for MacWindow { let alert: id = msg_send![class!(NSAlert), alloc]; let alert: id = msg_send![alert, init]; let alert_style = match level { - WindowPromptLevel::Info => 1, - WindowPromptLevel::Warning => 0, - WindowPromptLevel::Critical => 2, + PromptLevel::Info => 1, + PromptLevel::Warning => 0, + PromptLevel::Critical => 2, }; let _: () = msg_send![alert, setAlertStyle: alert_style]; let _: () = msg_send![alert, setMessageText: ns_string(msg)]; diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index 3cc4fdd4e3..d81df5b21c 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -98,6 +98,10 @@ pub struct WeakView { } impl WeakView { + pub fn entity_id(&self) -> EntityId { + self.model.entity_id + } + pub fn upgrade(&self) -> Option> { Entity::upgrade_from(self) } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 5f2de2e428..0202b7521e 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -4,14 +4,15 @@ use crate::{ Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, - MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, - Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, - Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, - UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, + MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, + PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, + SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, + Underline, UnderlineStyle, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; use collections::HashMap; use derive_more::{Deref, DerefMut}; +use futures::channel::oneshot; use parking_lot::RwLock; use slotmap::SlotMap; use smallvec::SmallVec; @@ -195,7 +196,7 @@ impl Window { options: WindowOptions, cx: &mut AppContext, ) -> Self { - let platform_window = cx.platform().open_window(handle, options); + let platform_window = cx.platform.open_window(handle, options); let display_id = platform_window.display().id(); let sprite_atlas = platform_window.sprite_atlas(); let mouse_position = platform_window.mouse_position(); @@ -419,7 +420,7 @@ impl<'a> WindowContext<'a> { } else { let mut async_cx = self.to_async(); self.next_frame_callbacks.insert(display_id, vec![f]); - self.platform().set_display_link_output_callback( + self.platform.set_display_link_output_callback( display_id, Box::new(move |_current_time, _output_time| { let _ = async_cx.update(|_, cx| { @@ -434,32 +435,26 @@ impl<'a> WindowContext<'a> { } if cx.next_frame_callbacks.get(&display_id).unwrap().is_empty() { - cx.platform().stop_display_link(display_id); + cx.platform.stop_display_link(display_id); } }); }), ); } - self.platform().start_display_link(display_id); + self.platform.start_display_link(display_id); } /// Spawn the future returned by the given closure on the application thread pool. /// The closure is provided a handle to the current window and an `AsyncWindowContext` for /// use within your future. - pub fn spawn( - &mut self, - f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut, - ) -> Task + pub fn spawn(&mut self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task where R: 'static, Fut: Future + 'static, { - let window = self.window.handle; - self.app.spawn(move |app| { - let cx = AsyncWindowContext::new(app, window); - f(window, cx) - }) + self.app + .spawn(|app| f(AsyncWindowContext::new(app, self.window.handle))) } /// Update the global of the given type. The given closure is given simultaneous mutable @@ -1153,6 +1148,19 @@ impl<'a> WindowContext<'a> { ) } + pub fn activate_window(&self) { + self.window.platform_window.activate(); + } + + pub fn prompt( + &self, + level: PromptLevel, + msg: &str, + answers: &[&str], + ) -> oneshot::Receiver { + self.window.platform_window.prompt(level, msg, answers) + } + fn dispatch_action( &mut self, action: Box, @@ -1809,7 +1817,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { Fut: Future + 'static, { let view = self.view().downgrade(); - self.window_cx.spawn(move |_, cx| f(view, cx)) + self.window_cx.spawn(|cx| f(view, cx)) } pub fn update_global(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R