1use crate::{
2 item::{Item, ItemEvent},
3 ItemNavHistory, WorkspaceId,
4};
5use call::participant::{Frame, RemoteVideoTrack};
6use client::{proto::PeerId, User};
7use futures::StreamExt;
8use gpui::{
9 elements::*,
10 geometry::{rect::RectF, vector::vec2f},
11 AppContext, Entity, MouseButton, RenderContext, Task, View, ViewContext,
12};
13use settings::Settings;
14use smallvec::SmallVec;
15use std::sync::{Arc, Weak};
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, cx: &mut RenderContext<Self>) -> ElementBox {
67 enum Focus {}
68
69 let frame = self.frame.clone();
70 MouseEventHandler::<Focus>::new(0, cx, |_, cx| {
71 Canvas::new(move |bounds, _, cx| {
72 if let Some(frame) = frame.clone() {
73 let size = constrain_size_preserving_aspect_ratio(
74 bounds.size(),
75 vec2f(frame.width() as f32, frame.height() as f32),
76 );
77 let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
78 cx.scene.push_surface(gpui::mac::Surface {
79 bounds: RectF::new(origin, size),
80 image_buffer: frame.image(),
81 });
82 }
83 })
84 .contained()
85 .with_style(cx.global::<Settings>().theme.shared_screen)
86 .boxed()
87 })
88 .on_down(MouseButton::Left, |_, cx| cx.focus_parent_view())
89 .boxed()
90 }
91}
92
93impl Item for SharedScreen {
94 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
95 if let Some(nav_history) = self.nav_history.as_ref() {
96 nav_history.push::<()>(None, cx);
97 }
98 }
99
100 fn tab_content(
101 &self,
102 _: Option<usize>,
103 style: &theme::Tab,
104 _: &AppContext,
105 ) -> gpui::ElementBox {
106 Flex::row()
107 .with_child(
108 Svg::new("icons/disable_screen_sharing_12.svg")
109 .with_color(style.label.text.color)
110 .constrained()
111 .with_width(style.type_icon_width)
112 .aligned()
113 .contained()
114 .with_margin_right(style.spacing)
115 .boxed(),
116 )
117 .with_child(
118 Label::new(
119 format!("{}'s screen", self.user.github_login),
120 style.label.clone(),
121 )
122 .aligned()
123 .boxed(),
124 )
125 .boxed()
126 }
127
128 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
129 self.nav_history = Some(history);
130 }
131
132 fn clone_on_split(
133 &self,
134 _workspace_id: WorkspaceId,
135 cx: &mut ViewContext<Self>,
136 ) -> Option<Self> {
137 let track = self.track.upgrade()?;
138 Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
139 }
140
141 fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
142 match event {
143 Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
144 }
145 }
146}