diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 4b3c5b7bc5..1cefc558f0 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -5,9 +5,11 @@ use futures::stream::StreamExt; use gpui::{ actions, anyhow::{anyhow, Result}, + color::Color, elements::{ - AnchorCorner, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, Label, - MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState, + AnchorCorner, Canvas, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, + KeystrokeLabel, Label, MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, + UniformList, UniformListState, }, geometry::vector::Vector2F, impl_internal_actions, @@ -1262,54 +1264,134 @@ impl View for ProjectPanel { let padding = std::mem::take(&mut container_style.padding); let last_worktree_root_id = self.last_worktree_root_id; - Stack::new() - .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { - UniformList::new( - self.list.clone(), - self.visible_entries - .iter() - .map(|(_, worktree_entries)| worktree_entries.len()) - .sum(), - cx, - move |this, range, items, cx| { - let theme = cx.global::().theme.clone(); - let mut dragged_entry_destination = - this.dragged_entry_destination.clone(); - this.for_each_visible_entry(range, cx, |id, details, cx| { - items.push(Self::render_entry( - id, - details, - &this.filename_editor, - &mut dragged_entry_destination, - &theme.project_panel, - cx, - )); - }); - this.dragged_entry_destination = dragged_entry_destination; - }, - ) - .with_padding_top(padding.top) - .with_padding_bottom(padding.bottom) - .contained() - .with_style(container_style) - .expanded() - .boxed() - }) - .on_down(MouseButton::Right, move |e, cx| { - // When deploying the context menu anywhere below the last project entry, - // act as if the user clicked the root of the last worktree. - if let Some(entry_id) = last_worktree_root_id { - cx.dispatch_action(DeployContextMenu { - entry_id, - position: e.position, - }) - } - }) - .boxed(), - ) - .with_child(ChildView::new(&self.context_menu, cx).boxed()) - .boxed() + let has_worktree = self.visible_entries.len() != 0; + + if has_worktree { + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + UniformList::new( + self.list.clone(), + self.visible_entries + .iter() + .map(|(_, worktree_entries)| worktree_entries.len()) + .sum(), + cx, + move |this, range, items, cx| { + let theme = cx.global::().theme.clone(); + let mut dragged_entry_destination = + this.dragged_entry_destination.clone(); + this.for_each_visible_entry(range, cx, |id, details, cx| { + items.push(Self::render_entry( + id, + details, + &this.filename_editor, + &mut dragged_entry_destination, + &theme.project_panel, + cx, + )); + }); + this.dragged_entry_destination = dragged_entry_destination; + }, + ) + .with_padding_top(padding.top) + .with_padding_bottom(padding.bottom) + .contained() + .with_style(container_style) + .expanded() + .boxed() + }) + .on_down(MouseButton::Right, move |e, cx| { + // When deploying the context menu anywhere below the last project entry, + // act as if the user clicked the root of the last worktree. + if let Some(entry_id) = last_worktree_root_id { + cx.dispatch_action(DeployContextMenu { + entry_id, + position: e.position, + }) + } + }) + .boxed(), + ) + .with_child(ChildView::new(&self.context_menu, cx).boxed()) + .boxed() + } else { + let parent_view_id = cx.handle().id(); + Stack::new() + .with_child( + MouseEventHandler::::new(1, cx, |_, cx| { + Stack::new() + .with_child( + Canvas::new(|bounds, _visible_bounds, cx| { + cx.scene.push_quad(gpui::Quad { + bounds, + background: Some(Color::red()), + ..Default::default() + }) + }) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(2, cx, |state, cx| { + let style = &cx + .global::() + .theme + .search + .option_button + .style_for(state, false); + + let context_menu_item = cx + .global::() + .theme + .context_menu + .clone() + .item + .style_for(state, true) + .clone(); + + Flex::row() + .with_child( + Label::new( + "Open a new project!".to_string(), + context_menu_item.label.clone(), + ) + .contained() + .boxed(), + ) + .with_child({ + KeystrokeLabel::new( + cx.window_id(), + parent_view_id, + Box::new(workspace::Open), + context_menu_item.keystroke.container, + context_menu_item.keystroke.text.clone(), + ) + .flex_float() + .boxed() + }) + .contained() + .with_style(style.container) + .aligned() + .top() + .constrained() + .with_width(100.) + .with_height(20.) + .boxed() + }) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(workspace::Open) + }) + .with_cursor_style(CursorStyle::PointingHand) + .boxed(), + ) + .boxed() + }) + // TODO is this nescessary? + .on_click(MouseButton::Left, |_, cx| cx.focus_parent_view()) + .boxed(), + ) + .boxed() + } } fn keymap_context(&self, _: &AppContext) -> KeymapContext { diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 08ed3ecc2d..5d03d6304e 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -720,7 +720,7 @@ impl Element for TerminalElement { cx.paint_layer(clip_bounds, |cx| { let origin = bounds.origin() + vec2f(layout.size.cell_width, 0.); - //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse + // Elements are ephemeral, only at paint time do we know what could be clicked by a mouse self.attach_mouse_handlers(origin, self.view.id(), visible_bounds, layout.mode, cx); cx.scene.push_cursor_region(gpui::CursorRegion { diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index afb38385a4..54898f92c8 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -2,7 +2,8 @@ use gpui::{ color::Color, elements::{Canvas, Empty, Flex, Label, MouseEventHandler, ParentElement, Stack, Svg}, geometry::rect::RectF, - Element, ElementBox, Entity, MutableAppContext, RenderContext, Subscription, View, ViewContext, + Element, ElementBox, Entity, MouseRegion, MutableAppContext, RenderContext, Subscription, View, + ViewContext, }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; use theme::CheckboxStyle; @@ -29,6 +30,7 @@ impl View for WelcomePage { } fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { + let self_handle = cx.handle(); let settings = cx.global::(); let theme = settings.theme.clone(); @@ -44,6 +46,7 @@ impl View for WelcomePage { Stack::new() .with_child( + // TODO: Can this be moved into the pane? Canvas::new(move |bounds, visible_bounds, cx| { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); @@ -53,7 +56,12 @@ impl View for WelcomePage { background: Some(background), ..Default::default() }) - }) + }); + + cx.scene.push_mouse_region( + MouseRegion::new::(self_handle.id(), 0, visible_bounds) + .on_down(gpui::MouseButton::Left, |_, cx| cx.focus_parent_view()), + ); }) .boxed(), )