WIP

Nathan Sobo created

Change summary

crates/gpui/src/app.rs                      | 645 +++++++++++-----------
crates/gpui/src/app/test_app_context.rs     | 103 +--
crates/gpui/src/app/window_input_handler.rs |   5 
crates/gpui/src/presenter.rs                |   7 
4 files changed, 381 insertions(+), 379 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -321,6 +321,17 @@ impl App {
         state.pending_notifications.clear();
         result
     }
+
+    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
+        &mut self,
+        window_id: usize,
+        callback: F,
+    ) -> Option<T> {
+        let mut state = self.0.borrow_mut();
+        let result = state.update_window(window_id, callback);
+        state.pending_notifications.clear();
+        result
+    }
 }
 
 impl AsyncAppContext {
@@ -341,6 +352,14 @@ impl AsyncAppContext {
         self.0.borrow_mut().update(callback)
     }
 
+    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
+        &mut self,
+        window_id: usize,
+        callback: F,
+    ) -> Option<T> {
+        self.0.borrow_mut().update_window(window_id, callback)
+    }
+
     pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
     where
         T: Entity,
@@ -366,7 +385,7 @@ impl AsyncAppContext {
     }
 
     pub fn activate_window(&mut self, window_id: usize) {
-        self.update(|cx| cx.activate_window(window_id))
+        self.update_window(window_id, |cx| cx.activate_window());
     }
 
     pub fn prompt(
@@ -375,8 +394,8 @@ impl AsyncAppContext {
         level: PromptLevel,
         msg: &str,
         answers: &[&str],
-    ) -> oneshot::Receiver<usize> {
-        self.update(|cx| cx.prompt(window_id, level, msg, answers))
+    ) -> Option<oneshot::Receiver<usize>> {
+        self.update_window(window_id, |cx| cx.prompt(level, msg, answers))
     }
 
     pub fn platform(&self) -> Arc<dyn Platform> {
@@ -528,9 +547,6 @@ pub struct AppContext {
     keystroke_observations: CallbackCollection<usize, KeystrokeCallback>,
     active_labeled_task_observations: CallbackCollection<(), ActiveLabeledTasksCallback>,
 
-    #[allow(clippy::type_complexity)]
-    presenters_and_platform_windows:
-        HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
     foreground: Rc<executor::Foreground>,
     pending_effects: VecDeque<Effect>,
     pending_notifications: HashSet<usize>,
@@ -588,7 +604,6 @@ impl AppContext {
             keystroke_observations: Default::default(),
             action_dispatch_observations: Default::default(),
             active_labeled_task_observations: Default::default(),
-            presenters_and_platform_windows: Default::default(),
             foreground,
             pending_effects: VecDeque::new(),
             pending_notifications: Default::default(),
@@ -659,9 +674,7 @@ impl AppContext {
     }
 
     pub fn remove_all_windows(&mut self) {
-        for (window_id, _) in self.windows.drain() {
-            self.presenters_and_platform_windows.remove(&window_id);
-        }
+        self.windows.clear();
         self.flush_effects();
     }
 
@@ -782,14 +795,6 @@ impl AppContext {
         }
     }
 
-    pub fn is_topmost_window_for_position(&self, window_id: usize, position: Vector2F) -> bool {
-        self.presenters_and_platform_windows
-            .get(&window_id)
-            .map_or(false, |(_, window)| {
-                window.is_topmost_for_position(position)
-            })
-    }
-
     pub fn has_window(&self, window_id: usize) -> bool {
         self.window_ids()
             .find(|window| window == &window_id)
@@ -800,12 +805,6 @@ impl AppContext {
         self.windows.keys().copied()
     }
 
-    pub fn activate_window(&self, window_id: usize) {
-        if let Some((_, window)) = self.presenters_and_platform_windows.get(&window_id) {
-            window.activate()
-        }
-    }
-
     pub fn window_is_active(&self, window_id: usize) -> bool {
         self.windows
             .get(&window_id)
@@ -818,16 +817,6 @@ impl AppContext {
             .map_or(false, |window| window.is_fullscreen)
     }
 
-    pub fn window_bounds(&self, window_id: usize) -> Option<WindowBounds> {
-        let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
-        Some(window.bounds())
-    }
-
-    pub fn window_display_uuid(&self, window_id: usize) -> Option<Uuid> {
-        let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
-        window.screen().display_uuid()
-    }
-
     pub fn root_view(&self, window_id: usize) -> Option<AnyViewHandle> {
         self.windows
             .get(&window_id)
@@ -891,12 +880,6 @@ impl AppContext {
         }
     }
 
-    pub fn debug_elements(&self, window_id: usize) -> Option<crate::json::Value> {
-        self.presenters_and_platform_windows
-            .get(&window_id)
-            .and_then(|(presenter, _)| presenter.borrow().debug_elements(self))
-    }
-
     pub fn active_labeled_tasks<'a>(
         &'a self,
     ) -> impl DoubleEndedIterator<Item = &'static str> + 'a {
@@ -966,35 +949,23 @@ impl AppContext {
         result
     }
 
-    fn show_character_palette(&self, window_id: usize) {
-        let (_, window) = &self.presenters_and_platform_windows[&window_id];
-        window.show_character_palette();
-    }
-
-    pub fn minimize_window(&self, window_id: usize) {
-        let (_, window) = &self.presenters_and_platform_windows[&window_id];
-        window.minimize();
-    }
-
-    pub fn zoom_window(&self, window_id: usize) {
-        let (_, window) = &self.presenters_and_platform_windows[&window_id];
-        window.zoom();
-    }
-
-    pub fn toggle_window_full_screen(&self, window_id: usize) {
-        let (_, window) = &self.presenters_and_platform_windows[&window_id];
-        window.toggle_full_screen();
-    }
-
-    pub fn prompt(
-        &self,
+    pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
+        &mut self,
         window_id: usize,
-        level: PromptLevel,
-        msg: &str,
-        answers: &[&str],
-    ) -> oneshot::Receiver<usize> {
-        let (_, window) = &self.presenters_and_platform_windows[&window_id];
-        window.prompt(level, msg, answers)
+        callback: F,
+    ) -> Option<T> {
+        self.update(|app_context| {
+            let mut window = app_context.windows.remove(&window_id)?;
+            let mut window_context = WindowContext {
+                app_context,
+                window: &mut window,
+                window_id,
+            };
+
+            let result = callback(&mut window_context);
+            app_context.windows.insert(window_id, window);
+            Some(result)
+        })
     }
 
     pub fn prompt_for_paths(
@@ -1560,54 +1531,6 @@ impl AppContext {
         false
     }
 
-    pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool {
-        if let Some(focused_view_id) = self.focused_view_id(window_id) {
-            let dispatch_path = self
-                .ancestors(window_id, focused_view_id)
-                .filter_map(|view_id| {
-                    self.views
-                        .get(&(window_id, view_id))
-                        .map(|view| (view_id, view.keymap_context(self)))
-                })
-                .collect();
-
-            let match_result = self
-                .keystroke_matcher
-                .push_keystroke(keystroke.clone(), dispatch_path);
-            let mut handled_by = None;
-
-            let keystroke_handled = match &match_result {
-                MatchResult::None => false,
-                MatchResult::Pending => true,
-                MatchResult::Matches(matches) => {
-                    for (view_id, action) in matches {
-                        if self.handle_dispatch_action_from_effect(
-                            window_id,
-                            Some(*view_id),
-                            action.as_ref(),
-                        ) {
-                            self.keystroke_matcher.clear_pending();
-                            handled_by = Some(action.boxed_clone());
-                            break;
-                        }
-                    }
-                    handled_by.is_some()
-                }
-            };
-
-            self.keystroke(
-                window_id,
-                keystroke.clone(),
-                handled_by,
-                match_result.clone(),
-            );
-            keystroke_handled
-        } else {
-            self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
-            false
-        }
-    }
-
     pub fn default_global<T: 'static + Default>(&mut self) -> &T {
         let type_id = TypeId::of::<T>();
         self.update(|this| {
@@ -1696,6 +1619,16 @@ impl AppContext {
             let root_view = this
                 .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
                 .unwrap();
+            let platform_window =
+                this.platform
+                    .open_window(window_id, window_options, this.foreground.clone());
+            let presenter = self.build_presenter(
+                window_id,
+                platform_window.titlebar_height(),
+                platform_window.appearance(),
+            );
+            this.register_platform_window(window_id, &mut presenter, platform_window.as_mut());
+
             this.windows.insert(
                 window_id,
                 Window {
@@ -1704,15 +1637,12 @@ impl AppContext {
                     is_active: false,
                     invalidation: None,
                     is_fullscreen: false,
+                    platform_window,
+                    presenter,
                 },
             );
             root_view.update(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
 
-            let window =
-                this.platform
-                    .open_window(window_id, window_options, this.foreground.clone());
-            this.register_platform_window(window_id, window);
-
             (window_id, root_view)
         })
     }
@@ -1727,6 +1657,15 @@ impl AppContext {
             let root_view = this
                 .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
                 .unwrap();
+
+            let mut platform_window = this.platform.add_status_item();
+            let mut presenter = self.build_presenter(
+                window_id,
+                platform_window.titlebar_height(),
+                platform_window.appearance(),
+            );
+            this.register_platform_window(window_id, &mut presenter, platform_window.as_mut());
+
             let focused_view_id = root_view.id();
             this.windows.insert(
                 window_id,
@@ -1736,13 +1675,12 @@ impl AppContext {
                     is_active: false,
                     invalidation: None,
                     is_fullscreen: false,
+                    platform_window,
+                    presenter,
                 },
             );
             root_view.update(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
 
-            let status_item = this.platform.add_status_item();
-            this.register_platform_window(window_id, status_item);
-
             (window_id, root_view)
         })
     }
@@ -1754,89 +1692,79 @@ impl AppContext {
     fn register_platform_window(
         &mut self,
         window_id: usize,
-        mut window: Box<dyn platform::Window>,
+        presenter: &mut Presenter,
+        platform_window: &mut dyn platform::Window,
     ) {
-        let presenter = Rc::new(RefCell::new(self.build_presenter(
-            window_id,
-            window.titlebar_height(),
-            window.appearance(),
-        )));
-
         {
             let mut app = self.upgrade();
-            let presenter = Rc::downgrade(&presenter);
 
-            window.on_event(Box::new(move |event| {
-                app.update(|cx| {
-                    if let Some(presenter) = presenter.upgrade() {
-                        if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
-                            if cx.dispatch_keystroke(window_id, keystroke) {
-                                return true;
-                            }
+            platform_window.on_event(Box::new(move |event| {
+                app.update_window(window_id, |cx| {
+                    if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
+                        if cx.dispatch_keystroke(keystroke) {
+                            return true;
                         }
-
-                        presenter.borrow_mut().dispatch_event(event, false, cx)
-                    } else {
-                        false
                     }
+
+                    cx.dispatch_event(event, false)
                 })
+                .unwrap_or(false)
             }));
         }
 
         {
             let mut app = self.upgrade();
-            window.on_active_status_change(Box::new(move |is_active| {
+            platform_window.on_active_status_change(Box::new(move |is_active| {
                 app.update(|cx| cx.window_changed_active_status(window_id, is_active))
             }));
         }
 
         {
             let mut app = self.upgrade();
-            window.on_resize(Box::new(move || {
+            platform_window.on_resize(Box::new(move || {
                 app.update(|cx| cx.window_was_resized(window_id))
             }));
         }
 
         {
             let mut app = self.upgrade();
-            window.on_moved(Box::new(move || {
+            platform_window.on_moved(Box::new(move || {
                 app.update(|cx| cx.window_was_moved(window_id))
             }));
         }
 
         {
             let mut app = self.upgrade();
-            window.on_fullscreen(Box::new(move |is_fullscreen| {
+            platform_window.on_fullscreen(Box::new(move |is_fullscreen| {
                 app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen))
             }));
         }
 
         {
             let mut app = self.upgrade();
-            window.on_close(Box::new(move || {
+            platform_window.on_close(Box::new(move || {
                 app.update(|cx| cx.remove_window(window_id));
             }));
         }
 
         {
             let mut app = self.upgrade();
-            window.on_appearance_changed(Box::new(move || app.update(|cx| cx.refresh_windows())));
+            platform_window
+                .on_appearance_changed(Box::new(move || app.update(|cx| cx.refresh_windows())));
         }
 
-        window.set_input_handler(Box::new(WindowInputHandler {
+        platform_window.set_input_handler(Box::new(WindowInputHandler {
             app: self.upgrade().0,
             window_id,
         }));
 
-        let scene = presenter.borrow_mut().build_scene(
-            window.content_size(),
-            window.scale_factor(),
+        let scene = presenter.build_scene(
+            platform_window.content_size(),
+            platform_window.scale_factor(),
             false,
             self,
         );
-        window.present_scene(scene);
-        self.presenters_and_platform_windows
-            .insert(window_id, (presenter.clone(), window));
+        platform_window.present_scene(scene);
     }
 
     pub fn replace_root_view<T, F>(&mut self, window_id: usize, build_root_view: F) -> ViewHandle<T>
@@ -1857,7 +1785,6 @@ impl AppContext {
 
     pub fn remove_window(&mut self, window_id: usize) {
         self.windows.remove(&window_id);
-        self.presenters_and_platform_windows.remove(&window_id);
         self.flush_effects();
     }
 
@@ -2217,30 +2144,20 @@ impl AppContext {
     }
 
     fn update_windows(&mut self) {
-        let mut invalidations: HashMap<_, _> = Default::default();
         for (window_id, window) in &mut self.windows {
-            if let Some(invalidation) = window.invalidation.take() {
-                invalidations.insert(*window_id, invalidation);
-            }
-        }
-
-        for (window_id, mut invalidation) in invalidations {
-            if let Some((presenter, mut window)) =
-                self.presenters_and_platform_windows.remove(&window_id)
-            {
-                {
-                    let mut presenter = presenter.borrow_mut();
-                    presenter.invalidate(&mut invalidation, window.appearance(), self);
-                    let scene = presenter.build_scene(
-                        window.content_size(),
-                        window.scale_factor(),
-                        false,
-                        self,
-                    );
-                    window.present_scene(scene);
-                }
-                self.presenters_and_platform_windows
-                    .insert(window_id, (presenter, window));
+            if let Some(mut invalidation) = window.invalidation.take() {
+                window.presenter.invalidate(
+                    &mut invalidation,
+                    window.platform_window.appearance(),
+                    self,
+                );
+                let scene = window.presenter.build_scene(
+                    window.platform_window.content_size(),
+                    window.platform_window.scale_factor(),
+                    false,
+                    self,
+                );
+                window.platform_window.present_scene(scene);
             }
         }
     }
@@ -2306,20 +2223,21 @@ impl AppContext {
     }
 
     fn perform_window_refresh(&mut self) {
-        let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
-        for (window_id, (presenter, window)) in &mut presenters {
-            let mut invalidation = self.windows.get_mut(window_id).unwrap().invalidation.take();
-            let mut presenter = presenter.borrow_mut();
-            presenter.refresh(
-                invalidation.as_mut().unwrap_or(&mut Default::default()),
-                window.appearance(),
+        for window in self.windows.values_mut() {
+            let mut invalidation = window.invalidation.take().unwrap_or_default();
+            window.presenter.invalidate(
+                &mut invalidation,
+                window.platform_window.appearance(),
+                self,
+            );
+            let scene = window.presenter.build_scene(
+                window.platform_window.content_size(),
+                window.platform_window.scale_factor(),
+                true,
                 self,
             );
-            let scene =
-                presenter.build_scene(window.content_size(), window.scale_factor(), true, self);
-            window.present_scene(scene);
+            window.platform_window.present_scene(scene);
         }
-        self.presenters_and_platform_windows = presenters;
     }
 
     fn emit_global_event(&mut self, payload: Box<dyn Any>) {
@@ -2365,33 +2283,19 @@ impl AppContext {
     }
 
     fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) {
-        //Short circuit evaluation if we're already g2g
-        if self
-            .windows
-            .get(&window_id)
-            .map(|w| w.is_fullscreen == is_fullscreen)
-            .unwrap_or(false)
-        {
-            return;
-        }
+        self.update_window(window_id, |cx| {
+            cx.window.is_fullscreen = is_fullscreen;
 
-        self.update(|this| {
-            let window = this.windows.get_mut(&window_id)?;
-            window.is_fullscreen = is_fullscreen;
-
-            let mut fullscreen_observations = this.window_fullscreen_observations.clone();
-            fullscreen_observations.emit(window_id, this, |callback, this| {
+            let mut fullscreen_observations = cx.window_fullscreen_observations.clone();
+            fullscreen_observations.emit(window_id, cx, |callback, this| {
                 callback(is_fullscreen, this)
             });
 
-            if let Some((uuid, bounds)) = this
-                .window_display_uuid(window_id)
-                .zip(this.window_bounds(window_id))
-            {
-                let mut bounds_observations = this.window_bounds_observations.clone();
-                bounds_observations.emit(window_id, this, |callback, this| {
-                    callback(bounds, uuid, this)
-                });
+            if let Some(uuid) = cx.window_display_uuid() {
+                let bounds = cx.window_bounds();
+                let mut bounds_observations = cx.window_bounds_observations.clone();
+                bounds_observations
+                    .emit(window_id, cx, |callback, this| callback(bounds, uuid, this));
             }
 
             Some(())
@@ -2563,23 +2467,27 @@ impl AppContext {
         mut callback: WindowShouldCloseSubscriptionCallback,
     ) {
         let mut app = self.upgrade();
-        if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
-            window.on_should_close(Box::new(move || app.update(|cx| callback(cx))))
+        if let Some(window) = self.windows.get_mut(&window_id) {
+            window
+                .platform_window
+                .on_should_close(Box::new(move || app.update(|cx| callback(cx))))
         }
     }
 
     fn handle_window_moved(&mut self, window_id: usize) {
-        if let Some((display, bounds)) = self
-            .window_display_uuid(window_id)
-            .zip(self.window_bounds(window_id))
-        {
-            self.window_bounds_observations
-                .clone()
-                .emit(window_id, self, move |callback, this| {
-                    callback(bounds, display, this);
-                    true
-                });
-        }
+        self.update_window(window_id, |cx| {
+            if let Some(display) = cx.window_display_uuid() {
+                let bounds = cx.window_bounds();
+                cx.window_bounds_observations.clone().emit(
+                    window_id,
+                    self,
+                    move |callback, this| {
+                        callback(bounds, display, this);
+                        true
+                    },
+                );
+            }
+        });
     }
 
     fn handle_active_labeled_tasks_changed_effect(&mut self) {
@@ -2806,6 +2714,8 @@ pub struct Window {
     is_active: bool,
     is_fullscreen: bool,
     invalidation: Option<WindowInvalidation>,
+    presenter: Presenter,
+    platform_window: Box<dyn platform::Window>,
 }
 
 #[derive(Default, Clone)]
@@ -3584,6 +3494,138 @@ impl<M> DerefMut for ModelContext<'_, M> {
     }
 }
 
+pub struct WindowContext<'a: 'b, 'b> {
+    app_context: &'a mut AppContext,
+    window: &'b mut Window,
+    window_id: usize,
+}
+
+impl Deref for WindowContext<'_, '_> {
+    type Target = AppContext;
+
+    fn deref(&self) -> &Self::Target {
+        self.app_context
+    }
+}
+
+impl DerefMut for WindowContext<'_, '_> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.app_context
+    }
+}
+
+impl WindowContext<'_, '_> {
+    pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
+        let window_id = self.window_id;
+        if let Some(focused_view_id) = self.focused_view_id(window_id) {
+            let dispatch_path = self
+                .ancestors(window_id, focused_view_id)
+                .filter_map(|view_id| {
+                    self.views
+                        .get(&(window_id, view_id))
+                        .map(|view| (view_id, view.keymap_context(self)))
+                })
+                .collect();
+
+            let match_result = self
+                .keystroke_matcher
+                .push_keystroke(keystroke.clone(), dispatch_path);
+            let mut handled_by = None;
+
+            let keystroke_handled = match &match_result {
+                MatchResult::None => false,
+                MatchResult::Pending => true,
+                MatchResult::Matches(matches) => {
+                    for (view_id, action) in matches {
+                        if self.handle_dispatch_action_from_effect(
+                            window_id,
+                            Some(*view_id),
+                            action.as_ref(),
+                        ) {
+                            self.keystroke_matcher.clear_pending();
+                            handled_by = Some(action.boxed_clone());
+                            break;
+                        }
+                    }
+                    handled_by.is_some()
+                }
+            };
+
+            self.keystroke(
+                window_id,
+                keystroke.clone(),
+                handled_by,
+                match_result.clone(),
+            );
+            keystroke_handled
+        } else {
+            self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
+            false
+        }
+    }
+
+    pub fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
+        self.window
+            .presenter
+            .dispatch_event(event, event_reused, self)
+    }
+
+    pub fn set_window_title(&mut self, title: &str) {
+        self.window.platform_window.set_title(title);
+    }
+
+    pub fn set_window_edited(&mut self, edited: bool) {
+        self.window.platform_window.set_edited(edited);
+    }
+
+    pub fn is_topmost_window_for_position(&self, position: Vector2F) -> bool {
+        self.window
+            .platform_window
+            .is_topmost_for_position(position)
+    }
+
+    pub fn activate_window(&self) {
+        self.window.platform_window.activate();
+    }
+
+    pub fn window_bounds(&self) -> WindowBounds {
+        self.window.platform_window.bounds()
+    }
+
+    pub fn window_display_uuid(&self) -> Option<Uuid> {
+        self.window.platform_window.screen().display_uuid()
+    }
+
+    pub fn debug_elements(&self) -> Option<crate::json::Value> {
+        self.window.presenter.debug_elements(self)
+    }
+
+    fn show_character_palette(&self) {
+        self.window.platform_window.show_character_palette();
+    }
+
+    pub fn minimize_window(&self) {
+        self.window.platform_window.minimize();
+    }
+
+    pub fn zoom_window(&self) {
+        self.window.platform_window.zoom();
+    }
+
+    pub fn toggle_window_full_screen(&self) {
+        self.window.platform_window.toggle_full_screen();
+    }
+
+    pub fn prompt(
+        &self,
+        level: PromptLevel,
+        msg: &str,
+        answers: &[&str],
+    ) -> oneshot::Receiver<usize> {
+        self.window.platform_window.prompt(level, msg, answers)
+    }
+}
+
 pub struct ViewContext<'a, T: ?Sized> {
     app: &'a mut AppContext,
     window_id: usize,
@@ -3633,31 +3675,6 @@ impl<'a, T: View> ViewContext<'a, T> {
         self.app.platform()
     }
 
-    pub fn show_character_palette(&self) {
-        self.app.show_character_palette(self.window_id);
-    }
-
-    pub fn minimize_window(&self) {
-        self.app.minimize_window(self.window_id)
-    }
-
-    pub fn zoom_window(&self) {
-        self.app.zoom_window(self.window_id)
-    }
-
-    pub fn toggle_full_screen(&self) {
-        self.app.toggle_window_full_screen(self.window_id)
-    }
-
-    pub fn prompt(
-        &self,
-        level: PromptLevel,
-        msg: &str,
-        answers: &[&str],
-    ) -> oneshot::Receiver<usize> {
-        self.app.prompt(self.window_id, level, msg, answers)
-    }
-
     pub fn prompt_for_paths(
         &self,
         options: PathPromptOptions,
@@ -3673,10 +3690,6 @@ impl<'a, T: View> ViewContext<'a, T> {
         self.app.reveal_path(path)
     }
 
-    pub fn debug_elements(&self) -> crate::json::Value {
-        self.app.debug_elements(self.window_id).unwrap()
-    }
-
     pub fn focus(&mut self, handle: &AnyViewHandle) {
         self.app.focus(handle.window_id, Some(handle.view_id));
     }
@@ -3703,20 +3716,6 @@ impl<'a, T: View> ViewContext<'a, T> {
         self.app.focus(self.window_id, None);
     }
 
-    pub fn set_window_title(&mut self, title: &str) {
-        let window_id = self.window_id();
-        if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
-            window.set_title(title);
-        }
-    }
-
-    pub fn set_window_edited(&mut self, edited: bool) {
-        let window_id = self.window_id();
-        if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
-            window.set_edited(edited);
-        }
-    }
-
     pub fn on_window_should_close<F>(&mut self, mut callback: F)
     where
         F: 'static + FnMut(&mut T, &mut ViewContext<T>) -> bool,
@@ -5492,19 +5491,20 @@ mod tests {
         let (window_id, _) = cx.add_window(Default::default(), |_| View {
             mouse_down_count: mouse_down_count.clone(),
         });
-        let presenter = cx.presenters_and_platform_windows[&window_id].0.clone();
-        // Ensure window's root element is in a valid lifecycle state.
-        presenter.borrow_mut().dispatch_event(
-            Event::MouseDown(MouseButtonEvent {
-                position: Default::default(),
-                button: MouseButton::Left,
-                modifiers: Default::default(),
-                click_count: 1,
-            }),
-            false,
-            cx,
-        );
-        assert_eq!(mouse_down_count.load(SeqCst), 1);
+
+        cx.update_window(window_id, |cx| {
+            // Ensure window's root element is in a valid lifecycle state.
+            cx.dispatch_event(
+                Event::MouseDown(MouseButtonEvent {
+                    position: Default::default(),
+                    button: MouseButton::Left,
+                    modifiers: Default::default(),
+                    click_count: 1,
+                }),
+                false,
+            );
+            assert_eq!(mouse_down_count.load(SeqCst), 1);
+        });
     }
 
     #[crate::test(self)]
@@ -6553,19 +6553,28 @@ mod tests {
             }
         });
 
-        cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap());
+        cx.update_window(window_id, |cx| {
+            cx.dispatch_keystroke(&Keystroke::parse("a").unwrap())
+        });
         assert_eq!(&*actions.borrow(), &["2 a"]);
         actions.borrow_mut().clear();
 
-        cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap());
+        cx.update_window(window_id, |cx| {
+            cx.dispatch_keystroke(&Keystroke::parse("b").unwrap());
+        });
+
         assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
         actions.borrow_mut().clear();
 
-        cx.dispatch_keystroke(window_id, &Keystroke::parse("c").unwrap());
+        cx.update_window(window_id, |cx| {
+            cx.dispatch_keystroke(&Keystroke::parse("c").unwrap());
+        });
         assert_eq!(&*actions.borrow(), &["3 c"]);
         actions.borrow_mut().clear();
 
-        cx.dispatch_keystroke(window_id, &Keystroke::parse("d").unwrap());
+        cx.update_window(window_id, |cx| {
+            cx.dispatch_keystroke(&Keystroke::parse("d").unwrap());
+        });
         assert_eq!(&*actions.borrow(), &["2 d"]);
         actions.borrow_mut().clear();
     }
@@ -6836,46 +6845,54 @@ mod tests {
         }
 
         let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0));
-        let presenter = cx.presenters_and_platform_windows[&window_id].0.clone();
-
-        assert_eq!(
-            presenter.borrow().rendered_views[&root_view.id()].name(),
-            Some("render count: 0")
-        );
+        cx.update_window(window_id, |cx| {
+            assert_eq!(
+                cx.window.presenter.rendered_views[&root_view.id()].name(),
+                Some("render count: 0")
+            );
+        });
 
         let view = cx.add_view(&root_view, |cx| {
             cx.refresh_windows();
             View(0)
         });
 
-        assert_eq!(
-            presenter.borrow().rendered_views[&root_view.id()].name(),
-            Some("render count: 1")
-        );
-        assert_eq!(
-            presenter.borrow().rendered_views[&view.id()].name(),
-            Some("render count: 0")
-        );
+        cx.update_window(window_id, |cx| {
+            assert_eq!(
+                cx.window.presenter.rendered_views[&root_view.id()].name(),
+                Some("render count: 1")
+            );
+            assert_eq!(
+                cx.window.presenter.rendered_views[&view.id()].name(),
+                Some("render count: 0")
+            );
+        });
 
         cx.update(|cx| cx.refresh_windows());
-        assert_eq!(
-            presenter.borrow().rendered_views[&root_view.id()].name(),
-            Some("render count: 2")
-        );
-        assert_eq!(
-            presenter.borrow().rendered_views[&view.id()].name(),
-            Some("render count: 1")
-        );
+
+        cx.update_window(window_id, |cx| {
+            assert_eq!(
+                cx.window.presenter.rendered_views[&root_view.id()].name(),
+                Some("render count: 2")
+            );
+            assert_eq!(
+                cx.window.presenter.rendered_views[&view.id()].name(),
+                Some("render count: 1")
+            );
+        });
 
         cx.update(|cx| {
             cx.refresh_windows();
             drop(view);
         });
-        assert_eq!(
-            presenter.borrow().rendered_views[&root_view.id()].name(),
-            Some("render count: 3")
-        );
-        assert_eq!(presenter.borrow().rendered_views.len(), 1);
+
+        cx.update_window(window_id, |cx| {
+            assert_eq!(
+                cx.window.presenter.rendered_views[&root_view.id()].name(),
+                Some("render count: 3")
+            );
+            assert_eq!(cx.window.presenter.rendered_views.len(), 1);
+        });
     }
 
     #[crate::test(self)]

crates/gpui/src/app/test_app_context.rs 🔗

@@ -84,31 +84,28 @@ impl TestAppContext {
     }
 
     pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) {
-        let handled = self.cx.borrow_mut().update(|cx| {
-            let presenter = cx
-                .presenters_and_platform_windows
-                .get(&window_id)
-                .unwrap()
-                .0
-                .clone();
-
-            if cx.dispatch_keystroke(window_id, &keystroke) {
-                return true;
-            }
+        let handled = self
+            .cx
+            .borrow_mut()
+            .update_window(window_id, |cx| {
+                if cx.dispatch_keystroke(&keystroke) {
+                    return true;
+                }
 
-            if presenter.borrow_mut().dispatch_event(
-                Event::KeyDown(KeyDownEvent {
-                    keystroke: keystroke.clone(),
-                    is_held,
-                }),
-                false,
-                cx,
-            ) {
-                return true;
-            }
+                if cx.window.presenter.dispatch_event(
+                    Event::KeyDown(KeyDownEvent {
+                        keystroke: keystroke.clone(),
+                        is_held,
+                    }),
+                    false,
+                    cx,
+                ) {
+                    return true;
+                }
 
-            false
-        });
+                false
+            })
+            .unwrap_or(false);
 
         if !handled && !keystroke.cmd && !keystroke.ctrl {
             WindowInputHandler {
@@ -244,7 +241,7 @@ impl TestAppContext {
         use postage::prelude::Sink as _;
 
         let mut done_tx = self
-            .window_mut(window_id)
+            .platform_window_mut(window_id)
             .pending_prompts
             .borrow_mut()
             .pop_front()
@@ -253,20 +250,23 @@ impl TestAppContext {
     }
 
     pub fn has_pending_prompt(&self, window_id: usize) -> bool {
-        let window = self.window_mut(window_id);
+        let window = self.platform_window_mut(window_id);
         let prompts = window.pending_prompts.borrow_mut();
         !prompts.is_empty()
     }
 
     pub fn current_window_title(&self, window_id: usize) -> Option<String> {
-        self.window_mut(window_id).title.clone()
+        self.platform_window_mut(window_id).title.clone()
     }
 
     pub fn simulate_window_close(&self, window_id: usize) -> bool {
-        let handler = self.window_mut(window_id).should_close_handler.take();
+        let handler = self
+            .platform_window_mut(window_id)
+            .should_close_handler
+            .take();
         if let Some(mut handler) = handler {
             let should_close = handler();
-            self.window_mut(window_id).should_close_handler = Some(handler);
+            self.platform_window_mut(window_id).should_close_handler = Some(handler);
             should_close
         } else {
             false
@@ -274,47 +274,34 @@ impl TestAppContext {
     }
 
     pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) {
-        let mut window = self.window_mut(window_id);
+        let mut window = self.platform_window_mut(window_id);
         window.size = size;
         let mut handlers = mem::take(&mut window.resize_handlers);
         drop(window);
         for handler in &mut handlers {
             handler();
         }
-        self.window_mut(window_id).resize_handlers = handlers;
+        self.platform_window_mut(window_id).resize_handlers = handlers;
     }
 
     pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
-        let mut handlers = BTreeMap::new();
-        {
-            let mut cx = self.cx.borrow_mut();
-            for (window_id, (_, window)) in &mut cx.presenters_and_platform_windows {
-                let window = window
-                    .as_any_mut()
-                    .downcast_mut::<platform::test::Window>()
-                    .unwrap();
-                handlers.insert(
-                    *window_id,
-                    mem::take(&mut window.active_status_change_handlers),
-                );
+        self.cx.borrow_mut().update(|cx| {
+            for window_id in cx
+                .windows
+                .keys()
+                .filter(|window_id| Some(**window_id) != to_activate)
+            {
+                cx.window_changed_active_status(*window_id, false)
             }
-        };
-        let mut handlers = handlers.into_iter().collect::<Vec<_>>();
-        handlers.sort_unstable_by_key(|(window_id, _)| Some(*window_id) == to_activate);
 
-        for (window_id, mut window_handlers) in handlers {
-            for window_handler in &mut window_handlers {
-                window_handler(Some(window_id) == to_activate);
+            if let Some(to_activate) = to_activate {
+                cx.window_changed_active_status(to_activate, true)
             }
-
-            self.window_mut(window_id)
-                .active_status_change_handlers
-                .extend(window_handlers);
-        }
+        });
     }
 
     pub fn is_window_edited(&self, window_id: usize) -> bool {
-        self.window_mut(window_id).edited
+        self.platform_window_mut(window_id).edited
     }
 
     pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
@@ -337,13 +324,11 @@ impl TestAppContext {
         self.assert_dropped(weak);
     }
 
-    fn window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
+    fn platform_window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
         std::cell::RefMut::map(self.cx.borrow_mut(), |state| {
-            let (_, window) = state
-                .presenters_and_platform_windows
-                .get_mut(&window_id)
-                .unwrap();
+            let window = state.windows.get_mut(&window_id).unwrap();
             let test_window = window
+                .platform_window
                 .as_any_mut()
                 .downcast_mut::<platform::test::Window>()
                 .unwrap();

crates/gpui/src/app/window_input_handler.rs 🔗

@@ -91,8 +91,7 @@ impl InputHandler for WindowInputHandler {
 
     fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
         let app = self.app.borrow();
-        let (presenter, _) = app.presenters_and_platform_windows.get(&self.window_id)?;
-        let presenter = presenter.borrow();
-        presenter.rect_for_text_range(range_utf16, &app)
+        let window = app.windows.get(&self.window_id)?;
+        window.presenter.rect_for_text_range(range_utf16, &app)
     }
 }

crates/gpui/src/presenter.rs 🔗

@@ -315,9 +315,10 @@ impl Presenter {
                     }
                 }
 
-                if cx.is_topmost_window_for_position(self.window_id, *position) {
-                    cx.platform().set_cursor_style(style_to_assign);
-                }
+                // TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+                // if cx.is_topmost_window_for_position(self.window_id, *position) {
+                //     cx.platform().set_cursor_style(style_to_assign);
+                // }
 
                 if !event_reused {
                     if pressed_button.is_some() {