From 053b6cc7156a02abf3e30e9956e54ac4b29ca019 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 25 Feb 2024 10:17:50 -0500 Subject: [PATCH] Rework extension filtering to use a `ToggleButton` (#8387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR reworks the extension filtering to use a `ToggleButton`, since the filter states are mutually-exclusive. Screenshot 2024-02-25 at 10 04 59 AM Release Notes: - N/A --- crates/extensions_ui/src/extensions_ui.rs | 137 +++++++++++++--------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 872053fe22..235cfb676d 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -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, is_fetching_extensions: bool, - is_showing_installed_extensions: bool, - is_showing_not_installed_extensions: bool, + filter: ExtensionFilter, extension_entries: Vec, query_editor: View, 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) -> 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);