shared_screen.rs

  1use crate::{
  2    item::{Item, ItemEvent},
  3    ItemNavHistory, WorkspaceId,
  4};
  5use anyhow::Result;
  6use call::participant::{Frame, RemoteVideoTrack};
  7use client::{proto::PeerId, User};
  8use futures::StreamExt;
  9use gpui::{
 10    elements::*,
 11    geometry::{rect::RectF, vector::vec2f},
 12    platform::MouseButton,
 13    AppContext, Entity, Task, View, ViewContext,
 14};
 15use smallvec::SmallVec;
 16use std::{
 17    borrow::Cow,
 18    sync::{Arc, Weak},
 19};
 20
 21pub enum Event {
 22    Close,
 23}
 24
 25pub struct SharedScreen {
 26    track: Weak<RemoteVideoTrack>,
 27    frame: Option<Frame>,
 28    pub peer_id: PeerId,
 29    user: Arc<User>,
 30    nav_history: Option<ItemNavHistory>,
 31    _maintain_frame: Task<Result<()>>,
 32}
 33
 34impl SharedScreen {
 35    pub fn new(
 36        track: &Arc<RemoteVideoTrack>,
 37        peer_id: PeerId,
 38        user: Arc<User>,
 39        cx: &mut ViewContext<Self>,
 40    ) -> Self {
 41        let mut frames = track.frames();
 42        Self {
 43            track: Arc::downgrade(track),
 44            frame: None,
 45            peer_id,
 46            user,
 47            nav_history: Default::default(),
 48            _maintain_frame: cx.spawn(|this, mut cx| async move {
 49                while let Some(frame) = frames.next().await {
 50                    this.update(&mut cx, |this, cx| {
 51                        this.frame = Some(frame);
 52                        cx.notify();
 53                    })?;
 54                }
 55                this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
 56                Ok(())
 57            }),
 58        }
 59    }
 60}
 61
 62impl Entity for SharedScreen {
 63    type Event = Event;
 64}
 65
 66impl View for SharedScreen {
 67    fn ui_name() -> &'static str {
 68        "SharedScreen"
 69    }
 70
 71    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
 72        enum Focus {}
 73
 74        let frame = self.frame.clone();
 75        MouseEventHandler::new::<Focus, _>(0, cx, |_, cx| {
 76            Canvas::new(move |bounds, _, _, cx| {
 77                if let Some(frame) = frame.clone() {
 78                    let size = constrain_size_preserving_aspect_ratio(
 79                        bounds.size(),
 80                        vec2f(frame.width() as f32, frame.height() as f32),
 81                    );
 82                    let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
 83                    cx.scene().push_surface(gpui::platform::mac::Surface {
 84                        bounds: RectF::new(origin, size),
 85                        image_buffer: frame.image(),
 86                    });
 87                }
 88            })
 89            .contained()
 90            .with_style(theme::current(cx).shared_screen)
 91        })
 92        .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
 93        .into_any()
 94    }
 95}
 96
 97impl Item for SharedScreen {
 98    fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
 99        Some(format!("{}'s screen", self.user.github_login).into())
100    }
101    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
102        if let Some(nav_history) = self.nav_history.as_mut() {
103            nav_history.push::<()>(None, cx);
104        }
105    }
106
107    fn tab_content<V: 'static>(
108        &self,
109        _: Option<usize>,
110        style: &theme::Tab,
111        _: &AppContext,
112    ) -> gpui::AnyElement<V> {
113        Flex::row()
114            .with_child(
115                Svg::new("icons/desktop.svg")
116                    .with_color(style.label.text.color)
117                    .constrained()
118                    .with_width(style.type_icon_width)
119                    .aligned()
120                    .contained()
121                    .with_margin_right(style.spacing),
122            )
123            .with_child(
124                Label::new(
125                    format!("{}'s screen", self.user.github_login),
126                    style.label.clone(),
127                )
128                .aligned(),
129            )
130            .into_any()
131    }
132
133    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
134        self.nav_history = Some(history);
135    }
136
137    fn clone_on_split(
138        &self,
139        _workspace_id: WorkspaceId,
140        cx: &mut ViewContext<Self>,
141    ) -> Option<Self> {
142        let track = self.track.upgrade()?;
143        Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
144    }
145
146    fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
147        match event {
148            Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
149        }
150    }
151}