Refactor buffer headers to collapse on click (#42021)

Release Notes:
Updated how clicking on multi-buffer headers works to provide better
control and prevent unexpected navigation:

Clicking the header now collapses/expands the file section instead of
opening the file.
Opening files can be done by clicking the filename or the "Open file"
button on the right side of the header.
Existing shortcuts continue to work: use the left chevron to collapse or
your keyboard shortcut to jump to the file

**Demo:**

https://github.com/user-attachments/assets/dca9ccc5-bd98-416c-97af-43b4e4b2f903
This commit is contained in:
Matt Miller
2025-11-05 14:59:50 -06:00
committed by GitHub
parent feed34cafe
commit 2f463370cc
+91 -51
View File
@@ -4004,41 +4004,51 @@ impl EditorElement {
.size_full()
.justify_between()
.overflow_hidden()
.child(
h_flex()
.gap_2()
.map(|path_header| {
let filename = filename
.map(SharedString::from)
.unwrap_or_else(|| "untitled".into());
.child(h_flex().gap_2().map(|path_header| {
let filename = filename
.map(SharedString::from)
.unwrap_or_else(|| "untitled".into());
path_header
.when(ItemSettings::get_global(cx).file_icons, |el| {
let path = path::Path::new(filename.as_str());
let icon = FileIcons::get_icon(path, cx)
.unwrap_or_default();
let icon =
Icon::from_path(icon).color(Color::Muted);
el.child(icon)
})
.child(Label::new(filename).single_line().when_some(
file_status,
|el, status| {
el.color(if status.is_conflicted() {
Color::Conflict
} else if status.is_modified() {
Color::Modified
} else if status.is_deleted() {
Color::Disabled
} else {
Color::Created
})
.when(status.is_deleted(), |el| {
el.strikethrough()
})
},
))
path_header
.when(ItemSettings::get_global(cx).file_icons, |el| {
let path = path::Path::new(filename.as_str());
let icon =
FileIcons::get_icon(path, cx).unwrap_or_default();
let icon = Icon::from_path(icon).color(Color::Muted);
el.child(icon)
})
.child(
ButtonLike::new("filename-button")
.style(ButtonStyle::Subtle)
.child(
div()
.child(
Label::new(filename)
.single_line()
.color(file_status_label_color(
file_status,
))
.when(
file_status.is_some_and(|s| {
s.is_deleted()
}),
|label| label.strikethrough(),
),
)
.group_hover("", |div| div.underline()),
)
.on_click(window.listener_for(&self.editor, {
let jump_data = jump_data.clone();
move |editor, e: &ClickEvent, window, cx| {
editor.open_excerpts_common(
Some(jump_data.clone()),
e.modifiers().secondary(),
window,
cx,
);
}
})),
)
.when_some(parent_path, |then, path| {
then.child(div().child(path).text_color(
if file_status.is_some_and(FileStatus::is_deleted) {
@@ -4047,33 +4057,47 @@ impl EditorElement {
colors.text_muted
},
))
}),
)
})
}))
.when(
can_open_excerpts && is_selected && relative_path.is_some(),
|el| {
el.child(
h_flex()
.id("jump-to-file-button")
.gap_2p5()
.child(Label::new("Jump To File"))
.child(KeyBinding::for_action_in(
&OpenExcerpts,
&focus_handle,
cx,
)),
ButtonLike::new("open-file-button")
.style(ButtonStyle::OutlinedGhost)
.child(
h_flex()
.gap_2p5()
.child(Label::new("Open file"))
.child(KeyBinding::for_action_in(
&OpenExcerpts,
&focus_handle,
cx,
)),
)
.on_click(window.listener_for(&self.editor, {
let jump_data = jump_data.clone();
move |editor, e: &ClickEvent, window, cx| {
editor.open_excerpts_common(
Some(jump_data.clone()),
e.modifiers().secondary(),
window,
cx,
);
}
})),
)
},
)
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
.on_click(window.listener_for(&self.editor, {
move |editor, e: &ClickEvent, window, cx| {
editor.open_excerpts_common(
Some(jump_data.clone()),
e.modifiers().secondary(),
window,
cx,
);
let buffer_id = for_excerpt.buffer_id;
move |editor, _e: &ClickEvent, _window, cx| {
if is_folded {
editor.unfold_buffer(buffer_id, cx);
} else {
editor.fold_buffer(buffer_id, cx);
}
}
})),
),
@@ -7512,6 +7536,22 @@ impl EditorElement {
}
}
fn file_status_label_color(file_status: Option<FileStatus>) -> Color {
file_status.map_or(Color::Default, |status| {
if status.is_conflicted() {
Color::Conflict
} else if status.is_modified() {
Color::Modified
} else if status.is_deleted() {
Color::Disabled
} else if status.is_created() {
Color::Created
} else {
Color::Default
}
})
}
fn header_jump_data(
snapshot: &EditorSnapshot,
block_row_start: DisplayRow,