Show shared screen as a pane item
This commit is contained in:
parent
7e411ae098
commit
476020ae84
10 changed files with 286 additions and 126 deletions
|
@ -2,9 +2,7 @@ use crate::{FollowerStatesByLeader, JoinProject, Pane, Workspace};
|
|||
use anyhow::{anyhow, Result};
|
||||
use call::{ActiveCall, ParticipantLocation};
|
||||
use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle,
|
||||
elements::*, Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle,
|
||||
};
|
||||
use project::Project;
|
||||
use serde::Deserialize;
|
||||
|
@ -144,30 +142,6 @@ impl Member {
|
|||
Border::default()
|
||||
};
|
||||
|
||||
let content = if leader.as_ref().map_or(false, |(_, leader)| {
|
||||
leader.location == ParticipantLocation::External && !leader.tracks.is_empty()
|
||||
}) {
|
||||
let (_, leader) = leader.unwrap();
|
||||
let track = leader.tracks.values().next().unwrap();
|
||||
let frame = track.frame().cloned();
|
||||
Canvas::new(move |bounds, _, cx| {
|
||||
if let Some(frame) = frame.clone() {
|
||||
let size = constrain_size_preserving_aspect_ratio(
|
||||
bounds.size(),
|
||||
vec2f(frame.width() as f32, frame.height() as f32),
|
||||
);
|
||||
let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
|
||||
cx.scene.push_surface(gpui::mac::Surface {
|
||||
bounds: RectF::new(origin, size),
|
||||
image_buffer: frame.image(),
|
||||
});
|
||||
}
|
||||
})
|
||||
.boxed()
|
||||
} else {
|
||||
ChildView::new(pane, cx).boxed()
|
||||
};
|
||||
|
||||
let prompt = if let Some((_, leader)) = leader {
|
||||
match leader.location {
|
||||
ParticipantLocation::SharedProject {
|
||||
|
@ -251,7 +225,12 @@ impl Member {
|
|||
};
|
||||
|
||||
Stack::new()
|
||||
.with_child(Container::new(content).with_border(border).boxed())
|
||||
.with_child(
|
||||
ChildView::new(pane, cx)
|
||||
.contained()
|
||||
.with_border(border)
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(prompt)
|
||||
.boxed()
|
||||
}
|
||||
|
|
175
crates/workspace/src/shared_screen.rs
Normal file
175
crates/workspace/src/shared_screen.rs
Normal file
|
@ -0,0 +1,175 @@
|
|||
use crate::{Item, ItemNavHistory};
|
||||
use anyhow::{anyhow, Result};
|
||||
use call::participant::{Frame, RemoteVideoTrack};
|
||||
use client::{PeerId, User};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
Entity, ModelHandle, RenderContext, Task, View, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
}
|
||||
|
||||
pub struct SharedScreen {
|
||||
track: Weak<RemoteVideoTrack>,
|
||||
frame: Option<Frame>,
|
||||
pub peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
_maintain_frame: Task<()>,
|
||||
}
|
||||
|
||||
impl SharedScreen {
|
||||
pub fn new(
|
||||
track: &Arc<RemoteVideoTrack>,
|
||||
peer_id: PeerId,
|
||||
user: Arc<User>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let mut frames = track.frames();
|
||||
Self {
|
||||
track: Arc::downgrade(track),
|
||||
frame: None,
|
||||
peer_id,
|
||||
user,
|
||||
nav_history: Default::default(),
|
||||
_maintain_frame: cx.spawn(|this, mut cx| async move {
|
||||
while let Some(frame) = frames.next().await {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.frame = Some(frame);
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
this.update(&mut cx, |_, cx| cx.emit(Event::Close));
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for SharedScreen {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl View for SharedScreen {
|
||||
fn ui_name() -> &'static str {
|
||||
"SharedScreen"
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
|
||||
let frame = self.frame.clone();
|
||||
Canvas::new(move |bounds, _, cx| {
|
||||
if let Some(frame) = frame.clone() {
|
||||
let size = constrain_size_preserving_aspect_ratio(
|
||||
bounds.size(),
|
||||
vec2f(frame.width() as f32, frame.height() as f32),
|
||||
);
|
||||
let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
|
||||
cx.scene.push_surface(gpui::mac::Surface {
|
||||
bounds: RectF::new(origin, size),
|
||||
image_buffer: frame.image(),
|
||||
});
|
||||
}
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_ref() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_content(
|
||||
&self,
|
||||
_: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
_: &gpui::AppContext,
|
||||
) -> gpui::ElementBox {
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Svg::new("icons/disable_screen_sharing_12.svg")
|
||||
.with_color(style.label.text.color)
|
||||
.constrained()
|
||||
.with_width(style.icon_width)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_margin_right(style.spacing)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
Label::new(
|
||||
format!("{}'s screen", self.user.github_login),
|
||||
style.label.clone(),
|
||||
)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn project_path(&self, _: &gpui::AppContext) -> Option<project::ProjectPath> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn project_entry_ids(&self, _: &gpui::AppContext) -> SmallVec<[project::ProjectEntryId; 3]> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn is_singleton(&self, _: &gpui::AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self> {
|
||||
let track = self.track.upgrade()?;
|
||||
Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
|
||||
}
|
||||
|
||||
fn can_save(&self, _: &gpui::AppContext) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn save(
|
||||
&mut self,
|
||||
_: ModelHandle<project::Project>,
|
||||
_: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
Task::ready(Err(anyhow!("Item::save called on SharedScreen")))
|
||||
}
|
||||
|
||||
fn save_as(
|
||||
&mut self,
|
||||
_: ModelHandle<project::Project>,
|
||||
_: PathBuf,
|
||||
_: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
Task::ready(Err(anyhow!("Item::save_as called on SharedScreen")))
|
||||
}
|
||||
|
||||
fn reload(
|
||||
&mut self,
|
||||
_: ModelHandle<project::Project>,
|
||||
_: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
Task::ready(Err(anyhow!("Item::reload called on SharedScreen")))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event) -> Vec<crate::ItemEvent> {
|
||||
match event {
|
||||
Event::Close => vec![crate::ItemEvent::CloseItem],
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ pub mod dock;
|
|||
pub mod pane;
|
||||
pub mod pane_group;
|
||||
pub mod searchable;
|
||||
mod shared_screen;
|
||||
pub mod sidebar;
|
||||
mod status_bar;
|
||||
mod toolbar;
|
||||
|
@ -36,6 +37,7 @@ use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, Work
|
|||
use searchable::SearchableItemHandle;
|
||||
use serde::Deserialize;
|
||||
use settings::{Autosave, DockAnchor, Settings};
|
||||
use shared_screen::SharedScreen;
|
||||
use sidebar::{Sidebar, SidebarButtons, SidebarSide, ToggleSidebarItem};
|
||||
use smallvec::SmallVec;
|
||||
use status_bar::StatusBar;
|
||||
|
@ -1097,14 +1099,7 @@ impl Workspace {
|
|||
if cx.has_global::<ModelHandle<ActiveCall>>() {
|
||||
let call = cx.global::<ModelHandle<ActiveCall>>().clone();
|
||||
let mut subscriptions = Vec::new();
|
||||
subscriptions.push(cx.observe(&call, |_, _, cx| cx.notify()));
|
||||
subscriptions.push(cx.subscribe(&call, |this, _, event, cx| {
|
||||
if let call::room::Event::Frame { participant_id, .. } = event {
|
||||
if this.follower_states_by_leader.contains_key(&participant_id) {
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
}));
|
||||
subscriptions.push(cx.subscribe(&call, Self::on_active_call_event));
|
||||
active_call = Some((call, subscriptions));
|
||||
}
|
||||
|
||||
|
@ -2517,13 +2512,43 @@ impl Workspace {
|
|||
}
|
||||
|
||||
fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Option<()> {
|
||||
cx.notify();
|
||||
|
||||
let call = self.active_call()?;
|
||||
let room = call.read(cx).room()?.read(cx);
|
||||
let participant = room.remote_participants().get(&leader_id)?;
|
||||
|
||||
let mut items_to_add = Vec::new();
|
||||
for (pane, state) in self.follower_states_by_leader.get(&leader_id)? {
|
||||
if let Some(FollowerItem::Loaded(item)) = state
|
||||
.active_view_id
|
||||
.and_then(|id| state.items_by_leader_view_id.get(&id))
|
||||
{
|
||||
items_to_add.push((pane.clone(), item.boxed_clone()));
|
||||
match participant.location {
|
||||
call::ParticipantLocation::SharedProject { project_id } => {
|
||||
if Some(project_id) == self.project.read(cx).remote_id() {
|
||||
for (pane, state) in self.follower_states_by_leader.get(&leader_id)? {
|
||||
if let Some(FollowerItem::Loaded(item)) = state
|
||||
.active_view_id
|
||||
.and_then(|id| state.items_by_leader_view_id.get(&id))
|
||||
{
|
||||
items_to_add.push((pane.clone(), item.boxed_clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
call::ParticipantLocation::UnsharedProject => {}
|
||||
call::ParticipantLocation::External => {
|
||||
let track = participant.tracks.values().next()?.clone();
|
||||
let user = participant.user.clone();
|
||||
|
||||
'outer: for (pane, _) in self.follower_states_by_leader.get(&leader_id)? {
|
||||
for item in pane.read(cx).items_of_type::<SharedScreen>() {
|
||||
if item.read(cx).peer_id == leader_id {
|
||||
items_to_add.push((pane.clone(), Box::new(item)));
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
let shared_screen =
|
||||
cx.add_view(|cx| SharedScreen::new(&track, leader_id, user.clone(), cx));
|
||||
items_to_add.push((pane.clone(), Box::new(shared_screen)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2532,8 +2557,8 @@ impl Workspace {
|
|||
if pane == self.active_pane {
|
||||
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -2561,6 +2586,27 @@ impl Workspace {
|
|||
fn active_call(&self) -> Option<&ModelHandle<ActiveCall>> {
|
||||
self.active_call.as_ref().map(|(call, _)| call)
|
||||
}
|
||||
|
||||
fn on_active_call_event(
|
||||
&mut self,
|
||||
_: ModelHandle<ActiveCall>,
|
||||
event: &call::room::Event,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
call::room::Event::ParticipantLocationChanged {
|
||||
participant_id: peer_id,
|
||||
}
|
||||
| call::room::Event::RemoteVideoTrackShared {
|
||||
participant_id: peer_id,
|
||||
..
|
||||
}
|
||||
| call::room::Event::RemoteVideoTrackUnshared { peer_id, .. } => {
|
||||
self.leader_updated(*peer_id, cx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Workspace {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue