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