Merge remote-tracking branch 'origin/main' into zed2-workspace

Antonio Scandurra created

Change summary

crates/gpui2/src/app.rs                 | 77 +++++++++++---------------
crates/gpui2/src/app/async_context.rs   | 16 +++--
crates/gpui2/src/app/test_context.rs    |  9 ---
crates/gpui2/src/platform.rs            | 15 ----
crates/gpui2/src/platform/mac/window.rs | 17 ++---
crates/gpui2/src/view.rs                |  4 +
crates/gpui2/src/window.rs              | 44 +++++++++------
7 files changed, 81 insertions(+), 101 deletions(-)

Detailed changes

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<R>(
-        &mut self,
-        handle: AnyWindowHandle,
-        update: impl FnOnce(AnyView, &mut WindowContext) -> R,
-    ) -> Result<R> {
-        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<V: Render>(
         &mut self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> View<V> + 'static,
+        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
     ) -> WindowHandle<V> {
         self.update(|cx| {
             let id = cx.windows.insert(None);
@@ -306,47 +281,63 @@ impl AppContext {
         })
     }
 
-    pub(crate) fn platform(&self) -> &Rc<dyn Platform> {
-        &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<Rc<dyn PlatformDisplay>> {
+        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<ClipboardItem> {
-        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<Option<(String, Vec<u8>)>> {
-        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<PathBuf> {
-        self.platform().path_for_auxiliary_executable(name)
+        self.platform.path_for_auxiliary_executable(name)
+    }
+
+    pub fn prompt_for_paths(
+        &self,
+        options: PathPromptOptions,
+    ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
+        self.platform.prompt_for_paths(options)
+    }
+
+    pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
+        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) {

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<R>(
+    pub fn open_window<V>(
         &self,
-        handle: AnyWindowHandle,
-        update: impl FnOnce(AnyView, &mut WindowContext) -> R,
-    ) -> Result<R> {
+        options: crate::WindowOptions,
+        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
+    ) -> Result<WindowHandle<V>>
+    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<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>

crates/gpui2/src/app/test_context.rs 🔗

@@ -87,15 +87,6 @@ impl TestAppContext {
         cx.update(f)
     }
 
-    pub fn update_window<R>(
-        &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<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
     where
         Fut: Future<Output = R> + 'static,

crates/gpui2/src/platform.rs 🔗

@@ -138,12 +138,7 @@ pub(crate) trait PlatformWindow {
     fn mouse_position(&self) -> Point<Pixels>;
     fn as_any_mut(&mut self) -> &mut dyn Any;
     fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
-    fn prompt(
-        &self,
-        level: WindowPromptLevel,
-        msg: &str,
-        answers: &[&str],
-    ) -> oneshot::Receiver<usize>;
+    fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
     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,

crates/gpui2/src/platform/mac/window.rs 🔗

@@ -3,8 +3,8 @@ use crate::{
     display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
     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,
+    Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
+    PromptLevel, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
 };
 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<usize> {
+    fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> {
         // 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)];

crates/gpui2/src/view.rs 🔗

@@ -98,6 +98,10 @@ pub struct WeakView<V> {
 }
 
 impl<V: 'static> WeakView<V> {
+    pub fn entity_id(&self) -> EntityId {
+        self.model.entity_id
+    }
+
     pub fn upgrade(&self) -> Option<View<V>> {
         Entity::upgrade_from(self)
     }

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<Fut, R>(
-        &mut self,
-        f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut,
-    ) -> Task<R>
+    pub fn spawn<Fut, R>(&mut self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
     where
         R: 'static,
         Fut: Future<Output = R> + '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<usize> {
+        self.window.platform_window.prompt(level, msg, answers)
+    }
+
     fn dispatch_action(
         &mut self,
         action: Box<dyn Action>,
@@ -1809,7 +1817,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
         Fut: Future<Output = R> + '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<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R