Initial dedicated face pile element

Rather than overload Flex with yet another special case, add a dedicated
element to handle this overlap instead

Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Julia 2023-02-21 16:12:11 -05:00
parent 6a731233c5
commit 7f4da80386
4 changed files with 110 additions and 15 deletions

View File

@ -1,6 +1,7 @@
use crate::{
collaborator_list_popover, collaborator_list_popover::CollaboratorListPopover,
contact_notification::ContactNotification, contacts_popover, ToggleScreenSharing,
contact_notification::ContactNotification, contacts_popover, face_pile::FacePile,
ToggleScreenSharing,
};
use call::{ActiveCall, ParticipantLocation, Room};
use client::{proto::PeerId, Authenticate, ContactEventKind, User, UserStore};
@ -627,7 +628,7 @@ impl CollabTitlebarItem {
let content = Stack::new()
.with_children(user.avatar.as_ref().map(|avatar| {
let flex = Flex::row()
let face_pile = FacePile::new(theme.workspace.titlebar.follower_avatar_overlap)
.with_child(Self::render_face(avatar.clone(), avatar_style.clone()))
.with_children(
(|| {
@ -652,16 +653,10 @@ impl CollabTitlebarItem {
}
})?;
Some(
Container::new(Self::render_face(
avatar.clone(),
theme.workspace.titlebar.follower_avatar.clone(),
))
.with_margin_left(
-1.0 * theme.workspace.titlebar.follower_avatar_overlap,
)
.boxed(),
)
Some(Self::render_face(
avatar.clone(),
theme.workspace.titlebar.follower_avatar.clone(),
))
}))
})()
.into_iter()
@ -679,11 +674,11 @@ impl CollabTitlebarItem {
});
if followed_by_self {
let color = theme.editor.replica_selection_style(replica_id).selection;
return flex.contained().with_background_color(color).boxed();
return face_pile.contained().with_background_color(color).boxed();
}
}
flex.boxed()
face_pile.boxed()
}))
.with_children((|| {
let replica_id = replica_id?;

View File

@ -4,6 +4,7 @@ mod contact_finder;
mod contact_list;
mod contact_notification;
mod contacts_popover;
mod face_pile;
mod incoming_call_notification;
mod notifications;
mod project_shared_notification;

View File

@ -0,0 +1,99 @@
use std::ops::Range;
use gpui::{
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json::ToJson,
serde_json::{self, json},
Axis, DebugContext, Element, ElementBox, MeasurementContext, PaintContext,
};
pub(crate) struct FacePile {
overlap: f32,
faces: Vec<ElementBox>,
}
impl FacePile {
pub fn new(overlap: f32) -> FacePile {
FacePile {
overlap,
faces: Vec::new(),
}
}
}
impl Element for FacePile {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: gpui::SizeConstraint,
cx: &mut gpui::LayoutContext,
) -> (Vector2F, Self::LayoutState) {
debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
let mut width = 0.;
for face in &mut self.faces {
width += face.layout(constraint, cx).x();
}
width -= self.overlap * self.faces.len().saturating_sub(1) as f32;
(Vector2F::new(width, constraint.max.y()), ())
}
fn paint(
&mut self,
bounds: RectF,
visible_bounds: RectF,
_layout: &mut Self::LayoutState,
cx: &mut PaintContext,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
let origin_y = bounds.upper_right().y();
let mut origin_x = bounds.upper_right().x();
for face in self.faces.iter_mut().rev() {
let size = face.size();
origin_x -= size.x();
face.paint(vec2f(origin_x, origin_y), visible_bounds, cx);
origin_x += self.overlap;
}
()
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &DebugContext,
) -> serde_json::Value {
json!({
"type": "FacePile",
"bounds": bounds.to_json()
})
}
}
impl Extend<ElementBox> for FacePile {
fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
self.faces.extend(children);
}
}

View File

@ -119,7 +119,7 @@ export default function workspace(colorScheme: ColorScheme) {
width: 1,
},
},
followerAvatarOverlap: 4,
followerAvatarOverlap: 6,
avatarRibbon: {
height: 3,
width: 12,