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    AppContext, Entity, ModelHandle, MouseButton, RenderContext, Task, View, ViewContext,
 12    ViewHandle, WeakViewHandle,
 13};
 14use project::Project;
 15use settings::Settings;
 16use std::{
 17    path::PathBuf,
 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<()>,
 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            }),
 57        }
 58    }
 59}
 60
 61impl Entity for SharedScreen {
 62    type Event = Event;
 63}
 64
 65impl View for SharedScreen {
 66    fn ui_name() -> &'static str {
 67        "SharedScreen"
 68    }
 69
 70    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
 71        enum Focus {}
 72
 73        let frame = self.frame.clone();
 74        MouseEventHandler::<Focus>::new(0, cx, |_, cx| {
 75            Canvas::new(move |bounds, _, cx| {
 76                if let Some(frame) = frame.clone() {
 77                    let size = constrain_size_preserving_aspect_ratio(
 78                        bounds.size(),
 79                        vec2f(frame.width() as f32, frame.height() as f32),
 80                    );
 81                    let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
 82                    cx.scene.push_surface(gpui::mac::Surface {
 83                        bounds: RectF::new(origin, size),
 84                        image_buffer: frame.image(),
 85                    });
 86                }
 87            })
 88            .contained()
 89            .with_style(cx.global::<Settings>().theme.shared_screen)
 90            .boxed()
 91        })
 92        .on_down(MouseButton::Left, |_, cx| cx.focus_parent_view())
 93        .boxed()
 94    }
 95}
 96
 97impl Item for SharedScreen {
 98    fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
 99        if let Some(nav_history) = self.nav_history.as_ref() {
100            nav_history.push::<()>(None, cx);
101        }
102    }
103
104    fn tab_content(
105        &self,
106        _: Option<usize>,
107        style: &theme::Tab,
108        _: &AppContext,
109    ) -> gpui::ElementBox {
110        Flex::row()
111            .with_child(
112                Svg::new("icons/disable_screen_sharing_12.svg")
113                    .with_color(style.label.text.color)
114                    .constrained()
115                    .with_width(style.icon_width)
116                    .aligned()
117                    .contained()
118                    .with_margin_right(style.spacing)
119                    .boxed(),
120            )
121            .with_child(
122                Label::new(
123                    format!("{}'s screen", self.user.github_login),
124                    style.label.clone(),
125                )
126                .aligned()
127                .boxed(),
128            )
129            .boxed()
130    }
131
132    fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project::Item)) {}
133
134    fn is_singleton(&self, _: &AppContext) -> bool {
135        false
136    }
137
138    fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
139        self.nav_history = Some(history);
140    }
141
142    fn clone_on_split(
143        &self,
144        _workspace_id: WorkspaceId,
145        cx: &mut ViewContext<Self>,
146    ) -> Option<Self> {
147        let track = self.track.upgrade()?;
148        Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
149    }
150
151    fn can_save(&self, _: &AppContext) -> bool {
152        false
153    }
154
155    fn save(
156        &mut self,
157        _: ModelHandle<project::Project>,
158        _: &mut ViewContext<Self>,
159    ) -> Task<Result<()>> {
160        Task::ready(Err(anyhow!("Item::save called on SharedScreen")))
161    }
162
163    fn save_as(
164        &mut self,
165        _: ModelHandle<project::Project>,
166        _: PathBuf,
167        _: &mut ViewContext<Self>,
168    ) -> Task<Result<()>> {
169        Task::ready(Err(anyhow!("Item::save_as called on SharedScreen")))
170    }
171
172    fn reload(
173        &mut self,
174        _: ModelHandle<project::Project>,
175        _: &mut ViewContext<Self>,
176    ) -> Task<Result<()>> {
177        Task::ready(Err(anyhow!("Item::reload called on SharedScreen")))
178    }
179
180    fn to_item_events(event: &Self::Event) -> Vec<ItemEvent> {
181        match event {
182            Event::Close => vec![ItemEvent::CloseItem],
183        }
184    }
185
186    fn serialized_item_kind() -> Option<&'static str> {
187        None
188    }
189
190    fn deserialize(
191        _project: ModelHandle<Project>,
192        _workspace: WeakViewHandle<Workspace>,
193        _workspace_id: WorkspaceId,
194        _item_id: ItemId,
195        _cx: &mut ViewContext<Pane>,
196    ) -> Task<Result<ViewHandle<Self>>> {
197        unreachable!("Shared screen can not be deserialized")
198    }
199}