Detailed changes
@@ -31,7 +31,6 @@ use workspace::{
use crate::system_specs::SystemSpecs;
const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
-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.";
@@ -117,7 +116,6 @@ impl FeedbackEditor {
let editor = cx.add_view(|cx| {
let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
editor.set_vertical_scroll_margin(5, cx);
- editor.set_placeholder_text(FEEDBACK_PLACEHOLDER_TEXT, cx);
editor
});
@@ -483,6 +481,13 @@ impl View for SubmitFeedbackButton {
.aligned()
.contained()
.with_margin_left(theme.feedback.button_margin)
+ .with_tooltip::<Self, _>(
+ 0,
+ "cmd-s".into(),
+ Some(Box::new(SubmitFeedback)),
+ theme.tooltip.clone(),
+ cx,
+ )
.boxed()
}
}
@@ -504,3 +509,56 @@ impl ToolbarItemView for SubmitFeedbackButton {
}
}
}
+
+pub struct FeedbackInfoText {
+ active_item: Option<ViewHandle<FeedbackEditor>>,
+}
+
+impl FeedbackInfoText {
+ pub fn new() -> Self {
+ Self {
+ active_item: Default::default(),
+ }
+ }
+}
+
+impl Entity for FeedbackInfoText {
+ type Event = ();
+}
+
+impl View for FeedbackInfoText {
+ fn ui_name() -> &'static str {
+ "FeedbackInfoText"
+ }
+
+ fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+ let theme = cx.global::<Settings>().theme.clone();
+ let text = "We read whatever you submit here. For issues and discussions, visit the community repo on GitHub.";
+ Label::new(text.to_string(), theme.feedback.info_text.text.clone())
+ .contained()
+ .aligned()
+ .left()
+ .clipped()
+ .boxed()
+ }
+}
+
+impl ToolbarItemView for FeedbackInfoText {
+ fn set_active_pane_item(
+ &mut self,
+ active_pane_item: Option<&dyn ItemHandle>,
+ cx: &mut ViewContext<Self>,
+ ) -> workspace::ToolbarItemLocation {
+ cx.notify();
+ if let Some(feedback_editor) = active_pane_item.and_then(|i| i.downcast::<FeedbackEditor>())
+ {
+ self.active_item = Some(feedback_editor);
+ ToolbarItemLocation::PrimaryLeft {
+ flex: Some((1., false)),
+ }
+ } else {
+ self.active_item = None;
+ ToolbarItemLocation::Hidden
+ }
+ }
+}
@@ -1,5 +1,6 @@
mod align;
mod canvas;
+mod clipped;
mod constrained_box;
mod container;
mod empty;
@@ -19,12 +20,12 @@ mod text;
mod tooltip;
mod uniform_list;
-use self::expanded::Expanded;
pub use self::{
align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*,
stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
};
+use self::{clipped::Clipped, expanded::Expanded};
pub use crate::presenter::ChildView;
use crate::{
geometry::{
@@ -135,6 +136,13 @@ pub trait Element {
Align::new(self.boxed())
}
+ fn clipped(self) -> Clipped
+ where
+ Self: 'static + Sized,
+ {
+ Clipped::new(self.boxed())
+ }
+
fn contained(self) -> Container
where
Self: 'static + Sized,
@@ -0,0 +1,69 @@
+use std::ops::Range;
+
+use pathfinder_geometry::{rect::RectF, vector::Vector2F};
+use serde_json::json;
+
+use crate::{
+ json, DebugContext, Element, ElementBox, LayoutContext, MeasurementContext, PaintContext,
+ SizeConstraint,
+};
+
+pub struct Clipped {
+ child: ElementBox,
+}
+
+impl Clipped {
+ pub fn new(child: ElementBox) -> Self {
+ Self { child }
+ }
+}
+
+impl Element for Clipped {
+ type LayoutState = ();
+ type PaintState = ();
+
+ fn layout(
+ &mut self,
+ constraint: SizeConstraint,
+ cx: &mut LayoutContext,
+ ) -> (Vector2F, Self::LayoutState) {
+ (self.child.layout(constraint, cx), ())
+ }
+
+ fn paint(
+ &mut self,
+ bounds: RectF,
+ visible_bounds: RectF,
+ _: &mut Self::LayoutState,
+ cx: &mut PaintContext,
+ ) -> Self::PaintState {
+ cx.scene.push_layer(Some(bounds));
+ self.child.paint(bounds.origin(), visible_bounds, cx);
+ cx.scene.pop_layer();
+ }
+
+ fn rect_for_text_range(
+ &self,
+ range_utf16: Range<usize>,
+ _: RectF,
+ _: RectF,
+ _: &Self::LayoutState,
+ _: &Self::PaintState,
+ cx: &MeasurementContext,
+ ) -> Option<RectF> {
+ self.child.rect_for_text_range(range_utf16, cx)
+ }
+
+ fn debug(
+ &self,
+ _: RectF,
+ _: &Self::LayoutState,
+ _: &Self::PaintState,
+ cx: &DebugContext,
+ ) -> json::Value {
+ json!({
+ "type": "Clipped",
+ "child": self.child.debug(cx)
+ })
+ }
+}
@@ -811,6 +811,7 @@ pub struct TerminalStyle {
pub struct FeedbackStyle {
pub submit_button: Interactive<ContainedText>,
pub button_margin: f32,
+ pub info_text: ContainedText,
}
#[derive(Clone, Deserialize, Default)]
@@ -11,7 +11,7 @@ use collections::VecDeque;
pub use editor;
use editor::{Editor, MultiBuffer};
-use feedback::feedback_editor::SubmitFeedbackButton;
+use feedback::feedback_editor::{FeedbackInfoText, SubmitFeedbackButton};
use futures::StreamExt;
use gpui::{
actions,
@@ -290,6 +290,8 @@ pub fn initialize_workspace(
toolbar.add_item(project_search_bar, cx);
let submit_feedback_button = cx.add_view(|_| SubmitFeedbackButton::new());
toolbar.add_item(submit_feedback_button, cx);
+ let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
+ toolbar.add_item(feedback_info_text, cx);
})
});
}
@@ -5,7 +5,6 @@ import { background, border, text } from "./components";
export default function feedback(colorScheme: ColorScheme) {
let layer = colorScheme.highest;
- // Currently feedback only needs style for the submit feedback button
return {
submit_button: {
...text(layer, "mono", "on"),
@@ -32,6 +31,7 @@ export default function feedback(colorScheme: ColorScheme) {
border: border(layer, "on", "hovered"),
},
},
- button_margin: 8
+ button_margin: 8,
+ info_text: text(layer, "sans", "default", { size: "xs" }),
};
}