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, 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, _: &mut RenderContext<Self>) -> ElementBox {
67 let frame = self.frame.clone();
68 Canvas::new(move |bounds, _, cx| {
69 if let Some(frame) = frame.clone() {
70 let size = constrain_size_preserving_aspect_ratio(
71 bounds.size(),
72 vec2f(frame.width() as f32, frame.height() as f32),
73 );
74 let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
75 cx.scene.push_surface(gpui::mac::Surface {
76 bounds: RectF::new(origin, size),
77 image_buffer: frame.image(),
78 });
79 }
80 })
81 .boxed()
82 }
83}
84
85impl Item for SharedScreen {
86 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
87 if let Some(nav_history) = self.nav_history.as_ref() {
88 nav_history.push::<()>(None, cx);
89 }
90 }
91
92 fn tab_content(
93 &self,
94 _: Option<usize>,
95 style: &theme::Tab,
96 _: &gpui::AppContext,
97 ) -> gpui::ElementBox {
98 Flex::row()
99 .with_child(
100 Svg::new("icons/disable_screen_sharing_12.svg")
101 .with_color(style.label.text.color)
102 .constrained()
103 .with_width(style.icon_width)
104 .aligned()
105 .contained()
106 .with_margin_right(style.spacing)
107 .boxed(),
108 )
109 .with_child(
110 Label::new(
111 format!("{}'s screen", self.user.github_login),
112 style.label.clone(),
113 )
114 .aligned()
115 .boxed(),
116 )
117 .boxed()
118 }
119
120 fn project_path(&self, _: &gpui::AppContext) -> Option<project::ProjectPath> {
121 Default::default()
122 }
123
124 fn project_entry_ids(&self, _: &gpui::AppContext) -> SmallVec<[project::ProjectEntryId; 3]> {
125 Default::default()
126 }
127
128 fn is_singleton(&self, _: &gpui::AppContext) -> bool {
129 false
130 }
131
132 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
133 self.nav_history = Some(history);
134 }
135
136 fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self> {
137 let track = self.track.upgrade()?;
138 Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
139 }
140
141 fn can_save(&self, _: &gpui::AppContext) -> bool {
142 false
143 }
144
145 fn save(
146 &mut self,
147 _: ModelHandle<project::Project>,
148 _: &mut ViewContext<Self>,
149 ) -> Task<Result<()>> {
150 Task::ready(Err(anyhow!("Item::save called on SharedScreen")))
151 }
152
153 fn save_as(
154 &mut self,
155 _: ModelHandle<project::Project>,
156 _: PathBuf,
157 _: &mut ViewContext<Self>,
158 ) -> Task<Result<()>> {
159 Task::ready(Err(anyhow!("Item::save_as called on SharedScreen")))
160 }
161
162 fn reload(
163 &mut self,
164 _: ModelHandle<project::Project>,
165 _: &mut ViewContext<Self>,
166 ) -> Task<Result<()>> {
167 Task::ready(Err(anyhow!("Item::reload called on SharedScreen")))
168 }
169
170 fn to_item_events(event: &Self::Event) -> Vec<crate::ItemEvent> {
171 match event {
172 Event::Close => vec![crate::ItemEvent::CloseItem],
173 }
174 }
175}