@@ -512,6 +512,7 @@ pub struct SettingsWindow {
title_bar: Option<Entity<PlatformTitleBar>>,
original_window: Option<WindowHandle<Workspace>>,
files: Vec<(SettingsUiFile, FocusHandle)>,
+ drop_down_file: Option<usize>,
worktree_root_dirs: HashMap<WorktreeId, String>,
current_file: SettingsUiFile,
pages: Vec<SettingsPage>,
@@ -977,6 +978,7 @@ impl SettingsWindow {
original_window,
worktree_root_dirs: HashMap::default(),
files: vec![],
+ drop_down_file: None,
current_file: current_file,
pages: vec![],
navbar_entries: vec![],
@@ -1446,7 +1448,7 @@ impl SettingsWindow {
.iter()
.any(|(file, _)| file == &self.current_file);
if !current_file_still_exists {
- self.change_file(0, window, cx);
+ self.change_file(0, window, false, cx);
}
}
@@ -1466,12 +1468,22 @@ impl SettingsWindow {
self.open_navbar_entry_page(first_navbar_entry_index);
}
- fn change_file(&mut self, ix: usize, window: &mut Window, cx: &mut Context<SettingsWindow>) {
+ fn change_file(
+ &mut self,
+ ix: usize,
+ window: &mut Window,
+ drop_down_file: bool,
+ cx: &mut Context<SettingsWindow>,
+ ) {
if ix >= self.files.len() {
self.current_file = SettingsUiFile::User;
self.build_ui(window, cx);
return;
}
+ if drop_down_file {
+ self.drop_down_file = Some(ix);
+ }
+
if self.files[ix].0 == self.current_file {
return;
}
@@ -1491,9 +1503,30 @@ impl SettingsWindow {
fn render_files_header(
&self,
- _window: &mut Window,
+ window: &mut Window,
cx: &mut Context<SettingsWindow>,
) -> impl IntoElement {
+ const OVERFLOW_LIMIT: usize = 1;
+
+ let file_button =
+ |ix, file: &SettingsUiFile, focus_handle, cx: &mut Context<SettingsWindow>| {
+ Button::new(
+ ix,
+ self.display_name(&file)
+ .expect("Files should always have a name"),
+ )
+ .toggle_state(file == &self.current_file)
+ .selected_style(ButtonStyle::Tinted(ui::TintColor::Accent))
+ .track_focus(focus_handle)
+ .on_click(cx.listener({
+ let focus_handle = focus_handle.clone();
+ move |this, _: &gpui::ClickEvent, window, cx| {
+ this.change_file(ix, window, false, cx);
+ focus_handle.focus(window);
+ }
+ }))
+ };
+ let this = cx.entity();
h_flex()
.w_full()
.pb_4()
@@ -1505,31 +1538,73 @@ impl SettingsWindow {
.child(
h_flex()
.id("file_buttons_container")
- .w_64() // Temporary fix until long-term solution is a fixed set of buttons representing a file location (User, Project, and Remote)
.gap_1()
.overflow_x_scroll()
.children(
- self.files
- .iter()
- .enumerate()
- .map(|(ix, (file, focus_handle))| {
- Button::new(
- ix,
- self.display_name(&file)
- .expect("Files should always have a name"),
+ self.files.iter().enumerate().take(OVERFLOW_LIMIT).map(
+ |(ix, (file, focus_handle))| file_button(ix, file, focus_handle, cx),
+ ),
+ )
+ .when(self.files.len() > OVERFLOW_LIMIT, |div| {
+ div.children(
+ self.files
+ .iter()
+ .enumerate()
+ .skip(OVERFLOW_LIMIT)
+ .find(|(_, (file, _))| file == &self.current_file)
+ .map(|(ix, (file, focus_handle))| {
+ file_button(ix, file, focus_handle, cx)
+ })
+ .or_else(|| {
+ let ix = self.drop_down_file.unwrap_or(OVERFLOW_LIMIT);
+ self.files.get(ix).map(|(file, focus_handle)| {
+ file_button(ix, file, focus_handle, cx)
+ })
+ }),
+ )
+ .when(
+ self.files.len() > OVERFLOW_LIMIT + 1,
+ |div| {
+ div.child(
+ DropdownMenu::new(
+ "more-files",
+ format!("+{}", self.files.len() - (OVERFLOW_LIMIT + 1)),
+ ContextMenu::build(window, cx, move |mut menu, _, _| {
+ for (ix, (file, focus_handle)) in self
+ .files
+ .iter()
+ .enumerate()
+ .skip(OVERFLOW_LIMIT + 1)
+ {
+ menu = menu.entry(
+ self.display_name(file)
+ .expect("Files should always have a name"),
+ None,
+ {
+ let this = this.clone();
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ this.update(cx, |this, cx| {
+ this.change_file(
+ ix, window, true, cx,
+ );
+ });
+ focus_handle.focus(window);
+ }
+ },
+ );
+ }
+
+ menu
+ }),
+ )
+ .style(DropdownStyle::Ghost)
+ .tab_index(0)
+ .no_chevron(),
)
- .toggle_state(file == &self.current_file)
- .selected_style(ButtonStyle::Tinted(ui::TintColor::Accent))
- .track_focus(focus_handle)
- .on_click(cx.listener({
- let focus_handle = focus_handle.clone();
- move |this, _: &gpui::ClickEvent, window, cx| {
- this.change_file(ix, window, cx);
- focus_handle.focus(window);
- }
- }))
- }),
- ),
+ },
+ )
+ }),
)
.child(
Button::new("edit-in-json", "Edit in settings.json")
@@ -2692,6 +2767,7 @@ mod test {
worktree_root_dirs: HashMap::default(),
files: Vec::default(),
current_file: crate::SettingsUiFile::User,
+ drop_down_file: None,
pages,
search_bar: cx.new(|cx| Editor::single_line(window, cx)),
navbar_entry: selected_idx.expect("Must have a selected navbar entry"),
@@ -30,6 +30,7 @@ pub struct DropdownMenu {
attach: Option<Corner>,
offset: Option<Point<Pixels>>,
tab_index: Option<isize>,
+ chevron: bool,
}
impl DropdownMenu {
@@ -50,6 +51,7 @@ impl DropdownMenu {
attach: None,
offset: None,
tab_index: None,
+ chevron: true,
}
}
@@ -70,6 +72,7 @@ impl DropdownMenu {
attach: None,
offset: None,
tab_index: None,
+ chevron: true,
}
}
@@ -109,6 +112,11 @@ impl DropdownMenu {
self.tab_index = Some(arg);
self
}
+
+ pub fn no_chevron(mut self) -> Self {
+ self.chevron = false;
+ self
+ }
}
impl Disableable for DropdownMenu {
@@ -132,19 +140,23 @@ impl RenderOnce for DropdownMenu {
let button = match self.label {
LabelKind::Text(text) => Button::new(self.id.clone(), text)
.style(button_style)
- .icon(IconName::ChevronUpDown)
- .icon_position(IconPosition::End)
- .icon_size(IconSize::XSmall)
- .icon_color(Color::Muted)
+ .when(self.chevron, |this| {
+ this.icon(IconName::ChevronUpDown)
+ .icon_position(IconPosition::End)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ })
.when(full_width, |this| this.full_width())
.size(trigger_size)
.disabled(self.disabled),
LabelKind::Element(_element) => Button::new(self.id.clone(), "")
.style(button_style)
- .icon(IconName::ChevronUpDown)
- .icon_position(IconPosition::End)
- .icon_size(IconSize::XSmall)
- .icon_color(Color::Muted)
+ .when(self.chevron, |this| {
+ this.icon(IconName::ChevronUpDown)
+ .icon_position(IconPosition::End)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ })
.when(full_width, |this| this.full_width())
.size(trigger_size)
.disabled(self.disabled),