diff --git a/Cargo.lock b/Cargo.lock index 02b27566e4..7bb3fcdd59 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 99476fdc0a..412a79a317 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 47b7f88453..184dbe8468 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 1d273a991e..a053fd8aca 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 f1a627e29d..7b1a261652 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 d64a1d2499..cb96fcf833 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 f4a5ad026a..66c0cc1a04 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 df10db91a0..55f1707766 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 423ce37d48..3582de4247 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 1de2fe9502..fe3faddcb9 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"),