1use crate::{
2 ItemNavHistory, WorkspaceId,
3 item::{Item, ItemEvent},
4};
5use call::{RemoteVideoTrack, RemoteVideoTrackView, Room};
6use client::{User, proto::PeerId};
7use gpui::{
8 AppContext as _, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
9 ParentElement, Render, SharedString, Styled, Task, div,
10};
11use std::sync::Arc;
12use ui::{Icon, IconName, prelude::*};
13
14pub enum Event {
15 Close,
16}
17
18pub struct SharedScreen {
19 pub peer_id: PeerId,
20 user: Arc<User>,
21 nav_history: Option<ItemNavHistory>,
22 view: Entity<RemoteVideoTrackView>,
23 focus: FocusHandle,
24}
25
26impl SharedScreen {
27 pub fn new(
28 track: RemoteVideoTrack,
29 peer_id: PeerId,
30 user: Arc<User>,
31 room: Entity<Room>,
32 window: &mut Window,
33 cx: &mut Context<Self>,
34 ) -> Self {
35 let my_sid = track.sid();
36 cx.subscribe(&room, move |_, _, ev, cx| {
37 if let call::room::Event::RemoteVideoTrackUnsubscribed { sid } = ev
38 && sid == &my_sid
39 {
40 cx.emit(Event::Close)
41 }
42 })
43 .detach();
44
45 cx.observe_release(&room, |_, _, cx| {
46 cx.emit(Event::Close);
47 })
48 .detach();
49
50 let view = cx.new(|cx| RemoteVideoTrackView::new(track.clone(), window, cx));
51 cx.subscribe(&view, |_, _, ev, cx| match ev {
52 call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
53 })
54 .detach();
55 Self {
56 view,
57 peer_id,
58 user,
59 nav_history: Default::default(),
60 focus: cx.focus_handle(),
61 }
62 }
63}
64
65impl EventEmitter<Event> for SharedScreen {}
66
67impl Focusable for SharedScreen {
68 fn focus_handle(&self, _: &App) -> FocusHandle {
69 self.focus.clone()
70 }
71}
72impl Render for SharedScreen {
73 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
74 div()
75 .bg(cx.theme().colors().editor_background)
76 .track_focus(&self.focus)
77 .key_context("SharedScreen")
78 .size_full()
79 .child(self.view.clone())
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, _window: &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, _detail: usize, _cx: &App) -> SharedString {
101 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 _cx: &mut Context<Self>,
113 ) {
114 self.nav_history = Some(history);
115 }
116
117 fn can_split(&self) -> bool {
118 true
119 }
120
121 fn clone_on_split(
122 &self,
123 _workspace_id: Option<WorkspaceId>,
124 window: &mut Window,
125 cx: &mut Context<Self>,
126 ) -> Task<Option<Entity<Self>>> {
127 Task::ready(Some(cx.new(|cx| Self {
128 view: self.view.update(cx, |view, cx| view.clone(window, cx)),
129 peer_id: self.peer_id,
130 user: self.user.clone(),
131 nav_history: Default::default(),
132 focus: cx.focus_handle(),
133 })))
134 }
135
136 fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
137 match event {
138 Event::Close => f(ItemEvent::CloseItem),
139 }
140 }
141}