Allow opening shared screen via the contacts popover
This commit is contained in:
parent
476020ae84
commit
f99d70500c
5 changed files with 184 additions and 20 deletions
|
@ -17,7 +17,7 @@ use serde::Deserialize;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use theme::IconButton;
|
use theme::IconButton;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::JoinProject;
|
use workspace::{JoinProject, OpenSharedScreen};
|
||||||
|
|
||||||
impl_actions!(contact_list, [RemoveContact, RespondToContactRequest]);
|
impl_actions!(contact_list, [RemoveContact, RespondToContactRequest]);
|
||||||
impl_internal_actions!(contact_list, [ToggleExpanded, Call, LeaveCall]);
|
impl_internal_actions!(contact_list, [ToggleExpanded, Call, LeaveCall]);
|
||||||
|
@ -67,6 +67,10 @@ enum ContactEntry {
|
||||||
host_user_id: u64,
|
host_user_id: u64,
|
||||||
is_last: bool,
|
is_last: bool,
|
||||||
},
|
},
|
||||||
|
ParticipantScreen {
|
||||||
|
peer_id: PeerId,
|
||||||
|
is_last: bool,
|
||||||
|
},
|
||||||
IncomingRequest(Arc<User>),
|
IncomingRequest(Arc<User>),
|
||||||
OutgoingRequest(Arc<User>),
|
OutgoingRequest(Arc<User>),
|
||||||
Contact(Arc<Contact>),
|
Contact(Arc<Contact>),
|
||||||
|
@ -97,6 +101,16 @@ impl PartialEq for ContactEntry {
|
||||||
return project_id_1 == project_id_2;
|
return project_id_1 == project_id_2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ContactEntry::ParticipantScreen {
|
||||||
|
peer_id: peer_id_1, ..
|
||||||
|
} => {
|
||||||
|
if let ContactEntry::ParticipantScreen {
|
||||||
|
peer_id: peer_id_2, ..
|
||||||
|
} = other
|
||||||
|
{
|
||||||
|
return peer_id_1 == peer_id_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
ContactEntry::IncomingRequest(user_1) => {
|
ContactEntry::IncomingRequest(user_1) => {
|
||||||
if let ContactEntry::IncomingRequest(user_2) = other {
|
if let ContactEntry::IncomingRequest(user_2) = other {
|
||||||
return user_1.id == user_2.id;
|
return user_1.id == user_2.id;
|
||||||
|
@ -216,6 +230,15 @@ impl ContactList {
|
||||||
&theme.contact_list,
|
&theme.contact_list,
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
|
ContactEntry::ParticipantScreen { peer_id, is_last } => {
|
||||||
|
Self::render_participant_screen(
|
||||||
|
*peer_id,
|
||||||
|
*is_last,
|
||||||
|
is_selected,
|
||||||
|
&theme.contact_list,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
ContactEntry::IncomingRequest(user) => Self::render_contact_request(
|
||||||
user.clone(),
|
user.clone(),
|
||||||
this.user_store.clone(),
|
this.user_store.clone(),
|
||||||
|
@ -347,6 +370,9 @@ impl ContactList {
|
||||||
follow_user_id: *host_user_id,
|
follow_user_id: *host_user_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
ContactEntry::ParticipantScreen { peer_id, .. } => {
|
||||||
|
cx.dispatch_action(OpenSharedScreen { peer_id: *peer_id });
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,11 +456,10 @@ impl ContactList {
|
||||||
executor.clone(),
|
executor.clone(),
|
||||||
));
|
));
|
||||||
for mat in matches {
|
for mat in matches {
|
||||||
let participant = &room.remote_participants()[&PeerId(mat.candidate_id as u32)];
|
let peer_id = PeerId(mat.candidate_id as u32);
|
||||||
|
let participant = &room.remote_participants()[&peer_id];
|
||||||
participant_entries.push(ContactEntry::CallParticipant {
|
participant_entries.push(ContactEntry::CallParticipant {
|
||||||
user: room.remote_participants()[&PeerId(mat.candidate_id as u32)]
|
user: participant.user.clone(),
|
||||||
.user
|
|
||||||
.clone(),
|
|
||||||
is_pending: false,
|
is_pending: false,
|
||||||
});
|
});
|
||||||
let mut projects = participant.projects.iter().peekable();
|
let mut projects = participant.projects.iter().peekable();
|
||||||
|
@ -443,7 +468,13 @@ impl ContactList {
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
worktree_root_names: project.worktree_root_names.clone(),
|
worktree_root_names: project.worktree_root_names.clone(),
|
||||||
host_user_id: participant.user.id,
|
host_user_id: participant.user.id,
|
||||||
is_last: projects.peek().is_none(),
|
is_last: projects.peek().is_none() && participant.tracks.is_empty(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if !participant.tracks.is_empty() {
|
||||||
|
participant_entries.push(ContactEntry::ParticipantScreen {
|
||||||
|
peer_id,
|
||||||
|
is_last: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,6 +794,102 @@ impl ContactList {
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_participant_screen(
|
||||||
|
peer_id: PeerId,
|
||||||
|
is_last: bool,
|
||||||
|
is_selected: bool,
|
||||||
|
theme: &theme::ContactList,
|
||||||
|
cx: &mut RenderContext<Self>,
|
||||||
|
) -> ElementBox {
|
||||||
|
let font_cache = cx.font_cache();
|
||||||
|
let host_avatar_height = theme
|
||||||
|
.contact_avatar
|
||||||
|
.width
|
||||||
|
.or(theme.contact_avatar.height)
|
||||||
|
.unwrap_or(0.);
|
||||||
|
let row = &theme.project_row.default;
|
||||||
|
let tree_branch = theme.tree_branch;
|
||||||
|
let line_height = row.name.text.line_height(font_cache);
|
||||||
|
let cap_height = row.name.text.cap_height(font_cache);
|
||||||
|
let baseline_offset =
|
||||||
|
row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
|
||||||
|
|
||||||
|
MouseEventHandler::<OpenSharedScreen>::new(peer_id.0 as usize, cx, |mouse_state, _| {
|
||||||
|
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
|
||||||
|
let row = theme.project_row.style_for(mouse_state, is_selected);
|
||||||
|
|
||||||
|
Flex::row()
|
||||||
|
.with_child(
|
||||||
|
Stack::new()
|
||||||
|
.with_child(
|
||||||
|
Canvas::new(move |bounds, _, cx| {
|
||||||
|
let start_x = bounds.min_x() + (bounds.width() / 2.)
|
||||||
|
- (tree_branch.width / 2.);
|
||||||
|
let end_x = bounds.max_x();
|
||||||
|
let start_y = bounds.min_y();
|
||||||
|
let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
|
||||||
|
|
||||||
|
cx.scene.push_quad(gpui::Quad {
|
||||||
|
bounds: RectF::from_points(
|
||||||
|
vec2f(start_x, start_y),
|
||||||
|
vec2f(
|
||||||
|
start_x + tree_branch.width,
|
||||||
|
if is_last { end_y } else { bounds.max_y() },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
background: Some(tree_branch.color),
|
||||||
|
border: gpui::Border::default(),
|
||||||
|
corner_radius: 0.,
|
||||||
|
});
|
||||||
|
cx.scene.push_quad(gpui::Quad {
|
||||||
|
bounds: RectF::from_points(
|
||||||
|
vec2f(start_x, end_y),
|
||||||
|
vec2f(end_x, end_y + tree_branch.width),
|
||||||
|
),
|
||||||
|
background: Some(tree_branch.color),
|
||||||
|
border: gpui::Border::default(),
|
||||||
|
corner_radius: 0.,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.constrained()
|
||||||
|
.with_width(host_avatar_height)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
Svg::new("icons/disable_screen_sharing_12.svg")
|
||||||
|
.with_color(row.icon.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(row.icon.width)
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.contained()
|
||||||
|
.with_style(row.icon.container)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
Label::new("Screen Sharing".into(), row.name.text.clone())
|
||||||
|
.aligned()
|
||||||
|
.left()
|
||||||
|
.contained()
|
||||||
|
.with_style(row.name.container)
|
||||||
|
.flex(1., false)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.constrained()
|
||||||
|
.with_height(theme.row_height)
|
||||||
|
.contained()
|
||||||
|
.with_style(row.container)
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
|
cx.dispatch_action(OpenSharedScreen { peer_id });
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
fn render_header(
|
fn render_header(
|
||||||
section: Section,
|
section: Section,
|
||||||
theme: &theme::ContactList,
|
theme: &theme::ContactList,
|
||||||
|
|
|
@ -3835,6 +3835,11 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
self.app.notify_view(self.window_id, self.view_id);
|
self.app.notify_view(self.window_id, self.view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_action(&mut self, action: impl Action) {
|
||||||
|
self.app
|
||||||
|
.dispatch_action_at(self.window_id, self.view_id, action)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
|
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
|
||||||
self.app
|
self.app
|
||||||
.dispatch_any_action_at(self.window_id, self.view_id, action)
|
.dispatch_any_action_at(self.window_id, self.view_id, action)
|
||||||
|
|
|
@ -120,6 +120,7 @@ pub struct ContactList {
|
||||||
pub struct ProjectRow {
|
pub struct ProjectRow {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
pub icon: Icon,
|
||||||
pub name: ContainedText,
|
pub name: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +382,6 @@ pub struct Icon {
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
pub path: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Copy, Default)]
|
#[derive(Deserialize, Clone, Copy, Default)]
|
||||||
|
|
|
@ -121,12 +121,18 @@ pub struct JoinProject {
|
||||||
pub follow_user_id: u64,
|
pub follow_user_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct OpenSharedScreen {
|
||||||
|
pub peer_id: PeerId,
|
||||||
|
}
|
||||||
|
|
||||||
impl_internal_actions!(
|
impl_internal_actions!(
|
||||||
workspace,
|
workspace,
|
||||||
[
|
[
|
||||||
OpenPaths,
|
OpenPaths,
|
||||||
ToggleFollow,
|
ToggleFollow,
|
||||||
JoinProject,
|
JoinProject,
|
||||||
|
OpenSharedScreen,
|
||||||
RemoveWorktreeFromProject
|
RemoveWorktreeFromProject
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -166,6 +172,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||||
cx.add_async_action(Workspace::follow_next_collaborator);
|
cx.add_async_action(Workspace::follow_next_collaborator);
|
||||||
cx.add_async_action(Workspace::close);
|
cx.add_async_action(Workspace::close);
|
||||||
cx.add_async_action(Workspace::save_all);
|
cx.add_async_action(Workspace::save_all);
|
||||||
|
cx.add_action(Workspace::open_shared_screen);
|
||||||
cx.add_action(Workspace::add_folder_to_project);
|
cx.add_action(Workspace::add_folder_to_project);
|
||||||
cx.add_action(Workspace::remove_folder_from_project);
|
cx.add_action(Workspace::remove_folder_from_project);
|
||||||
cx.add_action(
|
cx.add_action(
|
||||||
|
@ -1788,6 +1795,15 @@ impl Workspace {
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_shared_screen(&mut self, action: &OpenSharedScreen, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(shared_screen) =
|
||||||
|
self.shared_screen_for_peer(action.peer_id, &self.active_pane, cx)
|
||||||
|
{
|
||||||
|
let pane = self.active_pane.clone();
|
||||||
|
Pane::add_item(self, &pane, Box::new(shared_screen), false, true, None, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
|
pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
|
||||||
let result = self.panes.iter().find_map(|pane| {
|
let result = self.panes.iter().find_map(|pane| {
|
||||||
pane.read(cx)
|
pane.read(cx)
|
||||||
|
@ -2534,20 +2550,10 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
call::ParticipantLocation::UnsharedProject => {}
|
call::ParticipantLocation::UnsharedProject => {}
|
||||||
call::ParticipantLocation::External => {
|
call::ParticipantLocation::External => {
|
||||||
let track = participant.tracks.values().next()?.clone();
|
for (pane, _) in self.follower_states_by_leader.get(&leader_id)? {
|
||||||
let user = participant.user.clone();
|
if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) {
|
||||||
|
items_to_add.push((pane.clone(), Box::new(shared_screen)));
|
||||||
'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)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2562,6 +2568,27 @@ impl Workspace {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shared_screen_for_peer(
|
||||||
|
&self,
|
||||||
|
peer_id: PeerId,
|
||||||
|
pane: &ViewHandle<Pane>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<ViewHandle<SharedScreen>> {
|
||||||
|
let call = self.active_call()?;
|
||||||
|
let room = call.read(cx).room()?.read(cx);
|
||||||
|
let participant = room.remote_participants().get(&peer_id)?;
|
||||||
|
let track = participant.tracks.values().next()?.clone();
|
||||||
|
let user = participant.user.clone();
|
||||||
|
|
||||||
|
for item in pane.read(cx).items_of_type::<SharedScreen>() {
|
||||||
|
if item.read(cx).peer_id == peer_id {
|
||||||
|
return Some(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(cx.add_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
pub fn on_window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
||||||
if !active {
|
if !active {
|
||||||
for pane in &self.panes {
|
for pane in &self.panes {
|
||||||
|
|
|
@ -166,6 +166,11 @@ export default function contactsPanel(colorScheme: ColorScheme) {
|
||||||
projectRow: {
|
projectRow: {
|
||||||
...projectRow,
|
...projectRow,
|
||||||
background: background(layer, "on"),
|
background: background(layer, "on"),
|
||||||
|
icon: {
|
||||||
|
margin: { left: nameMargin },
|
||||||
|
color: foreground(layer, "variant"),
|
||||||
|
width: 12,
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
...projectRow.name,
|
...projectRow.name,
|
||||||
...text(layer, "mono", { size: "sm" }),
|
...text(layer, "mono", { size: "sm" }),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue