Open symbol outline when clicking on editor breadcrumbs

This commit is contained in:
Julia 2023-03-29 15:42:39 -04:00
parent 35b2aceffb
commit 737e2e1b3c
10 changed files with 102 additions and 29 deletions

1
Cargo.lock generated
View file

@ -785,6 +785,7 @@ dependencies = [
"gpui",
"itertools",
"language",
"outline",
"project",
"search",
"settings",

View file

@ -18,6 +18,7 @@ search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
outline = { path = "../outline" }
itertools = "0.10"
[dev-dependencies]

View file

@ -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;
}
}

View file

@ -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)
}

View file

@ -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()])
}

View file

@ -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,

View file

@ -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() {

View file

@ -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 {

View file

@ -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),

View file

@ -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"),