diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index 4120f38cfd663a245342aa12399c4871c0c5d716..bd9f32e73cb8956580e484b43dc997747e9f6554 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -25,7 +25,7 @@ use settings::Settings; use workspace::{ item::{Item, ItemHandle}, searchable::{SearchableItem, SearchableItemHandle}, - AppState, StatusItemView, Workspace, + AppState, StatusItemView, ToolbarItemLocation, ToolbarItemView, Workspace, }; use crate::system_specs::SystemSpecs; @@ -35,7 +35,7 @@ const FEEDBACK_PLACEHOLDER_TEXT: &str = "Save to submit feedback as Markdown."; const FEEDBACK_SUBMISSION_ERROR_TEXT: &str = "Feedback failed to submit, see error log for details."; -actions!(feedback, [SubmitFeedback, GiveFeedback, DeployFeedback]); +actions!(feedback, [GiveFeedback, SubmitFeedback]); pub fn init(system_specs: SystemSpecs, app_state: Arc, cx: &mut MutableAppContext) { cx.add_action({ @@ -43,17 +43,27 @@ pub fn init(system_specs: SystemSpecs, app_state: Arc, cx: &mut Mutabl FeedbackEditor::deploy(system_specs.clone(), workspace, app_state.clone(), cx); } }); + + cx.add_async_action( + |toolbar_button: &mut ToolbarButton, _: &SubmitFeedback, cx| { + if let Some(active_item) = toolbar_button.active_item.as_ref() { + Some(active_item.update(cx, |feedback_editor, cx| feedback_editor.handle_save(cx))) + } else { + None + } + }, + ); } -pub struct FeedbackButton; +pub struct StatusBarButton; -impl Entity for FeedbackButton { +impl Entity for StatusBarButton { type Event = (); } -impl View for FeedbackButton { +impl View for StatusBarButton { fn ui_name() -> &'static str { - "FeedbackButton" + "StatusBarButton" } fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { @@ -77,7 +87,7 @@ impl View for FeedbackButton { } } -impl StatusItemView for FeedbackButton { +impl StatusItemView for StatusBarButton { fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} } @@ -120,11 +130,7 @@ impl FeedbackEditor { } } - fn handle_save( - &mut self, - _: ModelHandle, - cx: &mut ViewContext, - ) -> Task> { + fn handle_save(&mut self, cx: &mut ViewContext) -> Task> { let feedback_text = self.editor.read(cx).text(cx); let feedback_char_count = feedback_text.chars().count(); let feedback_text = feedback_text.trim().to_string(); @@ -304,19 +310,19 @@ impl Item for FeedbackEditor { fn save( &mut self, - project: ModelHandle, + _: ModelHandle, cx: &mut ViewContext, ) -> Task> { - self.handle_save(project, cx) + self.handle_save(cx) } fn save_as( &mut self, - project: ModelHandle, + _: ModelHandle, _: std::path::PathBuf, cx: &mut ViewContext, ) -> Task> { - self.handle_save(project, cx) + self.handle_save(cx) } fn reload( @@ -435,3 +441,63 @@ impl SearchableItem for FeedbackEditor { .update(cx, |editor, cx| editor.active_match_index(matches, cx)) } } + +pub struct ToolbarButton { + active_item: Option>, +} + +impl ToolbarButton { + pub fn new() -> Self { + Self { + active_item: Default::default(), + } + } +} + +impl Entity for ToolbarButton { + type Event = (); +} + +impl View for ToolbarButton { + fn ui_name() -> &'static str { + "ToolbarButton" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let theme = cx.global::().theme.clone(); + enum SubmitFeedbackButton {} + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.feedback.submit_button.style_for(state, false); + Label::new("Submit as Markdown".into(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(SubmitFeedback) + }) + .aligned() + .contained() + .with_margin_left(theme.feedback.button_margin) + .boxed() + } +} + +impl ToolbarItemView for ToolbarButton { + fn set_active_pane_item( + &mut self, + active_pane_item: Option<&dyn ItemHandle>, + cx: &mut ViewContext, + ) -> workspace::ToolbarItemLocation { + cx.notify(); + if let Some(feedback_editor) = active_pane_item.and_then(|i| i.downcast::()) + { + self.active_item = Some(feedback_editor); + ToolbarItemLocation::PrimaryRight { flex: None } + } else { + self.active_item = None; + ToolbarItemLocation::Hidden + } + } +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index e463310b9810eb2d6f30e924b1b66166c9b2e295..b8ec3e0329fe8c720683bfcfe1addd29cacc86f0 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -36,6 +36,7 @@ pub struct Theme { pub incoming_call_notification: IncomingCallNotification, pub tooltip: TooltipStyle, pub terminal: TerminalStyle, + pub feedback: FeedbackStyle, pub color_scheme: ColorScheme, } @@ -806,6 +807,12 @@ pub struct TerminalStyle { pub dim_foreground: Color, } +#[derive(Clone, Deserialize, Default)] +pub struct FeedbackStyle { + pub submit_button: Interactive, + pub button_margin: f32, +} + #[derive(Clone, Deserialize, Default)] pub struct ColorScheme { pub name: String, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index fc7f96a384b348ebb1844c52d01bbab5ad37a3d5..c118951b34fd657ccff7d85aed81c8275437670e 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -11,6 +11,7 @@ use collections::VecDeque; pub use editor; use editor::{Editor, MultiBuffer}; +use feedback::feedback_editor::ToolbarButton; use futures::StreamExt; use gpui::{ actions, @@ -287,6 +288,8 @@ pub fn initialize_workspace( toolbar.add_item(buffer_search_bar, cx); let project_search_bar = cx.add_view(|_| ProjectSearchBar::new()); toolbar.add_item(project_search_bar, cx); + let submit_feedback_button = cx.add_view(|_| ToolbarButton::new()); + toolbar.add_item(submit_feedback_button, cx); }) }); } @@ -344,7 +347,7 @@ pub fn initialize_workspace( let activity_indicator = activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); - let feedback_button = cx.add_view(|_| feedback::feedback_editor::FeedbackButton {}); + let feedback_button = cx.add_view(|_| feedback::feedback_editor::StatusBarButton {}); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 267d83050667ccb130a8f0c4b20cf37574aaf2d7..5d04050fe123f78c736df9a266a1ca8eeeb58824 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -19,6 +19,7 @@ import terminal from "./terminal"; import contactList from "./contactList"; import incomingCallNotification from "./incomingCallNotification"; import { ColorScheme } from "../themes/common/colorScheme"; +import feedback from "./feedback"; export default function app(colorScheme: ColorScheme): Object { return { @@ -51,6 +52,7 @@ export default function app(colorScheme: ColorScheme): Object { simpleMessageNotification: simpleMessageNotification(colorScheme), tooltip: tooltip(colorScheme), terminal: terminal(colorScheme), + feedback: feedback(colorScheme), colorScheme: { ...colorScheme, players: Object.values(colorScheme.players), diff --git a/styles/src/styleTree/feedback.ts b/styles/src/styleTree/feedback.ts new file mode 100644 index 0000000000000000000000000000000000000000..daee2bb96950e593dd65dd848a5d5ec2921f135a --- /dev/null +++ b/styles/src/styleTree/feedback.ts @@ -0,0 +1,37 @@ + +import { ColorScheme } from "../themes/common/colorScheme"; +import { background, border, text } from "./components"; + +export default function search(colorScheme: ColorScheme) { + let layer = colorScheme.highest; + + // Currently feedback only needs style for the submit feedback button + return { + submit_button: { + ...text(layer, "mono", "on"), + background: background(layer, "on"), + cornerRadius: 6, + border: border(layer, "on"), + margin: { + right: 4, + }, + padding: { + bottom: 2, + left: 10, + right: 10, + top: 2, + }, + clicked: { + ...text(layer, "mono", "on", "pressed"), + background: background(layer, "on", "pressed"), + border: border(layer, "on", "pressed"), + }, + hover: { + ...text(layer, "mono", "on", "hovered"), + background: background(layer, "on", "hovered"), + border: border(layer, "on", "hovered"), + }, + }, + button_margin: 8 + }; +}