Polish screen sharing

Antonio Scandurra created

Change summary

crates/call2/src/call2.rs           | 21 +------
crates/call2/src/shared_screen.rs   | 80 +++++++-----------------------
crates/gpui2/src/elements/img.rs    | 18 +++++-
crates/workspace2/src/workspace2.rs | 24 ++++++--
4 files changed, 54 insertions(+), 89 deletions(-)

Detailed changes

crates/call2/src/call2.rs 🔗

@@ -15,7 +15,7 @@ use collections::HashSet;
 use futures::{channel::oneshot, future::Shared, Future, FutureExt};
 use gpui::{
     AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, PromptLevel,
-    Subscription, Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowHandle,
+    Subscription, Task, View, ViewContext, VisualContext, WeakModel, WindowHandle,
 };
 pub use participant::ParticipantLocation;
 use postage::watch;
@@ -557,24 +557,17 @@ pub fn report_call_event_for_channel(
 
 pub struct Call {
     active_call: Option<(Model<ActiveCall>, Vec<Subscription>)>,
-    parent_workspace: WeakView<Workspace>,
 }
 
 impl Call {
-    pub fn new(
-        parent_workspace: WeakView<Workspace>,
-        cx: &mut ViewContext<'_, Workspace>,
-    ) -> Box<dyn CallHandler> {
+    pub fn new(cx: &mut ViewContext<'_, Workspace>) -> Box<dyn CallHandler> {
         let mut active_call = None;
         if cx.has_global::<Model<ActiveCall>>() {
             let call = cx.global::<Model<ActiveCall>>().clone();
             let subscriptions = vec![cx.subscribe(&call, Self::on_active_call_event)];
             active_call = Some((call, subscriptions));
         }
-        Box::new(Self {
-            active_call,
-            parent_workspace,
-        })
+        Box::new(Self { active_call })
     }
     fn on_active_call_event(
         workspace: &mut Workspace,
@@ -597,6 +590,7 @@ impl CallHandler for Call {
     fn peer_state(
         &mut self,
         leader_id: PeerId,
+        project: &Model<Project>,
         cx: &mut ViewContext<Workspace>,
     ) -> Option<(bool, bool)> {
         let (call, _) = self.active_call.as_ref()?;
@@ -608,12 +602,7 @@ impl CallHandler for Call {
         match participant.location {
             ParticipantLocation::SharedProject { project_id } => {
                 leader_in_this_app = true;
-                leader_in_this_project = Some(project_id)
-                    == self
-                        .parent_workspace
-                        .update(cx, |this, cx| this.project().read(cx).remote_id())
-                        .log_err()
-                        .flatten();
+                leader_in_this_project = Some(project_id) == project.read(cx).remote_id();
             }
             ParticipantLocation::UnsharedProject => {
                 leader_in_this_app = true;

crates/call2/src/shared_screen.rs 🔗

@@ -3,10 +3,12 @@ use anyhow::Result;
 use client::{proto::PeerId, User};
 use futures::StreamExt;
 use gpui::{
-    div, img, AppContext, Div, Element, EventEmitter, FocusHandle, FocusableView, ParentElement,
-    Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WindowContext,
+    div, img, AppContext, Div, Element, EventEmitter, FocusHandle, Focusable, FocusableView,
+    InteractiveElement, ParentElement, Render, SharedString, Styled, Task, View, ViewContext,
+    VisualContext, WindowContext,
 };
 use std::sync::{Arc, Weak};
+use ui::{h_stack, Icon, IconElement};
 use workspace::{item::Item, ItemNavHistory, WorkspaceId};
 
 pub enum Event {
@@ -16,8 +18,6 @@ pub enum Event {
 pub struct SharedScreen {
     track: Weak<RemoteVideoTrack>,
     frame: Option<Frame>,
-    // temporary addition just to render something interactive.
-    current_frame_id: usize,
     pub peer_id: PeerId,
     user: Arc<User>,
     nav_history: Option<ItemNavHistory>,
@@ -51,7 +51,6 @@ impl SharedScreen {
                 Ok(())
             }),
             focus: cx.focus_handle(),
-            current_frame_id: 0,
         }
     }
 }
@@ -65,46 +64,16 @@ impl FocusableView for SharedScreen {
     }
 }
 impl Render for SharedScreen {
-    type Element = Div;
+    type Element = Focusable<Div>;
+
     fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
-        let frame = self.frame.clone();
-        // let frame_id = self.current_frame_id;
-        // self.current_frame_id = self.current_frame_id.wrapping_add(1);
-        div()
-            .size_full()
-            .children(frame.map(|frame| img(frame.image()).w_full()))
+        div().track_focus(&self.focus).size_full().children(
+            self.frame
+                .as_ref()
+                .map(|frame| img(frame.image()).size_full()),
+        )
     }
 }
-// impl View for SharedScreen {
-//     fn ui_name() -> &'static str {
-//         "SharedScreen"
-//     }
-
-//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-//         enum Focus {}
-
-//         let frame = self.frame.clone();
-//         MouseEventHandler::new::<Focus, _>(0, cx, |_, cx| {
-//             Canvas::new(move |bounds, _, _, cx| {
-//                 if let Some(frame) = frame.clone() {
-//                     let size = constrain_size_preserving_aspect_ratio(
-//                         bounds.size(),
-//                         vec2f(frame.width() as f32, frame.height() as f32),
-//                     );
-//                     let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
-//                     cx.scene().push_surface(gpui::platform::mac::Surface {
-//                         bounds: RectF::new(origin, size),
-//                         image_buffer: frame.image(),
-//                     });
-//                 }
-//             })
-//             .contained()
-//             .with_style(theme::current(cx).shared_screen)
-//         })
-//         .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
-//         .into_any()
-//     }
-// }
 
 impl Item for SharedScreen {
     fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
@@ -117,25 +86,14 @@ impl Item for SharedScreen {
     }
 
     fn tab_content(&self, _: Option<usize>, _: &WindowContext<'_>) -> gpui::AnyElement {
-        div().child("Shared screen").into_any()
-        // Flex::row()
-        //     .with_child(
-        //         Svg::new("icons/desktop.svg")
-        //             .with_color(style.label.text.color)
-        //             .constrained()
-        //             .with_width(style.type_icon_width)
-        //             .aligned()
-        //             .contained()
-        //             .with_margin_right(style.spacing),
-        //     )
-        //     .with_child(
-        //         Label::new(
-        //             format!("{}'s screen", self.user.github_login),
-        //             style.label.clone(),
-        //         )
-        //         .aligned(),
-        //     )
-        //     .into_any()
+        h_stack()
+            .gap_1()
+            .child(IconElement::new(Icon::Screen))
+            .child(SharedString::from(format!(
+                "{}'s screen",
+                self.user.github_login
+            )))
+            .into_any()
     }
 
     fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {

crates/gpui2/src/elements/img.rs 🔗

@@ -147,12 +147,20 @@ impl InteractiveElement for Img {
 }
 
 fn preserve_aspect_ratio(bounds: Bounds<Pixels>, image_size: Size<DevicePixels>) -> Bounds<Pixels> {
-    let new_size = if bounds.size.width > bounds.size.height {
-        let ratio = u32::from(image_size.height) as f32 / u32::from(image_size.width) as f32;
-        size(bounds.size.width, bounds.size.width * ratio)
+    let image_size = image_size.map(|dimension| Pixels::from(u32::from(dimension)));
+    let image_ratio = image_size.width / image_size.height;
+    let bounds_ratio = bounds.size.width / bounds.size.height;
+
+    let new_size = if bounds_ratio > image_ratio {
+        size(
+            image_size.width * (bounds.size.height / image_size.height),
+            bounds.size.height,
+        )
     } else {
-        let ratio = u32::from(image_size.width) as f32 / u32::from(image_size.height) as f32;
-        size(bounds.size.width * ratio, bounds.size.height)
+        size(
+            bounds.size.width,
+            image_size.height * (bounds.size.width / image_size.width),
+        )
     };
 
     Bounds {

crates/workspace2/src/workspace2.rs 🔗

@@ -326,7 +326,12 @@ pub struct TestCallHandler;
 
 #[cfg(any(test, feature = "test-support"))]
 impl CallHandler for TestCallHandler {
-    fn peer_state(&mut self, id: PeerId, cx: &mut ViewContext<Workspace>) -> Option<(bool, bool)> {
+    fn peer_state(
+        &mut self,
+        id: PeerId,
+        project: &Model<Project>,
+        cx: &mut ViewContext<Workspace>,
+    ) -> Option<(bool, bool)> {
         None
     }
 
@@ -409,7 +414,7 @@ impl AppState {
             workspace_store,
             node_runtime: FakeNodeRuntime::new(),
             build_window_options: |_, _, _| Default::default(),
-            call_factory: |_, _| Box::new(TestCallHandler),
+            call_factory: |_| Box::new(TestCallHandler),
         })
     }
 }
@@ -468,7 +473,12 @@ pub enum Event {
 
 #[async_trait(?Send)]
 pub trait CallHandler {
-    fn peer_state(&mut self, id: PeerId, cx: &mut ViewContext<Workspace>) -> Option<(bool, bool)>;
+    fn peer_state(
+        &mut self,
+        id: PeerId,
+        project: &Model<Project>,
+        cx: &mut ViewContext<Workspace>,
+    ) -> Option<(bool, bool)>;
     fn shared_screen_for_peer(
         &self,
         peer_id: PeerId,
@@ -546,7 +556,7 @@ struct FollowerState {
 
 enum WorkspaceBounds {}
 
-type CallFactory = fn(WeakView<Workspace>, &mut ViewContext<Workspace>) -> Box<dyn CallHandler>;
+type CallFactory = fn(&mut ViewContext<Workspace>) -> Box<dyn CallHandler>;
 impl Workspace {
     pub fn new(
         workspace_id: WorkspaceId,
@@ -760,7 +770,7 @@ impl Workspace {
             last_leaders_by_pane: Default::default(),
             window_edited: false,
 
-            call_handler: (app_state.call_factory)(weak_handle.clone(), cx),
+            call_handler: (app_state.call_factory)(cx),
             database_id: workspace_id,
             app_state,
             _observe_current_user,
@@ -2884,7 +2894,7 @@ impl Workspace {
         cx.notify();
 
         let (leader_in_this_project, leader_in_this_app) =
-            self.call_handler.peer_state(leader_id, cx)?;
+            self.call_handler.peer_state(leader_id, &self.project, cx)?;
         let mut items_to_activate = Vec::new();
         for (pane, state) in &self.follower_states {
             if state.leader_id != leader_id {
@@ -3385,7 +3395,7 @@ impl Workspace {
             fs: project.read(cx).fs().clone(),
             build_window_options: |_, _, _| Default::default(),
             node_runtime: FakeNodeRuntime::new(),
-            call_factory: |_, _| Box::new(TestCallHandler),
+            call_factory: |_| Box::new(TestCallHandler),
         });
         let workspace = Self::new(0, project, app_state, cx);
         workspace.active_pane.update(cx, |pane, cx| pane.focus(cx));