diff --git a/Cargo.lock b/Cargo.lock index 4bbada23d044759885088153dfbc3746130de4b2..bf2e964ea8f533a8a0c0ada0e60077fdd140caa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10132,6 +10132,7 @@ dependencies = [ "chrono", "gpui2", "itertools 0.11.0", + "menu2", "rand 0.8.5", "serde", "settings2", diff --git a/crates/terminal_view2/Cargo.toml b/crates/terminal_view2/Cargo.toml index f0d2e6ccf02f2d5aa9673136b2cba6f4cf283322..12e2c06504fad5adc7e502c4225a28c9bf985f30 100644 --- a/crates/terminal_view2/Cargo.toml +++ b/crates/terminal_view2/Cargo.toml @@ -9,7 +9,6 @@ path = "src/terminal_view.rs" doctest = false [dependencies] -# context_menu = { package = "context_menu2", path = "../context_menu2" } editor = { package = "editor2", path = "../editor2" } language = { package = "language2", path = "../language2" } gpui = { package = "gpui2", path = "../gpui2" } diff --git a/crates/terminal_view2/src/terminal_element.rs b/crates/terminal_view2/src/terminal_element.rs index 00f1dca17d686766310fb1fe67c2704b9c736eaf..e93d82047d6782f518db309ac3c132d642dd2d46 100644 --- a/crates/terminal_view2/src/terminal_element.rs +++ b/crates/terminal_view2/src/terminal_element.rs @@ -1,8 +1,8 @@ // use editor::{Cursor, HighlightedRange, HighlightedRangeLine}; // use gpui::{ -// AnyElement, AppContext, Bounds, Component, Element, HighlightStyle, Hsla, LayoutId, Line, -// ModelContext, MouseButton, Pixels, Point, TextStyle, Underline, ViewContext, WeakModel, -// WindowContext, +// point, transparent_black, AnyElement, AppContext, Bounds, Component, CursorStyle, Element, +// FontStyle, FontWeight, HighlightStyle, Hsla, LayoutId, Line, ModelContext, MouseButton, +// Overlay, Pixels, Point, Quad, TextStyle, Underline, ViewContext, WeakModel, WindowContext, // }; // use itertools::Itertools; // use language::CursorShape; @@ -23,6 +23,7 @@ // TerminalSize, // }; // use theme::ThemeSettings; +// use workspace::ElementId; // use std::mem; // use std::{fmt::Debug, ops::RangeInclusive}; @@ -130,23 +131,24 @@ // cx: &mut ViewContext, // ) { // let position = { -// let point = self.point; -// vec2f( -// (origin.x() + point.column as f32 * layout.size.cell_width).floor(), -// origin.y() + point.line as f32 * layout.size.line_height, +// let alac_point = self.point; +// point( +// (origin.x + alac_point.column as f32 * layout.size.cell_width).floor(), +// origin.y + alac_point.line as f32 * layout.size.line_height, // ) // }; -// let size = vec2f( +// let size = point( // (layout.size.cell_width * self.num_of_cells as f32).ceil(), // layout.size.line_height, -// ); +// ) +// .into(); // cx.paint_quad( // Bounds::new(position, size), // Default::default(), // self.color, // Default::default(), -// Default::default(), +// transparent_black(), // ); // } // } @@ -281,9 +283,9 @@ // cursor_point: DisplayCursor, // size: TerminalSize, // text_fragment: &Line, -// ) -> Option<(Vector2F, f32)> { +// ) -> Option<(Point, Pixels)> { // if cursor_point.line() < size.total_lines() as i32 { -// let cursor_width = if text_fragment.width == 0. { +// let cursor_width = if text_fragment.width == Pixels::ZERO { // size.cell_width() // } else { // text_fragment.width @@ -292,7 +294,7 @@ // //Cursor should always surround as much of the text as possible, // //hence when on pixel boundaries round the origin down and the width up // Some(( -// vec2f( +// point( // (cursor_point.col() as f32 * size.cell_width()).floor(), // (cursor_point.line() as f32 * size.line_height()).floor(), // ), @@ -332,15 +334,15 @@ // let mut properties = Properties::new(); // if indexed.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) { -// properties = *properties.weight(Weight::BOLD); +// properties = *properties.weight(FontWeight::BOLD); // } // if indexed.flags.intersects(Flags::ITALIC) { -// properties = *properties.style(Italic); +// properties = *properties.style(FontStyle::Italic); // } // let font_id = font_cache -// .select_font(text_style.font_family_id, &properties) -// .unwrap_or(8text_style.font_id); +// .select_font(text_style.font_family, &properties) +// .unwrap_or(text_style.font_id); // let mut result = RunStyle { // color: fg, @@ -366,7 +368,7 @@ // fn generic_button_handler( // connection: WeakModel, // origin: Point, -// f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext), +// f: impl Fn(&mut Terminal, Point, E, &mut ModelContext), // ) -> impl Fn(E, &mut TerminalView, &mut EventContext) { // move |event, _: &mut TerminalView, cx| { // cx.focus_parent(); @@ -522,9 +524,9 @@ // fn layout( // &mut self, // view_state: &mut TerminalView, -// element_state: &mut Self::ElementState, +// element_state: Option, // cx: &mut ViewContext, -// ) -> LayoutId { +// ) -> (LayoutId, Self::ElementState) { // let settings = ThemeSettings::get_global(cx); // let terminal_settings = TerminalSettings::get_global(cx); @@ -569,7 +571,7 @@ // let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size); // gutter = cell_width; -// let size = constraint.max - vec2f(gutter, 0.); +// let size = constraint.max - point(gutter, 0.); // TerminalSize::new(line_height, cell_width, size) // }; @@ -607,11 +609,11 @@ // cx, // ), // ) -// .with_position_mode(gpui::elements::OverlayPositionMode::Local) +// .with_position_mode(gpui::OverlayPositionMode::Local) // .into_any(); // tooltip.layout( -// SizeConstraint::new(Vector2F::zero(), cx.window_size()), +// SizeConstraint::new(Point::zero(), cx.window_size()), // view_state, // cx, // ); @@ -735,7 +737,7 @@ // let clip_bounds = Some(visible_bounds); // cx.paint_layer(clip_bounds, |cx| { -// let origin = bounds.origin() + vec2f(element_state.gutter, 0.); +// let origin = bounds.origin + point(element_state.gutter, 0.); // // Elements are ephemeral, only at paint time do we know what could be clicked by a mouse // self.attach_mouse_handlers(origin, visible_bounds, element_state.mode, cx); @@ -808,7 +810,7 @@ // }); // } -// fn id(&self) -> Option { +// fn element_id(&self) -> Option { // todo!() // } @@ -842,12 +844,12 @@ // // ) -> Option> { // // // Use the same origin that's passed to `Cursor::paint` in the paint // // // method bove. -// // let mut origin = bounds.origin() + vec2f(layout.size.cell_width, 0.); +// // let mut origin = bounds.origin() + point(layout.size.cell_width, 0.); // // // TODO - Why is it necessary to move downward one line to get correct // // // positioning? I would think that we'd want the same rect that is // // // painted for the cursor. -// // origin += vec2f(0., layout.size.line_height); +// // origin += point(0., layout.size.line_height); // // Some(layout.cursor.as_ref()?.bounding_rect(origin)) // // } @@ -886,7 +888,7 @@ // range: &RangeInclusive, // layout: &LayoutState, // origin: Point, -// ) -> Option<(f32, Vec)> { +// ) -> Option<(Pixels, Vec)> { // // Step 1. Normalize the points to be viewport relative. // // When display_offset = 1, here's how the grid is arranged: // //-2,0 -2,1... @@ -937,8 +939,8 @@ // } // highlighted_range_lines.push(HighlightedRangeLine { -// start_x: origin.x() + line_start as f32 * layout.size.cell_width, -// end_x: origin.x() + line_end as f32 * layout.size.cell_width, +// start_x: origin.x + line_start as f32 * layout.size.cell_width, +// end_x: origin.x + line_end as f32 * layout.size.cell_width, // }); // } diff --git a/crates/terminal_view2/src/terminal_view.rs b/crates/terminal_view2/src/terminal_view.rs index 55b7c1af669c0c7039f664bf23b2a93c520009a4..14391ca2b2f357f56f084a5688601182bad780cf 100644 --- a/crates/terminal_view2/src/terminal_view.rs +++ b/crates/terminal_view2/src/terminal_view.rs @@ -7,27 +7,17 @@ pub mod terminal_panel; // todo!() // use crate::terminal_element::TerminalElement; -use anyhow::Context; -use dirs::home_dir; use editor::{scroll::autoscroll::Autoscroll, Editor}; use gpui::{ actions, div, img, red, register_action, AnyElement, AppContext, Component, DispatchPhase, Div, EventEmitter, FocusEvent, FocusHandle, Focusable, FocusableComponent, FocusableView, - InputHandler, InteractiveComponent, KeyDownEvent, Keystroke, Model, ParentComponent, Pixels, - Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView, + InputHandler, InteractiveComponent, KeyDownEvent, Keystroke, Model, MouseButton, + ParentComponent, Pixels, Render, SharedString, Styled, Task, View, ViewContext, VisualContext, + WeakView, }; use language::Bias; use persistence::TERMINAL_DB; use project::{search::SearchQuery, LocalWorktree, Project}; -use serde::Deserialize; -use settings::Settings; -use smol::Timer; -use std::{ - ops::RangeInclusive, - path::{Path, PathBuf}, - sync::Arc, - time::Duration, -}; use terminal::{ alacritty_terminal::{ index::Point, @@ -42,7 +32,21 @@ use workspace::{ notifications::NotifyResultExt, register_deserializable_item, searchable::{SearchEvent, SearchOptions, SearchableItem}, - NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId, + ui::{ContextMenu, ContextMenuItem, Label}, + CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId, +}; + +use anyhow::Context; +use dirs::home_dir; +use serde::Deserialize; +use settings::Settings; +use smol::Timer; + +use std::{ + ops::RangeInclusive, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, }; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); @@ -62,6 +66,7 @@ pub struct SendKeystroke(String); actions!(Clear, Copy, Paste, ShowCharacterPalette, SearchTest); pub fn init(cx: &mut AppContext) { + workspace::ui::init(cx); terminal_panel::init(cx); terminal::init(cx); @@ -82,7 +87,7 @@ pub struct TerminalView { has_new_content: bool, //Currently using iTerm bell, show bell emoji in tab until input is received has_bell: bool, - // context_menu: View, + context_menu: Option, blink_state: bool, blinking_on: bool, blinking_paused: bool, @@ -265,8 +270,7 @@ impl TerminalView { has_new_content: true, has_bell: false, focus_handle: cx.focus_handle(), - // todo!() - // context_menu: cx.build_view(|cx| ContextMenu::new(view_id, cx)), + context_menu: None, blink_state: true, blinking_on: false, blinking_paused: false, @@ -293,18 +297,20 @@ impl TerminalView { cx.emit(Event::Wakeup); } - pub fn deploy_context_menu(&mut self, _position: Point, _cx: &mut ViewContext) { - //todo!(context_menu) - // let menu_entries = vec![ - // ContextMenuItem::action("Clear", Clear), - // ContextMenuItem::action("Close", pane::CloseActiveItem { save_intent: None }), - // ]; - - // self.context_menu.update(cx, |menu, cx| { - // menu.show(position, AnchorCorner::TopLeft, menu_entries, cx) - // }); - - // cx.notify(); + pub fn deploy_context_menu( + &mut self, + position: gpui::Point, + cx: &mut ViewContext, + ) { + self.context_menu = Some(ContextMenu::new(vec![ + ContextMenuItem::entry(Label::new("Clear"), Clear), + ContextMenuItem::entry(Label::new("Close"), CloseActiveItem { save_intent: None }), + ])); + dbg!(&position); + // todo!() + // self.context_menu + // .show(position, AnchorCorner::TopLeft, menu_entries, cx); + // cx.notify(); } fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext) { @@ -541,28 +547,41 @@ impl Render for TerminalView { let focused = self.focus_handle.is_focused(cx); div() + .relative() + .child( + div() + .z_index(0) + .absolute() + .on_key_down(Self::key_down) + .on_action(TerminalView::send_text) + .on_action(TerminalView::send_keystroke) + .on_action(TerminalView::copy) + .on_action(TerminalView::paste) + .on_action(TerminalView::clear) + .on_action(TerminalView::show_character_palette) + .on_action(TerminalView::select_all) + // todo!() + .child( + "TERMINAL HERE", // TerminalElement::new( + // terminal_handle, + // focused, + // self.should_show_cursor(focused, cx), + // self.can_navigate_to_selected_word, + // ) + ) + .on_mouse_down(MouseButton::Right, |this, event, cx| { + this.deploy_context_menu(event.position, cx); + cx.notify(); + }), + ) + .children( + self.context_menu + .clone() + .map(|context_menu| div().z_index(1).absolute().child(context_menu.render())), + ) .track_focus(&self.focus_handle) .on_focus_in(Self::focus_in) .on_focus_out(Self::focus_out) - .on_key_down(Self::key_down) - .on_action(TerminalView::send_text) - .on_action(TerminalView::send_keystroke) - .on_action(TerminalView::copy) - .on_action(TerminalView::paste) - .on_action(TerminalView::clear) - .on_action(TerminalView::show_character_palette) - .on_action(TerminalView::select_all) - // todo!() - .child( - "TERMINAL HERE", // TerminalElement::new( - // terminal_handle, - // focused, - // self.should_show_cursor(focused, cx), - // self.can_navigate_to_selected_word, - // ) - ) - // todo!() - // .child(ChildView::new(&self.context_menu, cx)) } } diff --git a/crates/ui2/Cargo.toml b/crates/ui2/Cargo.toml index 754bca371f41e741356f1ad9df670de2154e9978..0a7de6299d1f0999df813ced30902c039615f662 100644 --- a/crates/ui2/Cargo.toml +++ b/crates/ui2/Cargo.toml @@ -9,6 +9,7 @@ anyhow.workspace = true chrono = "0.4" gpui = { package = "gpui2", path = "../gpui2" } itertools = { version = "0.11.0", optional = true } +menu = { package = "menu2", path = "../menu2"} serde.workspace = true settings2 = { path = "../settings2" } smallvec.workspace = true diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 117be127798bd56f9d0c3963f8119832aa170550..8f32c3ed5664ede770af66580e0abe2f7379f767 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -3,17 +3,29 @@ use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHea pub enum ContextMenuItem { Header(SharedString), - Entry(Label), + Entry(Label, Box), Separator, } +impl Clone for ContextMenuItem { + fn clone(&self) -> Self { + match self { + ContextMenuItem::Header(name) => ContextMenuItem::Header(name.clone()), + ContextMenuItem::Entry(label, action) => { + ContextMenuItem::Entry(label.clone(), action.boxed_clone()) + } + ContextMenuItem::Separator => ContextMenuItem::Separator, + } + } +} impl ContextMenuItem { fn to_list_item(self) -> ListItem { match self { ContextMenuItem::Header(label) => ListSubHeader::new(label).into(), - ContextMenuItem::Entry(label) => { - ListEntry::new(label).variant(ListItemVariant::Inset).into() - } + ContextMenuItem::Entry(label, action) => ListEntry::new(label) + .variant(ListItemVariant::Inset) + .on_click(action) + .into(), ContextMenuItem::Separator => ListSeparator::new().into(), } } @@ -26,12 +38,12 @@ impl ContextMenuItem { Self::Separator } - pub fn entry(label: Label) -> Self { - Self::Entry(label) + pub fn entry(label: Label, action: impl Action) -> Self { + Self::Entry(label, Box::new(action)) } } -#[derive(Component)] +#[derive(Component, Clone)] pub struct ContextMenu { items: Vec, } @@ -42,7 +54,12 @@ impl ContextMenu { items: items.into_iter().collect(), } } - + // todo!() + // cx.add_action(ContextMenu::select_first); + // cx.add_action(ContextMenu::select_last); + // cx.add_action(ContextMenu::select_next); + // cx.add_action(ContextMenu::select_prev); + // cx.add_action(ContextMenu::confirm); fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { v_stack() .flex() @@ -55,9 +72,11 @@ impl ContextMenu { .map(ContextMenuItem::to_list_item::) .collect(), )) + .on_mouse_down_out(|_, _, cx| cx.dispatch_action(Box::new(menu::Cancel))) } } +use gpui::Action; #[cfg(feature = "stories")] pub use stories::*; @@ -65,7 +84,7 @@ pub use stories::*; mod stories { use super::*; use crate::story::Story; - use gpui::{Div, Render}; + use gpui::{action, Div, Render}; pub struct ContextMenuStory; @@ -73,14 +92,22 @@ mod stories { type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + #[action] + struct PrintCurrentDate {} + Story::container(cx) .child(Story::title_for::<_, ContextMenu>(cx)) .child(Story::label(cx, "Default")) .child(ContextMenu::new([ ContextMenuItem::header("Section header"), ContextMenuItem::Separator, - ContextMenuItem::entry(Label::new("Some entry")), + ContextMenuItem::entry(Label::new("Print current time"), PrintCurrentDate {}), ])) + .on_action(|_, _: &PrintCurrentDate, _| { + if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() { + println!("Current Unix time is {:?}", unix_time.as_secs()); + } + }) } } } diff --git a/crates/ui2/src/components/label.rs b/crates/ui2/src/components/label.rs index cbb75278c29487e0b5466421ce33f52342e60fed..1beee5c8b731bdcf984e53818bec4e960f6f41da 100644 --- a/crates/ui2/src/components/label.rs +++ b/crates/ui2/src/components/label.rs @@ -60,7 +60,7 @@ pub enum LineHeightStyle { UILabel, } -#[derive(Component)] +#[derive(Clone, Component)] pub struct Label { label: SharedString, size: LabelSize, diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index 1ddad269dd752fbfce95028c11fcc555f7bbfeb0..4b355dd5b6c159c906117e64b3008f8d100129c7 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -1,11 +1,10 @@ -use gpui::div; +use gpui::{div, Action}; -use crate::prelude::*; use crate::settings::user_settings; use crate::{ - disclosure_control, h_stack, v_stack, Avatar, GraphicSlot, Icon, IconElement, IconSize, Label, - TextColor, Toggle, + disclosure_control, h_stack, v_stack, Avatar, Icon, IconElement, IconSize, Label, Toggle, }; +use crate::{prelude::*, GraphicSlot}; #[derive(Clone, Copy, Default, Debug, PartialEq)] pub enum ListItemVariant { @@ -232,6 +231,7 @@ pub struct ListEntry { size: ListEntrySize, toggle: Toggle, variant: ListItemVariant, + on_click: Option>, } impl ListEntry { @@ -245,9 +245,15 @@ impl ListEntry { size: ListEntrySize::default(), toggle: Toggle::NotToggleable, variant: ListItemVariant::default(), + on_click: Default::default(), } } + pub fn on_click(mut self, action: impl Into>) -> Self { + self.on_click = Some(action.into()); + self + } + pub fn variant(mut self, variant: ListItemVariant) -> Self { self.variant = variant; self @@ -303,9 +309,21 @@ impl ListEntry { ListEntrySize::Small => div().h_6(), ListEntrySize::Medium => div().h_7(), }; - div() .relative() + .hover(|mut style| { + style.background = Some(cx.theme().colors().editor_background.into()); + style + }) + .on_mouse_down(gpui::MouseButton::Left, { + let action = self.on_click.map(|action| action.boxed_clone()); + + move |entry: &mut V, event, cx| { + if let Some(action) = action.as_ref() { + cx.dispatch_action(action.boxed_clone()); + } + } + }) .group("") .bg(cx.theme().colors().surface_background) // TODO: Add focus state @@ -401,7 +419,7 @@ impl List { v_stack() .w_full() .py_1() - .children(self.header) + .children(self.header.map(|header| header)) .child(list_content) } } diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index f471ea230656bc116aab196a0e21c4a2d54d47e0..df3332803e790a7d7e32575487b40e041ee9e5cb 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -27,7 +27,6 @@ collab_ui = { package = "collab_ui2", path = "../collab_ui2" } collections = { path = "../collections" } command_palette = { package="command_palette2", path = "../command_palette2" } # component_test = { path = "../component_test" } -# context_menu = { path = "../context_menu" } client = { package = "client2", path = "../client2" } # clock = { path = "../clock" } copilot = { package = "copilot2", path = "../copilot2" } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index ee1a067a29d1e09b4f6e1893d1268d52f634b065..ef65d00fc386503d68a6aaa457ecadabbf21e1a0 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -141,7 +141,6 @@ fn main() { cx.set_global(client.clone()); theme::init(cx); - // context_menu::init(cx); project::Project::init(&client, cx); client::init(&client, cx); command_palette::init(cx);