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