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}