1use crate::{
2 item::{Item, ItemEvent},
3 ItemNavHistory, Pane, WorkspaceId,
4};
5use anyhow::Result;
6use call::participant::{Frame, RemoteVideoTrack};
7use client::{proto::PeerId, User};
8use futures::StreamExt;
9use gpui::{
10 elements::*,
11 geometry::{rect::RectF, vector::vec2f},
12 platform::MouseButton,
13 AppContext, Entity, Task, View, ViewContext,
14};
15use settings::Settings;
16use smallvec::SmallVec;
17use std::sync::{Arc, Weak};
18
19pub enum Event {
20 Close,
21}
22
23pub struct SharedScreen {
24 track: Weak<RemoteVideoTrack>,
25 frame: Option<Frame>,
26 pub peer_id: PeerId,
27 user: Arc<User>,
28 nav_history: Option<ItemNavHistory>,
29 _maintain_frame: Task<Result<()>>,
30}
31
32impl SharedScreen {
33 pub fn new(
34 track: &Arc<RemoteVideoTrack>,
35 peer_id: PeerId,
36 user: Arc<User>,
37 cx: &mut ViewContext<Self>,
38 ) -> Self {
39 let mut frames = track.frames();
40 Self {
41 track: Arc::downgrade(track),
42 frame: None,
43 peer_id,
44 user,
45 nav_history: Default::default(),
46 _maintain_frame: cx.spawn(|this, mut cx| async move {
47 while let Some(frame) = frames.next().await {
48 this.update(&mut cx, |this, cx| {
49 this.frame = Some(frame);
50 cx.notify();
51 })?;
52 }
53 this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
54 Ok(())
55 }),
56 }
57 }
58}
59
60impl Entity for SharedScreen {
61 type Event = Event;
62}
63
64impl View for SharedScreen {
65 fn ui_name() -> &'static str {
66 "SharedScreen"
67 }
68
69 fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
70 enum Focus {}
71
72 let frame = self.frame.clone();
73 MouseEventHandler::<Focus, _>::new(0, cx, |_, cx| {
74 Canvas::new(move |scene, bounds, _, _, _| {
75 if let Some(frame) = frame.clone() {
76 let size = constrain_size_preserving_aspect_ratio(
77 bounds.size(),
78 vec2f(frame.width() as f32, frame.height() as f32),
79 );
80 let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
81 scene.push_surface(gpui::platform::mac::Surface {
82 bounds: RectF::new(origin, size),
83 image_buffer: frame.image(),
84 });
85 }
86 })
87 .contained()
88 .with_style(cx.global::<Settings>().theme.shared_screen)
89 .boxed()
90 })
91 .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent_view())
92 .boxed()
93 }
94}
95
96impl Item for SharedScreen {
97 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
98 if let Some(nav_history) = self.nav_history.as_ref() {
99 nav_history.push::<()>(None, cx);
100 }
101 }
102
103 fn tab_content(
104 &self,
105 _: Option<usize>,
106 style: &theme::Tab,
107 _: &AppContext,
108 ) -> gpui::Element<Pane> {
109 Flex::row()
110 .with_child(
111 Svg::new("icons/disable_screen_sharing_12.svg")
112 .with_color(style.label.text.color)
113 .constrained()
114 .with_width(style.type_icon_width)
115 .aligned()
116 .contained()
117 .with_margin_right(style.spacing)
118 .boxed(),
119 )
120 .with_child(
121 Label::new(
122 format!("{}'s screen", self.user.github_login),
123 style.label.clone(),
124 )
125 .aligned()
126 .boxed(),
127 )
128 .boxed()
129 }
130
131 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
132 self.nav_history = Some(history);
133 }
134
135 fn clone_on_split(
136 &self,
137 _workspace_id: WorkspaceId,
138 cx: &mut ViewContext<Self>,
139 ) -> Option<Self> {
140 let track = self.track.upgrade()?;
141 Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
142 }
143
144 fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
145 match event {
146 Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
147 }
148 }
149}