shared_screen.rs

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