Use facepile for avatars

Piotr Osiewicz created

Change summary

crates/collab_ui2/src/collab_titlebar_item.rs |  23 +++-
crates/collab_ui2/src/face_pile.rs            | 107 +++++++-------------
crates/workspace2/src/workspace2.rs           |   2 
3 files changed, 54 insertions(+), 78 deletions(-)

Detailed changes

crates/collab_ui2/src/collab_titlebar_item.rs 🔗

@@ -31,9 +31,9 @@ use std::sync::Arc;
 use call::ActiveCall;
 use client::{Client, UserStore};
 use gpui::{
-    div, px, rems, AppContext, Div, InteractiveElement, IntoElement, Model, MouseButton,
-    ParentElement, Render, Stateful, StatefulInteractiveElement, Styled, Subscription, ViewContext,
-    VisualContext, WeakView, WindowBounds,
+    div, px, rems, AppContext, Div, Element, InteractiveElement, IntoElement, Model, MouseButton,
+    ParentElement, Render, RenderOnce, Stateful, StatefulInteractiveElement, Styled, Subscription,
+    ViewContext, VisualContext, WeakView, WindowBounds,
 };
 use project::Project;
 use theme::ActiveTheme;
@@ -41,6 +41,8 @@ use ui::{h_stack, Avatar, Button, ButtonVariant, Color, IconButton, KeyBinding,
 use util::ResultExt;
 use workspace::Workspace;
 
+use crate::face_pile::FacePile;
+
 // const MAX_PROJECT_NAME_LENGTH: usize = 40;
 // const MAX_BRANCH_NAME_LENGTH: usize = 40;
 
@@ -178,16 +180,21 @@ impl Render for CollabTitlebarItem {
             .when_some(
                 users.zip(current_user.clone()),
                 |this, (remote_participants, current_user)| {
-                    this.children(
+                    let mut pile = FacePile::default();
+                    pile.extend(
                         current_user
                             .avatar
                             .clone()
-                            .map(|avatar| div().child(Avatar::data(avatar.clone())))
+                            .map(|avatar| {
+                                div().child(Avatar::data(avatar.clone())).into_any_element()
+                            })
                             .into_iter()
                             .chain(remote_participants.into_iter().flat_map(|(user, peer_id)| {
                                 user.avatar.as_ref().map(|avatar| {
                                     div()
-                                        .child(Avatar::data(avatar.clone()).into_element())
+                                        .child(
+                                            Avatar::data(avatar.clone()).into_element().into_any(),
+                                        )
                                         .on_mouse_down(MouseButton::Left, {
                                             let workspace = workspace.clone();
                                             move |_, cx| {
@@ -198,9 +205,11 @@ impl Render for CollabTitlebarItem {
                                                     .log_err();
                                             }
                                         })
+                                        .into_any_element()
                                 })
                             })),
-                    )
+                    );
+                    this.child(pile.render(cx))
                 },
             )
             .child(div().flex_1())

crates/collab_ui2/src/face_pile.rs 🔗

@@ -1,54 +1,50 @@
-// use std::ops::Range;
+use gpui::{
+    div, AnyElement, Div, IntoElement as _, ParentElement as _, Render, RenderOnce, Styled,
+    ViewContext, WindowContext,
+};
+use ui::Avatar;
 
-// use gpui::{
-//     geometry::{
-//         rect::RectF,
-//         vector::{vec2f, Vector2F},
-//     },
-//     json::ToJson,
-//     serde_json::{self, json},
-//     AnyElement, Axis, Element, View, ViewContext,
-// };
+#[derive(Default)]
+pub(crate) struct FacePile {
+    faces: Vec<AnyElement>,
+}
 
-// pub(crate) struct FacePile<V: View> {
-//     overlap: f32,
-//     faces: Vec<AnyElement<V>>,
-// }
+impl RenderOnce for FacePile {
+    type Rendered = Div;
 
-// impl<V: View> FacePile<V> {
-//     pub fn new(overlap: f32) -> Self {
-//         Self {
-//             overlap,
-//             faces: Vec::new(),
-//         }
-//     }
-// }
+    fn render(self, _: &mut WindowContext) -> Self::Rendered {
+        let player_count = self.faces.len();
+        let player_list = self.faces.into_iter().enumerate().map(|(ix, player)| {
+            let isnt_last = ix < player_count - 1;
 
-// impl<V: View> Element<V> for FacePile<V> {
-//     type LayoutState = ();
-//     type PaintState = ();
+            div().when(isnt_last, |div| div.neg_mr_2()).child(player)
+        });
+        div().p_1().flex().items_center().children(player_list)
+    }
+}
 
+// impl Element for FacePile {
+//     type State = ();
 //     fn layout(
 //         &mut self,
-//         constraint: gpui::SizeConstraint,
-//         view: &mut V,
-//         cx: &mut ViewContext<V>,
-//     ) -> (Vector2F, Self::LayoutState) {
-//         debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
-
+//         state: Option<Self::State>,
+//         cx: &mut WindowContext,
+//     ) -> (LayoutId, Self::State) {
 //         let mut width = 0.;
 //         let mut max_height = 0.;
+//         let mut faces = Vec::with_capacity(self.faces.len());
 //         for face in &mut self.faces {
-//             let layout = face.layout(constraint, view, cx);
+//             let layout = face.layout(cx);
 //             width += layout.x();
 //             max_height = f32::max(max_height, layout.y());
+//             faces.push(layout);
 //         }
 //         width -= self.overlap * self.faces.len().saturating_sub(1) as f32;
-
-//         (
-//             Vector2F::new(width, max_height.clamp(1., constraint.max.y())),
-//             (),
-//         )
+//         (cx.request_layout(&Style::default(), faces), ())
+//         // (
+//         //     Vector2F::new(width, max_height.clamp(1., constraint.max.y())),
+//         //     (),
+//         // ))
 //     }
 
 //     fn paint(
@@ -77,37 +73,10 @@
 
 //         ()
 //     }
-
-//     fn rect_for_text_range(
-//         &self,
-//         _: Range<usize>,
-//         _: RectF,
-//         _: RectF,
-//         _: &Self::LayoutState,
-//         _: &Self::PaintState,
-//         _: &V,
-//         _: &ViewContext<V>,
-//     ) -> Option<RectF> {
-//         None
-//     }
-
-//     fn debug(
-//         &self,
-//         bounds: RectF,
-//         _: &Self::LayoutState,
-//         _: &Self::PaintState,
-//         _: &V,
-//         _: &ViewContext<V>,
-//     ) -> serde_json::Value {
-//         json!({
-//             "type": "FacePile",
-//             "bounds": bounds.to_json()
-//         })
-//     }
 // }
 
-// impl<V: View> Extend<AnyElement<V>> for FacePile<V> {
-//     fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
-//         self.faces.extend(children);
-//     }
-// }
+impl Extend<AnyElement> for FacePile {
+    fn extend<T: IntoIterator<Item = AnyElement>>(&mut self, children: T) {
+        self.faces.extend(children);
+    }
+}

crates/workspace2/src/workspace2.rs 🔗

@@ -2003,8 +2003,6 @@ impl Workspace {
             self.active_pane.update(cx, |pane, cx| {
                 pane.add_item(shared_screen, false, true, None, cx)
             });
-        } else {
-            dbg!("WTF");
         }
     }