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
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -170,17 +170,6 @@ dependencies = [
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-broadcast"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b"
|
|
||||||
dependencies = [
|
|
||||||
"easy-parallel",
|
|
||||||
"event-listener",
|
|
||||||
"futures-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -727,6 +716,7 @@ name = "call"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-broadcast",
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"futures 0.3.24",
|
"futures 0.3.24",
|
||||||
|
@ -2983,7 +2973,7 @@ name = "language"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-broadcast 0.3.4",
|
"async-broadcast",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
|
@ -3156,7 +3146,7 @@ name = "live_kit_client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-broadcast 0.4.1",
|
"async-broadcast",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"block",
|
"block",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
|
|
@ -27,6 +27,7 @@ project = { path = "../project" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
|
async-broadcast = "0.4"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mod participant;
|
pub mod participant;
|
||||||
pub mod room;
|
pub mod room;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use client::{proto, User};
|
use client::{proto, User};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{Task, WeakModelHandle};
|
use gpui::WeakModelHandle;
|
||||||
use live_kit_client::Frame;
|
pub use live_kit_client::Frame;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -41,18 +41,16 @@ pub struct RemoteParticipant {
|
||||||
pub user: Arc<User>,
|
pub user: Arc<User>,
|
||||||
pub projects: Vec<proto::ParticipantProject>,
|
pub projects: Vec<proto::ParticipantProject>,
|
||||||
pub location: ParticipantLocation,
|
pub location: ParticipantLocation,
|
||||||
pub tracks: HashMap<live_kit_client::Sid, RemoteVideoTrack>,
|
pub tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RemoteVideoTrack {
|
pub struct RemoteVideoTrack {
|
||||||
pub(crate) frame: Option<Frame>,
|
pub(crate) live_kit_track: Arc<live_kit_client::RemoteVideoTrack>,
|
||||||
pub(crate) _live_kit_track: Arc<live_kit_client::RemoteVideoTrack>,
|
|
||||||
pub(crate) _maintain_frame: Arc<Task<()>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteVideoTrack {
|
impl RemoteVideoTrack {
|
||||||
pub fn frame(&self) -> Option<&Frame> {
|
pub fn frames(&self) -> async_broadcast::Receiver<Frame> {
|
||||||
self.frame.as_ref()
|
self.live_kit_track.frames()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
|
||||||
use collections::{BTreeMap, HashSet};
|
use collections::{BTreeMap, HashSet};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
|
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
|
||||||
use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate};
|
use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate, Sid};
|
||||||
use postage::stream::Stream;
|
use postage::stream::Stream;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use std::{mem, os::unix::prelude::OsStrExt, sync::Arc};
|
use std::{mem, os::unix::prelude::OsStrExt, sync::Arc};
|
||||||
|
@ -15,9 +15,16 @@ use util::{post_inc, ResultExt};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Frame {
|
ParticipantLocationChanged {
|
||||||
participant_id: PeerId,
|
participant_id: PeerId,
|
||||||
track_id: live_kit_client::Sid,
|
},
|
||||||
|
RemoteVideoTrackShared {
|
||||||
|
participant_id: PeerId,
|
||||||
|
track_id: Sid,
|
||||||
|
},
|
||||||
|
RemoteVideoTrackUnshared {
|
||||||
|
peer_id: PeerId,
|
||||||
|
track_id: Sid,
|
||||||
},
|
},
|
||||||
RemoteProjectShared {
|
RemoteProjectShared {
|
||||||
owner: Arc<User>,
|
owner: Arc<User>,
|
||||||
|
@ -356,7 +363,12 @@ impl Room {
|
||||||
if let Some(remote_participant) = this.remote_participants.get_mut(&peer_id)
|
if let Some(remote_participant) = this.remote_participants.get_mut(&peer_id)
|
||||||
{
|
{
|
||||||
remote_participant.projects = participant.projects;
|
remote_participant.projects = participant.projects;
|
||||||
remote_participant.location = location;
|
if location != remote_participant.location {
|
||||||
|
remote_participant.location = location;
|
||||||
|
cx.emit(Event::ParticipantLocationChanged {
|
||||||
|
participant_id: peer_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.remote_participants.insert(
|
this.remote_participants.insert(
|
||||||
peer_id,
|
peer_id,
|
||||||
|
@ -430,44 +442,16 @@ impl Room {
|
||||||
.remote_participants
|
.remote_participants
|
||||||
.get_mut(&peer_id)
|
.get_mut(&peer_id)
|
||||||
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
|
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
|
||||||
let mut frames = track.frames();
|
|
||||||
participant.tracks.insert(
|
participant.tracks.insert(
|
||||||
track_id.clone(),
|
track_id.clone(),
|
||||||
RemoteVideoTrack {
|
Arc::new(RemoteVideoTrack {
|
||||||
frame: None,
|
live_kit_track: track,
|
||||||
_live_kit_track: track,
|
}),
|
||||||
_maintain_frame: Arc::new(cx.spawn_weak(|this, mut cx| async move {
|
|
||||||
while let Some(frame) = frames.next().await {
|
|
||||||
let this = if let Some(this) = this.upgrade(&cx) {
|
|
||||||
this
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
let done = this.update(&mut cx, |this, cx| {
|
|
||||||
if let Some(track) =
|
|
||||||
this.remote_participants.get_mut(&peer_id).and_then(
|
|
||||||
|participant| participant.tracks.get_mut(&track_id),
|
|
||||||
)
|
|
||||||
{
|
|
||||||
track.frame = Some(frame);
|
|
||||||
cx.emit(Event::Frame {
|
|
||||||
participant_id: peer_id,
|
|
||||||
track_id: track_id.clone(),
|
|
||||||
});
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if done {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
cx.emit(Event::RemoteVideoTrackShared {
|
||||||
|
participant_id: peer_id,
|
||||||
|
track_id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
RemoteVideoTrackUpdate::Unsubscribed {
|
RemoteVideoTrackUpdate::Unsubscribed {
|
||||||
publisher_id,
|
publisher_id,
|
||||||
|
@ -479,6 +463,7 @@ impl Room {
|
||||||
.get_mut(&peer_id)
|
.get_mut(&peer_id)
|
||||||
.ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?;
|
.ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?;
|
||||||
participant.tracks.remove(&track_id);
|
participant.tracks.remove(&track_id);
|
||||||
|
cx.emit(Event::RemoteVideoTrackUnshared { peer_id, track_id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +735,7 @@ struct LiveKitRoom {
|
||||||
_maintain_tracks: Task<()>,
|
_maintain_tracks: Task<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ScreenTrack {
|
enum ScreenTrack {
|
||||||
None,
|
None,
|
||||||
Pending { publish_id: usize },
|
Pending { publish_id: usize },
|
||||||
Published(LocalTrackPublication),
|
Published(LocalTrackPublication),
|
||||||
|
|
|
@ -5,10 +5,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use ::rpc::Peer;
|
use ::rpc::Peer;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use call::{
|
use call::{room, ActiveCall, ParticipantLocation, Room};
|
||||||
room::{self, Event},
|
|
||||||
ActiveCall, ParticipantLocation, Room,
|
|
||||||
};
|
|
||||||
use client::{
|
use client::{
|
||||||
self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection,
|
self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection,
|
||||||
Credentials, EstablishConnectionError, PeerId, User, UserStore, RECEIVE_TIMEOUT,
|
Credentials, EstablishConnectionError, PeerId, User, UserStore, RECEIVE_TIMEOUT,
|
||||||
|
@ -33,7 +30,7 @@ use language::{
|
||||||
range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
|
range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
|
||||||
LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
|
LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
|
||||||
};
|
};
|
||||||
use live_kit_client::{Frame, MacOSDisplay};
|
use live_kit_client::MacOSDisplay;
|
||||||
use lsp::{self, FakeLanguageServer};
|
use lsp::{self, FakeLanguageServer};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{
|
use project::{
|
||||||
|
@ -202,36 +199,25 @@ async fn test_basic_calls(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let frame = Frame {
|
|
||||||
width: 800,
|
|
||||||
height: 600,
|
|
||||||
label: "a".into(),
|
|
||||||
};
|
|
||||||
display.send_frame(frame.clone());
|
|
||||||
deterministic.run_until_parked();
|
deterministic.run_until_parked();
|
||||||
|
|
||||||
assert_eq!(events_b.borrow().len(), 1);
|
assert_eq!(events_b.borrow().len(), 1);
|
||||||
let event = events_b.borrow().first().unwrap().clone();
|
let event = events_b.borrow().first().unwrap().clone();
|
||||||
if let Event::Frame {
|
if let call::room::Event::RemoteVideoTrackShared {
|
||||||
participant_id,
|
participant_id,
|
||||||
track_id,
|
track_id,
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
assert_eq!(participant_id, client_a.peer_id().unwrap());
|
assert_eq!(participant_id, client_a.peer_id().unwrap());
|
||||||
room_b.read_with(cx_b, |room, _| {
|
room_b.read_with(cx_b, |room, _| {
|
||||||
assert_eq!(
|
assert!(room.remote_participants()[&client_a.peer_id().unwrap()]
|
||||||
room.remote_participants()[&client_a.peer_id().unwrap()].tracks[&track_id].frame(),
|
.tracks
|
||||||
Some(&frame)
|
.contains_key(&track_id));
|
||||||
);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected event")
|
panic!("unexpected event")
|
||||||
}
|
}
|
||||||
|
|
||||||
display.send_frame(frame.clone());
|
|
||||||
deterministic.run_until_parked();
|
|
||||||
assert_eq!(events_b.borrow().len(), 2);
|
|
||||||
|
|
||||||
// User A leaves the room.
|
// User A leaves the room.
|
||||||
active_call_a.update(cx_a, |call, cx| {
|
active_call_a.update(cx_a, |call, cx| {
|
||||||
call.hang_up(cx).unwrap();
|
call.hang_up(cx).unwrap();
|
||||||
|
|
|
@ -36,7 +36,7 @@ text = { path = "../text" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
async-broadcast = "0.3.4"
|
async-broadcast = "0.4"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
|
|
@ -2,9 +2,7 @@ use crate::{FollowerStatesByLeader, JoinProject, Pane, Workspace};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use call::{ActiveCall, ParticipantLocation};
|
use call::{ActiveCall, ParticipantLocation};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*,
|
elements::*, Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle,
|
||||||
geometry::{rect::RectF, vector::vec2f},
|
|
||||||
Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle,
|
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -144,30 +142,6 @@ impl Member {
|
||||||
Border::default()
|
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 {
|
let prompt = if let Some((_, leader)) = leader {
|
||||||
match leader.location {
|
match leader.location {
|
||||||
ParticipantLocation::SharedProject {
|
ParticipantLocation::SharedProject {
|
||||||
|
@ -251,7 +225,12 @@ impl Member {
|
||||||
};
|
};
|
||||||
|
|
||||||
Stack::new()
|
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)
|
.with_children(prompt)
|
||||||
.boxed()
|
.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;
|
||||||
pub mod pane_group;
|
pub mod pane_group;
|
||||||
pub mod searchable;
|
pub mod searchable;
|
||||||
|
mod shared_screen;
|
||||||
pub mod sidebar;
|
pub mod sidebar;
|
||||||
mod status_bar;
|
mod status_bar;
|
||||||
mod toolbar;
|
mod toolbar;
|
||||||
|
@ -36,6 +37,7 @@ use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, Work
|
||||||
use searchable::SearchableItemHandle;
|
use searchable::SearchableItemHandle;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use settings::{Autosave, DockAnchor, Settings};
|
use settings::{Autosave, DockAnchor, Settings};
|
||||||
|
use shared_screen::SharedScreen;
|
||||||
use sidebar::{Sidebar, SidebarButtons, SidebarSide, ToggleSidebarItem};
|
use sidebar::{Sidebar, SidebarButtons, SidebarSide, ToggleSidebarItem};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use status_bar::StatusBar;
|
use status_bar::StatusBar;
|
||||||
|
@ -1097,14 +1099,7 @@ impl Workspace {
|
||||||
if cx.has_global::<ModelHandle<ActiveCall>>() {
|
if cx.has_global::<ModelHandle<ActiveCall>>() {
|
||||||
let call = cx.global::<ModelHandle<ActiveCall>>().clone();
|
let call = cx.global::<ModelHandle<ActiveCall>>().clone();
|
||||||
let mut subscriptions = Vec::new();
|
let mut subscriptions = Vec::new();
|
||||||
subscriptions.push(cx.observe(&call, |_, _, cx| cx.notify()));
|
subscriptions.push(cx.subscribe(&call, Self::on_active_call_event));
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
active_call = Some((call, subscriptions));
|
active_call = Some((call, subscriptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2517,13 +2512,43 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn leader_updated(&mut self, leader_id: PeerId, cx: &mut ViewContext<Self>) -> Option<()> {
|
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();
|
let mut items_to_add = Vec::new();
|
||||||
for (pane, state) in self.follower_states_by_leader.get(&leader_id)? {
|
match participant.location {
|
||||||
if let Some(FollowerItem::Loaded(item)) = state
|
call::ParticipantLocation::SharedProject { project_id } => {
|
||||||
.active_view_id
|
if Some(project_id) == self.project.read(cx).remote_id() {
|
||||||
.and_then(|id| state.items_by_leader_view_id.get(&id))
|
for (pane, state) in self.follower_states_by_leader.get(&leader_id)? {
|
||||||
{
|
if let Some(FollowerItem::Loaded(item)) = state
|
||||||
items_to_add.push((pane.clone(), item.boxed_clone()));
|
.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 {
|
if pane == self.active_pane {
|
||||||
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
||||||
}
|
}
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2561,6 +2586,27 @@ impl Workspace {
|
||||||
fn active_call(&self) -> Option<&ModelHandle<ActiveCall>> {
|
fn active_call(&self) -> Option<&ModelHandle<ActiveCall>> {
|
||||||
self.active_call.as_ref().map(|(call, _)| call)
|
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 {
|
impl Entity for Workspace {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue