1use crate::{
2 item::{Item, ItemEvent},
3 ItemNavHistory, 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 smallvec::SmallVec;
16use std::{
17 borrow::Cow,
18 sync::{Arc, Weak},
19};
20
21pub enum Event {
22 Close,
23}
24
25pub struct SharedScreen {
26 track: Weak<RemoteVideoTrack>,
27 frame: Option<Frame>,
28 pub peer_id: PeerId,
29 user: Arc<User>,
30 nav_history: Option<ItemNavHistory>,
31 _maintain_frame: Task<Result<()>>,
32}
33
34impl SharedScreen {
35 pub fn new(
36 track: &Arc<RemoteVideoTrack>,
37 peer_id: PeerId,
38 user: Arc<User>,
39 cx: &mut ViewContext<Self>,
40 ) -> Self {
41 let mut frames = track.frames();
42 Self {
43 track: Arc::downgrade(track),
44 frame: None,
45 peer_id,
46 user,
47 nav_history: Default::default(),
48 _maintain_frame: cx.spawn(|this, mut cx| async move {
49 while let Some(frame) = frames.next().await {
50 this.update(&mut cx, |this, cx| {
51 this.frame = Some(frame);
52 cx.notify();
53 })?;
54 }
55 this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
56 Ok(())
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 ViewContext<Self>) -> AnyElement<Self> {
72 enum Focus {}
73
74 let frame = self.frame.clone();
75 MouseEventHandler::new::<Focus, _>(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::platform::mac::Surface {
84 bounds: RectF::new(origin, size),
85 image_buffer: frame.image(),
86 });
87 }
88 })
89 .contained()
90 .with_style(theme::current(cx).shared_screen)
91 })
92 .on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
93 .into_any()
94 }
95}
96
97impl Item for SharedScreen {
98 fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
99 Some(format!("{}'s screen", self.user.github_login).into())
100 }
101 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
102 if let Some(nav_history) = self.nav_history.as_mut() {
103 nav_history.push::<()>(None, cx);
104 }
105 }
106
107 fn tab_content<V: 'static>(
108 &self,
109 _: Option<usize>,
110 style: &theme::Tab,
111 _: &AppContext,
112 ) -> gpui::AnyElement<V> {
113 Flex::row()
114 .with_child(
115 Svg::new("icons/desktop.svg")
116 .with_color(style.label.text.color)
117 .constrained()
118 .with_width(style.type_icon_width)
119 .aligned()
120 .contained()
121 .with_margin_right(style.spacing),
122 )
123 .with_child(
124 Label::new(
125 format!("{}'s screen", self.user.github_login),
126 style.label.clone(),
127 )
128 .aligned(),
129 )
130 .into_any()
131 }
132
133 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
134 self.nav_history = Some(history);
135 }
136
137 fn clone_on_split(
138 &self,
139 _workspace_id: WorkspaceId,
140 cx: &mut ViewContext<Self>,
141 ) -> Option<Self> {
142 let track = self.track.upgrade()?;
143 Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
144 }
145
146 fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
147 match event {
148 Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
149 }
150 }
151}