Improve UI of popover buttons when hovering over chat messages (#9041)

Evren Sen created

### Before


https://github.com/zed-industries/zed/assets/146845123/4a16c1ce-a671-4e39-abc9-3a0cb25bc0cd

### After


https://github.com/zed-industries/zed/assets/146845123/cfab3d00-246e-427d-9c40-8ee520a0a186




Release Notes:
- Improved the UI of popover buttons when hovering over chat messages.

Change summary

assets/icons/reply_arrow.svg       |   4 +
crates/collab_ui/src/chat_panel.rs | 122 ++++++++++++++++++++++---------
crates/ui/src/components/icon.rs   |   2 
3 files changed, 92 insertions(+), 36 deletions(-)

Detailed changes

assets/icons/reply_arrow.svg 🔗

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->

+<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">

+<path d="M20 17V15.8C20 14.1198 20 13.2798 19.673 12.638C19.3854 12.0735 18.9265 11.6146 18.362 11.327C17.7202 11 16.8802 11 15.2 11H4M4 11L8 7M4 11L8 15" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>

+</svg>

crates/collab_ui/src/chat_panel.rs 🔗

@@ -464,19 +464,25 @@ impl ChatPanel {
         v_flex()
             .w_full()
             .relative()
+            .group("")
+            .when(!is_continuation_from_previous, |this| this.pt_2())
+            .child(
+                self.render_popover_buttons(&cx, message_id, can_delete_message)
+                    .neg_mt_2p5(),
+            )
             .child(
                 div()
                     .group("")
                     .bg(background)
                     .rounded_md()
                     .overflow_hidden()
-                    .px_1()
+                    .px_1p5()
                     .py_0p5()
                     .when(!self.has_open_menu(message_id), |this| {
                         this.hover(|style| style.bg(cx.theme().colors().element_hover))
                     })
                     .when(!is_continuation_from_previous, |this| {
-                        this.mt_2().child(
+                        this.child(
                             h_flex()
                                 .text_ui_sm()
                                 .child(div().absolute().child(
@@ -497,19 +503,9 @@ impl ChatPanel {
                                     ))
                                     .size(LabelSize::Small)
                                     .color(Color::Muted),
-                                )
-                                .map(|el| {
-                                    el.child(self.render_popover_button(
-                                        &cx,
-                                        message_id,
-                                        can_delete_message,
-                                    ))
-                                }),
+                                ),
                         )
                     })
-                    .when(is_continuation_from_previous, |el| {
-                        el.child(self.render_popover_button(&cx, message_id, can_delete_message))
-                    })
                     .when(
                         message.reply_to_message_id.is_some() && reply_to_message.is_none(),
                         |this| {
@@ -596,7 +592,7 @@ impl ChatPanel {
         }
     }
 
-    fn render_popover_button(
+    fn render_popover_buttons(
         &self,
         cx: &ViewContext<Self>,
         message_id: Option<u64>,
@@ -605,28 +601,82 @@ impl ChatPanel {
         div()
             .absolute()
             .z_index(1)
-            .right_0()
-            .w_6()
-            .bg(cx.theme().colors().element_hover)
-            .when(!self.has_open_menu(message_id), |el| {
-                el.visible_on_hover("")
-            })
-            .when_some(message_id, |el, message_id| {
-                let chat_panel_view = cx.view().clone();
-
-                el.child(
-                    popover_menu(("menu", message_id))
-                        .trigger(IconButton::new(("trigger", message_id), IconName::Ellipsis))
-                        .menu(move |cx| {
-                            Some(Self::render_message_menu(
-                                &chat_panel_view,
-                                message_id,
-                                can_delete_message,
-                                cx,
-                            ))
-                        }),
-                )
-            })
+            .child(
+                div()
+                    .absolute()
+                    .z_index(1)
+                    .right_8()
+                    .w_6()
+                    .rounded_tl_md()
+                    .rounded_bl_md()
+                    .border_l_1()
+                    .border_t_1()
+                    .border_b_1()
+                    .border_color(cx.theme().colors().element_selected)
+                    .bg(cx.theme().colors().element_background)
+                    .hover(|style| style.bg(cx.theme().colors().element_hover))
+                    .when(!self.has_open_menu(message_id), |el| {
+                        el.visible_on_hover("")
+                    })
+                    .when_some(message_id, |el, message_id| {
+                        el.child(
+                            div()
+                                .id("reply")
+                                .child(
+                                    IconButton::new(("reply", message_id), IconName::ReplyArrow)
+                                        .on_click(cx.listener(move |this, _, cx| {
+                                            this.message_editor.update(cx, |editor, cx| {
+                                                editor.set_reply_to_message_id(message_id);
+                                                editor.focus_handle(cx).focus(cx);
+                                            })
+                                        })),
+                                )
+                                .tooltip(|cx| Tooltip::text("Reply", cx)),
+                        )
+                    }),
+            )
+            .child(
+                div()
+                    .absolute()
+                    .z_index(1)
+                    .right_2()
+                    .w_6()
+                    .rounded_tr_md()
+                    .rounded_br_md()
+                    .border_r_1()
+                    .border_t_1()
+                    .border_b_1()
+                    .border_color(cx.theme().colors().element_selected)
+                    .bg(cx.theme().colors().element_background)
+                    .hover(|style| style.bg(cx.theme().colors().element_hover))
+                    .when(!self.has_open_menu(message_id), |el| {
+                        el.visible_on_hover("")
+                    })
+                    .when_some(message_id, |el, message_id| {
+                        let this = cx.view().clone();
+
+                        el.child(
+                            div()
+                                .id("more")
+                                .child(
+                                    popover_menu(("menu", message_id))
+                                        .trigger(IconButton::new(
+                                            ("trigger", message_id),
+                                            IconName::Ellipsis,
+                                        ))
+                                        .menu(move |cx| {
+                                            Some(Self::render_message_menu(
+                                                &this,
+                                                message_id,
+                                                can_delete_message,
+                                                cx,
+                                            ))
+                                        }),
+                                )
+                                .tooltip(|cx| Tooltip::text("More", cx)),
+                        )
+                    }),
+            )
     }
 
     fn render_message_menu(

crates/ui/src/components/icon.rs 🔗

@@ -101,6 +101,7 @@ pub enum IconName {
     ReplaceAll,
     ReplaceNext,
     Return,
+    ReplyArrow,
     Screen,
     SelectAll,
     Shift,
@@ -194,6 +195,7 @@ impl IconName {
             IconName::ReplaceAll => "icons/replace_all.svg",
             IconName::ReplaceNext => "icons/replace_next.svg",
             IconName::Return => "icons/return.svg",
+            IconName::ReplyArrow => "icons/reply_arrow.svg",
             IconName::Screen => "icons/desktop.svg",
             IconName::SelectAll => "icons/select_all.svg",
             IconName::Shift => "icons/shift.svg",