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}