Rework extension filtering to use a ToggleButton (#8387)

This PR reworks the extension filtering to use a `ToggleButton`, since
the filter states are mutually-exclusive.

<img width="1136" alt="Screenshot 2024-02-25 at 10 04 59 AM"
src="https://github.com/zed-industries/zed/assets/1486634/52c621da-201c-42b9-805d-62e3ab66f94b">

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-02-25 10:17:50 -05:00 committed by GitHub
parent 37a12a366f
commit 053b6cc715
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,7 +11,7 @@ use settings::Settings;
use std::time::Duration;
use std::{ops::Range, sync::Arc};
use theme::ThemeSettings;
use ui::{prelude::*, CheckboxWithLabel, Tooltip};
use ui::{prelude::*, ToggleButton, Tooltip};
use workspace::{
item::{Item, ItemEvent},
@ -30,12 +30,18 @@ pub fn init(cx: &mut AppContext) {
.detach();
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
enum ExtensionFilter {
All,
Installed,
NotInstalled,
}
pub struct ExtensionsPage {
list: UniformListScrollHandle,
telemetry: Arc<Telemetry>,
is_fetching_extensions: bool,
is_showing_installed_extensions: bool,
is_showing_not_installed_extensions: bool,
filter: ExtensionFilter,
extension_entries: Vec<Extension>,
query_editor: View<Editor>,
query_contains_error: bool,
@ -56,8 +62,7 @@ impl ExtensionsPage {
list: UniformListScrollHandle::new(),
telemetry: workspace.client().telemetry().clone(),
is_fetching_extensions: false,
is_showing_installed_extensions: true,
is_showing_not_installed_extensions: true,
filter: ExtensionFilter::All,
extension_entries: Vec::new(),
query_contains_error: false,
extension_fetch_task: None,
@ -74,17 +79,17 @@ impl ExtensionsPage {
self.extension_entries
.iter()
.filter(|extension| {
let status = extension_store.extension_status(&extension.id);
.filter(|extension| match self.filter {
ExtensionFilter::All => true,
ExtensionFilter::Installed => {
let status = extension_store.extension_status(&extension.id);
match [
self.is_showing_installed_extensions,
self.is_showing_not_installed_extensions,
] {
[true, true] => true,
[true, false] => matches!(status, ExtensionStatus::Installed(_)),
[false, true] => matches!(status, ExtensionStatus::NotInstalled),
[false, false] => false,
matches!(status, ExtensionStatus::Installed(_))
}
ExtensionFilter::NotInstalled => {
let status = extension_store.extension_status(&extension.id);
matches!(status, ExtensionStatus::NotInstalled)
}
})
.cloned()
@ -421,17 +426,33 @@ impl ExtensionsPage {
}
fn render_empty_state(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let is_filtering = self.search_query(cx).is_some()
|| self.is_showing_installed_extensions
|| self.is_showing_not_installed_extensions;
let has_search = self.search_query(cx).is_some();
let message = if self.is_fetching_extensions {
"Loading extensions..."
} else {
if is_filtering {
"No extensions that match your search criteria."
} else {
"No extensions."
match self.filter {
ExtensionFilter::All => {
if has_search {
"No extensions that match your search."
} else {
"No extensions."
}
}
ExtensionFilter::Installed => {
if has_search {
"No installed extensions that match your search."
} else {
"No installed extensions."
}
}
ExtensionFilter::NotInstalled => {
if has_search {
"No not installed extensions that match your search."
} else {
"No not installed extensions."
}
}
}
};
@ -456,38 +477,46 @@ impl Render for ExtensionsPage {
.w_full()
.gap_2()
.child(h_flex().child(self.render_search(cx)))
.child(CheckboxWithLabel::new(
"Installed",
Label::new("Installed"),
if self.is_showing_installed_extensions {
Selection::Selected
} else {
Selection::Unselected
},
cx.listener(|this, selection, _cx| {
this.is_showing_installed_extensions = match selection {
Selection::Selected => true,
Selection::Unselected => false,
Selection::Indeterminate => return,
}
}),
))
.child(CheckboxWithLabel::new(
"not installed",
Label::new("Not installed"),
if self.is_showing_not_installed_extensions {
Selection::Selected
} else {
Selection::Unselected
},
cx.listener(|this, selection, _cx| {
this.is_showing_not_installed_extensions = match selection {
Selection::Selected => true,
Selection::Unselected => false,
Selection::Indeterminate => return,
}
}),
)),
.child(
h_flex()
.child(
ToggleButton::new("filter-all", "All")
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.selected(self.filter == ExtensionFilter::All)
.on_click(cx.listener(|this, _event, _cx| {
this.filter = ExtensionFilter::All;
}))
.tooltip(move |cx| Tooltip::text("Show all extensions", cx))
.first(),
)
.child(
ToggleButton::new("filter-installed", "Installed")
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.selected(self.filter == ExtensionFilter::Installed)
.on_click(cx.listener(|this, _event, _cx| {
this.filter = ExtensionFilter::Installed;
}))
.tooltip(move |cx| {
Tooltip::text("Show installed extensions", cx)
})
.middle(),
)
.child(
ToggleButton::new("filter-not-installed", "Not Installed")
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.selected(self.filter == ExtensionFilter::NotInstalled)
.on_click(cx.listener(|this, _event, _cx| {
this.filter = ExtensionFilter::NotInstalled;
}))
.tooltip(move |cx| {
Tooltip::text("Show not installed extensions", cx)
})
.last(),
),
),
)
.child(v_flex().size_full().overflow_y_hidden().map(|this| {
let entries = self.filtered_extension_entries(cx);