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, surface, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
11 ParentElement, Render, SharedString, Styled, Task, Window,
12};
13use std::sync::{Arc, Weak};
14use ui::{prelude::*, Icon, IconName};
15
16pub enum Event {
17 Close,
18}
19
20pub struct SharedScreen {
21 track: Weak<RemoteVideoTrack>,
22 frame: Option<Frame>,
23 pub peer_id: PeerId,
24 user: Arc<User>,
25 nav_history: Option<ItemNavHistory>,
26 _maintain_frame: Task<Result<()>>,
27 focus: FocusHandle,
28}
29
30impl SharedScreen {
31 pub fn new(
32 track: Arc<RemoteVideoTrack>,
33 peer_id: PeerId,
34 user: Arc<User>,
35 window: &mut Window,
36 cx: &mut Context<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_in(window, async move |this, cx| {
47 while let Some(frame) = frames.next().await {
48 this.update(cx, |this, cx| {
49 this.frame = Some(frame);
50 cx.notify();
51 })?;
52 }
53 this.update(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 Focusable for SharedScreen {
64 fn focus_handle(&self, _: &App) -> FocusHandle {
65 self.focus.clone()
66 }
67}
68impl Render for SharedScreen {
69 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
70 div()
71 .bg(cx.theme().colors().editor_background)
72 .track_focus(&self.focus)
73 .key_context("SharedScreen")
74 .size_full()
75 .children(
76 self.frame
77 .as_ref()
78 .map(|frame| surface(frame.image()).size_full()),
79 )
80 }
81}
82
83impl Item for SharedScreen {
84 type Event = Event;
85
86 fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
87 Some(format!("{}'s screen", self.user.github_login).into())
88 }
89
90 fn deactivated(&mut self, _: &mut Window, cx: &mut Context<Self>) {
91 if let Some(nav_history) = self.nav_history.as_mut() {
92 nav_history.push::<()>(None, cx);
93 }
94 }
95
96 fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
97 Some(Icon::new(IconName::Screen))
98 }
99
100 fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
101 Some(format!("{}'s screen", self.user.github_login).into())
102 }
103
104 fn telemetry_event_text(&self) -> Option<&'static str> {
105 None
106 }
107
108 fn set_nav_history(
109 &mut self,
110 history: ItemNavHistory,
111 _window: &mut Window,
112 _: &mut Context<Self>,
113 ) {
114 self.nav_history = Some(history);
115 }
116
117 fn clone_on_split(
118 &self,
119 _workspace_id: Option<WorkspaceId>,
120 window: &mut Window,
121 cx: &mut Context<Self>,
122 ) -> Option<Entity<Self>> {
123 let track = self.track.upgrade()?;
124 Some(cx.new(|cx| Self::new(track, self.peer_id, self.user.clone(), window, cx)))
125 }
126
127 fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
128 match event {
129 Event::Close => f(ItemEvent::CloseItem),
130 }
131 }
132}