Style the chat panel message input (#3956)

Marshall Bowers created

This PR styles the message input in the chat panel.

<img width="242" alt="Screenshot 2024-01-08 at 4 28 33 PM"
src="https://github.com/zed-industries/zed/assets/1486634/48e83692-878e-4891-8e40-8738cdeb23a8">

Release Notes:

- Improved the styling of the message editor in the chat panel.

Change summary

crates/collab_ui/src/chat_panel.rs                | 19 +---
crates/collab_ui/src/chat_panel/message_editor.rs | 59 ++++++++++++++--
2 files changed, 56 insertions(+), 22 deletions(-)

Detailed changes

crates/collab_ui/src/chat_panel.rs 🔗

@@ -19,7 +19,6 @@ use rich_text::RichText;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use std::sync::Arc;
-use theme::ActiveTheme as _;
 use time::{OffsetDateTime, UtcOffset};
 use ui::{prelude::*, Avatar, Button, Icon, IconButton, Label, TabBar, Tooltip};
 use util::{ResultExt, TryFutureExt};
@@ -48,7 +47,7 @@ pub struct ChatPanel {
     languages: Arc<LanguageRegistry>,
     message_list: ListState,
     active_chat: Option<(Model<ChannelChat>, Subscription)>,
-    input_editor: View<MessageEditor>,
+    message_editor: View<MessageEditor>,
     local_timezone: UtcOffset,
     fs: Arc<dyn Fs>,
     width: Option<Pixels>,
@@ -120,7 +119,7 @@ impl ChatPanel {
                 message_list,
                 active_chat: Default::default(),
                 pending_serialization: Task::ready(None),
-                input_editor,
+                message_editor: input_editor,
                 local_timezone: cx.local_timezone(),
                 subscriptions: Vec::new(),
                 workspace: workspace_handle,
@@ -209,7 +208,7 @@ impl ChatPanel {
                 self.message_list.reset(chat.message_count());
 
                 let channel_name = chat.channel(cx).map(|channel| channel.name.clone());
-                self.input_editor.update(cx, |editor, cx| {
+                self.message_editor.update(cx, |editor, cx| {
                     editor.set_channel(channel_id, channel_name, cx);
                 });
             };
@@ -300,13 +299,7 @@ impl ChatPanel {
                     this
                 }
             }))
-            .child(
-                div()
-                    .z_index(1)
-                    .p_2()
-                    .bg(cx.theme().colors().background)
-                    .child(self.input_editor.clone()),
-            )
+            .child(h_stack().p_2().child(self.message_editor.clone()))
             .into_any()
     }
 
@@ -469,7 +462,7 @@ impl ChatPanel {
     fn send(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
         if let Some((chat, _)) = self.active_chat.as_ref() {
             let message = self
-                .input_editor
+                .message_editor
                 .update(cx, |editor, cx| editor.take_message(cx));
 
             if let Some(task) = chat
@@ -585,7 +578,7 @@ impl Render for ChatPanel {
 
 impl FocusableView for ChatPanel {
     fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
-        self.input_editor.read(cx).focus_handle(cx)
+        self.message_editor.read(cx).focus_handle(cx)
     }
 }
 

crates/collab_ui/src/chat_panel/message_editor.rs 🔗

@@ -1,16 +1,19 @@
+use std::{sync::Arc, time::Duration};
+
 use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
 use client::UserId;
 use collections::HashMap;
-use editor::{AnchorRangeExt, Editor};
+use editor::{AnchorRangeExt, Editor, EditorElement, EditorStyle};
 use gpui::{
-    AsyncWindowContext, FocusableView, IntoElement, Model, Render, SharedString, Task, View,
-    ViewContext, WeakView,
+    AsyncWindowContext, FocusableView, FontStyle, FontWeight, HighlightStyle, IntoElement, Model,
+    Render, SharedString, Task, TextStyle, View, ViewContext, WeakView, WhiteSpace,
 };
 use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
 use lazy_static::lazy_static;
 use project::search::SearchQuery;
-use std::{sync::Arc, time::Duration};
-use workspace::item::ItemHandle;
+use settings::Settings;
+use theme::ThemeSettings;
+use ui::prelude::*;
 
 const MENTIONS_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(50);
 
@@ -181,7 +184,14 @@ impl MessageEditor {
                 }
 
                 editor.clear_highlights::<Self>(cx);
-                editor.highlight_text::<Self>(anchor_ranges, gpui::red().into(), cx)
+                editor.highlight_text::<Self>(
+                    anchor_ranges,
+                    HighlightStyle {
+                        font_weight: Some(FontWeight::BOLD),
+                        ..Default::default()
+                    },
+                    cx,
+                )
             });
 
             this.mentions = mentioned_user_ids;
@@ -196,8 +206,39 @@ impl MessageEditor {
 }
 
 impl Render for MessageEditor {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
-        self.editor.to_any()
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let settings = ThemeSettings::get_global(cx);
+        let text_style = TextStyle {
+            color: if self.editor.read(cx).read_only(cx) {
+                cx.theme().colors().text_disabled
+            } else {
+                cx.theme().colors().text
+            },
+            font_family: settings.ui_font.family.clone(),
+            font_features: settings.ui_font.features,
+            font_size: rems(0.875).into(),
+            font_weight: FontWeight::NORMAL,
+            font_style: FontStyle::Normal,
+            line_height: relative(1.3).into(),
+            background_color: None,
+            underline: None,
+            white_space: WhiteSpace::Normal,
+        };
+
+        div()
+            .w_full()
+            .px_2()
+            .py_1()
+            .bg(cx.theme().colors().editor_background)
+            .rounded_md()
+            .child(EditorElement::new(
+                &self.editor,
+                EditorStyle {
+                    local_player: cx.theme().players().local(),
+                    text: text_style,
+                    ..Default::default()
+                },
+            ))
     }
 }
 
@@ -205,7 +246,7 @@ impl Render for MessageEditor {
 mod tests {
     use super::*;
     use client::{Client, User, UserStore};
-    use gpui::{Context as _, TestAppContext, VisualContext as _};
+    use gpui::TestAppContext;
     use language::{Language, LanguageConfig};
     use rpc::proto;
     use settings::SettingsStore;