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 div, img, AppContext, Element, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
11 ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
12 WindowContext,
13};
14use std::sync::{Arc, Weak};
15use ui::{h_flex, prelude::*, Icon, IconName, Label};
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<Result<()>>,
28 focus: FocusHandle,
29}
30
31impl SharedScreen {
32 pub fn new(
33 track: &Arc<RemoteVideoTrack>,
34 peer_id: PeerId,
35 user: Arc<User>,
36 cx: &mut ViewContext<Self>,
37 ) -> Self {
38 cx.focus_handle();
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 focus: cx.focus_handle(),
57 }
58 }
59}
60
61impl EventEmitter<Event> for SharedScreen {}
62
63impl FocusableView for SharedScreen {
64 fn focus_handle(&self, _: &AppContext) -> FocusHandle {
65 self.focus.clone()
66 }
67}
68impl Render for SharedScreen {
69 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
70 div()
71 .bg(cx.theme().colors().editor_background)
72 .track_focus(&self.focus)
73 .size_full()
74 .children(
75 self.frame
76 .as_ref()
77 .map(|frame| img(frame.image()).size_full()),
78 )
79 }
80}
81
82impl Item for SharedScreen {
83 type Event = Event;
84
85 fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
86 Some(format!("{}'s screen", self.user.github_login).into())
87 }
88
89 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
90 if let Some(nav_history) = self.nav_history.as_mut() {
91 nav_history.push::<()>(None, cx);
92 }
93 }
94
95 fn tab_content(
96 &self,
97 _: Option<usize>,
98 selected: bool,
99 _: &WindowContext<'_>,
100 ) -> gpui::AnyElement {
101 h_flex()
102 .gap_1()
103 .child(Icon::new(IconName::Screen))
104 .child(
105 Label::new(format!("{}'s screen", self.user.github_login)).color(if selected {
106 Color::Default
107 } else {
108 Color::Muted
109 }),
110 )
111 .into_any()
112 }
113
114 fn telemetry_event_text(&self) -> Option<&'static str> {
115 None
116 }
117
118 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
119 self.nav_history = Some(history);
120 }
121
122 fn clone_on_split(
123 &self,
124 _workspace_id: WorkspaceId,
125 cx: &mut ViewContext<Self>,
126 ) -> Option<View<Self>> {
127 let track = self.track.upgrade()?;
128 Some(cx.new_view(|cx| Self::new(&track, self.peer_id, self.user.clone(), cx)))
129 }
130
131 fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
132 match event {
133 Event::Close => f(ItemEvent::CloseItem),
134 }
135 }
136}