From 8b7be8f6149dba633a7f8870da0a902fb2a43c8c Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 30 Nov 2023 16:07:54 -0500 Subject: [PATCH] Get `feedback2` compiling Co-Authored-By: Joseph T. Lyons <19867440+JosephTLyons@users.noreply.github.com> Co-Authored-By: Conrad Irwin --- .../feedback2/src/deploy_feedback_button.rs | 137 ++++++--------- crates/feedback2/src/feedback2.rs | 11 +- crates/feedback2/src/feedback_editor.rs | 31 ++-- crates/feedback2/src/feedback_modal.rs | 164 ++++++++++++++++++ .../feedback2/src/submit_feedback_button.rs | 118 +++++++------ crates/feedback2/src/system_specs.rs | 27 ++- crates/workspace2/src/workspace2.rs | 20 --- crates/zed2/src/zed2.rs | 11 +- 8 files changed, 313 insertions(+), 206 deletions(-) create mode 100644 crates/feedback2/src/feedback_modal.rs diff --git a/crates/feedback2/src/deploy_feedback_button.rs b/crates/feedback2/src/deploy_feedback_button.rs index ba58ed13d3..800f8d70f8 100644 --- a/crates/feedback2/src/deploy_feedback_button.rs +++ b/crates/feedback2/src/deploy_feedback_button.rs @@ -1,91 +1,58 @@ -// use gpui::{ -// elements::*, -// platform::{CursorStyle, MouseButton}, -// Entity, View, ViewContext, WeakViewHandle, -// }; -// use workspace::{item::ItemHandle, StatusItemView, Workspace}; +use gpui::{Action, AnyElement, Render, ViewContext, WeakView}; +use ui::{prelude::*, ButtonCommon, Icon, IconButton, Tooltip}; +use workspace::{StatusItemView, Workspace}; -// use crate::feedback_editor::{FeedbackEditor, GiveFeedback}; +use crate::{feedback_editor::GiveFeedback, feedback_modal::FeedbackModal}; -// pub struct DeployFeedbackButton { -// active: bool, -// workspace: WeakViewHandle, -// } +pub struct DeployFeedbackButton { + active: bool, + workspace: WeakView, +} -// impl Entity for DeployFeedbackButton { -// type Event = (); -// } +impl DeployFeedbackButton { + pub fn new(workspace: &Workspace) -> Self { + DeployFeedbackButton { + active: false, + workspace: workspace.weak_handle(), + } + } +} -// impl DeployFeedbackButton { -// pub fn new(workspace: &Workspace) -> Self { -// DeployFeedbackButton { -// active: false, -// workspace: workspace.weak_handle(), -// } -// } -// } +impl Render for DeployFeedbackButton { + type Element = AnyElement; -// impl View for DeployFeedbackButton { -// fn ui_name() -> &'static str { -// "DeployFeedbackButton" -// } + 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(); -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// let active = self.active; -// let theme = theme::current(cx).clone(); -// Stack::new() -// .with_child( -// MouseEventHandler::new::(0, cx, |state, _| { -// let style = &theme -// .workspace -// .status_bar -// .panel_buttons -// .button -// .in_state(active) -// .style_for(state); - -// Svg::new("icons/feedback.svg") -// .with_color(style.icon_color) -// .constrained() -// .with_width(style.icon_size) -// .aligned() -// .constrained() -// .with_width(style.icon_size) -// .with_height(style.icon_size) -// .contained() -// .with_style(style.container) -// }) -// .with_cursor_style(CursorStyle::PointingHand) -// .on_click(MouseButton::Left, move |_, this, cx| { -// if !active { -// if let Some(workspace) = this.workspace.upgrade(cx) { -// workspace -// .update(cx, |workspace, cx| FeedbackEditor::deploy(workspace, cx)) -// } -// } -// }) -// .with_tooltip::( -// 0, -// "Send Feedback", -// Some(Box::new(GiveFeedback)), -// theme.tooltip.clone(), -// cx, -// ), -// ) -// .into_any() -// } -// } - -// impl StatusItemView for DeployFeedbackButton { -// 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(); -// } -// } + 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| { + let Some(workspace) = this.workspace.upgrade() else { + return; + }; + workspace.update(cx, |workspace, cx| { + workspace.toggle_modal(cx, |cx| FeedbackModal::new(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 + } +} diff --git a/crates/feedback2/src/feedback2.rs b/crates/feedback2/src/feedback2.rs index 5b26d60074..8bacc4255e 100644 --- a/crates/feedback2/src/feedback2.rs +++ b/crates/feedback2/src/feedback2.rs @@ -1,12 +1,17 @@ +use gpui::AppContext; + pub mod deploy_feedback_button; pub mod feedback_editor; pub mod feedback_info_text; +pub mod feedback_modal; pub mod submit_feedback_button; mod system_specs; -use gpui::{actions, platform::PromptLevel, AppContext, ClipboardItem, ViewContext}; -use system_specs::SystemSpecs; -use workspace::Workspace; + +pub fn init(cx: &mut AppContext) { + cx.observe_new_views(feedback_modal::FeedbackModal::register) + .detach(); +} // actions!( // zed, diff --git a/crates/feedback2/src/feedback_editor.rs b/crates/feedback2/src/feedback_editor.rs index 8f850f7293..258b3553f8 100644 --- a/crates/feedback2/src/feedback_editor.rs +++ b/crates/feedback2/src/feedback_editor.rs @@ -3,38 +3,27 @@ // use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; // use editor::{Anchor, Editor}; // use futures::AsyncReadExt; -// use gpui::{ -// actions, -// elements::{ChildView, Flex, Label, ParentElement, Svg}, -// platform::PromptLevel, -// serde_json, AnyElement, AnyViewHandle, AppContext, Element, Entity, ModelHandle, Task, View, -// ViewContext, ViewHandle, -// }; +// use gpui::{actions, serde_json, AppContext, Model, PromptLevel, Task, View, ViewContext}; // use isahc::Request; // use language::Buffer; // use postage::prelude::Stream; // use project::{search::SearchQuery, Project}; // use regex::Regex; // use serde::Serialize; -// use smallvec::SmallVec; // use std::{ -// any::TypeId, -// borrow::Cow, // ops::{Range, RangeInclusive}, // sync::Arc, // }; // use util::ResultExt; -// use workspace::{ -// item::{Item, ItemEvent, ItemHandle}, -// searchable::{SearchableItem, SearchableItemHandle}, -// Workspace, -// }; +// use workspace::{searchable::SearchableItem, Workspace}; // const FEEDBACK_CHAR_LIMIT: RangeInclusive = 10..=5000; // const FEEDBACK_SUBMISSION_ERROR_TEXT: &str = // "Feedback failed to submit, see error log for details."; -// actions!(feedback, [GiveFeedback, SubmitFeedback]); +use gpui::actions; + +actions!(GiveFeedback, SubmitFeedback); // pub fn init(cx: &mut AppContext) { // cx.add_action({ @@ -58,16 +47,16 @@ // #[derive(Clone)] // pub(crate) struct FeedbackEditor { // system_specs: SystemSpecs, -// editor: ViewHandle, -// project: ModelHandle, +// editor: View, +// project: Model, // pub allow_submission: bool, // } // impl FeedbackEditor { // fn new( // system_specs: SystemSpecs, -// project: ModelHandle, -// buffer: ModelHandle, +// project: Model, +// buffer: Model, // cx: &mut ViewContext, // ) -> Self { // let editor = cx.add_view(|cx| { @@ -135,7 +124,7 @@ // match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await { // Ok(_) => { -// this.update(&mut cx, |_, cx| cx.emit(editor::Event::Closed)) +// this.update(&mut cx, |_, cx| cx.emit(editor::EditorEvent::Closed)) // .log_err(); // } diff --git a/crates/feedback2/src/feedback_modal.rs b/crates/feedback2/src/feedback_modal.rs new file mode 100644 index 0000000000..3335576028 --- /dev/null +++ b/crates/feedback2/src/feedback_modal.rs @@ -0,0 +1,164 @@ +use gpui::{ + div, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Render, + ViewContext, +}; +use ui::prelude::*; +use workspace::Workspace; + +use crate::feedback_editor::GiveFeedback; + +pub struct FeedbackModal { + // editor: View, + tmp_focus_handle: FocusHandle, // TODO: should be editor.focus_handle(cx) +} + +impl FocusableView for FeedbackModal { + fn focus_handle(&self, _cx: &AppContext) -> FocusHandle { + self.tmp_focus_handle.clone() + } +} +impl EventEmitter for FeedbackModal {} + +impl FeedbackModal { + pub fn register(workspace: &mut Workspace, cx: &mut ViewContext) { + let _handle = cx.view().downgrade(); + workspace.register_action(move |workspace, _: &GiveFeedback, cx| { + workspace.toggle_modal(cx, move |cx| FeedbackModal::new(cx)); + }); + } + + pub fn new(cx: &mut ViewContext) -> Self { + Self { + tmp_focus_handle: cx.focus_handle(), + } + } + + // fn release(&mut self, cx: &mut WindowContext) { + // let scroll_position = self.prev_scroll_position.take(); + // self.active_editor.update(cx, |editor, cx| { + // editor.highlight_rows(None); + // if let Some(scroll_position) = scroll_position { + // editor.set_scroll_position(scroll_position, cx); + // } + // cx.notify(); + // }) + // } + + // fn on_feedback_editor_event( + // &mut self, + // _: View, + // event: &editor::EditorEvent, + // cx: &mut ViewContext, + // ) { + // match event { + // // todo!() this isn't working... + // editor::EditorEvent::Blurred => cx.emit(DismissEvent), + // editor::EditorEvent::BufferEdited { .. } => self.highlight_current_line(cx), + // _ => {} + // } + // } + + // fn highlight_current_line(&mut self, cx: &mut ViewContext) { + // if let Some(point) = self.point_from_query(cx) { + // self.active_editor.update(cx, |active_editor, cx| { + // let snapshot = active_editor.snapshot(cx).display_snapshot; + // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + // let display_point = point.to_display_point(&snapshot); + // let row = display_point.row(); + // active_editor.highlight_rows(Some(row..row + 1)); + // active_editor.request_autoscroll(Autoscroll::center(), cx); + // }); + // cx.notify(); + // } + // } + + // fn point_from_query(&self, cx: &ViewContext) -> Option { + // let line_editor = self.line_editor.read(cx).text(cx); + // let mut components = line_editor + // .splitn(2, FILE_ROW_COLUMN_DELIMITER) + // .map(str::trim) + // .fuse(); + // let row = components.next().and_then(|row| row.parse::().ok())?; + // let column = components.next().and_then(|col| col.parse::().ok()); + // Some(Point::new( + // row.saturating_sub(1), + // column.unwrap_or(0).saturating_sub(1), + // )) + // } + + // fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext) { + // cx.emit(DismissEvent); + // } + + // fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { + // if let Some(point) = self.point_from_query(cx) { + // self.active_editor.update(cx, |editor, cx| { + // let snapshot = editor.snapshot(cx).display_snapshot; + // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left); + // editor.change_selections(Some(Autoscroll::center()), cx, |s| { + // s.select_ranges([point..point]) + // }); + // editor.focus(cx); + // cx.notify(); + // }); + // self.prev_scroll_position.take(); + // } + + // cx.emit(DismissEvent); + // } +} + +impl Render for FeedbackModal { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + div().elevation_3(cx).w_1_2().h_2_3().child( + v_stack() + .w_full() + .child(h_stack().child("header")) + .child("editor"), + // Header + // - has some info, maybe some links + // Body + // - Markdown Editor + // - Email address + // Footer + // - CTA buttons (Send, Cancel) + ) + + // div() + // .elevation_2(cx) + // .key_context( + // "FeedbackModal + // ", + // ) + // .on_action(cx.listener(Self::cancel)) + // .on_action(cx.listener(Self::confirm)) + // .w_96() + // .child( + // v_stack() + // .px_1() + // .pt_0p5() + // .gap_px() + // .child( + // v_stack() + // .py_0p5() + // .px_1() + // .child(div().px_1().py_0p5().child(self.line_editor.clone())), + // ) + // .child( + // div() + // .h_px() + // .w_full() + // .bg(cx.theme().colors().element_background), + // ) + // .child( + // h_stack() + // .justify_between() + // .px_2() + // .py_1() + // .child(Label::new(self.current_text.clone()).color(Color::Muted)), + // ), + // ) + } +} diff --git a/crates/feedback2/src/submit_feedback_button.rs b/crates/feedback2/src/submit_feedback_button.rs index 4b6fc70920..78a66619a2 100644 --- a/crates/feedback2/src/submit_feedback_button.rs +++ b/crates/feedback2/src/submit_feedback_button.rs @@ -1,10 +1,7 @@ -// use crate::feedback_editor::{FeedbackEditor, SubmitFeedback}; +// use crate::{feedback_editor::SubmitFeedback, feedback_modal::FeedbackModal}; // use anyhow::Result; -// use gpui::{ -// elements::{Label, MouseEventHandler}, -// platform::{CursorStyle, MouseButton}, -// AnyElement, AppContext, Element, Entity, Task, View, ViewContext, ViewHandle, -// }; +// use gpui::{AppContext, Render, Task, View, ViewContext}; +// use ui::IconButton; // use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView}; // pub fn init(cx: &mut AppContext) { @@ -12,7 +9,7 @@ // } // pub struct SubmitFeedbackButton { -// pub(crate) active_item: Option>, +// pub(crate) active_item: Option>, // } // impl SubmitFeedbackButton { @@ -35,60 +32,67 @@ // } // } -// impl Entity for SubmitFeedbackButton { -// type Event = (); -// } +// impl Render for SubmitFeedbackbutton { +// type Element; -// 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() +// 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, diff --git a/crates/feedback2/src/system_specs.rs b/crates/feedback2/src/system_specs.rs index b2541c2bab..1a2c8775a6 100644 --- a/crates/feedback2/src/system_specs.rs +++ b/crates/feedback2/src/system_specs.rs @@ -1,17 +1,16 @@ -// use client::ZED_APP_VERSION; -// use gpui::{platform::AppVersion, AppContext}; +// // use client::ZED_APP_VERSION; +// use gpui::AppContext; // use human_bytes::human_bytes; // use serde::Serialize; // use std::{env, fmt::Display}; // use sysinfo::{System, SystemExt}; // use util::channel::ReleaseChannel; -// TODO: Move this file out of feedback and into a more general place - // #[derive(Clone, Debug, Serialize)] // pub struct SystemSpecs { -// #[serde(serialize_with = "serialize_app_version")] -// app_version: Option, +// // todo!() +// // #[serde(serialize_with = "serialize_app_version")] +// // app_version: Option, // release_channel: &'static str, // os_name: &'static str, // os_version: Option, @@ -22,7 +21,7 @@ // impl SystemSpecs { // pub fn new(cx: &AppContext) -> Self { // let platform = cx.platform(); -// let app_version = ZED_APP_VERSION.or_else(|| platform.app_version().ok()); +// // let app_version = ZED_APP_VERSION.or_else(|| platform.app_version().ok()); // let release_channel = cx.global::().dev_name(); // let os_name = platform.os_name(); // let system = System::new_all(); @@ -34,7 +33,7 @@ // .map(|os_version| os_version.to_string()); // SystemSpecs { -// app_version, +// // app_version, // release_channel, // os_name, // os_version, @@ -69,9 +68,9 @@ // } // } -// fn serialize_app_version(version: &Option, serializer: S) -> Result -// where -// S: serde::Serializer, -// { -// version.map(|v| v.to_string()).serialize(serializer) -// } +// // fn serialize_app_version(version: &Option, serializer: S) -> Result +// // where +// // S: serde::Serializer, +// // { +// // version.map(|v| v.to_string()).serialize(serializer) +// // } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index a096deb97f..7d5db4d8d1 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -3723,26 +3723,6 @@ impl Render for Workspace { .text_color(cx.theme().colors().text) .bg(cx.theme().colors().background) .children(self.titlebar_item.clone()) - .child( - div() - .absolute() - .ml_1_4() - .mt_20() - .elevation_3(cx) - .z_index(999) - .w_1_2() - .h_2_3() - .child( - v_stack().w_full().child(h_stack().child("header")), - // Header - // - has some info, maybe some links - // Body - // - Markdown Editor - // - Email address - // Footer - // - CTA buttons (Send, Cancel) - ), - ) .child( div() .id("workspace") diff --git a/crates/zed2/src/zed2.rs b/crates/zed2/src/zed2.rs index 07b75bf8d4..2fc3a46549 100644 --- a/crates/zed2/src/zed2.rs +++ b/crates/zed2/src/zed2.rs @@ -109,8 +109,8 @@ 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.add_view(|_| SubmitFeedbackButton::new()); + // 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); @@ -144,15 +144,14 @@ pub fn initialize_workspace(app_state: Arc, cx: &mut AppContext) { // let active_buffer_language = // cx.add_view(|_| language_selector::ActiveBufferLanguage::new(workspace)); // let vim_mode_indicator = cx.add_view(|cx| vim::ModeIndicator::new(cx)); - // let feedback_button = cx.add_view(|_| { - // feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace) - // }); + let feedback_button = cx + .build_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)); // let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); - // status_bar.add_right_item(feedback_button, cx); + status_bar.add_right_item(feedback_button, cx); // status_bar.add_right_item(copilot, cx); // status_bar.add_right_item(active_buffer_language, cx); // status_bar.add_right_item(vim_mode_indicator, cx);