WIP

Nate Butler created

Change summary

crates/feedback2/src/feedback_modal.rs          | 65 ++++++++++--------
crates/ui2/src/components/button/button.rs      | 48 +++++++++----
crates/ui2/src/components/button/button_like.rs |  8 ++
crates/ui2/src/components/icon.rs               |  3 
crates/ui2/src/prelude.rs                       |  2 
5 files changed, 79 insertions(+), 47 deletions(-)

Detailed changes

crates/feedback2/src/feedback_modal.rs 🔗

@@ -6,15 +6,15 @@ use db::kvp::KEY_VALUE_STORE;
 use editor::{Editor, EditorEvent};
 use futures::AsyncReadExt;
 use gpui::{
-    div, red, rems, serde_json, AppContext, DismissEvent, Div, EventEmitter, FocusHandle,
-    FocusableView, Model, PromptLevel, Render, Task, View, ViewContext,
+    div, rems, serde_json, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
+    Model, PromptLevel, Render, Task, View, ViewContext,
 };
 use isahc::Request;
 use language::Buffer;
 use project::Project;
 use regex::Regex;
 use serde_derive::Serialize;
-use ui::{prelude::*, Button, ButtonStyle, Label, Tooltip};
+use ui::{prelude::*, Button, ButtonStyle, IconPosition, Label, Tooltip};
 use util::ResultExt;
 use workspace::Workspace;
 
@@ -285,29 +285,22 @@ impl Render for FeedbackModal {
         let open_community_repo =
             cx.listener(|_, _, cx| cx.dispatch_action(Box::new(OpenZedCommunityRepo)));
 
-        // TODO: Nate UI pass
         v_stack()
             .elevation_3(cx)
             .key_context("GiveFeedback")
             .on_action(cx.listener(Self::cancel))
             .min_w(rems(40.))
             .max_w(rems(96.))
-            .min_h(rems(24.))
-            .max_h(rems(42.))
-            .gap_2()
+            .h(rems(32.))
             .child(
                 v_stack()
-                                .p_4()
-                    .child(
-                    div()
-                        .size_full()
-                        .child(Label::new("Give Feedback").color(Color::Default))
-                        .child(Label::new("This editor supports markdown").color(Color::Muted)),
-                ),
+                    .px_4()
+                    .pt_4()
+                    .pb_2()
+                    .child(Label::new("Give Feedback").color(Color::Default))
+                    .child(Label::new("This editor supports markdown").color(Color::Muted)),
             )
-            .child(v_stack()
-                .p_4()
-                .child(
+            .child(
                     div()
                         .flex_1()
                         .bg(cx.theme().colors().editor_background)
@@ -317,37 +310,49 @@ impl Render for FeedbackModal {
                 )
                 .child(
                     div().child(
-                        Label::new(format!(
-                            "Characters: {}",
-                            characters_remaining
-                        ))
+                        Label::new(
+                            if !valid_character_count && characters_remaining < 0 {
+                                "Feedback must be at least 10 characters.".to_string()
+                            } else if !valid_character_count && characters_remaining > 5000 {
+                                "Feedback must be less than 5000 characters.".to_string()
+                            } else {
+                                format!(
+                                "Characters: {}",
+                                characters_remaining
+                                )
+                            }
+                        )
                         .map(|this|
                             if valid_character_count {
                                 this.color(Color::Success)
                             } else {
                                 this.color(Color::Error)
-
                             }
                         )
-                    ),
-                )
-                .child(
-                    div()
+                    )
+                    .child(
+                    v_stack()
+                        .p_4()
+                        .child(
+                    h_stack()
                     .bg(cx.theme().colors().editor_background)
                     .border()
                     .border_color(cx.theme().colors().border)
-                    .child(self.email_address_editor.clone())
+                    .child(self.email_address_editor.clone()))
                 )
                 .child(
                     h_stack()
+                        .p_4()
                         .justify_between()
                         .gap_1()
                         .child(Button::new("community_repo", "Community Repo")
-                            .style(ButtonStyle::Filled)
-                            .color(Color::Muted)
+                            .style(ButtonStyle::Transparent)
+                            .icon(Icon::ExternalLink)
+                            .icon_position(IconPosition::End)
+                            .icon_size(IconSize::Small)
                             .on_click(open_community_repo)
                         )
-                        .child(h_stack().justify_between().gap_1()
+                        .child(h_stack().gap_1()
                             .child(
                                 Button::new("cancel_feedback", "Cancel")
                                     .style(ButtonStyle::Subtle)

crates/ui2/src/components/button/button.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{AnyView, DefiniteLength};
 
-use crate::prelude::*;
+use crate::{prelude::*, IconPosition};
 use crate::{
     ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize, Label, LineHeightStyle,
 };
@@ -14,6 +14,7 @@ pub struct Button {
     label_color: Option<Color>,
     selected_label: Option<SharedString>,
     icon: Option<Icon>,
+    icon_position: Option<IconPosition>,
     icon_size: Option<IconSize>,
     icon_color: Option<Color>,
     selected_icon: Option<Icon>,
@@ -27,6 +28,7 @@ impl Button {
             label_color: None,
             selected_label: None,
             icon: None,
+            icon_position: None,
             icon_size: None,
             icon_color: None,
             selected_icon: None,
@@ -48,6 +50,11 @@ impl Button {
         self
     }
 
+    pub fn icon_position(mut self, icon_position: impl Into<Option<IconPosition>>) -> Self {
+        self.icon_position = icon_position.into();
+        self
+    }
+
     pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
         self.icon_size = icon_size.into();
         self
@@ -141,19 +148,30 @@ impl RenderOnce for Button {
             self.label_color.unwrap_or_default()
         };
 
-        self.base
-            .children(self.icon.map(|icon| {
-                ButtonIcon::new(icon)
-                    .disabled(is_disabled)
-                    .selected(is_selected)
-                    .selected_icon(self.selected_icon)
-                    .size(self.icon_size)
-                    .color(self.icon_color)
-            }))
-            .child(
-                Label::new(label)
-                    .color(label_color)
-                    .line_height_style(LineHeightStyle::UILabel),
-            )
+        self.base.child(
+            h_stack()
+                .gap_1()
+                .map(|this| {
+                    if self.icon_position == Some(IconPosition::End) {
+                        this.flex_row_reverse()
+                    } else {
+                        this
+                    }
+                })
+                .flex_row_reverse()
+                .child(
+                    Label::new(label)
+                        .color(label_color)
+                        .line_height_style(LineHeightStyle::UILabel),
+                )
+                .children(self.icon.map(|icon| {
+                    ButtonIcon::new(icon)
+                        .disabled(is_disabled)
+                        .selected(is_selected)
+                        .selected_icon(self.selected_icon)
+                        .size(self.icon_size)
+                        .color(self.icon_color)
+                })),
+        )
     }
 }

crates/ui2/src/components/button/button_like.rs 🔗

@@ -30,6 +30,13 @@ pub trait ButtonCommon: Clickable + Disableable {
     fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
 }
 
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
+pub enum IconPosition {
+    #[default]
+    Start,
+    End,
+}
+
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
 pub enum ButtonStyle {
     /// A filled button with a solid background color. Provides emphasis versus
@@ -344,6 +351,7 @@ impl RenderOnce for ButtonLike {
             .gap_1()
             .px_1()
             .bg(self.style.enabled(cx).background)
+            .when(self.disabled, |this| this.cursor_not_allowed())
             .when(!self.disabled, |this| {
                 this.cursor_pointer()
                     .hover(|hover| hover.bg(self.style.hovered(cx).background))

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

@@ -51,6 +51,7 @@ pub enum Icon {
     CopilotDisabled,
     Dash,
     Envelope,
+    ExternalLink,
     ExclamationTriangle,
     Exit,
     File,
@@ -122,13 +123,13 @@ impl Icon {
             Icon::Close => "icons/x.svg",
             Icon::Collab => "icons/user_group_16.svg",
             Icon::Copilot => "icons/copilot.svg",
-
             Icon::CopilotInit => "icons/copilot_init.svg",
             Icon::CopilotError => "icons/copilot_error.svg",
             Icon::CopilotDisabled => "icons/copilot_disabled.svg",
             Icon::Dash => "icons/dash.svg",
             Icon::Envelope => "icons/feedback.svg",
             Icon::ExclamationTriangle => "icons/warning.svg",
+            Icon::ExternalLink => "icons/external_link.svg",
             Icon::Exit => "icons/exit.svg",
             Icon::File => "icons/file.svg",
             Icon::FileDoc => "icons/file_icons/book.svg",

crates/ui2/src/prelude.rs 🔗

@@ -12,6 +12,6 @@ pub use crate::selectable::*;
 pub use crate::{h_stack, v_stack};
 pub use crate::{Button, ButtonSize, ButtonStyle, IconButton};
 pub use crate::{ButtonCommon, Color, StyledExt};
-pub use crate::{Icon, IconElement, IconSize};
+pub use crate::{Icon, IconElement, IconPosition, IconSize};
 pub use crate::{Label, LabelCommon, LabelSize, LineHeightStyle};
 pub use theme::ActiveTheme;