diff --git a/Cargo.lock b/Cargo.lock index 3a3e7c2dce97bd104acb5c9c6bb6d6e1717c8c8d..ee107643f18804d7abd1c6b0ed4aa52b1636756f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -785,6 +785,7 @@ dependencies = [ "gpui", "itertools", "language", + "outline", "project", "search", "settings", diff --git a/crates/breadcrumbs/Cargo.toml b/crates/breadcrumbs/Cargo.toml index 99476fdc0ac1e49210bee22204923c537599de48..412a79a317f05a17bcaa96ac290348796f38d3f7 100644 --- a/crates/breadcrumbs/Cargo.toml +++ b/crates/breadcrumbs/Cargo.toml @@ -18,6 +18,7 @@ search = { path = "../search" } settings = { path = "../settings" } theme = { path = "../theme" } workspace = { path = "../workspace" } +outline = { path = "../outline" } itertools = "0.10" [dev-dependencies] diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index 47b7f884530ef997a686c9768f80433d0d7d44dc..184dbe8468bbe3b0671b81677ed6c8efc379b809 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -1,5 +1,6 @@ use gpui::{ - elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle, + elements::*, AppContext, Entity, MouseButton, RenderContext, Subscription, View, ViewContext, + ViewHandle, }; use itertools::Itertools; use search::ProjectSearchView; @@ -14,6 +15,7 @@ pub enum Event { } pub struct Breadcrumbs { + pane_focused: bool, active_item: Option>, project_search: Option>, subscription: Option, @@ -22,6 +24,7 @@ pub struct Breadcrumbs { impl Breadcrumbs { pub fn new() -> Self { Self { + pane_focused: false, active_item: Default::default(), subscription: Default::default(), project_search: Default::default(), @@ -39,24 +42,53 @@ impl View for Breadcrumbs { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let active_item = match &self.active_item { + Some(active_item) => active_item, + None => return Empty::new().boxed(), + }; + let not_editor = active_item.downcast::().is_none(); + let theme = cx.global::().theme.clone(); - if let Some(breadcrumbs) = self - .active_item - .as_ref() - .and_then(|item| item.breadcrumbs(&theme, cx)) - { - Flex::row() - .with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || { - Label::new(" 〉 ", theme.breadcrumbs.text.clone()).boxed() - })) - .contained() - .with_style(theme.breadcrumbs.container) + let style = &theme.workspace.breadcrumbs; + + let breadcrumbs = match active_item.breadcrumbs(&theme, cx) { + Some(breadcrumbs) => breadcrumbs, + None => return Empty::new().boxed(), + }; + + let crumbs = Flex::row() + .with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || { + Label::new(" 〉 ", style.default.text.clone()).boxed() + })) + .constrained() + .with_height(theme.workspace.breadcrumb_height) + .contained(); + + if not_editor || !self.pane_focused { + return crumbs + .with_style(style.default.container) .aligned() .left() - .boxed() - } else { - Empty::new().boxed() + .boxed(); } + + MouseEventHandler::::new(0, cx, |state, _| { + let style = style.style_for(state, false); + crumbs.with_style(style.container).boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(outline::Toggle); + }) + .with_tooltip::( + 0, + "Show symbol outline".to_owned(), + Some(Box::new(outline::Toggle)), + theme.tooltip.clone(), + cx, + ) + .aligned() + .left() + .boxed() } } @@ -103,4 +135,8 @@ impl ToolbarItemView for Breadcrumbs { current_location } } + + fn pane_focus_update(&mut self, pane_focused: bool, _: &mut gpui::MutableAppContext) { + self.pane_focused = pane_focused; + } } diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 1d273a991ee3cc4b8877ee845e8ae7cf13e7468a..a053fd8acafe6dcae54a111a7a9be2af094a7cac 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -747,11 +747,15 @@ impl Item for Editor { .map(|path| path.to_string_lossy().to_string()) .unwrap_or_else(|| "untitled".to_string()); - let mut breadcrumbs = vec![Label::new(filename, theme.breadcrumbs.text.clone()).boxed()]; + let filename_label = Label::new(filename, theme.workspace.breadcrumbs.default.text.clone()); + let mut breadcrumbs = vec![filename_label.boxed()]; breadcrumbs.extend(symbols.into_iter().map(|symbol| { - Text::new(symbol.text, theme.breadcrumbs.text.clone()) - .with_highlights(symbol.highlight_ranges) - .boxed() + Text::new( + symbol.text, + theme.workspace.breadcrumbs.default.text.clone(), + ) + .with_highlights(symbol.highlight_ranges) + .boxed() })); Some(breadcrumbs) } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index f1a627e29d2dbade2e4d8398727c166028709352..7b1a2616528f26a4cff39bd4b0325acf3e2eef09 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -612,7 +612,7 @@ impl Item for TerminalView { fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option> { Some(vec![Text::new( self.terminal().read(cx).breadcrumb_text.clone(), - theme.breadcrumbs.text.clone(), + theme.workspace.breadcrumbs.default.text.clone(), ) .boxed()]) } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d64a1d2499a690e245e16e94b1d76dcdc7327132..cb96fcf83306d8023baec780b0a447429481cfde 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -30,7 +30,6 @@ pub struct Theme { pub editor: Editor, pub search: Search, pub project_diagnostics: ProjectDiagnostics, - pub breadcrumbs: ContainedText, pub shared_screen: ContainerStyle, pub contact_notification: ContactNotification, pub update_notification: UpdateNotification, @@ -62,6 +61,8 @@ pub struct Workspace { pub sidebar: Sidebar, pub status_bar: StatusBar, pub toolbar: Toolbar, + pub breadcrumb_height: f32, + pub breadcrumbs: Interactive, pub disconnected_overlay: ContainedText, pub modal: ContainerStyle, pub notification: ContainerStyle, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index f4a5ad026a09a7d2fef17a9a8dee843a6dbef27b..66c0cc1a04abf52489e620bc00094aed0315b7df 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1603,6 +1603,10 @@ impl View for Pane { } fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { + self.toolbar.update(cx, |toolbar, cx| { + toolbar.pane_focus_update(true, cx); + }); + if let Some(active_item) = self.active_item() { if cx.is_self_focused() { // Pane was focused directly. We need to either focus a view inside the active item, @@ -1626,6 +1630,12 @@ impl View for Pane { } } + fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + self.toolbar.update(cx, |toolbar, cx| { + toolbar.pane_focus_update(false, cx); + }); + } + fn keymap_context(&self, _: &AppContext) -> KeymapContext { let mut keymap = Self::default_keymap_context(); if self.docked.is_some() { diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index df10db91a05dbd24a31d6122eb3a39ee6ca4de2f..55f1707766dddfc5070f3fc4dd72bba96383f43d 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -20,6 +20,8 @@ pub trait ToolbarItemView: View { ) -> ToolbarItemLocation { current_location } + + fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut MutableAppContext) {} } trait ToolbarItemViewHandle { @@ -30,6 +32,7 @@ trait ToolbarItemViewHandle { active_pane_item: Option<&dyn ItemHandle>, cx: &mut MutableAppContext, ) -> ToolbarItemLocation; + fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext); } #[derive(Copy, Clone, Debug, PartialEq)] @@ -260,6 +263,12 @@ impl Toolbar { } } + pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext) { + for (toolbar_item, _) in self.items.iter_mut() { + toolbar_item.pane_focus_update(pane_focused, cx); + } + } + pub fn item_of_type(&self) -> Option> { self.items .iter() @@ -289,6 +298,10 @@ impl ToolbarItemViewHandle for ViewHandle { this.set_active_pane_item(active_pane_item, cx) }) } + + fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext) { + self.update(cx, |this, cx| this.pane_focus_update(pane_focused, cx)); + } } impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle { diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 423ce37d481e8f47bf9118ad6134b5c123a4ed31..3582de424728a6835db858cb296836b0669ea8cb 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -44,12 +44,6 @@ export default function app(colorScheme: ColorScheme): Object { contactList: contactList(colorScheme), search: search(colorScheme), sharedScreen: sharedScreen(colorScheme), - breadcrumbs: { - ...text(colorScheme.highest, "sans", "variant"), - padding: { - left: 6, - }, - }, updateNotification: updateNotification(colorScheme), simpleMessageNotification: simpleMessageNotification(colorScheme), tooltip: tooltip(colorScheme), diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 1de2fe95020527765eae2c4b892642b447d11a0f..fe3faddcb9f64579f00b89dfbb9c1c881e6c4ebe 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -276,9 +276,22 @@ export default function workspace(colorScheme: ColorScheme) { }, padding: { left: 8, right: 8, top: 4, bottom: 4 }, }, + breadcrumbHeight: 24, breadcrumbs: { - ...text(layer, "mono", "variant"), - padding: { left: 6 }, + ...text(colorScheme.highest, "sans", "variant"), + cornerRadius: 6, + padding: { + left: 6, + right: 6, + }, + hover: { + color: foreground(colorScheme.highest, "on", "hovered"), + background: background( + colorScheme.highest, + "on", + "hovered" + ), + }, }, disconnectedOverlay: { ...text(layer, "sans"),