T07b navigation places: nav item and group, navigation menu, back and forward history, table of contents with anchors, go to line; measuring canvases pinned to their parent

This commit is contained in:
ZacharyZhang-NY committed 2026-09-25 12:14:58 -04:00
1 parent 0883a999f4
commit c02dd7d1c3
21 files changed
+1548 -56

No files matched your search

+49 -40
View File
@@ -1,17 +1,57 @@
use std::rc::Rc;
use gpui::{
App, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString,
Styled, Window, div,
App, Div, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString,
Stateful, Styled, Window, div,
};
use crate::{
forms::{Choice, Listing, listing},
forms::{Choice, Listing, OnValue, listing},
primitives::{Icon, IconName, tab_stop},
theme::{ActiveTheme, ControlSize, IconSize, Radius},
};
type OnValue = Rc<dyn Fn(&SharedString, &mut Window, &mut App)>;
/// A small icon button that lists `rows`; picking one runs `on_pick`.
pub(crate) fn list_button(
id: &ElementId,
icon: IconName,
rows: Rc<Vec<Choice>>,
selected: Option<&SharedString>,
on_pick: impl Fn(&SharedString, &mut Window, &mut App) + 'static,
window: &mut Window,
cx: &mut App,
) -> Stateful<Div> {
let focus = tab_stop((id.clone(), "focus").into(), true, window, cx);
let focused = focus.is_focused(window);
let theme = cx.theme();
let colors = &theme.colors;
let trigger = div()
.id((id.clone(), "button"))
.track_focus(&focus)
.relative()
.flex()
.flex_none()
.items_center()
.justify_center()
.size(theme.control_height(ControlSize::Sm))
.rounded(theme.radius(Radius::Md))
.border_1()
.border_color(if focused {
colors.focus
} else {
gpui::transparent_black()
})
.cursor_pointer()
.hover(|style| style.bg(colors.hover))
.child(Icon::new(icon).size(IconSize::Sm).color(colors.fg_muted));
let list = Listing {
id,
rows,
selected,
focused,
};
listing(list, trigger, on_pick, window, cx)
}
/// A button that lists every tab, for a strip too narrow to show them all.
#[derive(IntoElement)]
@@ -53,43 +93,12 @@ impl RenderOnce for TabOverflowMenu {
"tab overflow menu {:?} has no tabs",
self.id
);
let focus = tab_stop((self.id.clone(), "focus").into(), true, window, cx);
let focused = focus.is_focused(window);
let theme = cx.theme();
let colors = &theme.colors;
let trigger = div()
.id((self.id.clone(), "button"))
.track_focus(&focus)
.relative()
.flex()
.flex_none()
.items_center()
.justify_center()
.size(theme.control_height(ControlSize::Sm))
.rounded(theme.radius(Radius::Md))
.border_1()
.border_color(if focused {
colors.focus
} else {
gpui::transparent_black()
})
.cursor_pointer()
.hover(|style| style.bg(colors.hover))
.child(
Icon::new(IconName::ChevronDown)
.size(IconSize::Sm)
.color(colors.fg_muted),
);
let (id, on_select) = (self.id.clone(), self.on_select);
let list = Listing {
id: &self.id,
rows: Rc::new(self.tabs),
selected: self.selected.as_ref(),
focused,
};
listing(
list,
trigger,
list_button(
&self.id,
IconName::ChevronDown,
Rc::new(self.tabs),
self.selected.as_ref(),
move |value, window, cx| {
log::info!("tab overflow menu {id:?}: {value}");
if let Some(on_select) = &on_select {