diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 529438648a..7cf50a50e1 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -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); pub struct EditorBlurred(pub View); pub struct EditorReleased(pub WeakView); -// impl Entity for Editor { -// type Event = Event; - -// fn release(&mut self, cx: &mut AppContext) { -// cx.emit_global(EditorReleased(self.handle.clone())); -// } -// } -// impl EventEmitter for Editor {} impl FocusableView for Editor { diff --git a/crates/feedback2/src/deploy_feedback_button.rs b/crates/feedback2/src/deploy_feedback_button.rs index 147db443a5..f0f06f5bd0 100644 --- a/crates/feedback2/src/deploy_feedback_button.rs +++ b/crates/feedback2/src/deploy_feedback_button.rs @@ -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, } 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::Element { - let is_open = self - .workspace - .upgrade() - .and_then(|workspace| { - workspace.update(cx, |workspace, cx| { - workspace.active_modal::(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, - ) { - // no-op + fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext) { + if let Some(item) = item { + if let Some(_) = item.downcast::() { + self.active = true; + cx.notify(); + return; + } + } + self.active = false; + cx.notify(); } } diff --git a/crates/feedback2/src/feedback2.rs b/crates/feedback2/src/feedback2.rs index c013b4323e..30caa1aa0e 100644 --- a/crates/feedback2/src/feedback2.rs +++ b/crates/feedback2/src/feedback2.rs @@ -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; diff --git a/crates/feedback2/src/feedback_editor.rs b/crates/feedback2/src/feedback_editor.rs index 4726e42e58..d569566ba6 100644 --- a/crates/feedback2/src/feedback_editor.rs +++ b/crates/feedback2/src/feedback_editor.rs @@ -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 = 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| { - 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 for FeedbackEditor {} +impl EventEmitter 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) -> AnyElement { -// ChildView::new(&self.editor, cx).into_any() -// } + fn render(&mut self, _: &mut ViewContext) -> Self::Element { + div().size_full().child(self.editor.clone()) + } +} -// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { -// if cx.is_self_focused() { -// cx.focus(&self.editor); -// } -// } -// } +impl EventEmitter 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> { -// Some("Send Feedback".into()) -// } +impl Item for FeedbackEditor { + fn tab_tooltip_text(&self, _: &AppContext) -> Option { + Some("Send Feedback".into()) + } -// fn tab_content( -// &self, -// _: Option, -// style: &theme::Tab, -// _: &AppContext, -// ) -> AnyElement { -// 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, 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, -// cx: &mut ViewContext, -// ) -> Task> { -// self.submit(cx) -// } + fn save( + &mut self, + _project: Model, + cx: &mut ViewContext, + ) -> Task> { + self.submit(cx) + } -// fn save_as( -// &mut self, -// _: ModelHandle, -// _: std::path::PathBuf, -// cx: &mut ViewContext, -// ) -> Task> { -// self.submit(cx) -// } + fn save_as( + &mut self, + _: Model, + _: std::path::PathBuf, + cx: &mut ViewContext, + ) -> Task> { + self.submit(cx) + } -// fn reload( -// &mut self, -// _: ModelHandle, -// _: &mut ViewContext, -// ) -> Task> { -// Task::Ready(Some(Ok(()))) -// } + fn reload(&mut self, _: Model, _: &mut ViewContext) -> Task> { + Task::Ready(Some(Ok(()))) + } -// fn clone_on_split( -// &self, -// _workspace_id: workspace::WorkspaceId, -// cx: &mut ViewContext, -// ) -> Option -// 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, + ) -> Option> + 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) -> Option> { -// Some(Box::new(handle.clone())) -// } + fn as_searchable(&self, handle: &View) -> Option> { + Some(Box::new(handle.clone())) + } -// fn act_as_type<'a>( -// &'a self, -// type_id: TypeId, -// self_handle: &'a ViewHandle, -// _: &'a AppContext, -// ) -> Option<&'a AnyViewHandle> { -// if type_id == TypeId::of::() { -// Some(self_handle) -// } else if type_id == TypeId::of::() { -// Some(&self.editor) -// } else { -// None -// } -// } + fn act_as_type<'a>( + &'a self, + type_id: TypeId, + self_handle: &'a View, + cx: &'a AppContext, + ) -> Option { + if type_id == TypeId::of::() { + Some(self_handle.to_any()) + } else if type_id == TypeId::of::() { + 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) {} -// impl SearchableItem for FeedbackEditor { -// type Match = Range; + fn workspace_deactivated(&mut self, _: &mut ViewContext) {} -// fn to_search_event( -// &mut self, -// event: &Self::Event, -// cx: &mut ViewContext, -// ) -> Option { -// self.editor -// .update(cx, |editor, cx| editor.to_search_event(event, cx)) -// } + fn navigate(&mut self, _: Box, _: &mut ViewContext) -> bool { + false + } -// fn clear_matches(&mut self, cx: &mut ViewContext) { -// self.editor -// .update(cx, |editor, cx| editor.clear_matches(cx)) -// } + fn tab_description(&self, _: usize, _: &AppContext) -> Option { + None + } -// fn update_matches(&mut self, matches: Vec, cx: &mut ViewContext) { -// self.editor -// .update(cx, |editor, cx| editor.update_matches(matches, cx)) -// } + fn set_nav_history(&mut self, _: workspace::ItemNavHistory, _: &mut ViewContext) {} -// fn query_suggestion(&mut self, cx: &mut ViewContext) -> 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, -// cx: &mut ViewContext, -// ) { -// 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, cx: &mut ViewContext) { -// self.editor -// .update(cx, |e, cx| e.select_matches(matches, cx)) -// } -// fn replace(&mut self, matches: &Self::Match, query: &SearchQuery, cx: &mut ViewContext) { -// self.editor -// .update(cx, |e, cx| e.replace(matches, query, cx)); -// } -// fn find_matches( -// &mut self, -// query: Arc, -// cx: &mut ViewContext, -// ) -> Task> { -// 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, -// cx: &mut ViewContext, -// ) -> Option { -// self.editor -// .update(cx, |editor, cx| editor.active_match_index(matches, cx)) -// } -// } + fn breadcrumbs( + &self, + _theme: &theme::Theme, + _cx: &AppContext, + ) -> Option> { + None + } + + fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext) {} + + fn serialized_item_kind() -> Option<&'static str> { + Some("feedback") + } + + fn deserialize( + _project: gpui::Model, + _workspace: gpui::WeakView, + _workspace_id: workspace::WorkspaceId, + _item_id: workspace::ItemId, + _cx: &mut ViewContext, + ) -> Task>> { + 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> { + None + } +} + +impl EventEmitter for FeedbackEditor {} + +impl SearchableItem for FeedbackEditor { + type Match = Range; + + fn clear_matches(&mut self, cx: &mut ViewContext) { + self.editor + .update(cx, |editor, cx| editor.clear_matches(cx)) + } + + fn update_matches(&mut self, matches: Vec, cx: &mut ViewContext) { + self.editor + .update(cx, |editor, cx| editor.update_matches(matches, cx)) + } + + fn query_suggestion(&mut self, cx: &mut ViewContext) -> String { + self.editor + .update(cx, |editor, cx| editor.query_suggestion(cx)) + } + + fn activate_match( + &mut self, + index: usize, + matches: Vec, + cx: &mut ViewContext, + ) { + self.editor + .update(cx, |editor, cx| editor.activate_match(index, matches, cx)) + } + + fn select_matches(&mut self, matches: Vec, cx: &mut ViewContext) { + self.editor + .update(cx, |e, cx| e.select_matches(matches, cx)) + } + fn replace(&mut self, matches: &Self::Match, query: &SearchQuery, cx: &mut ViewContext) { + self.editor + .update(cx, |e, cx| e.replace(matches, query, cx)); + } + fn find_matches( + &mut self, + query: Arc, + cx: &mut ViewContext, + ) -> Task> { + self.editor + .update(cx, |editor, cx| editor.find_matches(query, cx)) + } + + fn active_match_index( + &mut self, + matches: Vec, + cx: &mut ViewContext, + ) -> Option { + self.editor + .update(cx, |editor, cx| editor.active_match_index(matches, cx)) + } +} diff --git a/crates/feedback2/src/feedback_info_text.rs b/crates/feedback2/src/feedback_info_text.rs index 96c76929ea..643e5c7b0c 100644 --- a/crates/feedback2/src/feedback_info_text.rs +++ b/crates/feedback2/src/feedback_info_text.rs @@ -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>, -// } +pub struct FeedbackInfoText { + active_item: Option>, +} -// 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::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, -// ) -> workspace::ToolbarItemLocation { -// cx.notify(); -// if let Some(feedback_editor) = active_pane_item.and_then(|i| i.downcast::()) -// { -// self.active_item = Some(feedback_editor); -// ToolbarItemLocation::PrimaryLeft { -// flex: Some((1., false)), -// } -// } else { -// self.active_item = None; -// ToolbarItemLocation::Hidden -// } -// } -// } +impl EventEmitter for FeedbackInfoText {} + +impl ToolbarItemView for FeedbackInfoText { + 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::()) + { + dbg!("Editor"); + self.active_item = Some(feedback_editor); + ToolbarItemLocation::PrimaryLeft + } else { + dbg!("no editor"); + self.active_item = None; + ToolbarItemLocation::Hidden + } + } +} diff --git a/crates/feedback2/src/submit_feedback_button.rs b/crates/feedback2/src/submit_feedback_button.rs index 78a66619a2..220a6d3406 100644 --- a/crates/feedback2/src/submit_feedback_button.rs +++ b/crates/feedback2/src/submit_feedback_button.rs @@ -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>, -// } +pub struct SubmitFeedbackButton { + pub(crate) active_item: Option>, +} -// 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, -// ) -> Option>> { -// 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, + ) -> Option>> { + 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::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) -> AnyElement { +// 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::(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::( +// 0, +// "cmd-s", +// Some(Box::new(SubmitFeedback)), +// theme.tooltip.clone(), +// cx, +// ) +// .into_any() // } // } -// impl Render for SubmitFeedbackbutton { -// type Element; +impl EventEmitter for SubmitFeedbackButton {} -// fn render(&mut self, cx: &mut ViewContext) -> 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) -> AnyElement { -// // 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::(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::( -// // 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, -// ) -> 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 -// } -// } -// } +impl ToolbarItemView for SubmitFeedbackButton { + 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 + } else { + self.active_item = None; + ToolbarItemLocation::Hidden + } + } +} diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 9ce1966a7a..c4d5831589 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -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, 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);