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::{
18 borrow::Cow,
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<Result<()>>,
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 Ok(())
58 }),
59 }
60 }
61}
62
63impl Entity for SharedScreen {
64 type Event = Event;
65}
66
67impl View for SharedScreen {
68 fn ui_name() -> &'static str {
69 "SharedScreen"
70 }
71
72 fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
73 enum Focus {}
74
75 let frame = self.frame.clone();
76 MouseEventHandler::<Focus, _>::new(0, cx, |_, cx| {
77 Canvas::new(move |scene, bounds, _, _, _| {
78 if let Some(frame) = frame.clone() {
79 let size = constrain_size_preserving_aspect_ratio(
80 bounds.size(),
81 vec2f(frame.width() as f32, frame.height() as f32),
82 );
83 let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
84 scene.push_surface(gpui::platform::mac::Surface {
85 bounds: RectF::new(origin, size),
86 image_buffer: frame.image(),
87 });
88 }
89 })
90 .contained()
91 .with_style(cx.global::<Settings>().theme.shared_screen)
92 .boxed()
93 })
94 .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent_view())
95 .boxed()
96 }
97}
98
99impl Item for SharedScreen {
100 fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
101 Some(format!("{}'s screen", self.user.github_login).into())
102 }
103 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
104 if let Some(nav_history) = self.nav_history.as_ref() {
105 nav_history.push::<()>(None, cx);
106 }
107 }
108
109 fn tab_content(
110 &self,
111 _: Option<usize>,
112 style: &theme::Tab,
113 _: &AppContext,
114 ) -> gpui::Element<Pane> {
115 Flex::row()
116 .with_child(
117 Svg::new("icons/disable_screen_sharing_12.svg")
118 .with_color(style.label.text.color)
119 .constrained()
120 .with_width(style.type_icon_width)
121 .aligned()
122 .contained()
123 .with_margin_right(style.spacing)
124 .boxed(),
125 )
126 .with_child(
127 Label::new(
128 format!("{}'s screen", self.user.github_login),
129 style.label.clone(),
130 )
131 .aligned()
132 .boxed(),
133 )
134 .boxed()
135 }
136
137 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
138 self.nav_history = Some(history);
139 }
140
141 fn clone_on_split(
142 &self,
143 _workspace_id: WorkspaceId,
144 cx: &mut ViewContext<Self>,
145 ) -> Option<Self> {
146 let track = self.track.upgrade()?;
147 Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
148 }
149
150 fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
151 match event {
152 Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
153 }
154 }
155}