Associate collaborator avatars with "ribbons" corresponding to their cursor color
This commit is contained in:
parent
a5039cad65
commit
fbc307cd5e
6 changed files with 70 additions and 24 deletions
|
@ -371,12 +371,7 @@ impl EditorElement {
|
||||||
let content_origin = bounds.origin() + layout.text_offset;
|
let content_origin = bounds.origin() + layout.text_offset;
|
||||||
|
|
||||||
for (replica_id, selections) in &layout.selections {
|
for (replica_id, selections) in &layout.selections {
|
||||||
let style_ix = *replica_id as usize % (style.guest_selections.len() + 1);
|
let style = style.replica_selection_style(*replica_id);
|
||||||
let style = if style_ix == 0 {
|
|
||||||
&style.selection
|
|
||||||
} else {
|
|
||||||
&style.guest_selections[style_ix - 1]
|
|
||||||
};
|
|
||||||
|
|
||||||
for selection in selections {
|
for selection in selections {
|
||||||
if selection.start != selection.end {
|
if selection.start != selection.end {
|
||||||
|
|
|
@ -25,6 +25,11 @@ impl Align {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bottom(mut self) -> Self {
|
||||||
|
self.alignment.set_y(1.0);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn left(mut self) -> Self {
|
pub fn left(mut self) -> Self {
|
||||||
self.alignment.set_x(-1.0);
|
self.alignment.set_x(-1.0);
|
||||||
self
|
self
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub enum Event {
|
||||||
Closed,
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Collaborator {
|
pub struct Collaborator {
|
||||||
pub user: Arc<User>,
|
pub user: Arc<User>,
|
||||||
pub peer_id: PeerId,
|
pub peer_id: PeerId,
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub struct Titlebar {
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
pub title: TextStyle,
|
pub title: TextStyle,
|
||||||
pub avatar_width: f32,
|
pub avatar_width: f32,
|
||||||
|
pub avatar_ribbon: AvatarRibbon,
|
||||||
pub offline_icon: OfflineIcon,
|
pub offline_icon: OfflineIcon,
|
||||||
pub icon_color: Color,
|
pub icon_color: Color,
|
||||||
pub avatar: ImageStyle,
|
pub avatar: ImageStyle,
|
||||||
|
@ -53,6 +54,14 @@ pub struct Titlebar {
|
||||||
pub outdated_warning: ContainedText,
|
pub outdated_warning: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
pub struct AvatarRibbon {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub container: ContainerStyle,
|
||||||
|
pub width: f32,
|
||||||
|
pub height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
pub struct OfflineIcon {
|
pub struct OfflineIcon {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -276,6 +285,15 @@ impl EditorStyle {
|
||||||
pub fn placeholder_text(&self) -> &TextStyle {
|
pub fn placeholder_text(&self) -> &TextStyle {
|
||||||
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
|
||||||
|
let style_ix = replica_id as usize % (self.guest_selections.len() + 1);
|
||||||
|
if style_ix == 0 {
|
||||||
|
&self.selection
|
||||||
|
} else {
|
||||||
|
&self.guest_selections[style_ix - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputEditorStyle {
|
impl InputEditorStyle {
|
||||||
|
|
|
@ -968,11 +968,17 @@ impl Workspace {
|
||||||
Align::new(
|
Align::new(
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_children(self.render_collaborators(theme, cx))
|
.with_children(self.render_collaborators(theme, cx))
|
||||||
.with_child(self.render_avatar(
|
.with_child(
|
||||||
self.user_store.read(cx).current_user().as_ref(),
|
self.render_avatar(
|
||||||
theme,
|
self.user_store.read(cx).current_user().as_ref(),
|
||||||
cx,
|
self.project
|
||||||
))
|
.read(cx)
|
||||||
|
.active_worktree()
|
||||||
|
.map(|worktree| worktree.read(cx).replica_id()),
|
||||||
|
theme,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
)
|
||||||
.with_children(self.render_connection_status())
|
.with_children(self.render_connection_status())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
|
@ -991,14 +997,19 @@ impl Workspace {
|
||||||
fn render_collaborators(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> Vec<ElementBox> {
|
fn render_collaborators(&self, theme: &Theme, cx: &mut RenderContext<Self>) -> Vec<ElementBox> {
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
if let Some(active_worktree) = self.project.read(cx).active_worktree() {
|
if let Some(active_worktree) = self.project.read(cx).active_worktree() {
|
||||||
let users = active_worktree
|
let collaborators = active_worktree
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.collaborators()
|
.collaborators()
|
||||||
.values()
|
.values()
|
||||||
.map(|c| c.user.clone())
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for user in users {
|
for collaborator in collaborators {
|
||||||
elements.push(self.render_avatar(Some(&user), theme, cx));
|
elements.push(self.render_avatar(
|
||||||
|
Some(&collaborator.user),
|
||||||
|
Some(collaborator.replica_id),
|
||||||
|
theme,
|
||||||
|
cx,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elements
|
elements
|
||||||
|
@ -1007,21 +1018,37 @@ impl Workspace {
|
||||||
fn render_avatar(
|
fn render_avatar(
|
||||||
&self,
|
&self,
|
||||||
user: Option<&Arc<User>>,
|
user: Option<&Arc<User>>,
|
||||||
|
replica_id: Option<u16>,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
cx: &mut RenderContext<Self>,
|
cx: &mut RenderContext<Self>,
|
||||||
) -> ElementBox {
|
) -> ElementBox {
|
||||||
if let Some(avatar) = user.and_then(|user| user.avatar.clone()) {
|
if let Some(avatar) = user.and_then(|user| user.avatar.clone()) {
|
||||||
ConstrainedBox::new(
|
ConstrainedBox::new(
|
||||||
Align::new(
|
Stack::new()
|
||||||
ConstrainedBox::new(
|
.with_child(
|
||||||
Image::new(avatar)
|
ConstrainedBox::new(
|
||||||
.with_style(theme.workspace.titlebar.avatar)
|
Image::new(avatar)
|
||||||
|
.with_style(theme.workspace.titlebar.avatar)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_width(theme.workspace.titlebar.avatar_width)
|
||||||
|
.aligned()
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
Container::new(Empty::new().boxed())
|
||||||
|
.with_style(theme.workspace.titlebar.avatar_ribbon.container)
|
||||||
|
.with_background_color(replica_id.map_or(Default::default(), |id| {
|
||||||
|
theme.editor.replica_selection_style(id).cursor
|
||||||
|
}))
|
||||||
|
.constrained()
|
||||||
|
.with_width(theme.workspace.titlebar.avatar_ribbon.width)
|
||||||
|
.with_height(theme.workspace.titlebar.avatar_ribbon.height)
|
||||||
|
.aligned()
|
||||||
|
.bottom()
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_width(theme.workspace.titlebar.avatar_width)
|
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
|
||||||
.boxed(),
|
|
||||||
)
|
)
|
||||||
.with_width(theme.workspace.right_sidebar.width)
|
.with_width(theme.workspace.right_sidebar.width)
|
||||||
.boxed()
|
.boxed()
|
||||||
|
|
|
@ -10,8 +10,9 @@ height = 32
|
||||||
border = { width = 1, bottom = true, color = "$border.0" }
|
border = { width = 1, bottom = true, color = "$border.0" }
|
||||||
title = "$text.0"
|
title = "$text.0"
|
||||||
avatar_width = 18
|
avatar_width = 18
|
||||||
icon_color = "$text.2.color"
|
|
||||||
avatar = { corner_radius = 10, border = { width = 1, color = "#00000088" } }
|
avatar = { corner_radius = 10, border = { width = 1, color = "#00000088" } }
|
||||||
|
avatar_ribbon = { background = "#ff0000", height = 3, width = 12 }
|
||||||
|
icon_color = "$text.2.color"
|
||||||
outdated_warning = { extends = "$text.2", size = 13 }
|
outdated_warning = { extends = "$text.2", size = 13 }
|
||||||
|
|
||||||
[workspace.titlebar.sign_in_prompt]
|
[workspace.titlebar.sign_in_prompt]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue