mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
settings_ui: Dynamic navbar filtering (#39494)
Closes #ISSUE Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
parent
d9c7f44b0b
commit
6b980ecad3
7 changed files with 183 additions and 181 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -14551,6 +14551,7 @@ dependencies = [
|
|||
"menu",
|
||||
"node_runtime",
|
||||
"paths",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"serde",
|
||||
"session",
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ paths.workspace = true
|
|||
session.workspace = true
|
||||
settings.workspace = true
|
||||
zlog.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
|
||||
[[example]]
|
||||
name = "ui"
|
||||
|
|
|
|||
|
|
@ -144,7 +144,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
vec![
|
||||
SettingsPage {
|
||||
title: "General Page",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("General"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -303,7 +302,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Appearance & Behavior",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Theme"),
|
||||
// todo(settings_ui): Figure out how we want to add these
|
||||
|
|
@ -663,7 +661,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Editor",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Indentation"),
|
||||
// todo(settings_ui): Needs numeric stepper
|
||||
|
|
@ -1360,7 +1357,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Languages & Frameworks",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("General"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -1393,7 +1389,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Workbench & Window",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Workbench"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -1495,7 +1490,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Panels & Tools",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Project Panel"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -1700,7 +1694,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Version Control",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Git"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -1863,7 +1856,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "System & Network",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Network"),
|
||||
// todo(settings_ui): Proxy needs a default
|
||||
|
|
@ -1903,7 +1895,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Diagnostics & Errors",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Display"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -2121,7 +2112,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Collaboration",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Calls"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -2201,7 +2191,6 @@ fn user_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "AI",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("General"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -2224,7 +2213,6 @@ fn project_settings_data() -> Vec<SettingsPage> {
|
|||
vec![
|
||||
SettingsPage {
|
||||
title: "Project",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Worktree Settings Content"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -2244,7 +2232,6 @@ fn project_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Appearance & Behavior",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Guides"),
|
||||
SettingsPageItem::SettingItem(SettingItem {
|
||||
|
|
@ -2316,7 +2303,6 @@ fn project_settings_data() -> Vec<SettingsPage> {
|
|||
},
|
||||
SettingsPage {
|
||||
title: "Editing",
|
||||
expanded: false,
|
||||
items: vec![
|
||||
SettingsPageItem::SectionHeader("Indentation"),
|
||||
// todo(settings_ui): Needs numeric stepper
|
||||
|
|
@ -2788,12 +2774,13 @@ struct SubPage {
|
|||
struct NavBarEntry {
|
||||
title: &'static str,
|
||||
is_root: bool,
|
||||
expanded: bool,
|
||||
page_index: usize,
|
||||
item_index: Option<usize>,
|
||||
}
|
||||
|
||||
struct SettingsPage {
|
||||
title: &'static str,
|
||||
expanded: bool,
|
||||
items: Vec<SettingsPageItem>,
|
||||
}
|
||||
|
||||
|
|
@ -3058,66 +3045,80 @@ impl SettingsWindow {
|
|||
let toggle_page_index = self.page_index_from_navbar_index(ix);
|
||||
let selected_page_index = self.page_index_from_navbar_index(self.navbar_entry);
|
||||
|
||||
let expanded = &mut self.page_for_navbar_index(ix).expanded;
|
||||
let expanded = &mut self.navbar_entries[ix].expanded;
|
||||
*expanded = !*expanded;
|
||||
let expanded = *expanded;
|
||||
// if currently selected page is a child of the parent page we are folding,
|
||||
// set the current page to the parent page
|
||||
if selected_page_index == toggle_page_index {
|
||||
if !*expanded && selected_page_index == toggle_page_index {
|
||||
self.navbar_entry = ix;
|
||||
} else if selected_page_index > toggle_page_index {
|
||||
let sub_items_count = self.pages[toggle_page_index]
|
||||
.items
|
||||
.iter()
|
||||
.filter(|item| matches!(item, SettingsPageItem::SectionHeader(_)))
|
||||
.count();
|
||||
if expanded {
|
||||
self.navbar_entry += sub_items_count;
|
||||
} else {
|
||||
self.navbar_entry -= sub_items_count;
|
||||
}
|
||||
}
|
||||
|
||||
self.build_navbar();
|
||||
}
|
||||
|
||||
fn build_navbar(&mut self) {
|
||||
let mut navbar_entries = Vec::with_capacity(self.navbar_entries.len());
|
||||
for (page_index, page) in self.pages.iter().enumerate() {
|
||||
if !self.search_matches[page_index]
|
||||
.iter()
|
||||
.any(|is_match| *is_match)
|
||||
&& !self.search_matches[page_index].is_empty()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
navbar_entries.push(NavBarEntry {
|
||||
title: page.title,
|
||||
is_root: true,
|
||||
expanded: false,
|
||||
page_index,
|
||||
item_index: None,
|
||||
});
|
||||
if !page.expanded {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (item_index, item) in page.items.iter().enumerate() {
|
||||
let SettingsPageItem::SectionHeader(title) = item else {
|
||||
continue;
|
||||
};
|
||||
if !self.search_matches[page_index][item_index] {
|
||||
continue;
|
||||
}
|
||||
|
||||
navbar_entries.push(NavBarEntry {
|
||||
title,
|
||||
is_root: false,
|
||||
expanded: false,
|
||||
page_index,
|
||||
item_index: Some(item_index),
|
||||
});
|
||||
}
|
||||
}
|
||||
self.navbar_entries = navbar_entries;
|
||||
}
|
||||
|
||||
fn visible_navbar_entries(&self) -> impl Iterator<Item = (usize, &NavBarEntry)> {
|
||||
let mut index = 0;
|
||||
let entries = &self.navbar_entries;
|
||||
let search_matches = &self.search_matches;
|
||||
std::iter::from_fn(move || {
|
||||
while index < entries.len() {
|
||||
let entry = &entries[index];
|
||||
let included_in_search = if let Some(item_index) = entry.item_index {
|
||||
search_matches[entry.page_index][item_index]
|
||||
} else {
|
||||
search_matches[entry.page_index].iter().any(|b| *b)
|
||||
|| search_matches[entry.page_index].is_empty()
|
||||
};
|
||||
if included_in_search {
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
if index >= self.navbar_entries.len() {
|
||||
return None;
|
||||
}
|
||||
let entry = &entries[index];
|
||||
let entry_index = index;
|
||||
|
||||
index += 1;
|
||||
if entry.is_root && !entry.expanded {
|
||||
while index < entries.len() {
|
||||
if entries[index].is_root {
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Some((entry_index, entry));
|
||||
})
|
||||
}
|
||||
|
||||
fn update_matches(&mut self, cx: &mut Context<SettingsWindow>) {
|
||||
self.search_task.take();
|
||||
let query = self.search_bar.read(cx).text(cx);
|
||||
|
|
@ -3125,7 +3126,6 @@ impl SettingsWindow {
|
|||
for page in &mut self.search_matches {
|
||||
page.fill(true);
|
||||
}
|
||||
self.build_navbar();
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
|
|
@ -3169,7 +3169,7 @@ impl SettingsWindow {
|
|||
candidates.as_slice(),
|
||||
&query,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
candidates.len(),
|
||||
&atomic_bool,
|
||||
cx.background_executor().clone(),
|
||||
|
|
@ -3191,21 +3191,29 @@ impl SettingsWindow {
|
|||
page[header_index] = true;
|
||||
page[item_index] = true;
|
||||
}
|
||||
this.build_navbar();
|
||||
this.navbar_entry = 0;
|
||||
let first_navbar_entry_index = this
|
||||
.visible_navbar_entries()
|
||||
.next()
|
||||
.map(|e| e.0)
|
||||
.unwrap_or(0);
|
||||
this.navbar_entry = first_navbar_entry_index;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
}));
|
||||
}
|
||||
|
||||
fn build_ui(&mut self, cx: &mut Context<SettingsWindow>) {
|
||||
self.pages = self.current_file.pages();
|
||||
fn build_search_matches(&mut self) {
|
||||
self.search_matches = self
|
||||
.pages
|
||||
.iter()
|
||||
.map(|page| vec![true; page.items.len()])
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
|
||||
fn build_ui(&mut self, cx: &mut Context<SettingsWindow>) {
|
||||
self.pages = self.current_file.pages();
|
||||
self.build_search_matches();
|
||||
self.build_navbar();
|
||||
|
||||
if !self.search_bar.read(cx).is_empty(cx) {
|
||||
|
|
@ -3283,25 +3291,20 @@ impl SettingsWindow {
|
|||
"settings-ui-nav-bar",
|
||||
self.navbar_entries.len(),
|
||||
cx.processor(|this, range: Range<usize>, _, cx| {
|
||||
range
|
||||
.into_iter()
|
||||
.map(|ix| {
|
||||
let entry = &this.navbar_entries[ix];
|
||||
|
||||
this.visible_navbar_entries()
|
||||
.skip(range.start.saturating_sub(1))
|
||||
.take(range.len())
|
||||
.map(|(ix, entry)| {
|
||||
TreeViewItem::new(("settings-ui-navbar-entry", ix), entry.title)
|
||||
.root_item(entry.is_root)
|
||||
.toggle_state(this.is_navbar_entry_selected(ix))
|
||||
.when(entry.is_root, |item| {
|
||||
item.toggle(
|
||||
this.pages[this.page_index_from_navbar_index(ix)]
|
||||
.expanded,
|
||||
)
|
||||
.on_toggle(
|
||||
cx.listener(move |this, _, _, cx| {
|
||||
item.expanded(entry.expanded).on_toggle(cx.listener(
|
||||
move |this, _, _, cx| {
|
||||
this.toggle_navbar_entry(ix);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
},
|
||||
))
|
||||
})
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.navbar_entry = ix;
|
||||
|
|
@ -3423,11 +3426,6 @@ impl SettingsWindow {
|
|||
self.navbar_entries[index].page_index
|
||||
}
|
||||
|
||||
fn page_for_navbar_index(&mut self, index: usize) -> &mut SettingsPage {
|
||||
let index = self.page_index_from_navbar_index(index);
|
||||
&mut self.pages[index]
|
||||
}
|
||||
|
||||
fn is_navbar_entry_selected(&self, ix: usize) -> bool {
|
||||
ix == self.navbar_entry
|
||||
}
|
||||
|
|
@ -3626,10 +3624,6 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
impl SettingsWindow {
|
||||
fn navbar(&self) -> &[NavBarEntry] {
|
||||
self.navbar_entries.as_slice()
|
||||
}
|
||||
|
||||
fn navbar_entry(&self) -> usize {
|
||||
self.navbar_entry
|
||||
}
|
||||
|
|
@ -3642,6 +3636,7 @@ mod test {
|
|||
}
|
||||
|
||||
fn build(mut self) -> Self {
|
||||
self.build_search_matches();
|
||||
self.build_navbar();
|
||||
self
|
||||
}
|
||||
|
|
@ -3653,7 +3648,6 @@ mod test {
|
|||
) -> Self {
|
||||
let page = SettingsPage {
|
||||
title,
|
||||
expanded: false,
|
||||
items: Vec::default(),
|
||||
};
|
||||
|
||||
|
|
@ -3671,13 +3665,25 @@ mod test {
|
|||
|
||||
fn assert_search_results(&self, other: &Self) {
|
||||
// page index could be different because of filtered out pages
|
||||
assert!(
|
||||
self.navbar_entries
|
||||
.iter()
|
||||
.zip(other.navbar_entries.iter())
|
||||
.all(|(entry, other)| {
|
||||
entry.is_root == other.is_root && entry.title == other.title
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct EntryMinimal {
|
||||
is_root: bool,
|
||||
title: &'static str,
|
||||
}
|
||||
pretty_assertions::assert_eq!(
|
||||
other
|
||||
.visible_navbar_entries()
|
||||
.map(|(_, entry)| EntryMinimal {
|
||||
is_root: entry.is_root,
|
||||
title: entry.title,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
self.visible_navbar_entries()
|
||||
.map(|(_, entry)| EntryMinimal {
|
||||
is_root: entry.is_root,
|
||||
title: entry.title,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
assert_eq!(
|
||||
self.current_page().items.iter().collect::<Vec<_>>(),
|
||||
|
|
@ -3719,55 +3725,50 @@ mod test {
|
|||
|
||||
fn parse(input: &'static str, window: &mut Window, cx: &mut App) -> SettingsWindow {
|
||||
let mut pages: Vec<SettingsPage> = Vec::new();
|
||||
let mut current_page = None;
|
||||
let mut expanded_pages = Vec::new();
|
||||
let mut selected_idx = None;
|
||||
let mut ix = 0;
|
||||
let mut in_closed_subentry = false;
|
||||
let mut index = 0;
|
||||
let mut in_expanded_section = false;
|
||||
|
||||
for mut line in input
|
||||
.lines()
|
||||
.map(|line| line.trim())
|
||||
.filter(|line| !line.is_empty())
|
||||
{
|
||||
let mut is_selected = false;
|
||||
if line.ends_with("*") {
|
||||
assert!(
|
||||
selected_idx.is_none(),
|
||||
"Can only have one selected navbar entry at a time"
|
||||
);
|
||||
selected_idx = Some(ix);
|
||||
line = &line[..line.len() - 1];
|
||||
is_selected = true;
|
||||
if let Some(pre) = line.strip_suffix('*') {
|
||||
assert!(selected_idx.is_none(), "Only one selected entry allowed");
|
||||
selected_idx = Some(index);
|
||||
line = pre;
|
||||
}
|
||||
|
||||
if line.starts_with("v") || line.starts_with(">") {
|
||||
if let Some(current_page) = current_page.take() {
|
||||
pages.push(current_page);
|
||||
}
|
||||
|
||||
let expanded = line.starts_with("v");
|
||||
in_closed_subentry = !expanded;
|
||||
ix += 1;
|
||||
|
||||
current_page = Some(SettingsPage {
|
||||
title: line.split_once(" ").unwrap().1,
|
||||
expanded,
|
||||
items: Vec::default(),
|
||||
let (kind, title) = line.split_once(" ").unwrap();
|
||||
assert_eq!(kind.len(), 1);
|
||||
let kind = kind.chars().next().unwrap();
|
||||
if kind == 'v' {
|
||||
let page_idx = pages.len();
|
||||
expanded_pages.push(page_idx);
|
||||
pages.push(SettingsPage {
|
||||
title,
|
||||
items: vec![],
|
||||
});
|
||||
} else if line.starts_with("-") {
|
||||
if !in_closed_subentry {
|
||||
ix += 1;
|
||||
} else if is_selected && in_closed_subentry {
|
||||
panic!("Can't select sub entry if it's parent is closed");
|
||||
index += 1;
|
||||
in_expanded_section = true;
|
||||
} else if kind == '>' {
|
||||
pages.push(SettingsPage {
|
||||
title,
|
||||
items: vec![],
|
||||
});
|
||||
index += 1;
|
||||
in_expanded_section = false;
|
||||
} else if kind == '-' {
|
||||
pages
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.items
|
||||
.push(SettingsPageItem::SectionHeader(title));
|
||||
if selected_idx == Some(index) && !in_expanded_section {
|
||||
panic!("Items in unexpanded sections cannot be selected");
|
||||
}
|
||||
|
||||
let Some(current_page) = current_page.as_mut() else {
|
||||
panic!("Sub entries must be within a page");
|
||||
};
|
||||
|
||||
current_page.items.push(SettingsPageItem::SectionHeader(
|
||||
line.split_once(" ").unwrap().1,
|
||||
));
|
||||
index += 1;
|
||||
} else {
|
||||
panic!(
|
||||
"Entries must start with one of 'v', '>', or '-'\n line: {}",
|
||||
|
|
@ -3776,15 +3777,6 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(current_page) = current_page.take() {
|
||||
pages.push(current_page);
|
||||
}
|
||||
|
||||
let search_matches = pages
|
||||
.iter()
|
||||
.map(|page| vec![true; page.items.len()])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut settings_window = SettingsWindow {
|
||||
files: Vec::default(),
|
||||
current_file: crate::SettingsUiFile::User,
|
||||
|
|
@ -3793,43 +3785,70 @@ mod test {
|
|||
navbar_entry: selected_idx.expect("Must have a selected navbar entry"),
|
||||
navbar_entries: Vec::default(),
|
||||
list_handle: UniformListScrollHandle::default(),
|
||||
search_matches,
|
||||
search_matches: vec![],
|
||||
search_task: None,
|
||||
sub_page_stack: vec![],
|
||||
};
|
||||
|
||||
settings_window.build_search_matches();
|
||||
settings_window.build_navbar();
|
||||
for expanded_page_index in expanded_pages {
|
||||
for entry in &mut settings_window.navbar_entries {
|
||||
if entry.page_index == expanded_page_index && entry.is_root {
|
||||
entry.expanded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
settings_window
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_navbar_toggle(
|
||||
before: &'static str,
|
||||
toggle_idx: usize,
|
||||
toggle_page: &'static str,
|
||||
after: &'static str,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let mut settings_window = parse(before, window, cx);
|
||||
let toggle_page_idx = settings_window
|
||||
.pages
|
||||
.iter()
|
||||
.position(|page| page.title == toggle_page)
|
||||
.expect("page not found");
|
||||
let toggle_idx = settings_window
|
||||
.navbar_entries
|
||||
.iter()
|
||||
.position(|entry| entry.page_index == toggle_page_idx)
|
||||
.expect("page not found");
|
||||
settings_window.toggle_navbar_entry(toggle_idx);
|
||||
|
||||
let expected_settings_window = parse(after, window, cx);
|
||||
|
||||
assert_eq!(settings_window.navbar(), expected_settings_window.navbar());
|
||||
assert_eq!(
|
||||
settings_window.navbar_entry(),
|
||||
expected_settings_window.navbar_entry()
|
||||
pretty_assertions::assert_eq!(
|
||||
settings_window
|
||||
.visible_navbar_entries()
|
||||
.map(|(_, entry)| entry)
|
||||
.collect::<Vec<_>>(),
|
||||
expected_settings_window
|
||||
.visible_navbar_entries()
|
||||
.map(|(_, entry)| entry)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
pretty_assertions::assert_eq!(
|
||||
settings_window.navbar_entries[settings_window.navbar_entry()],
|
||||
expected_settings_window.navbar_entries[expected_settings_window.navbar_entry()],
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! check_navbar_toggle {
|
||||
($name:ident, before: $before:expr, toggle_idx: $toggle_idx:expr, after: $after:expr) => {
|
||||
($name:ident, before: $before:expr, toggle_page: $toggle_page:expr, after: $after:expr) => {
|
||||
#[gpui::test]
|
||||
fn $name(cx: &mut gpui::TestAppContext) {
|
||||
let window = cx.add_empty_window();
|
||||
window.update(|window, cx| {
|
||||
register_settings(cx);
|
||||
check_navbar_toggle($before, $toggle_idx, $after, window, cx);
|
||||
check_navbar_toggle($before, $toggle_page, $after, window, cx);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -3844,7 +3863,7 @@ mod test {
|
|||
v Project
|
||||
- Project Settings
|
||||
",
|
||||
toggle_idx: 0,
|
||||
toggle_page: "General",
|
||||
after: r"
|
||||
> General*
|
||||
v Project
|
||||
|
|
@ -3861,7 +3880,7 @@ mod test {
|
|||
v Project
|
||||
- Project Settings
|
||||
",
|
||||
toggle_idx: 0,
|
||||
toggle_page: "General",
|
||||
after: r"
|
||||
v General*
|
||||
- General
|
||||
|
|
@ -3880,7 +3899,7 @@ mod test {
|
|||
v Project
|
||||
- Project Settings*
|
||||
",
|
||||
toggle_idx: 1,
|
||||
toggle_page: "Project",
|
||||
after: r"
|
||||
> General
|
||||
> Project*
|
||||
|
|
@ -3899,7 +3918,7 @@ mod test {
|
|||
- General
|
||||
> Appearance & Behavior
|
||||
",
|
||||
toggle_idx: 3,
|
||||
toggle_page: "Project",
|
||||
after: r"
|
||||
v General Page
|
||||
- General
|
||||
|
|
@ -3923,7 +3942,7 @@ mod test {
|
|||
- General*
|
||||
> Appearance & Behavior
|
||||
",
|
||||
toggle_idx: 0,
|
||||
toggle_page: "General Page",
|
||||
after: r"
|
||||
> General Page
|
||||
v Project
|
||||
|
|
@ -3946,7 +3965,7 @@ mod test {
|
|||
- General*
|
||||
> Appearance & Behavior
|
||||
",
|
||||
toggle_idx: 0,
|
||||
toggle_page: "General Page",
|
||||
after: r"
|
||||
v General Page
|
||||
- General
|
||||
|
|
@ -3959,31 +3978,6 @@ mod test {
|
|||
"
|
||||
);
|
||||
|
||||
check_navbar_toggle!(
|
||||
navbar_toggle_sub_entry_does_nothing,
|
||||
before: r"
|
||||
> General Page
|
||||
- General
|
||||
- Privacy
|
||||
v Project
|
||||
- Worktree Settings Content
|
||||
v AI
|
||||
- General*
|
||||
> Appearance & Behavior
|
||||
",
|
||||
toggle_idx: 4,
|
||||
after: r"
|
||||
> General Page
|
||||
- General
|
||||
- Privacy
|
||||
v Project
|
||||
- Worktree Settings Content
|
||||
v AI
|
||||
- General*
|
||||
> Appearance & Behavior
|
||||
"
|
||||
);
|
||||
|
||||
#[gpui::test]
|
||||
fn test_basic_search(cx: &mut gpui::TestAppContext) {
|
||||
let cx = cx.add_empty_window();
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub struct Disclosure {
|
|||
is_open: bool,
|
||||
selected: bool,
|
||||
disabled: bool,
|
||||
on_toggle: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
on_toggle_expanded: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
cursor_style: CursorStyle,
|
||||
opened_icon: IconName,
|
||||
closed_icon: IconName,
|
||||
|
|
@ -24,7 +24,7 @@ impl Disclosure {
|
|||
is_open,
|
||||
selected: false,
|
||||
disabled: false,
|
||||
on_toggle: None,
|
||||
on_toggle_expanded: None,
|
||||
cursor_style: CursorStyle::PointingHand,
|
||||
opened_icon: IconName::ChevronDown,
|
||||
closed_icon: IconName::ChevronRight,
|
||||
|
|
@ -32,11 +32,11 @@ impl Disclosure {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn on_toggle(
|
||||
pub fn on_toggle_expanded(
|
||||
mut self,
|
||||
handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>>,
|
||||
) -> Self {
|
||||
self.on_toggle = handler.into();
|
||||
self.on_toggle_expanded = handler.into();
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ impl Toggleable for Disclosure {
|
|||
|
||||
impl Clickable for Disclosure {
|
||||
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
|
||||
self.on_toggle = Some(Arc::new(handler));
|
||||
self.on_toggle_expanded = Some(Arc::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ impl RenderOnce for Disclosure {
|
|||
.when_some(self.visible_on_hover.clone(), |this, group_name| {
|
||||
this.visible_on_hover(group_name)
|
||||
})
|
||||
.when_some(self.on_toggle, move |this, on_toggle| {
|
||||
.when_some(self.on_toggle_expanded, move |this, on_toggle| {
|
||||
this.on_click(move |event, window, cx| on_toggle(event, window, cx))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@ impl RenderOnce for ListHeader {
|
|||
h_flex()
|
||||
.gap(DynamicSpacing::Base04.rems(cx))
|
||||
.children(self.toggle.map(|is_open| {
|
||||
Disclosure::new("toggle", is_open).on_toggle(self.on_toggle.clone())
|
||||
Disclosure::new("toggle", is_open)
|
||||
.on_toggle_expanded(self.on_toggle.clone())
|
||||
}))
|
||||
.child(
|
||||
div()
|
||||
|
|
|
|||
|
|
@ -308,7 +308,10 @@ impl RenderOnce for ListItem {
|
|||
.when(is_open && !self.always_show_disclosure_icon, |this| {
|
||||
this.visible_on_hover("")
|
||||
})
|
||||
.child(Disclosure::new("toggle", is_open).on_toggle(self.on_toggle))
|
||||
.child(
|
||||
Disclosure::new("toggle", is_open)
|
||||
.on_toggle_expanded(self.on_toggle),
|
||||
)
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub struct TreeViewItem {
|
|||
id: ElementId,
|
||||
group_name: Option<SharedString>,
|
||||
label: SharedString,
|
||||
toggle: bool,
|
||||
expanded: bool,
|
||||
selected: bool,
|
||||
disabled: bool,
|
||||
focused: bool,
|
||||
|
|
@ -28,7 +28,7 @@ impl TreeViewItem {
|
|||
id: id.into(),
|
||||
group_name: None,
|
||||
label: label.into(),
|
||||
toggle: false,
|
||||
expanded: false,
|
||||
selected: false,
|
||||
disabled: false,
|
||||
focused: false,
|
||||
|
|
@ -73,8 +73,8 @@ impl TreeViewItem {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn toggle(mut self, toggle: bool) -> Self {
|
||||
self.toggle = toggle;
|
||||
pub fn expanded(mut self, toggle: bool) -> Self {
|
||||
self.expanded = toggle;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -161,10 +161,12 @@ impl RenderOnce for TreeViewItem {
|
|||
})
|
||||
.hover(|s| s.bg(cx.theme().colors().element_hover))
|
||||
.child(
|
||||
Disclosure::new("toggle", self.toggle)
|
||||
Disclosure::new("toggle", self.expanded)
|
||||
.when_some(
|
||||
self.on_toggle.clone(),
|
||||
|disclosure, on_toggle| disclosure.on_toggle(on_toggle),
|
||||
|disclosure, on_toggle| {
|
||||
disclosure.on_toggle_expanded(on_toggle)
|
||||
},
|
||||
)
|
||||
.opened_icon(IconName::ChevronDown)
|
||||
.closed_icon(IconName::ChevronRight),
|
||||
|
|
|
|||
Loading…
Reference in a new issue