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