This commit is contained in:
Joseph T. Lyons 2023-12-06 14:13:44 -05:00
parent eff925cb6a
commit 4863c9ac25
7 changed files with 441 additions and 377 deletions

View file

@ -1696,6 +1696,11 @@ impl Editor {
let focus_handle = cx.focus_handle();
cx.on_focus(&focus_handle, Self::handle_focus).detach();
cx.on_blur(&focus_handle, Self::handle_blur).detach();
cx.on_release(|this, cx| {
//todo!()
//cx.emit_global(EditorReleased(self.handle.clone()));
})
.detach();
let mut this = Self {
handle: cx.view().downgrade(),
@ -9240,14 +9245,6 @@ pub struct EditorFocused(pub View<Editor>);
pub struct EditorBlurred(pub View<Editor>);
pub struct EditorReleased(pub WeakView<Editor>);
// impl Entity for Editor {
// type Event = Event;
// fn release(&mut self, cx: &mut AppContext) {
// cx.emit_global(EditorReleased(self.handle.clone()));
// }
// }
//
impl EventEmitter<EditorEvent> for Editor {}
impl FocusableView for Editor {

View file

@ -1,18 +1,18 @@
use gpui::{AnyElement, Render, ViewContext, WeakView};
use ui::{prelude::*, ButtonCommon, Icon, IconButton, Tooltip};
use workspace::{StatusItemView, Workspace};
use workspace::{item::ItemHandle, StatusItemView, Workspace};
use crate::feedback_modal::FeedbackModal;
use crate::feedback_editor::FeedbackEditor;
pub struct DeployFeedbackButton {
_active: bool,
active: bool,
workspace: WeakView<Workspace>,
}
impl DeployFeedbackButton {
pub fn new(workspace: &Workspace) -> Self {
DeployFeedbackButton {
_active: false,
active: false,
workspace: workspace.weak_handle(),
}
}
@ -22,37 +22,34 @@ impl Render for DeployFeedbackButton {
type Element = AnyElement;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let is_open = self
.workspace
.upgrade()
.and_then(|workspace| {
workspace.update(cx, |workspace, cx| {
workspace.active_modal::<FeedbackModal>(cx)
})
})
.is_some();
let active = self.active;
IconButton::new("give-feedback", Icon::Envelope)
.style(ui::ButtonStyle::Subtle)
.selected(is_open)
.tooltip(|cx| Tooltip::text("Give Feedback", cx))
.on_click(cx.listener(|this, _, cx| {
.on_click(cx.listener(move |this, _, cx| {
let Some(workspace) = this.workspace.upgrade() else {
return;
};
workspace.update(cx, |workspace, cx| {
workspace.toggle_modal(cx, |cx| FeedbackModal::new(cx))
})
if !active {
workspace.update(cx, |workspace, cx| FeedbackEditor::deploy(workspace, cx))
}
}))
.into_any_element()
}
}
impl StatusItemView for DeployFeedbackButton {
fn set_active_pane_item(
&mut self,
_active_pane_item: Option<&dyn workspace::item::ItemHandle>,
_cx: &mut ViewContext<Self>,
) {
// no-op
fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext<Self>) {
if let Some(item) = item {
if let Some(_) = item.downcast::<FeedbackEditor>() {
self.active = true;
cx.notify();
return;
}
}
self.active = false;
cx.notify();
}
}

View file

@ -5,7 +5,7 @@ use workspace::Workspace;
pub mod deploy_feedback_button;
pub mod feedback_editor;
pub mod feedback_info_text;
pub mod feedback_modal;
// pub mod feedback_modal;
pub mod submit_feedback_button;
mod system_specs;

View file

@ -1,21 +1,29 @@
use std::{ops::RangeInclusive, sync::Arc};
use crate::system_specs::SystemSpecs;
use anyhow::bail;
use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
use editor::Editor;
use editor::{Anchor, Editor, EditorEvent};
use futures::AsyncReadExt;
use gpui::{
actions, serde_json, AppContext, Model, PromptLevel, Task, View, ViewContext, VisualContext,
actions, serde_json, AnyElement, AnyView, AppContext, Div, EntityId, EventEmitter,
FocusableView, Model, PromptLevel, Task, View, ViewContext, WindowContext,
};
use isahc::Request;
use language::Buffer;
use project::Project;
use language::{Buffer, Event};
use project::{search::SearchQuery, Project};
use regex::Regex;
use serde_derive::Serialize;
use serde::Serialize;
use std::{
any::TypeId,
ops::{Range, RangeInclusive},
sync::Arc,
};
use ui::{prelude::*, Icon, IconElement, Label};
use util::ResultExt;
use workspace::Workspace;
use crate::system_specs::SystemSpecs;
use workspace::{
item::{Item, ItemEvent, ItemHandle},
searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
Workspace,
};
const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
@ -24,12 +32,10 @@ const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
actions!(GiveFeedback, SubmitFeedback);
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(|workspace: &mut Workspace, cx| {
workspace.register_action(
move |workspace: &mut Workspace, _: &GiveFeedback, cx: &mut ViewContext<Workspace>| {
FeedbackEditor::deploy(workspace, cx);
},
);
cx.observe_new_views(|workspace: &mut Workspace, _| {
workspace.register_action(|workspace, _: &GiveFeedback, cx| {
FeedbackEditor::deploy(workspace, cx);
});
})
.detach();
}
@ -53,6 +59,9 @@ pub(crate) struct FeedbackEditor {
pub allow_submission: bool,
}
impl EventEmitter<Event> for FeedbackEditor {}
impl EventEmitter<EditorEvent> for FeedbackEditor {}
impl FeedbackEditor {
fn new(
system_specs: SystemSpecs,
@ -66,8 +75,11 @@ impl FeedbackEditor {
editor
});
cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()))
.detach();
cx.subscribe(
&editor,
|&mut _, _, e: &EditorEvent, cx: &mut ViewContext<_>| cx.emit(e.clone()),
)
.detach();
Self {
system_specs: system_specs.clone(),
@ -101,11 +113,15 @@ impl FeedbackEditor {
};
if let Some(error) = error {
cx.prompt(PromptLevel::Critical, &error, &["OK"]);
let prompt = cx.prompt(PromptLevel::Critical, &error, &["OK"]);
cx.spawn(|_, _cx| async move {
prompt.await.ok();
})
.detach();
return Task::ready(Ok(()));
}
let mut answer = cx.prompt(
let answer = cx.prompt(
PromptLevel::Info,
"Ready to submit your feedback?",
&["Yes, Submit!", "No"],
@ -115,7 +131,7 @@ impl FeedbackEditor {
let specs = self.system_specs.clone();
cx.spawn(|this, mut cx| async move {
let answer = answer.recv().await;
let answer = answer.await.ok();
if answer == Some(0) {
this.update(&mut cx, |feedback_editor, cx| {
@ -125,18 +141,22 @@ impl FeedbackEditor {
match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await {
Ok(_) => {
this.update(&mut cx, |_, cx| cx.emit(editor::EditorEvent::Closed))
this.update(&mut cx, |_, cx| cx.emit(Event::Closed))
.log_err();
}
Err(error) => {
log::error!("{}", error);
this.update(&mut cx, |feedback_editor, cx| {
cx.prompt(
let prompt = cx.prompt(
PromptLevel::Critical,
FEEDBACK_SUBMISSION_ERROR_TEXT,
&["OK"],
);
cx.spawn(|_, _cx| async move {
prompt.await.ok();
})
.detach();
feedback_editor.set_allow_submission(true, cx);
})
.log_err();
@ -234,200 +254,232 @@ impl FeedbackEditor {
}
}
// impl View for FeedbackEditor {
// fn ui_name() -> &'static str {
// "FeedbackEditor"
// }
// TODO
impl Render for FeedbackEditor {
type Element = Div;
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
// ChildView::new(&self.editor, cx).into_any()
// }
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
div().size_full().child(self.editor.clone())
}
}
// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
// if cx.is_self_focused() {
// cx.focus(&self.editor);
// }
// }
// }
impl EventEmitter<ItemEvent> for FeedbackEditor {}
// impl Entity for FeedbackEditor {
// type Event = editor::Event;
// }
impl FocusableView for FeedbackEditor {
fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
self.editor.focus_handle(cx)
}
}
// impl Item for FeedbackEditor {
// fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
// Some("Send Feedback".into())
// }
impl Item for FeedbackEditor {
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some("Send Feedback".into())
}
// fn tab_content<T: 'static>(
// &self,
// _: Option<usize>,
// style: &theme::Tab,
// _: &AppContext,
// ) -> AnyElement<T> {
// Flex::row()
// .with_child(
// Svg::new("icons/feedback.svg")
// .with_color(style.label.text.color)
// .constrained()
// .with_width(style.type_icon_width)
// .aligned()
// .contained()
// .with_margin_right(style.spacing),
// )
// .with_child(
// Label::new("Send Feedback", style.label.clone())
// .aligned()
// .contained(),
// )
// .into_any()
// }
fn tab_content(&self, detail: Option<usize>, cx: &WindowContext) -> AnyElement {
h_stack()
.gap_1()
.child(IconElement::new(Icon::Envelope).color(Color::Accent))
.child(Label::new("Send Feedback".to_string()))
.into_any_element()
}
// fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) {
// self.editor.for_each_project_item(cx, f)
// }
fn for_each_project_item(
&self,
cx: &AppContext,
f: &mut dyn FnMut(EntityId, &dyn project::Item),
) {
self.editor.for_each_project_item(cx, f)
}
// fn is_singleton(&self, _: &AppContext) -> bool {
// true
// }
fn is_singleton(&self, _: &AppContext) -> bool {
true
}
// fn can_save(&self, _: &AppContext) -> bool {
// true
// }
fn can_save(&self, _: &AppContext) -> bool {
true
}
// fn save(
// &mut self,
// _: ModelHandle<Project>,
// cx: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<()>> {
// self.submit(cx)
// }
fn save(
&mut self,
_project: Model<Project>,
cx: &mut ViewContext<Self>,
) -> Task<anyhow::Result<()>> {
self.submit(cx)
}
// fn save_as(
// &mut self,
// _: ModelHandle<Project>,
// _: std::path::PathBuf,
// cx: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<()>> {
// self.submit(cx)
// }
fn save_as(
&mut self,
_: Model<Project>,
_: std::path::PathBuf,
cx: &mut ViewContext<Self>,
) -> Task<anyhow::Result<()>> {
self.submit(cx)
}
// fn reload(
// &mut self,
// _: ModelHandle<Project>,
// _: &mut ViewContext<Self>,
// ) -> Task<anyhow::Result<()>> {
// Task::Ready(Some(Ok(())))
// }
fn reload(&mut self, _: Model<Project>, _: &mut ViewContext<Self>) -> Task<anyhow::Result<()>> {
Task::Ready(Some(Ok(())))
}
// fn clone_on_split(
// &self,
// _workspace_id: workspace::WorkspaceId,
// cx: &mut ViewContext<Self>,
// ) -> Option<Self>
// where
// Self: Sized,
// {
// let buffer = self
// .editor
// .read(cx)
// .buffer()
// .read(cx)
// .as_singleton()
// .expect("Feedback buffer is only ever singleton");
fn clone_on_split(
&self,
_workspace_id: workspace::WorkspaceId,
cx: &mut ViewContext<Self>,
) -> Option<View<Self>>
where
Self: Sized,
{
let buffer = self
.editor
.read(cx)
.buffer()
.read(cx)
.as_singleton()
.expect("Feedback buffer is only ever singleton");
// Some(Self::new(
// self.system_specs.clone(),
// self.project.clone(),
// buffer.clone(),
// cx,
// ))
// }
Some(cx.build_view(|cx| {
Self::new(
self.system_specs.clone(),
self.project.clone(),
buffer.clone(),
cx,
)
}))
}
// fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
// Some(Box::new(handle.clone()))
// }
fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
Some(Box::new(handle.clone()))
}
// fn act_as_type<'a>(
// &'a self,
// type_id: TypeId,
// self_handle: &'a ViewHandle<Self>,
// _: &'a AppContext,
// ) -> Option<&'a AnyViewHandle> {
// if type_id == TypeId::of::<Self>() {
// Some(self_handle)
// } else if type_id == TypeId::of::<Editor>() {
// Some(&self.editor)
// } else {
// None
// }
// }
fn act_as_type<'a>(
&'a self,
type_id: TypeId,
self_handle: &'a View<Self>,
cx: &'a AppContext,
) -> Option<AnyView> {
if type_id == TypeId::of::<Self>() {
Some(self_handle.to_any())
} else if type_id == TypeId::of::<Editor>() {
Some(self.editor.to_any())
} else {
None
}
}
// fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
// Editor::to_item_events(event)
// }
// }
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
// impl SearchableItem for FeedbackEditor {
// type Match = Range<Anchor>;
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
// fn to_search_event(
// &mut self,
// event: &Self::Event,
// cx: &mut ViewContext<Self>,
// ) -> Option<workspace::searchable::SearchEvent> {
// self.editor
// .update(cx, |editor, cx| editor.to_search_event(event, cx))
// }
fn navigate(&mut self, _: Box<dyn std::any::Any>, _: &mut ViewContext<Self>) -> bool {
false
}
// fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
// self.editor
// .update(cx, |editor, cx| editor.clear_matches(cx))
// }
fn tab_description(&self, _: usize, _: &AppContext) -> Option<ui::prelude::SharedString> {
None
}
// fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
// self.editor
// .update(cx, |editor, cx| editor.update_matches(matches, cx))
// }
fn set_nav_history(&mut self, _: workspace::ItemNavHistory, _: &mut ViewContext<Self>) {}
// fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
// self.editor
// .update(cx, |editor, cx| editor.query_suggestion(cx))
// }
fn is_dirty(&self, _: &AppContext) -> bool {
false
}
// fn activate_match(
// &mut self,
// index: usize,
// matches: Vec<Self::Match>,
// cx: &mut ViewContext<Self>,
// ) {
// self.editor
// .update(cx, |editor, cx| editor.activate_match(index, matches, cx))
// }
fn has_conflict(&self, _: &AppContext) -> bool {
false
}
// fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
// self.editor
// .update(cx, |e, cx| e.select_matches(matches, cx))
// }
// fn replace(&mut self, matches: &Self::Match, query: &SearchQuery, cx: &mut ViewContext<Self>) {
// self.editor
// .update(cx, |e, cx| e.replace(matches, query, cx));
// }
// fn find_matches(
// &mut self,
// query: Arc<project::search::SearchQuery>,
// cx: &mut ViewContext<Self>,
// ) -> Task<Vec<Self::Match>> {
// self.editor
// .update(cx, |editor, cx| editor.find_matches(query, cx))
// }
fn breadcrumb_location(&self) -> workspace::ToolbarItemLocation {
workspace::ToolbarItemLocation::Hidden
}
// fn active_match_index(
// &mut self,
// matches: Vec<Self::Match>,
// cx: &mut ViewContext<Self>,
// ) -> Option<usize> {
// self.editor
// .update(cx, |editor, cx| editor.active_match_index(matches, cx))
// }
// }
fn breadcrumbs(
&self,
_theme: &theme::Theme,
_cx: &AppContext,
) -> Option<Vec<workspace::item::BreadcrumbText>> {
None
}
fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
fn serialized_item_kind() -> Option<&'static str> {
Some("feedback")
}
fn deserialize(
_project: gpui::Model<Project>,
_workspace: gpui::WeakView<Workspace>,
_workspace_id: workspace::WorkspaceId,
_item_id: workspace::ItemId,
_cx: &mut ViewContext<workspace::Pane>,
) -> Task<anyhow::Result<View<Self>>> {
unimplemented!(
"deserialize() must be implemented if serialized_item_kind() returns Some(_)"
)
}
fn show_toolbar(&self) -> bool {
true
}
fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<gpui::Point<gpui::Pixels>> {
None
}
}
impl EventEmitter<SearchEvent> for FeedbackEditor {}
impl SearchableItem for FeedbackEditor {
type Match = Range<Anchor>;
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |editor, cx| editor.clear_matches(cx))
}
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |editor, cx| editor.update_matches(matches, cx))
}
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
self.editor
.update(cx, |editor, cx| editor.query_suggestion(cx))
}
fn activate_match(
&mut self,
index: usize,
matches: Vec<Self::Match>,
cx: &mut ViewContext<Self>,
) {
self.editor
.update(cx, |editor, cx| editor.activate_match(index, matches, cx))
}
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |e, cx| e.select_matches(matches, cx))
}
fn replace(&mut self, matches: &Self::Match, query: &SearchQuery, cx: &mut ViewContext<Self>) {
self.editor
.update(cx, |e, cx| e.replace(matches, query, cx));
}
fn find_matches(
&mut self,
query: Arc<project::search::SearchQuery>,
cx: &mut ViewContext<Self>,
) -> Task<Vec<Self::Match>> {
self.editor
.update(cx, |editor, cx| editor.find_matches(query, cx))
}
fn active_match_index(
&mut self,
matches: Vec<Self::Match>,
cx: &mut ViewContext<Self>,
) -> Option<usize> {
self.editor
.update(cx, |editor, cx| editor.active_match_index(matches, cx))
}
}

View file

@ -1,28 +1,36 @@
// use gpui::{
// elements::{Flex, Label, MouseEventHandler, ParentElement, Text},
// platform::{CursorStyle, MouseButton},
// AnyElement, Element, Entity, View, ViewContext, ViewHandle,
// };
// use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
use gpui::{Div, EventEmitter, View, ViewContext};
use ui::{prelude::*, Label};
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
// use crate::{feedback_editor::FeedbackEditor, open_zed_community_repo, OpenZedCommunityRepo};
use crate::feedback_editor::FeedbackEditor;
// pub struct FeedbackInfoText {
// active_item: Option<ViewHandle<FeedbackEditor>>,
// }
pub struct FeedbackInfoText {
active_item: Option<View<FeedbackEditor>>,
}
// impl FeedbackInfoText {
// pub fn new() -> Self {
// Self {
// active_item: Default::default(),
// }
// }
// }
impl FeedbackInfoText {
pub fn new() -> Self {
Self {
active_item: Default::default(),
}
}
}
// impl Entity for FeedbackInfoText {
// type Event = ();
// }
// TODO
impl Render for FeedbackInfoText {
type Element = Div;
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
// TODO - get this into the toolbar area like before - ensure things work the same when horizontally shrinking app
div()
.size_full()
.child(Label::new("Share your feedback. Include your email for replies. For issues and discussions, visit the ").color(Color::Muted))
.child(Label::new("community repo").color(Color::Muted)) // TODO - this needs to be a link
.child(Label::new(".").color(Color::Muted))
}
}
// TODO - delete
// impl View for FeedbackInfoText {
// fn ui_name() -> &'static str {
// "FeedbackInfoText"
@ -73,22 +81,25 @@
// }
// }
// 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
// }
// }
// }
impl EventEmitter<ToolbarItemEvent> for FeedbackInfoText {}
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>())
{
dbg!("Editor");
self.active_item = Some(feedback_editor);
ToolbarItemLocation::PrimaryLeft
} else {
dbg!("no editor");
self.active_item = None;
ToolbarItemLocation::Hidden
}
}
}

View file

@ -1,112 +1,115 @@
// use crate::{feedback_editor::SubmitFeedback, feedback_modal::FeedbackModal};
// use anyhow::Result;
// use gpui::{AppContext, Render, Task, View, ViewContext};
// use ui::IconButton;
// use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView};
use crate::feedback_editor::{FeedbackEditor, SubmitFeedback};
use anyhow::Result;
use gpui::{AppContext, Div, EventEmitter, Render, Task, View, ViewContext};
use ui::prelude::*;
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
// pub fn init(cx: &mut AppContext) {
// cx.add_async_action(SubmitFeedbackButton::submit);
// }
pub fn init(cx: &mut AppContext) {
// cx.add_action(SubmitFeedbackButton::submit);
}
// pub struct SubmitFeedbackButton {
// pub(crate) active_item: Option<View<FeedbackModal>>,
// }
pub struct SubmitFeedbackButton {
pub(crate) active_item: Option<View<FeedbackEditor>>,
}
// impl SubmitFeedbackButton {
// pub fn new() -> Self {
// Self {
// active_item: Default::default(),
// }
// }
impl SubmitFeedbackButton {
pub fn new() -> Self {
Self {
active_item: Default::default(),
}
}
// pub fn submit(
// &mut self,
// _: &SubmitFeedback,
// cx: &mut ViewContext<Self>,
// ) -> Option<Task<Result<()>>> {
// if let Some(active_item) = self.active_item.as_ref() {
// Some(active_item.update(cx, |feedback_editor, cx| feedback_editor.submit(cx)))
// } else {
// None
// }
pub fn submit(
&mut self,
_: &SubmitFeedback,
cx: &mut ViewContext<Self>,
) -> Option<Task<Result<()>>> {
if let Some(active_item) = self.active_item.as_ref() {
Some(active_item.update(cx, |feedback_editor, cx| feedback_editor.submit(cx)))
} else {
None
}
}
}
// TODO
impl Render for SubmitFeedbackButton {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let allow_submission = self
.active_item
.as_ref()
.map_or(true, |i| i.read(cx).allow_submission);
div()
}
}
// TODO - delete
// impl View for SubmitFeedbackButton {
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
// let theme = theme::current(cx).clone();
// let allow_submission = self
// .active_item
// .as_ref()
// .map_or(true, |i| i.read(cx).allow_submission);
// enum SubmitFeedbackButton {}
// MouseEventHandler::new::<SubmitFeedbackButton, _>(0, cx, |state, _| {
// let text;
// let style = if allow_submission {
// text = "Submit as Markdown";
// theme.feedback.submit_button.style_for(state)
// } else {
// text = "Submitting...";
// theme
// .feedback
// .submit_button
// .disabled
// .as_ref()
// .unwrap_or(&theme.feedback.submit_button.default)
// };
// Label::new(text, style.text.clone())
// .contained()
// .with_style(style.container)
// })
// .with_cursor_style(CursorStyle::PointingHand)
// .on_click(MouseButton::Left, |_, this, cx| {
// this.submit(&Default::default(), cx);
// })
// .aligned()
// .contained()
// .with_margin_left(theme.feedback.button_margin)
// .with_tooltip::<Self>(
// 0,
// "cmd-s",
// Some(Box::new(SubmitFeedback)),
// theme.tooltip.clone(),
// cx,
// )
// .into_any()
// }
// }
// impl Render for SubmitFeedbackbutton {
// type Element;
impl EventEmitter<ToolbarItemEvent> for SubmitFeedbackButton {}
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// todo!();
// // IconButton::new("give-feedback", Icon::Envelope)
// // .style(ui::ButtonStyle::Subtle)
// // .on_click(|_, cx| cx.dispatch_action(GiveFeedback))
// }
// }
// // impl View for SubmitFeedbackButton {
// // fn ui_name() -> &'static str {
// // "SubmitFeedbackButton"
// // }
// // fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
// // let theme = theme::current(cx).clone();
// // let allow_submission = self
// // .active_item
// // .as_ref()
// // .map_or(true, |i| i.read(cx).allow_submission);
// // enum SubmitFeedbackButton {}
// // MouseEventHandler::new::<SubmitFeedbackButton, _>(0, cx, |state, _| {
// // let text;
// // let style = if allow_submission {
// // text = "Submit as Markdown";
// // theme.feedback.submit_button.style_for(state)
// // } else {
// // text = "Submitting...";
// // theme
// // .feedback
// // .submit_button
// // .disabled
// // .as_ref()
// // .unwrap_or(&theme.feedback.submit_button.default)
// // };
// // Label::new(text, style.text.clone())
// // .contained()
// // .with_style(style.container)
// // })
// // .with_cursor_style(CursorStyle::PointingHand)
// // .on_click(MouseButton::Left, |_, this, cx| {
// // this.submit(&Default::default(), cx);
// // })
// // .aligned()
// // .contained()
// // .with_margin_left(theme.feedback.button_margin)
// // .with_tooltip::<Self>(
// // 0,
// // "cmd-s",
// // Some(Box::new(SubmitFeedback)),
// // theme.tooltip.clone(),
// // cx,
// // )
// // .into_any()
// // }
// // }
// impl ToolbarItemView for SubmitFeedbackButton {
// 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::PrimaryRight { flex: None }
// } else {
// self.active_item = None;
// ToolbarItemLocation::Hidden
// }
// }
// }
impl ToolbarItemView for SubmitFeedbackButton {
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::PrimaryRight
} else {
self.active_item = None;
ToolbarItemLocation::Hidden
}
}
}

View file

@ -10,6 +10,9 @@ pub use assets::*;
use breadcrumbs::Breadcrumbs;
use collections::VecDeque;
use editor::{Editor, MultiBuffer};
use feedback::{
feedback_info_text::FeedbackInfoText, submit_feedback_button::SubmitFeedbackButton,
};
use gpui::{
actions, point, px, AppContext, Context, FocusableView, PromptLevel, TitlebarOptions,
ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions,
@ -110,11 +113,12 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
// toolbar.add_item(diagnostic_editor_controls, cx);
// let project_search_bar = cx.add_view(|_| ProjectSearchBar::new());
// toolbar.add_item(project_search_bar, cx);
// let submit_feedback_button =
// cx.build_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);
let submit_feedback_button =
cx.build_view(|_| SubmitFeedbackButton::new());
// todo!(tool bar does not display or fire correctly right now, this is only stubbed in)
toolbar.add_item(submit_feedback_button, cx);
let feedback_info_text = cx.build_view(|_| FeedbackInfoText::new());
toolbar.add_item(feedback_info_text, cx);
// let lsp_log_item =
// cx.add_view(|_| language_tools::LspLogToolbarItemView::new());
// toolbar.add_item(lsp_log_item, cx);