Sketched out program manager API

Mikayla Maki created

Change summary

crates/gpui/src/app.rs       | 21 ++++++++++++++
crates/terminal/src/modal.rs | 56 +++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 1 deletion(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -5083,6 +5083,7 @@ impl Drop for AnyModelHandle {
     }
 }
 
+#[derive(Hash, PartialEq, Eq, Debug)]
 pub struct AnyWeakModelHandle {
     model_id: usize,
     model_type: TypeId,
@@ -5092,6 +5093,26 @@ impl AnyWeakModelHandle {
     pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<AnyModelHandle> {
         cx.upgrade_any_model_handle(self)
     }
+    pub fn model_type(&self) -> TypeId {
+        self.model_type
+    }
+
+    fn is<T: 'static>(&self) -> bool {
+        TypeId::of::<T>() == self.model_type
+    }
+
+    pub fn downcast<T: Entity>(&self) -> Option<WeakModelHandle<T>> {
+        if self.is::<T>() {
+            let result = Some(WeakModelHandle {
+                model_id: self.model_id,
+                model_type: PhantomData,
+            });
+
+            result
+        } else {
+            None
+        }
+    }
 }
 
 impl<T: Entity> From<WeakModelHandle<T>> for AnyWeakModelHandle {

crates/terminal/src/modal.rs 🔗

@@ -1,4 +1,9 @@
-use gpui::{ModelHandle, ViewContext};
+use std::{
+    any::TypeId,
+    collections::{HashMap, HashSet},
+};
+
+use gpui::{AnyWeakModelHandle, Entity, ModelHandle, ViewContext, WeakModelHandle};
 use settings::{Settings, WorkingDirectory};
 use workspace::Workspace;
 
@@ -9,10 +14,59 @@ use crate::{
     Event, Terminal,
 };
 
+// TODO: Need to put this basic structure in workspace, and make 'program handles'
+// based off of the 'searchable item' pattern except with models this way, the workspace's clients
+// can register their models as programs.
+// Programs are:
+//  - Kept alive by the program manager, they need to emit an event to get dropped from it
+//  - Can be interacted with directly, (closed, activated), etc, bypassing associated view(s)
+//  - Have special rendering methods that the program manager offers to fill out the status bar
+//  - Can emit events for the program manager which:
+//    - Add a jewel (notification, change, etc.)
+//    - Drop the program
+//    - ???
+//  - Program Manager is kept in a global, listens for window drop so it can drop all it's program handles
+//  - Start by making up the infrastructure, then just render the first item as the modal terminal in it's spot
+// update),
+
+struct ProgramManager {
+    window_to_programs: HashMap<usize, HashSet<AnyWeakModelHandle>>,
+}
+
+impl ProgramManager {
+    pub fn add_program<T: Entity>(&mut self, window: usize, program: WeakModelHandle<T>) {
+        let mut programs = if let Some(programs) = self.window_to_programs.remove(&window) {
+            programs
+        } else {
+            HashSet::default()
+        };
+
+        programs.insert(AnyWeakModelHandle::from(program));
+        self.window_to_programs.insert(window, programs);
+    }
+
+    pub fn get_programs<T: Entity>(
+        &self,
+        window: &usize,
+    ) -> impl Iterator<Item = WeakModelHandle<T>> + '_ {
+        self.window_to_programs
+            .get(window)
+            .into_iter()
+            .flat_map(|programs| {
+                programs
+                    .iter()
+                    .filter(|program| program.model_type() != TypeId::of::<T>())
+                    .map(|program| program.downcast().unwrap())
+            })
+    }
+}
+
 #[derive(Debug)]
 struct StoredTerminal(ModelHandle<Terminal>);
 
 pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext<Workspace>) {
+    // cx.window_id()
+
     // Pull the terminal connection out of the global if it has been stored
     let possible_terminal =
         cx.update_default_global::<Option<StoredTerminal>, _, _>(|possible_connection, _| {