shared_screen.rs

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