WIP

Nathan Sobo created

Change summary

crates/gpui3/src/app.rs             | 85 ++++++++++++++++++++++++++----
crates/gpui3/src/executor.rs        | 17 +++++
crates/gpui3/src/gpui3.rs           | 20 ------
crates/gpui3/src/window.rs          |  9 ++-
crates/storybook2/src/storybook2.rs |  7 ++
5 files changed, 102 insertions(+), 36 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -8,8 +8,9 @@ pub use model_context::*;
 use refineable::Refineable;
 
 use crate::{
-    current_platform, Context, LayoutId, MainThreadOnly, Platform, RootView, TextStyle,
-    TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
+    current_platform, run_on_main, spawn_on_main, Context, LayoutId, MainThreadOnly, Platform,
+    PlatformDispatcher, Reference, RootView, TextStyle, TextStyleRefinement, TextSystem, Window,
+    WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, VecDeque};
@@ -19,6 +20,7 @@ use slotmap::SlotMap;
 use smallvec::SmallVec;
 use std::{
     any::{type_name, Any, TypeId},
+    ops::{Deref, DerefMut},
     sync::{Arc, Weak},
 };
 use util::ResultExt;
@@ -77,6 +79,7 @@ type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 's
 pub struct AppContext {
     this: Weak<Mutex<AppContext>>,
     platform: MainThreadOnly<dyn Platform>,
+    dispatcher: Arc<dyn PlatformDispatcher>,
     text_system: Arc<TextSystem>,
     pending_updates: usize,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -98,19 +101,36 @@ impl AppContext {
         AsyncContext(self.this.clone())
     }
 
+    pub fn run_on_main<R>(
+        &self,
+        f: impl FnOnce(&mut MainThreadContext) -> R + Send + 'static,
+    ) -> impl Future<Output = R>
+    where
+        R: Send + 'static,
+    {
+        let this = self.this.upgrade().unwrap();
+        run_on_main(self.dispatcher.clone(), move || {
+            let cx = &mut *this.lock();
+            let platform = cx.platform.borrow_on_main_thread().clone();
+            cx.update(|cx| f(&mut MainThreadContext::mutable(cx, platform.as_ref())))
+        })
+    }
+
     pub fn spawn_on_main<F, R>(
         &self,
-        f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
+        f: impl FnOnce(&mut MainThreadContext) -> F + Send + 'static,
     ) -> impl Future<Output = R>
     where
         F: Future<Output = R> + 'static,
         R: Send + 'static,
     {
         let this = self.this.upgrade().unwrap();
-        self.platform.read(move |platform| {
+        spawn_on_main(self.dispatcher.clone(), move || {
             let cx = &mut *this.lock();
-            cx.update(|cx| f(platform, cx))
+            let platform = cx.platform.borrow_on_main_thread().clone();
+            cx.update(|cx| f(&mut MainThreadContext::mutable(cx, platform.as_ref())))
         })
+        // self.platform.read(move |platform| {
     }
 
     pub fn open_window<S: 'static + Send + Sync>(
@@ -120,13 +140,7 @@ impl AppContext {
     ) -> impl Future<Output = WindowHandle<S>> {
         let id = self.windows.insert(None);
         let handle = WindowHandle::new(id);
-        self.spawn_on_main(move |platform, cx| {
-            let mut window = Window::new(handle.into(), options, platform, cx);
-            let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
-            window.root_view.replace(root_view.into_any());
-            cx.windows.get_mut(id).unwrap().replace(window);
-            future::ready(handle)
-        })
+        self.spawn_on_main(move |cx| future::ready(cx.open_window(options, build_root_view)))
     }
 
     pub fn text_style(&self) -> TextStyle {
@@ -275,6 +289,53 @@ impl Context for AppContext {
     }
 }
 
+pub struct MainThreadContext<'a> {
+    app: Reference<'a, AppContext>,
+    platform: &'a dyn Platform,
+}
+
+impl<'a> MainThreadContext<'a> {
+    fn mutable(cx: &'a mut AppContext, platform: &'a dyn Platform) -> Self {
+        Self {
+            app: Reference::Mutable(cx),
+            platform,
+        }
+    }
+
+    pub fn activate(&mut self, ignoring_other_apps: bool) {
+        self.platform.activate(ignoring_other_apps);
+    }
+
+    pub fn open_window<S: 'static + Send + Sync>(
+        &mut self,
+        options: crate::WindowOptions,
+        build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
+    ) -> WindowHandle<S> {
+        let id = self.windows.insert(None);
+        let handle = WindowHandle::new(id);
+        let cx = &mut *self.app;
+        let mut window = Window::new(handle.into(), options, self.platform, cx);
+        let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
+        window.root_view.replace(root_view.into_any());
+        cx.windows.get_mut(id).unwrap().replace(window);
+        handle
+    }
+}
+
+impl<'a> Deref for MainThreadContext<'a> {
+    type Target = AppContext;
+
+    fn deref(&self) -> &Self::Target {
+        &*self.app
+    }
+}
+
+impl<'a> DerefMut for MainThreadContext<'a> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut *self.app
+    }
+}
+
 pub(crate) enum Effect {
     Notify(EntityId),
 }

crates/gpui3/src/executor.rs 🔗

@@ -17,8 +17,21 @@ use std::{
     time::Duration,
 };
 
-/// Enqueues the given closure to be run on the application's event loop. Can be
-/// called on any thread.
+/// Enqueues the given closure to run on the application's event loop.
+/// Returns the result asynchronously.
+pub(crate) fn run_on_main<F, R>(
+    dispatcher: Arc<dyn PlatformDispatcher>,
+    func: F,
+) -> impl Future<Output = R>
+where
+    F: FnOnce() -> R + Send + 'static,
+    R: Send + 'static,
+{
+    spawn_on_main(dispatcher, move || async move { func() })
+}
+
+/// Enqueues the given closure to be run on the application's event loop. The
+/// closure returns a future which will be run to completion on the main thread.
 pub(crate) fn spawn_on_main<F, R>(
     dispatcher: Arc<dyn PlatformDispatcher>,
     func: impl FnOnce() -> F + Send + 'static,

crates/gpui3/src/gpui3.rs 🔗

@@ -31,7 +31,6 @@ pub use serde_json;
 pub use smallvec;
 pub use smol::Timer;
 use std::{
-    future::Future,
     ops::{Deref, DerefMut},
     sync::Arc,
 };
@@ -176,27 +175,10 @@ impl<T: 'static + ?Sized> MainThreadOnly<T> {
         Self { dispatcher, value }
     }
 
-    pub(crate) fn borrow_on_main_thread(&self) -> &T {
+    pub(crate) fn borrow_on_main_thread(&self) -> &Arc<T> {
         assert!(self.dispatcher.is_main_thread());
         &self.value
     }
-
-    pub(crate) fn read<R, F>(
-        &self,
-        f: impl FnOnce(&T) -> F + Send + 'static,
-    ) -> impl Future<Output = R>
-    where
-        F: Future<Output = R> + 'static,
-        R: Send + 'static,
-    {
-        let this = self.clone();
-        crate::spawn_on_main(self.dispatcher.clone(), || async move {
-            // Required so we move `this` instead of this.value. Only `this` is `Send`.
-            let this = this;
-            let result = f(&this.value);
-            result.await
-        })
-    }
 }
 
 unsafe impl<T: ?Sized> Send for MainThreadOnly<T> {}

crates/gpui3/src/window.rs 🔗

@@ -92,9 +92,12 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             cx.window.root_view = Some(root_view);
             let scene = cx.window.scene.take();
             dbg!(&scene);
-            let _ = cx.window.platform_window.read(|platform_window| {
-                platform_window.draw(scene);
-                future::ready(())
+
+            self.run_on_main(|cx| {
+                cx.window
+                    .platform_window
+                    .borrow_on_main_thread()
+                    .draw(scene);
             });
 
             Ok(())

crates/storybook2/src/storybook2.rs 🔗

@@ -1,5 +1,7 @@
 #![allow(dead_code, unused_variables)]
 
+use std::future;
+
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 
@@ -18,6 +20,11 @@ fn main() {
 
     gpui3::App::production().run(|cx| {
         let window = cx.open_window(Default::default(), |cx| workspace(cx));
+
+        cx.spawn_on_main(move |platform, _| {
+            platform.activate(true);
+            future::ready(())
+        });
     });
 }