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", "gpui",
"itertools", "itertools",
"language", "language",
"outline",
"project", "project",
"search", "search",
"settings", "settings",

View File

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

View File

@ -1,5 +1,6 @@
use gpui::{ use gpui::{
elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle, elements::*, AppContext, Entity, MouseButton, RenderContext, Subscription, View, ViewContext,
ViewHandle,
}; };
use itertools::Itertools; use itertools::Itertools;
use search::ProjectSearchView; use search::ProjectSearchView;
@ -14,6 +15,7 @@ pub enum Event {
} }
pub struct Breadcrumbs { pub struct Breadcrumbs {
pane_focused: bool,
active_item: Option<Box<dyn ItemHandle>>, active_item: Option<Box<dyn ItemHandle>>,
project_search: Option<ViewHandle<ProjectSearchView>>, project_search: Option<ViewHandle<ProjectSearchView>>,
subscription: Option<Subscription>, subscription: Option<Subscription>,
@ -22,6 +24,7 @@ pub struct Breadcrumbs {
impl Breadcrumbs { impl Breadcrumbs {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
pane_focused: false,
active_item: Default::default(), active_item: Default::default(),
subscription: Default::default(), subscription: Default::default(),
project_search: Default::default(), project_search: Default::default(),
@ -39,24 +42,53 @@ impl View for Breadcrumbs {
} }
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox { 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(); let theme = cx.global::<Settings>().theme.clone();
if let Some(breadcrumbs) = self let style = &theme.workspace.breadcrumbs;
.active_item
.as_ref() let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
.and_then(|item| item.breadcrumbs(&theme, cx)) Some(breadcrumbs) => breadcrumbs,
{ None => return Empty::new().boxed(),
Flex::row() };
.with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
Label::new("", theme.breadcrumbs.text.clone()).boxed() let crumbs = Flex::row()
})) .with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
.contained() Label::new("", style.default.text.clone()).boxed()
.with_style(theme.breadcrumbs.container) }))
.constrained()
.with_height(theme.workspace.breadcrumb_height)
.contained();
if not_editor || !self.pane_focused {
return crumbs
.with_style(style.default.container)
.aligned() .aligned()
.left() .left()
.boxed() .boxed();
} else {
Empty::new().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 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()) .map(|path| path.to_string_lossy().to_string())
.unwrap_or_else(|| "untitled".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| { breadcrumbs.extend(symbols.into_iter().map(|symbol| {
Text::new(symbol.text, theme.breadcrumbs.text.clone()) Text::new(
.with_highlights(symbol.highlight_ranges) symbol.text,
.boxed() theme.workspace.breadcrumbs.default.text.clone(),
)
.with_highlights(symbol.highlight_ranges)
.boxed()
})); }));
Some(breadcrumbs) Some(breadcrumbs)
} }

View File

@ -612,7 +612,7 @@ impl Item for TerminalView {
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> { fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
Some(vec![Text::new( Some(vec![Text::new(
self.terminal().read(cx).breadcrumb_text.clone(), self.terminal().read(cx).breadcrumb_text.clone(),
theme.breadcrumbs.text.clone(), theme.workspace.breadcrumbs.default.text.clone(),
) )
.boxed()]) .boxed()])
} }

View File

@ -30,7 +30,6 @@ pub struct Theme {
pub editor: Editor, pub editor: Editor,
pub search: Search, pub search: Search,
pub project_diagnostics: ProjectDiagnostics, pub project_diagnostics: ProjectDiagnostics,
pub breadcrumbs: ContainedText,
pub shared_screen: ContainerStyle, pub shared_screen: ContainerStyle,
pub contact_notification: ContactNotification, pub contact_notification: ContactNotification,
pub update_notification: UpdateNotification, pub update_notification: UpdateNotification,
@ -62,6 +61,8 @@ pub struct Workspace {
pub sidebar: Sidebar, pub sidebar: Sidebar,
pub status_bar: StatusBar, pub status_bar: StatusBar,
pub toolbar: Toolbar, pub toolbar: Toolbar,
pub breadcrumb_height: f32,
pub breadcrumbs: Interactive<ContainedText>,
pub disconnected_overlay: ContainedText, pub disconnected_overlay: ContainedText,
pub modal: ContainerStyle, pub modal: ContainerStyle,
pub notification: 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>) { 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 let Some(active_item) = self.active_item() {
if cx.is_self_focused() { if cx.is_self_focused() {
// Pane was focused directly. We need to either focus a view inside the active item, // 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 { fn keymap_context(&self, _: &AppContext) -> KeymapContext {
let mut keymap = Self::default_keymap_context(); let mut keymap = Self::default_keymap_context();
if self.docked.is_some() { if self.docked.is_some() {

View File

@ -20,6 +20,8 @@ pub trait ToolbarItemView: View {
) -> ToolbarItemLocation { ) -> ToolbarItemLocation {
current_location current_location
} }
fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut MutableAppContext) {}
} }
trait ToolbarItemViewHandle { trait ToolbarItemViewHandle {
@ -30,6 +32,7 @@ trait ToolbarItemViewHandle {
active_pane_item: Option<&dyn ItemHandle>, active_pane_item: Option<&dyn ItemHandle>,
cx: &mut MutableAppContext, cx: &mut MutableAppContext,
) -> ToolbarItemLocation; ) -> ToolbarItemLocation;
fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut MutableAppContext);
} }
#[derive(Copy, Clone, Debug, PartialEq)] #[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>> { pub fn item_of_type<T: ToolbarItemView>(&self) -> Option<ViewHandle<T>> {
self.items self.items
.iter() .iter()
@ -289,6 +298,10 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
this.set_active_pane_item(active_pane_item, cx) 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 { impl From<&dyn ToolbarItemViewHandle> for AnyViewHandle {

View File

@ -44,12 +44,6 @@ export default function app(colorScheme: ColorScheme): Object {
contactList: contactList(colorScheme), contactList: contactList(colorScheme),
search: search(colorScheme), search: search(colorScheme),
sharedScreen: sharedScreen(colorScheme), sharedScreen: sharedScreen(colorScheme),
breadcrumbs: {
...text(colorScheme.highest, "sans", "variant"),
padding: {
left: 6,
},
},
updateNotification: updateNotification(colorScheme), updateNotification: updateNotification(colorScheme),
simpleMessageNotification: simpleMessageNotification(colorScheme), simpleMessageNotification: simpleMessageNotification(colorScheme),
tooltip: tooltip(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 }, padding: { left: 8, right: 8, top: 4, bottom: 4 },
}, },
breadcrumbHeight: 24,
breadcrumbs: { breadcrumbs: {
...text(layer, "mono", "variant"), ...text(colorScheme.highest, "sans", "variant"),
padding: { left: 6 }, cornerRadius: 6,
padding: {
left: 6,
right: 6,
},
hover: {
color: foreground(colorScheme.highest, "on", "hovered"),
background: background(
colorScheme.highest,
"on",
"hovered"
),
},
}, },
disconnectedOverlay: { disconnectedOverlay: {
...text(layer, "sans"), ...text(layer, "sans"),