1use crate::participant::{Frame, RemoteVideoTrack};
2use anyhow::Result;
3use client::{proto::PeerId, User};
4use futures::StreamExt;
5use gpui::{
6 div, img, AppContext, Div, Element, EventEmitter, FocusHandle, Focusable, FocusableView,
7 InteractiveElement, ParentElement, Render, SharedString, Styled, Task, View, ViewContext,
8 VisualContext, WindowContext,
9};
10use std::sync::{Arc, Weak};
11use ui::{h_stack, Icon, IconElement};
12use workspace::{item::Item, ItemNavHistory, WorkspaceId};
13
14pub enum Event {
15 Close,
16}
17
18pub struct SharedScreen {
19 track: Weak<RemoteVideoTrack>,
20 frame: Option<Frame>,
21 pub peer_id: PeerId,
22 user: Arc<User>,
23 nav_history: Option<ItemNavHistory>,
24 _maintain_frame: Task<Result<()>>,
25 focus: FocusHandle,
26}
27
28impl SharedScreen {
29 pub fn new(
30 track: &Arc<RemoteVideoTrack>,
31 peer_id: PeerId,
32 user: Arc<User>,
33 cx: &mut ViewContext<Self>,
34 ) -> Self {
35 cx.focus_handle();
36 let mut frames = track.frames();
37 Self {
38 track: Arc::downgrade(track),
39 frame: None,
40 peer_id,
41 user,
42 nav_history: Default::default(),
43 _maintain_frame: cx.spawn(|this, mut cx| async move {
44 while let Some(frame) = frames.next().await {
45 this.update(&mut cx, |this, cx| {
46 this.frame = Some(frame);
47 cx.notify();
48 })?;
49 }
50 this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
51 Ok(())
52 }),
53 focus: cx.focus_handle(),
54 }
55 }
56}
57
58impl EventEmitter<Event> for SharedScreen {}
59impl EventEmitter<workspace::item::ItemEvent> for SharedScreen {}
60
61impl FocusableView for SharedScreen {
62 fn focus_handle(&self, _: &AppContext) -> FocusHandle {
63 self.focus.clone()
64 }
65}
66impl Render for SharedScreen {
67 type Element = Focusable<Div>;
68
69 fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
70 div().track_focus(&self.focus).size_full().children(
71 self.frame
72 .as_ref()
73 .map(|frame| img(frame.image()).size_full()),
74 )
75 }
76}
77
78impl Item for SharedScreen {
79 fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
80 Some(format!("{}'s screen", self.user.github_login).into())
81 }
82 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
83 if let Some(nav_history) = self.nav_history.as_mut() {
84 nav_history.push::<()>(None, cx);
85 }
86 }
87
88 fn tab_content(&self, _: Option<usize>, _: &WindowContext<'_>) -> gpui::AnyElement {
89 h_stack()
90 .gap_1()
91 .child(IconElement::new(Icon::Screen))
92 .child(SharedString::from(format!(
93 "{}'s screen",
94 self.user.github_login
95 )))
96 .into_any()
97 }
98
99 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
100 self.nav_history = Some(history);
101 }
102
103 fn clone_on_split(
104 &self,
105 _workspace_id: WorkspaceId,
106 cx: &mut ViewContext<Self>,
107 ) -> Option<View<Self>> {
108 let track = self.track.upgrade()?;
109 Some(cx.build_view(|cx| Self::new(&track, self.peer_id, self.user.clone(), cx)))
110 }
111}