Begin adding collaborator list popover
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
e96d52f35a
commit
2b6aa3f5d1
4 changed files with 175 additions and 11 deletions
|
@ -1,4 +1,7 @@
|
||||||
use crate::{contact_notification::ContactNotification, contacts_popover, ToggleScreenSharing};
|
use crate::{
|
||||||
|
collaborator_list_popover, collaborator_list_popover::CollaboratorListPopover,
|
||||||
|
contact_notification::ContactNotification, contacts_popover, ToggleScreenSharing,
|
||||||
|
};
|
||||||
use call::{ActiveCall, ParticipantLocation};
|
use call::{ActiveCall, ParticipantLocation};
|
||||||
use client::{proto::PeerId, Authenticate, ContactEventKind, User, UserStore};
|
use client::{proto::PeerId, Authenticate, ContactEventKind, User, UserStore};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
|
@ -20,10 +23,16 @@ use workspace::{FollowNextCollaborator, JoinProject, ToggleFollow, Workspace};
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
collab,
|
collab,
|
||||||
[ToggleCollaborationMenu, ShareProject, UnshareProject]
|
[
|
||||||
|
ToggleCollaboratorList,
|
||||||
|
ToggleCollaborationMenu,
|
||||||
|
ShareProject,
|
||||||
|
UnshareProject
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn init(cx: &mut MutableAppContext) {
|
pub fn init(cx: &mut MutableAppContext) {
|
||||||
|
cx.add_action(CollabTitlebarItem::toggle_collaborator_list_popover);
|
||||||
cx.add_action(CollabTitlebarItem::toggle_contacts_popover);
|
cx.add_action(CollabTitlebarItem::toggle_contacts_popover);
|
||||||
cx.add_action(CollabTitlebarItem::share_project);
|
cx.add_action(CollabTitlebarItem::share_project);
|
||||||
cx.add_action(CollabTitlebarItem::unshare_project);
|
cx.add_action(CollabTitlebarItem::unshare_project);
|
||||||
|
@ -33,6 +42,7 @@ pub struct CollabTitlebarItem {
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
contacts_popover: Option<ViewHandle<ContactsPopover>>,
|
contacts_popover: Option<ViewHandle<ContactsPopover>>,
|
||||||
|
collaborator_list_popover: Option<ViewHandle<CollaboratorListPopover>>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,9 +86,11 @@ impl View for CollabTitlebarItem {
|
||||||
left_container.add_child(self.render_share_unshare_button(&workspace, &theme, cx));
|
left_container.add_child(self.render_share_unshare_button(&workspace, &theme, cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut container = Flex::row();
|
left_container.add_child(self.render_toggle_collaborator_list_button(&theme, cx));
|
||||||
|
|
||||||
container.add_children(self.render_toggle_screen_sharing_button(&theme, cx));
|
let mut right_container = Flex::row();
|
||||||
|
|
||||||
|
right_container.add_children(self.render_toggle_screen_sharing_button(&theme, cx));
|
||||||
|
|
||||||
if workspace.read(cx).client().status().borrow().is_connected() {
|
if workspace.read(cx).client().status().borrow().is_connected() {
|
||||||
let project = workspace.read(cx).project().read(cx);
|
let project = workspace.read(cx).project().read(cx);
|
||||||
|
@ -86,16 +98,16 @@ impl View for CollabTitlebarItem {
|
||||||
|| project.is_remote()
|
|| project.is_remote()
|
||||||
|| ActiveCall::global(cx).read(cx).room().is_none()
|
|| ActiveCall::global(cx).read(cx).room().is_none()
|
||||||
{
|
{
|
||||||
container.add_child(self.render_toggle_contacts_button(&theme, cx));
|
right_container.add_child(self.render_toggle_contacts_button(&theme, cx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container.add_children(self.render_collaborators(&workspace, &theme, cx));
|
right_container.add_children(self.render_collaborators(&workspace, &theme, cx));
|
||||||
container.add_children(self.render_current_user(&workspace, &theme, cx));
|
right_container.add_children(self.render_current_user(&workspace, &theme, cx));
|
||||||
container.add_children(self.render_connection_status(&workspace, cx));
|
right_container.add_children(self.render_connection_status(&workspace, cx));
|
||||||
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(left_container.boxed())
|
.with_child(left_container.boxed())
|
||||||
.with_child(container.aligned().right().boxed())
|
.with_child(right_container.aligned().right().boxed())
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +153,7 @@ impl CollabTitlebarItem {
|
||||||
workspace: workspace.downgrade(),
|
workspace: workspace.downgrade(),
|
||||||
user_store: user_store.clone(),
|
user_store: user_store.clone(),
|
||||||
contacts_popover: None,
|
contacts_popover: None,
|
||||||
|
collaborator_list_popover: None,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,6 +191,82 @@ impl CollabTitlebarItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_toggle_collaborator_list_button(
|
||||||
|
&self,
|
||||||
|
theme: &Theme,
|
||||||
|
cx: &mut RenderContext<Self>,
|
||||||
|
) -> ElementBox {
|
||||||
|
let titlebar = &theme.workspace.titlebar;
|
||||||
|
|
||||||
|
Stack::new()
|
||||||
|
.with_child(
|
||||||
|
MouseEventHandler::<ToggleCollaboratorList>::new(0, cx, |state, _| {
|
||||||
|
let style = titlebar
|
||||||
|
.toggle_contacts_button
|
||||||
|
.style_for(state, self.collaborator_list_popover.is_some());
|
||||||
|
Svg::new("icons/plus_8.svg")
|
||||||
|
.with_color(style.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.icon_width)
|
||||||
|
.aligned()
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.button_width)
|
||||||
|
.with_height(style.button_width)
|
||||||
|
.contained()
|
||||||
|
.with_style(style.container)
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
|
cx.dispatch_action(ToggleCollaboratorList);
|
||||||
|
})
|
||||||
|
.aligned()
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_children(self.collaborator_list_popover.as_ref().map(|popover| {
|
||||||
|
Overlay::new(
|
||||||
|
ChildView::new(popover, cx)
|
||||||
|
.contained()
|
||||||
|
.with_margin_top(titlebar.height)
|
||||||
|
.with_margin_left(titlebar.toggle_contacts_button.default.button_width)
|
||||||
|
.with_margin_right(-titlebar.toggle_contacts_button.default.button_width)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_fit_mode(OverlayFitMode::SwitchAnchor)
|
||||||
|
.with_anchor_corner(AnchorCorner::BottomLeft)
|
||||||
|
.with_z_index(999)
|
||||||
|
.boxed()
|
||||||
|
}))
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_collaborator_list_popover(
|
||||||
|
&mut self,
|
||||||
|
_: &ToggleCollaboratorList,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
match self.collaborator_list_popover.take() {
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
let view = cx.add_view(|cx| CollaboratorListPopover::new(cx));
|
||||||
|
|
||||||
|
cx.subscribe(&view, |this, _, event, cx| {
|
||||||
|
match event {
|
||||||
|
collaborator_list_popover::Event::Dismissed => {
|
||||||
|
this.collaborator_list_popover = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
self.collaborator_list_popover = Some(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggle_contacts_popover(
|
pub fn toggle_contacts_popover(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &ToggleCollaborationMenu,
|
_: &ToggleCollaborationMenu,
|
||||||
|
@ -213,6 +302,7 @@ impl CollabTitlebarItem {
|
||||||
cx: &mut RenderContext<Self>,
|
cx: &mut RenderContext<Self>,
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
let titlebar = &theme.workspace.titlebar;
|
let titlebar = &theme.workspace.titlebar;
|
||||||
|
|
||||||
let badge = if self
|
let badge = if self
|
||||||
.user_store
|
.user_store
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -233,6 +323,7 @@ impl CollabTitlebarItem {
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<ToggleCollaborationMenu>::new(0, cx, |state, _| {
|
MouseEventHandler::<ToggleCollaborationMenu>::new(0, cx, |state, _| {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod collab_titlebar_item;
|
mod collab_titlebar_item;
|
||||||
|
mod collaborator_list_popover;
|
||||||
mod contact_finder;
|
mod contact_finder;
|
||||||
mod contact_list;
|
mod contact_list;
|
||||||
mod contact_notification;
|
mod contact_notification;
|
||||||
|
|
71
crates/collab_ui/src/collaborator_list_popover.rs
Normal file
71
crates/collab_ui/src/collaborator_list_popover.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use call::ActiveCall;
|
||||||
|
use gpui::{elements::*, Entity, MouseButton, RenderContext, View, ViewContext};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
|
use crate::collab_titlebar_item::ToggleCollaboratorList;
|
||||||
|
|
||||||
|
pub(crate) enum Event {
|
||||||
|
Dismissed,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CollaboratorListPopover {
|
||||||
|
list_state: ListState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for CollaboratorListPopover {
|
||||||
|
type Event = Event;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for CollaboratorListPopover {
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"CollaboratorListPopover"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||||
|
let theme = cx.global::<Settings>().theme.clone();
|
||||||
|
|
||||||
|
MouseEventHandler::<Self>::new(0, cx, |_, _| {
|
||||||
|
List::new(self.list_state.clone())
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.contacts_popover.container) //TODO: Change the name of this theme key
|
||||||
|
.constrained()
|
||||||
|
.with_width(theme.contacts_popover.width)
|
||||||
|
.with_height(theme.contacts_popover.height)
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.on_down_out(MouseButton::Left, move |_, cx| {
|
||||||
|
cx.dispatch_action(ToggleCollaboratorList);
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
|
cx.emit(Event::Dismissed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CollaboratorListPopover {
|
||||||
|
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let active_call = ActiveCall::global(cx);
|
||||||
|
let collaborator_count = active_call
|
||||||
|
.read(cx)
|
||||||
|
.room()
|
||||||
|
.map_or(0, |room| room.read(cx).remote_participants().len());
|
||||||
|
Self {
|
||||||
|
list_state: ListState::new(
|
||||||
|
collaborator_count,
|
||||||
|
Orientation::Top,
|
||||||
|
0.,
|
||||||
|
cx,
|
||||||
|
|_, index, cx| {
|
||||||
|
let theme = &cx.global::<Settings>().theme;
|
||||||
|
Label::new(
|
||||||
|
format!("Participant {index}"),
|
||||||
|
theme.contact_list.contact_username.text.clone(),
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ impl_internal_actions!(contact_list, [ToggleExpanded, Call, LeaveCall]);
|
||||||
pub fn init(cx: &mut MutableAppContext) {
|
pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(ContactList::remove_contact);
|
cx.add_action(ContactList::remove_contact);
|
||||||
cx.add_action(ContactList::respond_to_contact_request);
|
cx.add_action(ContactList::respond_to_contact_request);
|
||||||
cx.add_action(ContactList::clear_filter);
|
cx.add_action(ContactList::cancel);
|
||||||
cx.add_action(ContactList::select_next);
|
cx.add_action(ContactList::select_next);
|
||||||
cx.add_action(ContactList::select_prev);
|
cx.add_action(ContactList::select_prev);
|
||||||
cx.add_action(ContactList::confirm);
|
cx.add_action(ContactList::confirm);
|
||||||
|
@ -326,7 +326,7 @@ impl ContactList {
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
|
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
|
||||||
let did_clear = self.filter_editor.update(cx, |editor, cx| {
|
let did_clear = self.filter_editor.update(cx, |editor, cx| {
|
||||||
if editor.buffer().read(cx).len(cx) > 0 {
|
if editor.buffer().read(cx).len(cx) > 0 {
|
||||||
editor.set_text("", cx);
|
editor.set_text("", cx);
|
||||||
|
@ -335,6 +335,7 @@ impl ContactList {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if !did_clear {
|
if !did_clear {
|
||||||
cx.emit(Event::Dismissed);
|
cx.emit(Event::Dismissed);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue