shared_screen.rs

  1use crate::{
  2    item::ItemEvent, persistence::model::ItemId, Item, ItemNavHistory, Pane, Workspace, WorkspaceId,
  3};
  4use anyhow::{anyhow, Result};
  5use call::participant::{Frame, RemoteVideoTrack};
  6use client::{proto::PeerId, User};
  7use futures::StreamExt;
  8use gpui::{
  9    elements::*,
 10    geometry::{rect::RectF, vector::vec2f},
 11    Entity, ModelHandle, MouseButton, RenderContext, Task, View, ViewContext, ViewHandle,
 12    WeakViewHandle,
 13};
 14use project::Project;
 15use settings::Settings;
 16use smallvec::SmallVec;
 17use std::{
 18    path::PathBuf,
 19    sync::{Arc, Weak},
 20};
 21
 22pub enum Event {
 23    Close,
 24}
 25
 26pub struct SharedScreen {
 27    track: Weak<RemoteVideoTrack>,
 28    frame: Option<Frame>,
 29    pub peer_id: PeerId,
 30    user: Arc<User>,
 31    nav_history: Option<ItemNavHistory>,
 32    _maintain_frame: Task<()>,
 33}
 34
 35impl SharedScreen {
 36    pub fn new(
 37        track: &Arc<RemoteVideoTrack>,
 38        peer_id: PeerId,
 39        user: Arc<User>,
 40        cx: &mut ViewContext<Self>,
 41    ) -> Self {
 42        let mut frames = track.frames();
 43        Self {
 44            track: Arc::downgrade(track),
 45            frame: None,
 46            peer_id,
 47            user,
 48            nav_history: Default::default(),
 49            _maintain_frame: cx.spawn(|this, mut cx| async move {
 50                while let Some(frame) = frames.next().await {
 51                    this.update(&mut cx, |this, cx| {
 52                        this.frame = Some(frame);
 53                        cx.notify();
 54                    })
 55                }
 56                this.update(&mut cx, |_, cx| cx.emit(Event::Close));
 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 RenderContext<Self>) -> ElementBox {
 72        enum Focus {}
 73
 74        let frame = self.frame.clone();
 75        MouseEventHandler::<Focus>::new(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::mac::Surface {
 84                        bounds: RectF::new(origin, size),
 85                        image_buffer: frame.image(),
 86                    });
 87                }
 88            })
 89            .contained()
 90            .with_style(cx.global::<Settings>().theme.shared_screen)
 91            .boxed()
 92        })
 93        .on_down(MouseButton::Left, |_, cx| cx.focus_parent_view())
 94        .boxed()
 95    }
 96}
 97
 98impl Item for SharedScreen {
 99    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
100        if let Some(nav_history) = self.nav_history.as_ref() {
101            nav_history.push::<()>(None, cx);
102        }
103    }
104
105    fn tab_content(
106        &self,
107        _: Option<usize>,
108        style: &theme::Tab,
109        _: &gpui::AppContext,
110    ) -> gpui::ElementBox {
111        Flex::row()
112            .with_child(
113                Svg::new("icons/disable_screen_sharing_12.svg")
114                    .with_color(style.label.text.color)
115                    .constrained()
116                    .with_width(style.icon_width)
117                    .aligned()
118                    .contained()
119                    .with_margin_right(style.spacing)
120                    .boxed(),
121            )
122            .with_child(
123                Label::new(
124                    format!("{}'s screen", self.user.github_login),
125                    style.label.clone(),
126                )
127                .aligned()
128                .boxed(),
129            )
130            .boxed()
131    }
132
133    fn project_path(&self, _: &gpui::AppContext) -> Option<project::ProjectPath> {
134        Default::default()
135    }
136
137    fn project_entry_ids(&self, _: &gpui::AppContext) -> SmallVec<[project::ProjectEntryId; 3]> {
138        Default::default()
139    }
140
141    fn is_singleton(&self, _: &gpui::AppContext) -> bool {
142        false
143    }
144
145    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
146        self.nav_history = Some(history);
147    }
148
149    fn clone_on_split(
150        &self,
151        _workspace_id: WorkspaceId,
152        cx: &mut ViewContext<Self>,
153    ) -> Option<Self> {
154        let track = self.track.upgrade()?;
155        Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
156    }
157
158    fn can_save(&self, _: &gpui::AppContext) -> bool {
159        false
160    }
161
162    fn save(
163        &mut self,
164        _: ModelHandle<project::Project>,
165        _: &mut ViewContext<Self>,
166    ) -> Task<Result<()>> {
167        Task::ready(Err(anyhow!("Item::save called on SharedScreen")))
168    }
169
170    fn save_as(
171        &mut self,
172        _: ModelHandle<project::Project>,
173        _: PathBuf,
174        _: &mut ViewContext<Self>,
175    ) -> Task<Result<()>> {
176        Task::ready(Err(anyhow!("Item::save_as called on SharedScreen")))
177    }
178
179    fn reload(
180        &mut self,
181        _: ModelHandle<project::Project>,
182        _: &mut ViewContext<Self>,
183    ) -> Task<Result<()>> {
184        Task::ready(Err(anyhow!("Item::reload called on SharedScreen")))
185    }
186
187    fn to_item_events(event: &Self::Event) -> Vec<ItemEvent> {
188        match event {
189            Event::Close => vec![ItemEvent::CloseItem],
190        }
191    }
192
193    fn serialized_item_kind() -> Option<&'static str> {
194        None
195    }
196
197    fn deserialize(
198        _project: ModelHandle<Project>,
199        _workspace: WeakViewHandle<Workspace>,
200        _workspace_id: WorkspaceId,
201        _item_id: ItemId,
202        _cx: &mut ViewContext<Pane>,
203    ) -> Task<Result<ViewHandle<Self>>> {
204        unreachable!("Shared screen can not be deserialized")
205    }
206}