Cargo.lock 🔗
@@ -785,6 +785,7 @@ dependencies = [
"gpui",
"itertools",
"language",
+ "outline",
"project",
"search",
"settings",
Julia created
Open symbol outline when clicking on editor breadcrumbs
Cargo.lock | 1
crates/breadcrumbs/Cargo.toml | 1
crates/breadcrumbs/src/breadcrumbs.rs | 66 +++++++++++++++++++-----
crates/editor/src/items.rs | 12 +++-
crates/terminal_view/src/terminal_view.rs | 2
crates/theme/src/theme.rs | 3
crates/workspace/src/pane.rs | 10 +++
crates/workspace/src/toolbar.rs | 13 ++++
styles/src/styleTree/app.ts | 6 --
styles/src/styleTree/workspace.ts | 17 +++++
10 files changed, 102 insertions(+), 29 deletions(-)
@@ -785,6 +785,7 @@ dependencies = [
"gpui",
"itertools",
"language",
+ "outline",
"project",
"search",
"settings",
@@ -18,6 +18,7 @@ search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
+outline = { path = "../outline" }
itertools = "0.10"
[dev-dependencies]
@@ -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<Box<dyn ItemHandle>>,
project_search: Option<ViewHandle<ProjectSearchView>>,
subscription: Option<Subscription>,
@@ -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<Self>) -> ElementBox {
+ let active_item = match &self.active_item {
+ Some(active_item) => active_item,
+ None => return Empty::new().boxed(),
+ };
+ let not_editor = active_item.downcast::<editor::Editor>().is_none();
+
let theme = cx.global::<Settings>().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::<Breadcrumbs>::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::<Breadcrumbs, _>(
+ 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;
+ }
}
@@ -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)
}
@@ -612,7 +612,7 @@ impl Item for TerminalView {
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
Some(vec![Text::new(
self.terminal().read(cx).breadcrumb_text.clone(),
- theme.breadcrumbs.text.clone(),
+ theme.workspace.breadcrumbs.default.text.clone(),
)
.boxed()])
}
@@ -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<ContainedText>,
pub disconnected_overlay: ContainedText,
pub modal: ContainerStyle,
pub notification: ContainerStyle,
@@ -1603,6 +1603,10 @@ impl View for Pane {
}
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ 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>) {
+ 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() {
@@ -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<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
self.items
.iter()
@@ -289,6 +298,10 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
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 {
@@ -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),
@@ -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"),