mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Show shared screen as a pane item
This commit is contained in:
parent
7e411ae098
commit
476020ae84
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -170,17 +170,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "async-broadcast"
|
||||
version = "0.4.1"
|
||||
@ -727,6 +716,7 @@ name = "call"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-broadcast",
|
||||
"client",
|
||||
"collections",
|
||||
"futures 0.3.24",
|
||||
@ -2983,7 +2973,7 @@ name = "language"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-broadcast 0.3.4",
|
||||
"async-broadcast",
|
||||
"async-trait",
|
||||
"client",
|
||||
"clock",
|
||||
@ -3156,7 +3146,7 @@ name = "live_kit_client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-broadcast 0.4.1",
|
||||
"async-broadcast",
|
||||
"async-trait",
|
||||
"block",
|
||||
"byteorder",
|
||||
|
@ -27,6 +27,7 @@ project = { path = "../project" }
|
||||
util = { path = "../util" }
|
||||
|
||||
anyhow = "1.0.38"
|
||||
async-broadcast = "0.4"
|
||||
futures = "0.3"
|
||||
postage = { version = "0.4.1", features = ["futures-traits"] }
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
mod participant;
|
||||
pub mod participant;
|
||||
pub mod room;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
@ -1,8 +1,8 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{proto, User};
|
||||
use collections::HashMap;
|
||||
use gpui::{Task, WeakModelHandle};
|
||||
use live_kit_client::Frame;
|
||||
use gpui::WeakModelHandle;
|
||||
pub use live_kit_client::Frame;
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -41,18 +41,16 @@ pub struct RemoteParticipant {
|
||||
pub user: Arc<User>,
|
||||
pub projects: Vec<proto::ParticipantProject>,
|
||||
pub location: ParticipantLocation,
|
||||
pub tracks: HashMap<live_kit_client::Sid, RemoteVideoTrack>,
|
||||
pub tracks: HashMap<live_kit_client::Sid, Arc<RemoteVideoTrack>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RemoteVideoTrack {
|
||||
pub(crate) frame: Option<Frame>,
|
||||
pub(crate) _live_kit_track: Arc<live_kit_client::RemoteVideoTrack>,
|
||||
pub(crate) _maintain_frame: Arc<Task<()>>,
|
||||
pub(crate) live_kit_track: Arc<live_kit_client::RemoteVideoTrack>,
|
||||
}
|
||||
|
||||
impl RemoteVideoTrack {
|
||||
pub fn frame(&self) -> Option<&Frame> {
|
||||
self.frame.as_ref()
|
||||
pub fn frames(&self) -> async_broadcast::Receiver<Frame> {
|
||||
self.live_kit_track.frames()
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore};
|
||||
use collections::{BTreeMap, HashSet};
|
||||
use futures::StreamExt;
|
||||
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 project::Project;
|
||||
use std::{mem, os::unix::prelude::OsStrExt, sync::Arc};
|
||||
@ -15,9 +15,16 @@ use util::{post_inc, ResultExt};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Event {
|
||||
Frame {
|
||||
ParticipantLocationChanged {
|
||||
participant_id: PeerId,
|
||||
track_id: live_kit_client::Sid,
|
||||
},
|
||||
RemoteVideoTrackShared {
|
||||
participant_id: PeerId,
|
||||
track_id: Sid,
|
||||
},
|
||||
RemoteVideoTrackUnshared {
|
||||
peer_id: PeerId,
|
||||
track_id: Sid,
|
||||
},
|
||||
RemoteProjectShared {
|
||||
owner: Arc<User>,
|
||||
@ -356,7 +363,12 @@ impl Room {
|
||||
if let Some(remote_participant) = this.remote_participants.get_mut(&peer_id)
|
||||
{
|
||||
remote_participant.projects = participant.projects;
|
||||
if location != remote_participant.location {
|
||||
remote_participant.location = location;
|
||||
cx.emit(Event::ParticipantLocationChanged {
|
||||
participant_id: peer_id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.remote_participants.insert(
|
||||
peer_id,
|
||||
@ -430,44 +442,16 @@ impl Room {
|
||||
.remote_participants
|
||||
.get_mut(&peer_id)
|
||||
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
|
||||
let mut frames = track.frames();
|
||||
participant.tracks.insert(
|
||||
track_id.clone(),
|
||||
RemoteVideoTrack {
|
||||
frame: None,
|
||||
_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;
|
||||
}
|
||||
}
|
||||
})),
|
||||
},
|
||||
Arc::new(RemoteVideoTrack {
|
||||
live_kit_track: track,
|
||||
}),
|
||||
);
|
||||
cx.emit(Event::RemoteVideoTrackShared {
|
||||
participant_id: peer_id,
|
||||
track_id,
|
||||
});
|
||||
}
|
||||
RemoteVideoTrackUpdate::Unsubscribed {
|
||||
publisher_id,
|
||||
@ -479,6 +463,7 @@ impl Room {
|
||||
.get_mut(&peer_id)
|
||||
.ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?;
|
||||
participant.tracks.remove(&track_id);
|
||||
cx.emit(Event::RemoteVideoTrackUnshared { peer_id, track_id });
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,7 +735,7 @@ struct LiveKitRoom {
|
||||
_maintain_tracks: Task<()>,
|
||||
}
|
||||
|
||||
pub enum ScreenTrack {
|
||||
enum ScreenTrack {
|
||||
None,
|
||||
Pending { publish_id: usize },
|
||||
Published(LocalTrackPublication),
|
||||
|
@ -5,10 +5,7 @@ use crate::{
|
||||
};
|
||||
use ::rpc::Peer;
|
||||
use anyhow::anyhow;
|
||||
use call::{
|
||||
room::{self, Event},
|
||||
ActiveCall, ParticipantLocation, Room,
|
||||
};
|
||||
use call::{room, ActiveCall, ParticipantLocation, Room};
|
||||
use client::{
|
||||
self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection,
|
||||
Credentials, EstablishConnectionError, PeerId, User, UserStore, RECEIVE_TIMEOUT,
|
||||
@ -33,7 +30,7 @@ use language::{
|
||||
range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
|
||||
LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
|
||||
};
|
||||
use live_kit_client::{Frame, MacOSDisplay};
|
||||
use live_kit_client::MacOSDisplay;
|
||||
use lsp::{self, FakeLanguageServer};
|
||||
use parking_lot::Mutex;
|
||||
use project::{
|
||||
@ -202,36 +199,25 @@ async fn test_basic_calls(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let frame = Frame {
|
||||
width: 800,
|
||||
height: 600,
|
||||
label: "a".into(),
|
||||
};
|
||||
display.send_frame(frame.clone());
|
||||
deterministic.run_until_parked();
|
||||
|
||||
assert_eq!(events_b.borrow().len(), 1);
|
||||
let event = events_b.borrow().first().unwrap().clone();
|
||||
if let Event::Frame {
|
||||
if let call::room::Event::RemoteVideoTrackShared {
|
||||
participant_id,
|
||||
track_id,
|
||||
} = event
|
||||
{
|
||||
assert_eq!(participant_id, client_a.peer_id().unwrap());
|
||||
room_b.read_with(cx_b, |room, _| {
|
||||
assert_eq!(
|
||||
room.remote_participants()[&client_a.peer_id().unwrap()].tracks[&track_id].frame(),
|
||||
Some(&frame)
|
||||
);
|
||||
assert!(room.remote_participants()[&client_a.peer_id().unwrap()]
|
||||
.tracks
|
||||
.contains_key(&track_id));
|
||||
});
|
||||
} else {
|
||||
panic!("unexpected event")
|
||||
}
|
||||
|
||||
display.send_frame(frame.clone());
|
||||
deterministic.run_until_parked();
|
||||
assert_eq!(events_b.borrow().len(), 2);
|
||||
|
||||
// User A leaves the room.
|
||||
active_call_a.update(cx_a, |call, cx| {
|
||||
call.hang_up(cx).unwrap();
|
||||
|
@ -36,7 +36,7 @@ text = { path = "../text" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
anyhow = "1.0.38"
|
||||
async-broadcast = "0.3.4"
|
||||
async-broadcast = "0.4"
|
||||
async-trait = "0.1"
|
||||
futures = "0.3"
|
||||
lazy_static = "1.4"
|
||||
|
@ -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,7 +2512,16 @@ 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();
|
||||
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
|
||||
@ -2526,14 +2530,35 @@ impl Workspace {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (pane, item) in items_to_add {
|
||||
Pane::add_item(self, &pane, item.boxed_clone(), false, false, None, cx);
|
||||
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…
Reference in New Issue
Block a user