diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index e7c9d4c21a30ac98f4cd2e03fbd965cc74d1e40c..359056d9d3d4826cbd9014261e0325c67069acd4 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -331,7 +331,7 @@ impl AssistantPanel { let measurements = Rc::new(Cell::new(BlockMeasurements::default())); let inline_assistant = cx.build_view(|cx| { - let assistant = InlineAssistant::new( + InlineAssistant::new( inline_assist_id, measurements.clone(), self.include_conversation_in_next_inline_assist, @@ -342,9 +342,7 @@ impl AssistantPanel { self.retrieve_context_in_next_inline_assist, self.semantic_index.clone(), project.clone(), - ); - assistant.focus_handle.focus(cx); - assistant + ) }); let block_id = editor.update(cx, |editor, cx| { editor.change_selections(None, cx, |selections| { @@ -391,10 +389,7 @@ impl AssistantPanel { if let Some(inline_assistant) = inline_assistant.upgrade() { if let EditorEvent::SelectionsChanged { local } = event { if *local - && inline_assistant - .read(cx) - .focus_handle - .contains_focused(cx) + && inline_assistant.focus_handle(cx).contains_focused(cx) { cx.focus_view(&editor); } @@ -553,9 +548,12 @@ impl AssistantPanel { fn hide_inline_assist(&mut self, assist_id: usize, cx: &mut ViewContext) { if let Some(pending_assist) = self.pending_inline_assists.get_mut(&assist_id) { if let Some(editor) = pending_assist.editor.upgrade() { - if let Some((block_id, _)) = pending_assist.inline_assistant.take() { + if let Some((block_id, inline_assistant)) = pending_assist.inline_assistant.take() { editor.update(cx, |editor, cx| { editor.remove_blocks(HashSet::from_iter([block_id]), None, cx); + if inline_assistant.focus_handle(cx).contains_focused(cx) { + editor.focus(cx); + } }); } } @@ -891,8 +889,9 @@ impl AssistantPanel { if search_bar.show(cx) { search_bar.search_suggested(cx); if action.focus { + let focus_handle = search_bar.focus_handle(cx); search_bar.select_query(cx); - cx.focus_self(); + cx.focus(&focus_handle); } propagate = false } @@ -956,7 +955,7 @@ impl AssistantPanel { } fn render_split_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("split_button", Icon::Menu) + IconButton::new("split_button", Icon::SplitMessage) .on_click(cx.listener(|this, _event, cx| { if let Some(active_editor) = this.active_editor() { active_editor.update(cx, |editor, cx| editor.split(&Default::default(), cx)); @@ -966,7 +965,7 @@ impl AssistantPanel { } fn render_assist_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("assist_button", Icon::Menu) + IconButton::new("assist_button", Icon::MagicWand) .on_click(cx.listener(|this, _event, cx| { if let Some(active_editor) = this.active_editor() { active_editor.update(cx, |editor, cx| editor.assist(&Default::default(), cx)); @@ -976,7 +975,7 @@ impl AssistantPanel { } fn render_quote_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("quote_button", Icon::Menu) + IconButton::new("quote_button", Icon::Quote) .on_click(cx.listener(|this, _event, cx| { if let Some(workspace) = this.workspace.upgrade() { cx.window_context().defer(move |cx| { @@ -990,7 +989,7 @@ impl AssistantPanel { } fn render_plus_button(cx: &mut ViewContext) -> impl IntoElement { - IconButton::new("plus_button", Icon::Menu) + IconButton::new("plus_button", Icon::Plus) .on_click(cx.listener(|this, _event, cx| { this.new_conversation(cx); })) @@ -999,7 +998,7 @@ impl AssistantPanel { fn render_zoom_button(&self, cx: &mut ViewContext) -> impl IntoElement { let zoomed = self.zoomed; - IconButton::new("zoom_button", Icon::Menu) + IconButton::new("zoom_button", Icon::MagnifyingGlass) .on_click(cx.listener(|this, _event, cx| { this.toggle_zoom(&ToggleZoom, cx); })) @@ -1028,6 +1027,8 @@ impl AssistantPanel { } fn open_conversation(&mut self, path: PathBuf, cx: &mut ViewContext) -> Task> { + cx.focus(&self.focus_handle); + if let Some(ix) = self.editor_index_for_path(&path, cx) { self.set_active_editor_index(Some(ix), cx); return Task::ready(Ok(())); @@ -2026,7 +2027,6 @@ struct ConversationEditor { editor: View, blocks: HashSet, scroll_position: Option, - focus_handle: FocusHandle, _subscriptions: Vec, } @@ -2057,13 +2057,10 @@ impl ConversationEditor { editor }); - let focus_handle = cx.focus_handle(); - let _subscriptions = vec![ cx.observe(&conversation, |_, _, cx| cx.notify()), cx.subscribe(&conversation, Self::handle_conversation_event), cx.subscribe(&editor, Self::handle_editor_event), - cx.on_focus(&focus_handle, |this, cx| cx.focus_view(&this.editor)), ]; let mut this = Self { @@ -2073,7 +2070,6 @@ impl ConversationEditor { scroll_position: None, fs, workspace, - focus_handle, _subscriptions, }; this.update_message_headers(cx); @@ -2484,8 +2480,8 @@ impl Render for ConversationEditor { } impl FocusableView for ConversationEditor { - fn focus_handle(&self, _cx: &AppContext) -> FocusHandle { - self.focus_handle.clone() + fn focus_handle(&self, cx: &AppContext) -> FocusHandle { + self.editor.focus_handle(cx) } } @@ -2539,7 +2535,6 @@ struct InlineAssistant { prompt_editor: View, workspace: WeakView, confirmed: bool, - focus_handle: FocusHandle, include_conversation: bool, measurements: Rc>, prompt_history: VecDeque, @@ -2635,8 +2630,8 @@ impl Render for InlineAssistant { } impl FocusableView for InlineAssistant { - fn focus_handle(&self, _cx: &AppContext) -> FocusHandle { - self.focus_handle.clone() + fn focus_handle(&self, cx: &AppContext) -> FocusHandle { + self.prompt_editor.focus_handle(cx) } } @@ -2662,12 +2657,11 @@ impl InlineAssistant { editor.set_placeholder_text(placeholder, cx); editor }); + cx.focus_view(&prompt_editor); - let focus_handle = cx.focus_handle(); let mut subscriptions = vec![ cx.observe(&codegen, Self::handle_codegen_changed), cx.subscribe(&prompt_editor, Self::handle_prompt_editor_events), - cx.on_focus(&focus_handle, |this, cx| cx.focus_view(&this.prompt_editor)), ]; if let Some(semantic_index) = semantic_index.clone() { @@ -2679,7 +2673,6 @@ impl InlineAssistant { prompt_editor, workspace, confirmed: false, - focus_handle, include_conversation, measurements, prompt_history, diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 05b459803cfac270431e681ae35c58fc60c49add..46d64fcf9d4c8f0860d741be8dc3016c17d18bc0 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -1814,34 +1814,34 @@ impl Editor { this } - fn dispatch_context(&self, cx: &AppContext) -> KeyContext { - let mut dispatch_context = KeyContext::default(); - dispatch_context.add("Editor"); + fn key_context(&self, cx: &AppContext) -> KeyContext { + let mut key_context = KeyContext::default(); + key_context.add("Editor"); let mode = match self.mode { EditorMode::SingleLine => "single_line", EditorMode::AutoHeight { .. } => "auto_height", EditorMode::Full => "full", }; - dispatch_context.set("mode", mode); + key_context.set("mode", mode); if self.pending_rename.is_some() { - dispatch_context.add("renaming"); + key_context.add("renaming"); } if self.context_menu_visible() { match self.context_menu.read().as_ref() { Some(ContextMenu::Completions(_)) => { - dispatch_context.add("menu"); - dispatch_context.add("showing_completions") + key_context.add("menu"); + key_context.add("showing_completions") } Some(ContextMenu::CodeActions(_)) => { - dispatch_context.add("menu"); - dispatch_context.add("showing_code_actions") + key_context.add("menu"); + key_context.add("showing_code_actions") } None => {} } } for layer in self.keymap_context_layers.values() { - dispatch_context.extend(layer); + key_context.extend(layer); } if let Some(extension) = self @@ -1850,10 +1850,10 @@ impl Editor { .as_singleton() .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str()) { - dispatch_context.set("extension", extension.to_string()); + key_context.set("extension", extension.to_string()); } - dispatch_context + key_context } pub fn new_file( diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 9447c3370b45ad131d3907bbae59eff889548331..ad66ed8090749ab3d69b71a9aa30d7b77e701465 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -275,36 +275,48 @@ impl EditorElement { register_action(view, cx, Editor::copy_relative_path); register_action(view, cx, Editor::copy_highlight_json); register_action(view, cx, |editor, action, cx| { - editor - .format(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.format(action, cx) { + task.detach_and_log_err(cx); + } else { + cx.propagate(); + } }); register_action(view, cx, Editor::restart_language_server); register_action(view, cx, Editor::show_character_palette); register_action(view, cx, |editor, action, cx| { - editor - .confirm_completion(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.confirm_completion(action, cx) { + task.detach_and_log_err(cx); + } else { + cx.propagate(); + } }); register_action(view, cx, |editor, action, cx| { - editor - .confirm_code_action(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.confirm_code_action(action, cx) { + task.detach_and_log_err(cx); + } else { + cx.propagate(); + } }); register_action(view, cx, |editor, action, cx| { - editor - .rename(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.rename(action, cx) { + task.detach_and_log_err(cx); + } else { + cx.propagate(); + } }); register_action(view, cx, |editor, action, cx| { - editor - .confirm_rename(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.confirm_rename(action, cx) { + task.detach_and_log_err(cx); + } else { + cx.propagate(); + } }); register_action(view, cx, |editor, action, cx| { - editor - .find_all_references(action, cx) - .map(|task| task.detach_and_log_err(cx)); + if let Some(task) = editor.find_all_references(action, cx) { + task.detach_and_log_err(cx); + } else { + cx.propagate(); + } }); register_action(view, cx, Editor::next_copilot_suggestion); register_action(view, cx, Editor::previous_copilot_suggestion); @@ -2802,49 +2814,38 @@ impl Element for EditorElement { }; let focus_handle = editor.focus_handle(cx); - let dispatch_context = self.editor.read(cx).dispatch_context(cx); - cx.with_key_dispatch( - Some(dispatch_context), - Some(focus_handle.clone()), - |_, cx| { - self.register_actions(cx); - self.register_key_listeners(cx); - - // We call with_z_index to establish a new stacking context. - cx.with_z_index(0, |cx| { - cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - // Paint mouse listeners at z-index 0 so any elements we paint on top of the editor - // take precedence. - cx.with_z_index(0, |cx| { - self.paint_mouse_listeners( - bounds, - gutter_bounds, - text_bounds, - &layout, - cx, - ); - }); - let input_handler = - ElementInputHandler::new(bounds, self.editor.clone(), cx); - cx.handle_input(&focus_handle, input_handler); + let key_context = self.editor.read(cx).key_context(cx); + cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| { + self.register_actions(cx); + self.register_key_listeners(cx); + + // We call with_z_index to establish a new stacking context. + cx.with_z_index(0, |cx| { + cx.with_content_mask(Some(ContentMask { bounds }), |cx| { + // Paint mouse listeners at z-index 0 so any elements we paint on top of the editor + // take precedence. + cx.with_z_index(0, |cx| { + self.paint_mouse_listeners(bounds, gutter_bounds, text_bounds, &layout, cx); + }); + let input_handler = ElementInputHandler::new(bounds, self.editor.clone(), cx); + cx.handle_input(&focus_handle, input_handler); - self.paint_background(gutter_bounds, text_bounds, &layout, cx); - if layout.gutter_size.width > Pixels::ZERO { - self.paint_gutter(gutter_bounds, &mut layout, cx); - } - self.paint_text(text_bounds, &mut layout, cx); + self.paint_background(gutter_bounds, text_bounds, &layout, cx); + if layout.gutter_size.width > Pixels::ZERO { + self.paint_gutter(gutter_bounds, &mut layout, cx); + } + self.paint_text(text_bounds, &mut layout, cx); - if !layout.blocks.is_empty() { - cx.with_z_index(1, |cx| { - cx.with_element_id(Some("editor_blocks"), |cx| { - self.paint_blocks(bounds, &mut layout, cx); - }) + if !layout.blocks.is_empty() { + cx.with_z_index(1, |cx| { + cx.with_element_id(Some("editor_blocks"), |cx| { + self.paint_blocks(bounds, &mut layout, cx); }) - } - }); + }) + } }); - }, - ) + }); + }) } } diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 9293302938c13b8948fab22dd4fc092e69da6c80..7d9ca68a82e05279b35e2a23c5c87b25be4315e5 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -704,14 +704,13 @@ impl AppContext { let focus_changed = focused.is_some() || blurred.is_some(); let event = FocusEvent { focused, blurred }; - let mut listeners = mem::take(&mut cx.window.current_frame.focus_listeners); + let mut listeners = mem::take(&mut cx.window.rendered_frame.focus_listeners); if focus_changed { for listener in &mut listeners { listener(&event, cx); } } - listeners.extend(cx.window.current_frame.focus_listeners.drain(..)); - cx.window.current_frame.focus_listeners = listeners; + cx.window.rendered_frame.focus_listeners = listeners; if focus_changed { cx.window @@ -1029,9 +1028,13 @@ impl AppContext { window .update(self, |_, cx| { cx.window - .current_frame + .rendered_frame .dispatch_tree - .clear_pending_keystrokes() + .clear_pending_keystrokes(); + cx.window + .next_frame + .dispatch_tree + .clear_pending_keystrokes(); }) .ok(); } @@ -1107,6 +1110,10 @@ impl AppContext { } } } + + pub fn has_active_drag(&self) -> bool { + self.active_drag.is_some() + } } impl Context for AppContext { diff --git a/crates/gpui2/src/element.rs b/crates/gpui2/src/element.rs index a67276b0bcd62c91aab801be3d61fc784a2717dd..226a477012a025474df2a58c70be56c10ef37fc3 100644 --- a/crates/gpui2/src/element.rs +++ b/crates/gpui2/src/element.rs @@ -69,24 +69,6 @@ pub trait IntoElement: Sized { self.map(|this| if condition { then(this) } else { this }) } - fn when_else( - self, - condition: bool, - then: impl FnOnce(Self) -> Self, - otherwise: impl FnOnce(Self) -> Self, - ) -> Self - where - Self: Sized, - { - self.map(|this| { - if condition { - then(this) - } else { - otherwise(this) - } - }) - } - fn when_some(self, option: Option, then: impl FnOnce(Self, T) -> Self) -> Self where Self: Sized, diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 2d48ec5a1100e61d493dd4ac92864915e4ce70bb..84636630f37b7216b94b85be68d52d1122c019b4 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -193,6 +193,12 @@ impl Deref for MouseExitEvent { #[derive(Debug, Clone, Default)] pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>); +impl ExternalPaths { + pub fn paths(&self) -> &[PathBuf] { + &self.0 + } +} + impl Render for ExternalPaths { type Element = Div; diff --git a/crates/gpui2/src/platform/mac/metal_renderer.rs b/crates/gpui2/src/platform/mac/metal_renderer.rs index 19afb503324907a0b84bcac7722fc71ead05451d..c477440df5d888658e96c8e9d4d3f7e70d214679 100644 --- a/crates/gpui2/src/platform/mac/metal_renderer.rs +++ b/crates/gpui2/src/platform/mac/metal_renderer.rs @@ -325,7 +325,7 @@ impl MetalRenderer { .entry(tile.texture_id) .or_insert(Vec::new()) .extend(path.vertices.iter().map(|vertex| PathVertex { - xy_position: vertex.xy_position - path.bounds.origin + xy_position: vertex.xy_position - clipped_bounds.origin + tile.bounds.origin.map(Into::into), st_position: vertex.st_position, content_mask: ContentMask { @@ -544,9 +544,10 @@ impl MetalRenderer { if let Some((path, tile)) = paths_and_tiles.peek() { if prev_texture_id.map_or(true, |texture_id| texture_id == tile.texture_id) { prev_texture_id = Some(tile.texture_id); + let origin = path.bounds.intersect(&path.content_mask.bounds).origin; sprites.push(PathSprite { bounds: Bounds { - origin: path.bounds.origin.map(|p| p.floor()), + origin: origin.map(|p| p.floor()), size: tile.bounds.size.map(Into::into), }, color: path.color, diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 455472a349449dfa5e84d7a285a789a3402d35f6..cab41067ce576dcab09d12537ab1da665dba4be4 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -4,12 +4,12 @@ use crate::{ DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, LayoutId, Model, - ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, - MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, - PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, - RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, - UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, + ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Path, + Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, + PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, + RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, + Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, + WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Context as _, Result}; use collections::HashMap; @@ -90,7 +90,7 @@ impl FocusId { /// Obtains whether this handle contains the given handle in the most recently rendered frame. pub(crate) fn contains(&self, other: Self, cx: &WindowContext) -> bool { cx.window - .current_frame + .rendered_frame .dispatch_tree .focus_contains(*self, other) } @@ -212,8 +212,8 @@ pub struct Window { layout_engine: Option, pub(crate) root_view: Option, pub(crate) element_id_stack: GlobalElementId, - pub(crate) previous_frame: Frame, - pub(crate) current_frame: Frame, + pub(crate) rendered_frame: Frame, + pub(crate) next_frame: Frame, pub(crate) focus_handles: Arc>>, pub(crate) focus_listeners: SubscriberSet<(), AnyWindowFocusListener>, default_prevented: bool, @@ -249,7 +249,7 @@ pub(crate) struct Frame { } impl Frame { - pub fn new(dispatch_tree: DispatchTree) -> Self { + fn new(dispatch_tree: DispatchTree) -> Self { Frame { element_states: HashMap::default(), mouse_listeners: HashMap::default(), @@ -262,6 +262,14 @@ impl Frame { element_offset_stack: Vec::new(), } } + + fn clear(&mut self) { + self.element_states.clear(); + self.mouse_listeners.values_mut().for_each(Vec::clear); + self.focus_listeners.clear(); + self.dispatch_tree.clear(); + self.depth_map.clear(); + } } impl Window { @@ -330,8 +338,8 @@ impl Window { layout_engine: Some(TaffyLayoutEngine::new()), root_view: None, element_id_stack: GlobalElementId::default(), - previous_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), - current_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), + rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), + next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_listeners: SubscriberSet::new(), default_prevented: true, @@ -428,7 +436,7 @@ impl<'a> WindowContext<'a> { self.window.focus = Some(focus_id); self.window - .current_frame + .rendered_frame .dispatch_tree .clear_pending_keystrokes(); self.app.push_effect(Effect::FocusChanged { @@ -459,11 +467,11 @@ impl<'a> WindowContext<'a> { let node_id = focus_handle .and_then(|handle| { cx.window - .current_frame + .rendered_frame .dispatch_tree .focusable_node_id(handle.id) }) - .unwrap_or_else(|| cx.window.current_frame.dispatch_tree.root_node_id()); + .unwrap_or_else(|| cx.window.rendered_frame.dispatch_tree.root_node_id()); cx.propagate_event = true; cx.dispatch_action_on_node(node_id, action); @@ -743,7 +751,7 @@ impl<'a> WindowContext<'a> { self.window.default_prevented } - /// Register a mouse event listener on the window for the current frame. The type of event + /// Register a mouse event listener on the window for the next frame. The type of event /// is determined by the first parameter of the given listener. When the next frame is rendered /// the listener will be cleared. /// @@ -753,9 +761,9 @@ impl<'a> WindowContext<'a> { &mut self, handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { - let order = self.window.current_frame.z_index_stack.clone(); + let order = self.window.next_frame.z_index_stack.clone(); self.window - .current_frame + .next_frame .mouse_listeners .entry(TypeId::of::()) .or_default() @@ -767,7 +775,7 @@ impl<'a> WindowContext<'a> { )) } - /// Register a key event listener on the window for the current frame. The type of event + /// Register a key event listener on the window for the next frame. The type of event /// is determined by the first parameter of the given listener. When the next frame is rendered /// the listener will be cleared. /// @@ -778,7 +786,7 @@ impl<'a> WindowContext<'a> { handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { self.window - .current_frame + .next_frame .dispatch_tree .on_key_event(Rc::new(move |event, phase, cx| { if let Some(event) = event.downcast_ref::() { @@ -787,7 +795,7 @@ impl<'a> WindowContext<'a> { })); } - /// Register an action listener on the window for the current frame. The type of action + /// Register an action listener on the window for the next frame. The type of action /// is determined by the first parameter of the given listener. When the next frame is rendered /// the listener will be cleared. /// @@ -798,7 +806,7 @@ impl<'a> WindowContext<'a> { action_type: TypeId, handler: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static, ) { - self.window.current_frame.dispatch_tree.on_action( + self.window.next_frame.dispatch_tree.on_action( action_type, Rc::new(move |action, phase, cx| handler(action, phase, cx)), ); @@ -809,13 +817,13 @@ impl<'a> WindowContext<'a> { .focused() .and_then(|focused_handle| { self.window - .current_frame + .rendered_frame .dispatch_tree .focusable_node_id(focused_handle.id) }) - .unwrap_or_else(|| self.window.current_frame.dispatch_tree.root_node_id()); + .unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id()); self.window - .current_frame + .rendered_frame .dispatch_tree .is_action_available(action, target) } @@ -832,16 +840,16 @@ impl<'a> WindowContext<'a> { /// Called during painting to invoke the given closure in a new stacking context. The given /// z-index is interpreted relative to the previous call to `stack`. pub fn with_z_index(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { - self.window.current_frame.z_index_stack.push(z_index); + self.window.next_frame.z_index_stack.push(z_index); let result = f(self); - self.window.current_frame.z_index_stack.pop(); + self.window.next_frame.z_index_stack.pop(); result } /// Called during painting to track which z-index is on top at each pixel position pub fn add_opaque_layer(&mut self, bounds: Bounds) { - let stacking_order = self.window.current_frame.z_index_stack.clone(); - let depth_map = &mut self.window.current_frame.depth_map; + let stacking_order = self.window.next_frame.z_index_stack.clone(); + let depth_map = &mut self.window.next_frame.depth_map; match depth_map.binary_search_by(|(level, _)| stacking_order.cmp(&level)) { Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, bounds)), } @@ -850,7 +858,7 @@ impl<'a> WindowContext<'a> { /// Returns true if the top-most opaque layer painted over this point was part of the /// same layer as the given stacking order. pub fn was_top_layer(&self, point: &Point, level: &StackingOrder) -> bool { - for (stack, bounds) in self.window.previous_frame.depth_map.iter() { + for (stack, bounds) in self.window.rendered_frame.depth_map.iter() { if bounds.contains_point(point) { return level.starts_with(stack) || stack.starts_with(level); } @@ -861,10 +869,10 @@ impl<'a> WindowContext<'a> { /// Called during painting to get the current stacking order. pub fn stacking_order(&self) -> &StackingOrder { - &self.window.current_frame.z_index_stack + &self.window.next_frame.z_index_stack } - /// Paint one or more drop shadows into the scene for the current frame at the current z-index. + /// Paint one or more drop shadows into the scene for the next frame at the current z-index. pub fn paint_shadows( &mut self, bounds: Bounds, @@ -878,8 +886,8 @@ impl<'a> WindowContext<'a> { let mut shadow_bounds = bounds; shadow_bounds.origin += shadow.offset; shadow_bounds.dilate(shadow.spread_radius); - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, Shadow { order: 0, bounds: shadow_bounds.scale(scale_factor), @@ -892,7 +900,7 @@ impl<'a> WindowContext<'a> { } } - /// Paint one or more quads into the scene for the current frame at the current stacking context. + /// Paint one or more quads into the scene for the next frame at the current stacking context. /// Quads are colored rectangular regions with an optional background, border, and corner radius. pub fn paint_quad( &mut self, @@ -906,8 +914,8 @@ impl<'a> WindowContext<'a> { let content_mask = self.content_mask(); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, Quad { order: 0, bounds: bounds.scale(scale_factor), @@ -920,20 +928,20 @@ impl<'a> WindowContext<'a> { ); } - /// Paint the given `Path` into the scene for the current frame at the current z-index. + /// Paint the given `Path` into the scene for the next frame at the current z-index. pub fn paint_path(&mut self, mut path: Path, color: impl Into) { let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); path.content_mask = content_mask; path.color = color.into(); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, - path.scale(scale_factor), - ); + window + .next_frame + .scene_builder + .insert(&window.next_frame.z_index_stack, path.scale(scale_factor)); } - /// Paint an underline into the scene for the current frame at the current z-index. + /// Paint an underline into the scene for the next frame at the current z-index. pub fn paint_underline( &mut self, origin: Point, @@ -952,8 +960,8 @@ impl<'a> WindowContext<'a> { }; let content_mask = self.content_mask(); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, Underline { order: 0, bounds: bounds.scale(scale_factor), @@ -965,7 +973,7 @@ impl<'a> WindowContext<'a> { ); } - /// Paint a monochrome (non-emoji) glyph into the scene for the current frame at the current z-index. + /// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index. /// The y component of the origin is the baseline of the glyph. pub fn paint_glyph( &mut self, @@ -1005,8 +1013,8 @@ impl<'a> WindowContext<'a> { }; let content_mask = self.content_mask().scale(scale_factor); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, MonochromeSprite { order: 0, bounds, @@ -1019,7 +1027,7 @@ impl<'a> WindowContext<'a> { Ok(()) } - /// Paint an emoji glyph into the scene for the current frame at the current z-index. + /// Paint an emoji glyph into the scene for the next frame at the current z-index. /// The y component of the origin is the baseline of the glyph. pub fn paint_emoji( &mut self, @@ -1056,8 +1064,8 @@ impl<'a> WindowContext<'a> { let content_mask = self.content_mask().scale(scale_factor); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, PolychromeSprite { order: 0, bounds, @@ -1071,7 +1079,7 @@ impl<'a> WindowContext<'a> { Ok(()) } - /// Paint a monochrome SVG into the scene for the current frame at the current stacking context. + /// Paint a monochrome SVG into the scene for the next frame at the current stacking context. pub fn paint_svg( &mut self, bounds: Bounds, @@ -1098,8 +1106,8 @@ impl<'a> WindowContext<'a> { let content_mask = self.content_mask().scale(scale_factor); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, MonochromeSprite { order: 0, bounds, @@ -1112,7 +1120,7 @@ impl<'a> WindowContext<'a> { Ok(()) } - /// Paint an image into the scene for the current frame at the current z-index. + /// Paint an image into the scene for the next frame at the current z-index. pub fn paint_image( &mut self, bounds: Bounds, @@ -1134,8 +1142,8 @@ impl<'a> WindowContext<'a> { let corner_radii = corner_radii.scale(scale_factor); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, PolychromeSprite { order: 0, bounds, @@ -1148,14 +1156,14 @@ impl<'a> WindowContext<'a> { Ok(()) } - /// Paint a surface into the scene for the current frame at the current z-index. + /// Paint a surface into the scene for the next frame at the current z-index. pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVImageBuffer) { let scale_factor = self.scale_factor(); let bounds = bounds.scale(scale_factor); let content_mask = self.content_mask().scale(scale_factor); let window = &mut *self.window; - window.current_frame.scene_builder.insert( - &window.current_frame.z_index_stack, + window.next_frame.scene_builder.insert( + &window.next_frame.z_index_stack, Surface { order: 0, bounds, @@ -1167,15 +1175,17 @@ impl<'a> WindowContext<'a> { /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) { + self.text_system().start_frame(); + self.window.platform_window.clear_input_handler(); + self.window.layout_engine.as_mut().unwrap().clear(); + self.window.next_frame.clear(); let root_view = self.window.root_view.take().unwrap(); - self.start_frame(); - self.with_z_index(0, |cx| { cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| { for (action_type, action_listeners) in &cx.app.global_action_listeners { for action_listener in action_listeners.iter().cloned() { - cx.window.current_frame.dispatch_tree.on_action( + cx.window.next_frame.dispatch_tree.on_action( *action_type, Rc::new(move |action, phase, cx| action_listener(action, phase, cx)), ) @@ -1204,16 +1214,18 @@ impl<'a> WindowContext<'a> { } self.window - .current_frame + .next_frame .dispatch_tree .preserve_pending_keystrokes( - &mut self.window.previous_frame.dispatch_tree, + &mut self.window.rendered_frame.dispatch_tree, self.window.focus, ); - self.window.root_view = Some(root_view); - let scene = self.window.current_frame.scene_builder.build(); + let window = &mut self.window; + mem::swap(&mut window.rendered_frame, &mut window.next_frame); + + let scene = self.window.rendered_frame.scene_builder.build(); self.window.platform_window.draw(scene); let cursor_style = self .window @@ -1225,24 +1237,6 @@ impl<'a> WindowContext<'a> { self.window.dirty = false; } - /// Rotate the current frame and the previous frame, then clear the current frame. - /// We repopulate all state in the current frame during each paint. - fn start_frame(&mut self) { - self.window.platform_window.clear_input_handler(); - self.text_system().start_frame(); - - let window = &mut *self.window; - window.layout_engine.as_mut().unwrap().clear(); - - mem::swap(&mut window.previous_frame, &mut window.current_frame); - let frame = &mut window.current_frame; - frame.element_states.clear(); - frame.mouse_listeners.values_mut().for_each(Vec::clear); - frame.focus_listeners.clear(); - frame.dispatch_tree.clear(); - frame.depth_map.clear(); - } - /// Dispatch a mouse or keyboard event on the window. pub fn dispatch_event(&mut self, event: InputEvent) -> bool { // Handlers may set this to false by calling `stop_propagation` @@ -1275,10 +1269,9 @@ impl<'a> WindowContext<'a> { cursor_offset: position, }); } - InputEvent::MouseDown(MouseDownEvent { + InputEvent::MouseMove(MouseMoveEvent { position, - button: MouseButton::Left, - click_count: 1, + pressed_button: Some(MouseButton::Left), modifiers: Modifiers::default(), }) } @@ -1291,6 +1284,7 @@ impl<'a> WindowContext<'a> { }) } FileDropEvent::Submit { position } => { + self.activate(true); self.window.mouse_position = position; InputEvent::MouseUp(MouseUpEvent { button: MouseButton::Left, @@ -1321,7 +1315,7 @@ impl<'a> WindowContext<'a> { fn dispatch_mouse_event(&mut self, event: &dyn Any) { if let Some(mut handlers) = self .window - .current_frame + .rendered_frame .mouse_listeners .remove(&event.type_id()) { @@ -1351,17 +1345,8 @@ impl<'a> WindowContext<'a> { self.active_drag = None; } - // Just in case any handlers added new handlers, which is weird, but possible. - handlers.extend( - self.window - .current_frame - .mouse_listeners - .get_mut(&event.type_id()) - .into_iter() - .flat_map(|handlers| handlers.drain(..)), - ); self.window - .current_frame + .rendered_frame .mouse_listeners .insert(event.type_id(), handlers); } @@ -1373,15 +1358,15 @@ impl<'a> WindowContext<'a> { .focus .and_then(|focus_id| { self.window - .current_frame + .rendered_frame .dispatch_tree .focusable_node_id(focus_id) }) - .unwrap_or_else(|| self.window.current_frame.dispatch_tree.root_node_id()); + .unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id()); let dispatch_path = self .window - .current_frame + .rendered_frame .dispatch_tree .dispatch_path(node_id); @@ -1392,7 +1377,7 @@ impl<'a> WindowContext<'a> { self.propagate_event = true; for node_id in &dispatch_path { - let node = self.window.current_frame.dispatch_tree.node(*node_id); + let node = self.window.rendered_frame.dispatch_tree.node(*node_id); if let Some(context) = node.context.clone() { context_stack.push(context); @@ -1409,7 +1394,7 @@ impl<'a> WindowContext<'a> { // Bubble phase for node_id in dispatch_path.iter().rev() { // Handle low level key events - let node = self.window.current_frame.dispatch_tree.node(*node_id); + let node = self.window.rendered_frame.dispatch_tree.node(*node_id); for key_listener in node.key_listeners.clone() { key_listener(event, DispatchPhase::Bubble, self); if !self.propagate_event { @@ -1418,12 +1403,12 @@ impl<'a> WindowContext<'a> { } // Match keystrokes - let node = self.window.current_frame.dispatch_tree.node(*node_id); + let node = self.window.rendered_frame.dispatch_tree.node(*node_id); if node.context.is_some() { if let Some(key_down_event) = event.downcast_ref::() { if let Some(found) = self .window - .current_frame + .rendered_frame .dispatch_tree .dispatch_key(&key_down_event.keystroke, &context_stack) { @@ -1446,13 +1431,13 @@ impl<'a> WindowContext<'a> { fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box) { let dispatch_path = self .window - .current_frame + .rendered_frame .dispatch_tree .dispatch_path(node_id); // Capture phase for node_id in &dispatch_path { - let node = self.window.current_frame.dispatch_tree.node(*node_id); + let node = self.window.rendered_frame.dispatch_tree.node(*node_id); for DispatchActionListener { action_type, listener, @@ -1469,7 +1454,7 @@ impl<'a> WindowContext<'a> { } // Bubble phase for node_id in dispatch_path.iter().rev() { - let node = self.window.current_frame.dispatch_tree.node(*node_id); + let node = self.window.rendered_frame.dispatch_tree.node(*node_id); for DispatchActionListener { action_type, listener, @@ -1529,25 +1514,25 @@ impl<'a> WindowContext<'a> { .focus .and_then(|focus_id| { self.window - .current_frame + .rendered_frame .dispatch_tree .focusable_node_id(focus_id) }) - .unwrap_or_else(|| self.window.current_frame.dispatch_tree.root_node_id()); + .unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id()); self.window - .current_frame + .rendered_frame .dispatch_tree .available_actions(node_id) } pub fn bindings_for_action(&self, action: &dyn Action) -> Vec { self.window - .previous_frame + .rendered_frame .dispatch_tree .bindings_for_action( action, - &self.window.previous_frame.dispatch_tree.context_stack, + &self.window.rendered_frame.dispatch_tree.context_stack, ) } @@ -1556,7 +1541,7 @@ impl<'a> WindowContext<'a> { action: &dyn Action, focus_handle: &FocusHandle, ) -> Vec { - let dispatch_tree = &self.window.previous_frame.dispatch_tree; + let dispatch_tree = &self.window.rendered_frame.dispatch_tree; let Some(node_id) = dispatch_tree.focusable_node_id(focus_handle.id) else { return vec![]; @@ -1599,24 +1584,21 @@ impl<'a> WindowContext<'a> { f: impl FnOnce(Option, &mut Self) -> R, ) -> R { let window = &mut self.window; - window - .current_frame - .dispatch_tree - .push_node(context.clone()); + window.next_frame.dispatch_tree.push_node(context.clone()); if let Some(focus_handle) = focus_handle.as_ref() { window - .current_frame + .next_frame .dispatch_tree .make_focusable(focus_handle.id); } let result = f(focus_handle, self); - self.window.current_frame.dispatch_tree.pop_node(); + self.window.next_frame.dispatch_tree.pop_node(); result } - /// Register a focus listener for the current frame only. It will be cleared + /// Register a focus listener for the next frame only. It will be cleared /// on the next frame render. You should use this method only from within elements, /// and we may want to enforce that better via a different context type. // todo!() Move this to `FrameContext` to emphasize its individuality? @@ -1625,7 +1607,7 @@ impl<'a> WindowContext<'a> { listener: impl Fn(&FocusEvent, &mut WindowContext) + 'static, ) { self.window - .current_frame + .next_frame .focus_listeners .push(Box::new(move |event, cx| { listener(event, cx); @@ -1876,12 +1858,9 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { ) -> R { if let Some(mask) = mask { let mask = mask.intersect(&self.content_mask()); - self.window_mut() - .current_frame - .content_mask_stack - .push(mask); + self.window_mut().next_frame.content_mask_stack.push(mask); let result = f(self); - self.window_mut().current_frame.content_mask_stack.pop(); + self.window_mut().next_frame.content_mask_stack.pop(); result } else { f(self) @@ -1897,12 +1876,9 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { size: self.window().viewport_size, }, }; - self.window_mut() - .current_frame - .content_mask_stack - .push(mask); + self.window_mut().next_frame.content_mask_stack.push(mask); let result = f(self); - self.window_mut().current_frame.content_mask_stack.pop(); + self.window_mut().next_frame.content_mask_stack.pop(); result } @@ -1929,26 +1905,26 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { f: impl FnOnce(&mut Self) -> R, ) -> R { self.window_mut() - .current_frame + .next_frame .element_offset_stack .push(offset); let result = f(self); - self.window_mut().current_frame.element_offset_stack.pop(); + self.window_mut().next_frame.element_offset_stack.pop(); result } /// Obtain the current element offset. fn element_offset(&self) -> Point { self.window() - .current_frame + .next_frame .element_offset_stack .last() .copied() .unwrap_or_default() } - /// Update or intialize state for an element with the given id that lives across multiple - /// frames. If an element with this id existed in the previous frame, its state will be passed + /// Update or initialize state for an element with the given id that lives across multiple + /// frames. If an element with this id existed in the rendered frame, its state will be passed /// to the given closure. The state returned by the closure will be stored so it can be referenced /// when drawing the next frame. fn with_element_state( @@ -1964,12 +1940,12 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { if let Some(any) = cx .window_mut() - .current_frame + .next_frame .element_states .remove(&global_id) .or_else(|| { cx.window_mut() - .previous_frame + .rendered_frame .element_states .remove(&global_id) }) @@ -2011,7 +1987,7 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { let (result, state) = f(Some(state), cx); state_box.replace(state); cx.window_mut() - .current_frame + .next_frame .element_states .insert(global_id, ElementStateBox { inner: state_box, @@ -2023,7 +1999,7 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { } else { let (result, state) = f(None, cx); cx.window_mut() - .current_frame + .next_frame .element_states .insert(global_id, ElementStateBox { @@ -2042,7 +2018,7 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { /// Obtain the current content mask. fn content_mask(&self) -> ContentMask { self.window() - .current_frame + .next_frame .content_mask_stack .last() .cloned() @@ -2130,9 +2106,9 @@ impl<'a, V: 'static> ViewContext<'a, V> { } pub fn with_z_index(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { - self.window.current_frame.z_index_stack.push(z_index); + self.window.next_frame.z_index_stack.push(z_index); let result = f(self); - self.window.current_frame.z_index_stack.pop(); + self.window.next_frame.z_index_stack.pop(); result } diff --git a/crates/gpui2_macros/src/action.rs b/crates/gpui2_macros/src/action.rs index abc75a875984842496c077f6e1841f5ec9915730..296e3f08b12b5fe0ede78d9ec1be0df96dd85735 100644 --- a/crates/gpui2_macros/src/action.rs +++ b/crates/gpui2_macros/src/action.rs @@ -38,6 +38,7 @@ pub fn action(input: TokenStream) -> TokenStream { let build_impl = if is_unit_struct { quote! { + let _ = value; Ok(std::boxed::Box::new(Self {})) } } else { diff --git a/crates/quick_action_bar2/src/quick_action_bar.rs b/crates/quick_action_bar2/src/quick_action_bar.rs index e933689e62ee2f59e49aeeaf080032c489702fe6..3eba0e60ec20672b0b759d5e041745eb6c46e162 100644 --- a/crates/quick_action_bar2/src/quick_action_bar.rs +++ b/crates/quick_action_bar2/src/quick_action_bar.rs @@ -5,7 +5,7 @@ use gpui::{ Action, ClickEvent, Div, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Stateful, Styled, Subscription, View, ViewContext, WeakView, }; -use search::BufferSearchBar; +use search::{buffer_search, BufferSearchBar}; use ui::{prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, Tooltip}; use workspace::{ item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, @@ -64,12 +64,14 @@ impl Render for QuickActionBar { "toggle buffer search", Icon::MagnifyingGlass, !self.buffer_search_bar.read(cx).is_dismissed(), - Box::new(search::buffer_search::Deploy { focus: false }), + Box::new(buffer_search::Deploy { focus: false }), "Buffer Search", { let buffer_search_bar = self.buffer_search_bar.clone(); move |_, cx| { - buffer_search_bar.update(cx, |search_bar, cx| search_bar.toggle(cx)); + buffer_search_bar.update(cx, |search_bar, cx| { + search_bar.toggle(&buffer_search::Deploy { focus: true }, cx) + }); } }, )) diff --git a/crates/recent_projects2/src/projects.rs b/crates/recent_projects2/src/projects.rs new file mode 100644 index 0000000000000000000000000000000000000000..2c4809dbd7635cf619002ffcac15d5feb3a1c3c2 --- /dev/null +++ b/crates/recent_projects2/src/projects.rs @@ -0,0 +1 @@ +gpui::actions!(OpenRecent); diff --git a/crates/recent_projects2/src/recent_projects.rs b/crates/recent_projects2/src/recent_projects.rs index 0c11f2b9c7d9d579aa5de810078236bccec432f4..d2c13b40a33645f14060cc421a8c43c2b19b7c8f 100644 --- a/crates/recent_projects2/src/recent_projects.rs +++ b/crates/recent_projects2/src/recent_projects.rs @@ -1,9 +1,10 @@ mod highlighted_workspace_location; +mod projects; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Result, Task, - View, ViewContext, WeakView, + AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Result, Task, View, + ViewContext, WeakView, }; use highlighted_workspace_location::HighlightedWorkspaceLocation; use ordered_float::OrderedFloat; @@ -16,7 +17,7 @@ use workspace::{ WORKSPACE_DB, }; -actions!(OpenRecent); +pub use projects::OpenRecent; pub fn init(cx: &mut AppContext) { cx.observe_new_views(RecentProjects::register).detach(); diff --git a/crates/search2/src/buffer_search.rs b/crates/search2/src/buffer_search.rs index da32f51194cc68d869a76ba8b88023f5450b2844..cbbeeb0f1260a57a9907e854c9aeb666c078fd90 100644 --- a/crates/search2/src/buffer_search.rs +++ b/crates/search2/src/buffer_search.rs @@ -170,14 +170,25 @@ impl Render for BufferSearchBar { h_stack() .key_context("BufferSearchBar") - .when(in_replace, |this| { - this.key_context("in_replace") - .on_action(cx.listener(Self::replace_next)) - .on_action(cx.listener(Self::replace_all)) - }) .on_action(cx.listener(Self::previous_history_query)) .on_action(cx.listener(Self::next_history_query)) .on_action(cx.listener(Self::dismiss)) + .on_action(cx.listener(Self::select_next_match)) + .on_action(cx.listener(Self::select_prev_match)) + .when(self.supported_options().replacement, |this| { + this.on_action(cx.listener(Self::toggle_replace)) + .when(in_replace, |this| { + this.key_context("in_replace") + .on_action(cx.listener(Self::replace_next)) + .on_action(cx.listener(Self::replace_all)) + }) + }) + .when(self.supported_options().case, |this| { + this.on_action(cx.listener(Self::toggle_case_sensitive)) + }) + .when(self.supported_options().word, |this| { + this.on_action(cx.listener(Self::toggle_whole_word)) + }) .w_full() .p_1() .child( @@ -305,7 +316,7 @@ impl BufferSearchBar { let handle = cx.view().downgrade(); - editor.register_action(move |a: &Deploy, cx| { + editor.register_action(move |deploy: &Deploy, cx| { let Some(pane) = handle.upgrade().and_then(|editor| editor.read(cx).pane(cx)) else { return; }; @@ -313,12 +324,12 @@ impl BufferSearchBar { pane.update(cx, |this, cx| { this.toolbar().update(cx, |this, cx| { if let Some(search_bar) = this.item_of_type::() { - search_bar.update(cx, |this, cx| this.toggle(cx)); + search_bar.update(cx, |this, cx| this.toggle(deploy, cx)); return; } let view = cx.build_view(|cx| BufferSearchBar::new(cx)); this.add_item(view.clone(), cx); - view.update(cx, |this, cx| this.deploy(a, cx)); + view.update(cx, |this, cx| this.deploy(deploy, cx)); cx.notify(); }) }); @@ -468,7 +479,7 @@ impl BufferSearchBar { self.search_suggested(cx); if deploy.focus { self.select_query(cx); - let handle = cx.focus_handle(); + let handle = self.query_editor.focus_handle(cx); cx.focus(&handle); } return true; @@ -477,9 +488,9 @@ impl BufferSearchBar { false } - pub fn toggle(&mut self, cx: &mut ViewContext) { + pub fn toggle(&mut self, action: &Deploy, cx: &mut ViewContext) { if self.is_dismissed() { - self.show(cx); + self.deploy(action, cx); } else { self.dismiss(&Dismiss, cx); } diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 216762060d83870e14177449befc5a0079332148..1e899a5783e28226f7c0bda65da4c9c066a7a81b 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -28,6 +28,7 @@ pub enum ComponentStory { ListHeader, ListItem, Scroll, + Tab, Text, ZIndex, Picker, @@ -53,6 +54,7 @@ impl ComponentStory { Self::ListItem => cx.build_view(|_| ui::ListItemStory).into(), Self::Scroll => ScrollStory::view(cx).into(), Self::Text => TextStory::view(cx).into(), + Self::Tab => cx.build_view(|_| ui::TabStory).into(), Self::ZIndex => cx.build_view(|_| ZIndexStory).into(), Self::Picker => PickerStory::new(cx).into(), } diff --git a/crates/terminal2/src/terminal2.rs b/crates/terminal2/src/terminal2.rs index 6036d65d6e8c59d3aff234952980564ec9f71bec..b429bda710aa09d61fe26ba259353f55ab64d5ec 100644 --- a/crates/terminal2/src/terminal2.rs +++ b/crates/terminal2/src/terminal2.rs @@ -50,7 +50,7 @@ use std::{ use thiserror::Error; use gpui::{ - px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla, Keystroke, + actions, px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla, Keystroke, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollWheelEvent, Size, Task, TouchPhase, }; @@ -58,6 +58,16 @@ use gpui::{ use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str}; use lazy_static::lazy_static; +actions!( + Clear, + Copy, + Paste, + ShowCharacterPalette, + SearchTest, + SendText, + SendKeystroke, +); + ///Scrolling is unbearably sluggish by default. Alacritty supports a configurable ///Scroll multiplier that is set to 3 by default. This will be removed when I ///Implement scroll bars. diff --git a/crates/terminal_view2/src/terminal_element.rs b/crates/terminal_view2/src/terminal_element.rs index 4a37ca91b779e6aa9c09fe510a1f5af9bbe6c48b..d61ba5988e23f3ccfe5adb9264b549859373adf7 100644 --- a/crates/terminal_view2/src/terminal_element.rs +++ b/crates/terminal_view2/src/terminal_element.rs @@ -1,9 +1,9 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine}; use gpui::{ black, div, point, px, red, relative, transparent_black, AnyElement, AsyncWindowContext, - AvailableSpace, Bounds, DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, - FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, IntoElement, - LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, + AvailableSpace, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font, + FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState, + IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels, PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, View, WhiteSpace, WindowContext, }; @@ -643,13 +643,11 @@ impl TerminalElement { let connection = connection.clone(); let focus = focus.clone(); move |e, cx| { - if e.pressed_button.is_some() { - if focus.is_focused(cx) { - connection.update(cx, |terminal, cx| { - terminal.mouse_drag(e, origin, bounds); - cx.notify(); - }) - } + if e.pressed_button.is_some() && focus.is_focused(cx) && !cx.has_active_drag() { + connection.update(cx, |terminal, cx| { + terminal.mouse_drag(e, origin, bounds); + cx.notify(); + }) } } }) @@ -806,7 +804,28 @@ impl Element for TerminalElement { .map(|cursor| cursor.bounding_rect(origin)), }; - let mut this = self.register_mouse_listeners(origin, layout.mode, bounds, cx); + let terminal_focus_handle = self.focus.clone(); + let terminal_handle = self.terminal.clone(); + let mut this: TerminalElement = self + .register_mouse_listeners(origin, layout.mode, bounds, cx) + .drag_over::(|style| { + // todo!() why does not it work? z-index of elements? + style.bg(cx.theme().colors().ghost_element_hover) + }) + .on_drop::(move |external_paths, cx| { + cx.focus(&terminal_focus_handle); + let mut new_text = external_paths + .read(cx) + .paths() + .iter() + .map(|path| format!(" {path:?}")) + .join(""); + new_text.push(' '); + terminal_handle.update(cx, |terminal, _| { + // todo!() long paths are not displayed properly albeit the text is there + terminal.paste(&new_text); + }); + }); let interactivity = mem::take(&mut this.interactivity); diff --git a/crates/terminal_view2/src/terminal_view.rs b/crates/terminal_view2/src/terminal_view.rs index 75b019f2ef24639d12577854c59ceedb48607669..5a81b494b300a09a947b4012f2e7460859df996c 100644 --- a/crates/terminal_view2/src/terminal_view.rs +++ b/crates/terminal_view2/src/terminal_view.rs @@ -9,9 +9,9 @@ pub mod terminal_panel; // use crate::terminal_element::TerminalElement; use editor::{scroll::autoscroll::Autoscroll, Editor}; use gpui::{ - actions, div, Action, AnyElement, AppContext, Div, EventEmitter, FocusEvent, FocusHandle, - Focusable, FocusableElement, FocusableView, KeyContext, KeyDownEvent, Keystroke, Model, - MouseButton, MouseDownEvent, Pixels, Render, Subscription, Task, View, VisualContext, WeakView, + div, Action, AnyElement, AppContext, Div, EventEmitter, FocusEvent, FocusHandle, Focusable, + FocusableElement, FocusableView, KeyContext, KeyDownEvent, Keystroke, Model, MouseButton, + MouseDownEvent, Pixels, Render, Subscription, Task, View, VisualContext, WeakView, }; use language::Bias; use persistence::TERMINAL_DB; @@ -22,7 +22,7 @@ use terminal::{ term::{search::RegexSearch, TermMode}, }, terminal_settings::{TerminalBlink, TerminalSettings, WorkingDirectory}, - Event, MaybeNavigationTarget, Terminal, + Clear, Copy, Event, MaybeNavigationTarget, Paste, ShowCharacterPalette, Terminal, }; use terminal_element::TerminalElement; use ui::{h_stack, prelude::*, ContextMenu, Icon, IconElement, Label}; @@ -60,8 +60,6 @@ pub struct SendText(String); #[derive(Clone, Debug, Default, Deserialize, PartialEq, Action)] pub struct SendKeystroke(String); -actions!(Clear, Copy, Paste, ShowCharacterPalette, SearchTest); - pub fn init(cx: &mut AppContext) { terminal_panel::init(cx); terminal::init(cx); diff --git a/crates/theme_selector2/src/theme_selector.rs b/crates/theme_selector2/src/theme_selector.rs index cbde6bc7a640ed07627437c618f2ab8115b61411..8430f0698baa1db1b1e18c3747b6ed5f473436e8 100644 --- a/crates/theme_selector2/src/theme_selector.rs +++ b/crates/theme_selector2/src/theme_selector.rs @@ -187,6 +187,10 @@ impl PickerDelegate for ThemeSelectorDelegate { Self::set_theme(self.original_theme.clone(), cx); self.selection_completed = true; } + + self.view + .update(cx, |_, cx| cx.emit(DismissEvent)) + .log_err(); } fn selected_index(&self) -> usize { diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 583b30a2e05fd647d7603d41ee4f0edbf8510713..5bda108a87cd6deca8b10eb81d0bdcb4c0295689 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -13,6 +13,7 @@ mod popover; mod popover_menu; mod right_click_menu; mod stack; +mod tab; mod tooltip; #[cfg(feature = "stories")] @@ -33,6 +34,7 @@ pub use popover::*; pub use popover_menu::*; pub use right_click_menu::*; pub use stack::*; +pub use tab::*; pub use tooltip::*; #[cfg(feature = "stories")] diff --git a/crates/ui2/src/components/keybinding.rs b/crates/ui2/src/components/keybinding.rs index 25a77f59e191f74ab445d564613f8ebe0859b7e2..8314f607fefd24a968fa2b5b099594985929736c 100644 --- a/crates/ui2/src/components/keybinding.rs +++ b/crates/ui2/src/components/keybinding.rs @@ -98,11 +98,11 @@ impl RenderOnce for Key { div() .py_0() - .map(|el| { + .map(|this| { if single_char { - el.w(rems(14. / 16.)).flex().flex_none().justify_center() + this.w(rems(14. / 16.)).flex().flex_none().justify_center() } else { - el.px_0p5() + this.px_0p5() } }) .h(rems(14. / 16.)) diff --git a/crates/ui2/src/components/stories.rs b/crates/ui2/src/components/stories.rs index 113c2679b7f2b9cfec58d92d51060f7712b8a384..c54ee99057951bd76b37bb464d20226a5abf1d0f 100644 --- a/crates/ui2/src/components/stories.rs +++ b/crates/ui2/src/components/stories.rs @@ -10,6 +10,7 @@ mod label; mod list; mod list_header; mod list_item; +mod tab; pub use avatar::*; pub use button::*; @@ -23,3 +24,4 @@ pub use label::*; pub use list::*; pub use list_header::*; pub use list_item::*; +pub use tab::*; diff --git a/crates/ui2/src/components/stories/tab.rs b/crates/ui2/src/components/stories/tab.rs new file mode 100644 index 0000000000000000000000000000000000000000..cd5e920396a1f6803f261537fcf0a577621a6496 --- /dev/null +++ b/crates/ui2/src/components/stories/tab.rs @@ -0,0 +1,114 @@ +use std::cmp::Ordering; + +use gpui::{Div, Render}; +use story::Story; + +use crate::{prelude::*, TabPosition}; +use crate::{Indicator, Tab}; + +pub struct TabStory; + +impl Render for TabStory { + type Element = Div; + + fn render(&mut self, _cx: &mut ViewContext) -> Self::Element { + Story::container() + .child(Story::title_for::()) + .child(Story::label("Default")) + .child(h_stack().child(Tab::new("tab_1").child("Tab 1"))) + .child(Story::label("With indicator")) + .child( + h_stack().child( + Tab::new("tab_1") + .start_slot(Indicator::dot().color(Color::Warning)) + .child("Tab 1"), + ), + ) + .child(Story::label("With close button")) + .child( + h_stack().child( + Tab::new("tab_1") + .end_slot( + IconButton::new("close_button", Icon::Close) + .icon_color(Color::Muted) + .size(ButtonSize::None) + .icon_size(IconSize::XSmall), + ) + .child("Tab 1"), + ), + ) + .child(Story::label("List of tabs")) + .child( + h_stack() + .child(Tab::new("tab_1").child("Tab 1")) + .child(Tab::new("tab_2").child("Tab 2")), + ) + .child(Story::label("List of tabs with first tab selected")) + .child( + h_stack() + .child( + Tab::new("tab_1") + .selected(true) + .position(TabPosition::First) + .child("Tab 1"), + ) + .child( + Tab::new("tab_2") + .position(TabPosition::Middle(Ordering::Greater)) + .child("Tab 2"), + ) + .child( + Tab::new("tab_3") + .position(TabPosition::Middle(Ordering::Greater)) + .child("Tab 3"), + ) + .child(Tab::new("tab_4").position(TabPosition::Last).child("Tab 4")), + ) + .child(Story::label("List of tabs with last tab selected")) + .child( + h_stack() + .child( + Tab::new("tab_1") + .position(TabPosition::First) + .child("Tab 1"), + ) + .child( + Tab::new("tab_2") + .position(TabPosition::Middle(Ordering::Less)) + .child("Tab 2"), + ) + .child( + Tab::new("tab_3") + .position(TabPosition::Middle(Ordering::Less)) + .child("Tab 3"), + ) + .child( + Tab::new("tab_4") + .position(TabPosition::Last) + .selected(true) + .child("Tab 4"), + ), + ) + .child(Story::label("List of tabs with second tab selected")) + .child( + h_stack() + .child( + Tab::new("tab_1") + .position(TabPosition::First) + .child("Tab 1"), + ) + .child( + Tab::new("tab_2") + .position(TabPosition::Middle(Ordering::Equal)) + .selected(true) + .child("Tab 2"), + ) + .child( + Tab::new("tab_3") + .position(TabPosition::Middle(Ordering::Greater)) + .child("Tab 3"), + ) + .child(Tab::new("tab_4").position(TabPosition::Last).child("Tab 4")), + ) + } +} diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs new file mode 100644 index 0000000000000000000000000000000000000000..7a40a6ed0d321e4f28d9ff8f7ee4019da444cd34 --- /dev/null +++ b/crates/ui2/src/components/tab.rs @@ -0,0 +1,198 @@ +use std::cmp::Ordering; +use std::rc::Rc; + +use gpui::{AnyElement, AnyView, ClickEvent, IntoElement, MouseButton}; +use smallvec::SmallVec; + +use crate::prelude::*; + +/// The position of a [`Tab`] within a list of tabs. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum TabPosition { + /// The tab is first in the list. + First, + + /// The tab is in the middle of the list (i.e., it is not the first or last tab). + /// + /// The [`Ordering`] is where this tab is positioned with respect to the selected tab. + Middle(Ordering), + + /// The tab is last in the list. + Last, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum TabCloseSide { + Start, + End, +} + +#[derive(IntoElement)] +pub struct Tab { + id: ElementId, + selected: bool, + position: TabPosition, + close_side: TabCloseSide, + on_click: Option>, + tooltip: Option AnyView + 'static>>, + start_slot: Option, + end_slot: Option, + children: SmallVec<[AnyElement; 2]>, +} + +impl Tab { + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + selected: false, + position: TabPosition::First, + close_side: TabCloseSide::End, + on_click: None, + tooltip: None, + start_slot: None, + end_slot: None, + children: SmallVec::new(), + } + } + + pub fn position(mut self, position: TabPosition) -> Self { + self.position = position; + self + } + + pub fn close_side(mut self, close_side: TabCloseSide) -> Self { + self.close_side = close_side; + self + } + + pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self { + self.on_click = Some(Rc::new(handler)); + self + } + + pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self { + self.tooltip = Some(Box::new(tooltip)); + self + } + + pub fn start_slot(mut self, element: impl Into>) -> Self { + self.start_slot = element.into().map(IntoElement::into_any_element); + self + } + + pub fn end_slot(mut self, element: impl Into>) -> Self { + self.end_slot = element.into().map(IntoElement::into_any_element); + self + } +} + +impl Selectable for Tab { + fn selected(mut self, selected: bool) -> Self { + self.selected = selected; + self + } +} + +impl ParentElement for Tab { + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { + &mut self.children + } +} + +impl RenderOnce for Tab { + type Rendered = Div; + + fn render(self, cx: &mut WindowContext) -> Self::Rendered { + const HEIGHT_IN_REMS: f32 = 30. / 16.; + + let (text_color, tab_bg, _tab_hover_bg, _tab_active_bg) = match self.selected { + false => ( + cx.theme().colors().text_muted, + cx.theme().colors().tab_inactive_background, + cx.theme().colors().ghost_element_hover, + cx.theme().colors().ghost_element_active, + ), + true => ( + cx.theme().colors().text, + cx.theme().colors().tab_active_background, + cx.theme().colors().element_hover, + cx.theme().colors().element_active, + ), + }; + + div() + .h(rems(HEIGHT_IN_REMS)) + .bg(tab_bg) + .border_color(cx.theme().colors().border) + .map(|this| match self.position { + TabPosition::First => { + if self.selected { + this.pl_px().border_r().pb_px() + } else { + this.pl_px().pr_px().border_b() + } + } + TabPosition::Last => { + if self.selected { + this.border_l().border_r().pb_px() + } else { + this.pr_px().pl_px().border_b() + } + } + TabPosition::Middle(Ordering::Equal) => this.border_l().border_r().pb_px(), + TabPosition::Middle(Ordering::Less) => this.border_l().pr_px().border_b(), + TabPosition::Middle(Ordering::Greater) => this.border_r().pl_px().border_b(), + }) + .child( + h_stack() + .group("") + .id(self.id) + .relative() + .h_full() + .px_5() + .gap_1() + .text_color(text_color) + // .hover(|style| style.bg(tab_hover_bg)) + // .active(|style| style.bg(tab_active_bg)) + .when_some(self.on_click, |tab, on_click| { + tab.cursor_pointer().on_click(move |event, cx| { + // HACK: GPUI currently fires `on_click` with any mouse button, + // but we only care about the left button. + if event.down.button == MouseButton::Left { + (on_click)(event, cx) + } + }) + }) + .when_some(self.tooltip, |tab, tooltip| { + tab.tooltip(move |cx| tooltip(cx)) + }) + .child( + h_stack() + .w_3() + .h_3() + .justify_center() + .absolute() + .map(|this| match self.close_side { + TabCloseSide::Start => this.right_1(), + TabCloseSide::End => this.left_1(), + }) + .children(self.start_slot), + ) + .child( + h_stack() + .invisible() + .w_3() + .h_3() + .justify_center() + .absolute() + .map(|this| match self.close_side { + TabCloseSide::Start => this.left_1(), + TabCloseSide::End => this.right_1(), + }) + .group_hover("", |style| style.visible()) + .children(self.end_slot), + ) + .children(self.children), + ) + } +} diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index a0a90293d603a3afde61dda2c4ca59ec5fa273a1..7bae7bc4199631f0b538a99d45ecb6e1d7a4aaa5 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -133,13 +133,13 @@ pub struct Dock { panel_entries: Vec, is_open: bool, active_panel_index: usize, + focus_handle: FocusHandle, + focus_subscription: Subscription, } impl FocusableView for Dock { - fn focus_handle(&self, cx: &AppContext) -> FocusHandle { - self.panel_entries[self.active_panel_index] - .panel - .focus_handle(cx) + fn focus_handle(&self, _: &AppContext) -> FocusHandle { + self.focus_handle.clone() } } @@ -190,12 +190,20 @@ pub struct PanelButtons { } impl Dock { - pub fn new(position: DockPosition) -> Self { + pub fn new(position: DockPosition, cx: &mut ViewContext<'_, Self>) -> Self { + let focus_handle = cx.focus_handle(); + let focus_subscription = cx.on_focus(&focus_handle, |dock, cx| { + if let Some(active_entry) = dock.panel_entries.get(dock.active_panel_index) { + active_entry.panel.focus_handle(cx).focus(cx) + } + }); Self { position, panel_entries: Default::default(), active_panel_index: 0, is_open: false, + focus_handle, + focus_subscription, } } @@ -207,6 +215,7 @@ impl Dock { self.is_open } + // todo!() // pub fn has_focus(&self, cx: &WindowContext) -> bool { // self.visible_panel() // .map_or(false, |panel| panel.has_focus(cx)) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 495819f6081745aaa243b4704e46ca84f10d8bda..f28759a7337c237aaa7958b1cdb18697882ef501 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -28,10 +28,10 @@ use std::{ use ui::{ h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize, - Indicator, Label, Tooltip, + Indicator, Label, Tab, TabPosition, Tooltip, }; use ui::{v_stack, ContextMenu}; -use util::truncate_and_remove_front; +use util::{maybe, truncate_and_remove_front}; #[derive(PartialEq, Clone, Copy, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -1438,42 +1438,49 @@ impl Pane { let is_active = ix == self.active_item_index; - let indicator = { + let indicator = maybe!({ let indicator_color = match (item.has_conflict(cx), item.is_dirty(cx)) { - (true, _) => Some(Color::Warning), - (_, true) => Some(Color::Accent), - (false, false) => None, + (true, _) => Color::Warning, + (_, true) => Color::Accent, + (false, false) => return None, }; - h_stack() - .w_3() - .h_3() - .justify_center() - .absolute() - .map(|this| match close_side { - ClosePosition::Left => this.right_1(), - ClosePosition::Right => this.left_1(), + Some(Indicator::dot().color(indicator_color)) + }); + + let id = item.item_id(); + + let is_first_item = ix == 0; + let is_last_item = ix == self.items.len() - 1; + let position_relative_to_active_item = ix.cmp(&self.active_item_index); + + let tab = + Tab::new(ix) + .position(if is_first_item { + TabPosition::First + } else if is_last_item { + TabPosition::Last + } else { + TabPosition::Middle(position_relative_to_active_item) }) - .when_some(indicator_color, |this, indicator_color| { - this.child(Indicator::dot().color(indicator_color)) + .close_side(match close_side { + ClosePosition::Left => ui::TabCloseSide::Start, + ClosePosition::Right => ui::TabCloseSide::End, }) - }; - - let close_button = { - let id = item.item_id(); - - h_stack() - .invisible() - .w_3() - .h_3() - .justify_center() - .absolute() - .map(|this| match close_side { - ClosePosition::Left => this.left_1(), - ClosePosition::Right => this.right_1(), + .selected(ix == self.active_item_index()) + .on_click(cx.listener(move |pane: &mut Self, event, cx| { + pane.activate_item(ix, true, true, cx) + })) + // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) + // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) + // .on_drop(|_view, state: View, cx| { + // eprintln!("{:?}", state.read(cx)); + // }) + .when_some(item.tab_tooltip_text(cx), |tab, text| { + tab.tooltip(move |cx| Tooltip::text(text.clone(), cx)) }) - .group_hover("", |style| style.visible()) - .child( + .start_slot::(indicator) + .end_slot( // TODO: Fix button size IconButton::new("close tab", Icon::Close) .icon_color(Color::Muted) @@ -1484,67 +1491,7 @@ impl Pane { .detach_and_log_err(cx); })), ) - }; - - let tab = div() - .border_color(cx.theme().colors().border) - .bg(tab_bg) - // 30px @ 16px/rem - .h(rems(1.875)) - .map(|this| { - let is_first_item = ix == 0; - let is_last_item = ix == self.items.len() - 1; - match ix.cmp(&self.active_item_index) { - cmp::Ordering::Less => { - if is_first_item { - this.pl_px().pr_px().border_b() - } else { - this.border_l().pr_px().border_b() - } - } - cmp::Ordering::Greater => { - if is_last_item { - this.pr_px().pl_px().border_b() - } else { - this.border_r().pl_px().border_b() - } - } - cmp::Ordering::Equal => { - if is_first_item { - this.pl_px().border_r().pb_px() - } else { - this.border_l().border_r().pb_px() - } - } - } - }) - .child( - h_stack() - .group("") - .id(ix) - .relative() - .h_full() - .cursor_pointer() - .when_some(item.tab_tooltip_text(cx), |div, text| { - div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into()) - }) - .on_click( - cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)), - ) - // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx)) - // .drag_over::(|d| d.bg(cx.theme().colors().element_drop_target)) - // .on_drop(|_view, state: View, cx| { - // eprintln!("{:?}", state.read(cx)); - // }) - .px_5() - // .hover(|h| h.bg(tab_hover_bg)) - // .active(|a| a.bg(tab_active_bg)) - .gap_1() - .text_color(text_color) - .child(indicator) - .child(close_button) - .child(label), - ); + .child(label); right_click_menu(ix).trigger(tab).menu(|cx| { ContextMenu::build(cx, |menu, cx| { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index abf908992978ff8e870eb88efe72f7ce0e332c41..251f0685b0c93f7529984f2afabe582f1b481392 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -566,9 +566,9 @@ impl Workspace { cx.emit(Event::WorkspaceCreated(weak_handle.clone())); - let left_dock = cx.build_view(|_| Dock::new(DockPosition::Left)); - let bottom_dock = cx.build_view(|_| Dock::new(DockPosition::Bottom)); - let right_dock = cx.build_view(|_| Dock::new(DockPosition::Right)); + let left_dock = cx.build_view(|cx| Dock::new(DockPosition::Left, cx)); + let bottom_dock = cx.build_view(|cx| Dock::new(DockPosition::Bottom, cx)); + let right_dock = cx.build_view(|cx| Dock::new(DockPosition::Right, cx)); let left_dock_buttons = cx.build_view(|cx| PanelButtons::new(left_dock.clone(), weak_handle.clone(), cx)); let bottom_dock_buttons = @@ -4188,14 +4188,14 @@ pub fn open_paths( }); cx.spawn(move |mut cx| async move { if let Some(existing) = existing { - // // Ok(( - // existing.clone(), - // cx.update_window_root(&existing, |workspace, cx| { - // workspace.open_paths(abs_paths, true, cx) - // })? - // .await, - // )) - todo!() + Ok(( + existing.clone(), + existing + .update(&mut cx, |workspace, cx| { + workspace.open_paths(abs_paths, true, cx) + })? + .await, + )) } else { cx.update(move |cx| { Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)