From 3afa864ae9148dc3a16bdca07e480ed2eb0b9c83 Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Wed, 24 Jun 2026 14:43:01 +0200 Subject: [PATCH] Add remove all filters/charts command Add the to filters and charts context menus to remove all filters or all charts at once. Removing them at once will be done in one batch internally to avoid updating search queries internally multiple times --- crates/app/src/session/ui/shared/mod.rs | 72 +++++++++++++++++++ .../session/ui/shared/searching/filters.rs | 26 +++++++ .../app/src/session/ui/side_panel/filters.rs | 40 +++++++++++ 3 files changed, 138 insertions(+) diff --git a/crates/app/src/session/ui/shared/mod.rs b/crates/app/src/session/ui/shared/mod.rs index e8fa50f08..316841b04 100644 --- a/crates/app/src/session/ui/shared/mod.rs +++ b/crates/app/src/session/ui/shared/mod.rs @@ -405,6 +405,17 @@ impl SessionShared { changed } + /// Removes all filters from this session and tracks recent-session dirtiness once. + /// + /// Returns `true` when at least one filter was removed. + pub fn unapply_all_filters(&mut self, registry: &mut FilterRegistry) -> bool { + let changed = self.filters.unapply_all_filters(registry); + if changed { + self.bump_recent_revision(); + } + changed + } + /// Rebinds an applied filter row to a new registry id and tracks recent-session dirtiness. pub fn rebind_filter(&mut self, current_id: &Uuid, next_id: Uuid) -> bool { let changed = self.filters.rebind_filter(current_id, next_id); @@ -453,6 +464,17 @@ impl SessionShared { changed } + /// Removes all search values from this session and tracks recent-session dirtiness once. + /// + /// Returns `true` when at least one search value was removed. + pub fn unapply_all_search_values(&mut self, registry: &mut FilterRegistry) -> bool { + let changed = self.filters.unapply_all_search_values(registry); + if changed { + self.bump_recent_revision(); + } + changed + } + /// Rebinds an applied search-value row to a new registry id and tracks recent-session dirtiness. pub fn rebind_search_value(&mut self, current_id: &Uuid, next_id: Uuid) -> bool { let changed = self.filters.rebind_search_value(current_id, next_id); @@ -594,6 +616,56 @@ mod tests { assert_eq!(shared.recent_revision(), baseline + 2); } + #[test] + fn unapply_all_filters_bumps_recent_revision_once() { + let mut shared = new_shared(); + let mut registry = FilterRegistry::default(); + let first_filter = FilterDefinition::new(SearchFilter::plain("first")); + let first_id = registry.add_filter(first_filter); + let second_filter = FilterDefinition::new(SearchFilter::plain("second")); + let second_id = registry.add_filter(second_filter); + shared.apply_filter(&mut registry, first_id); + shared.apply_filter(&mut registry, second_id); + let baseline = shared.recent_revision(); + + assert!(shared.unapply_all_filters(&mut registry)); + + assert!(shared.filters.filter_entries.is_empty()); + assert_eq!(registry.filter_usage_count(&first_id), 0); + assert_eq!(registry.filter_usage_count(&second_id), 0); + assert_eq!(registry.filters_map().len(), 2); + assert_eq!(shared.recent_revision(), baseline + 1); + + assert!(!shared.unapply_all_filters(&mut registry)); + assert_eq!(shared.recent_revision(), baseline + 1); + } + + #[test] + fn unapply_all_search_values_bumps_recent_revision_once() { + let mut shared = new_shared(); + let mut registry = FilterRegistry::default(); + let first_filter = SearchFilter::plain("first=(\\d+)").regex(true); + let first_value = SearchValueDefinition::new(first_filter); + let first_id = registry.add_search_value(first_value); + let second_filter = SearchFilter::plain("second=(\\d+)").regex(true); + let second_value = SearchValueDefinition::new(second_filter); + let second_id = registry.add_search_value(second_value); + shared.apply_search_value(&mut registry, first_id); + shared.apply_search_value(&mut registry, second_id); + let baseline = shared.recent_revision(); + + assert!(shared.unapply_all_search_values(&mut registry)); + + assert!(shared.filters.search_value_entries.is_empty()); + assert_eq!(registry.search_value_usage_count(&first_id), 0); + assert_eq!(registry.search_value_usage_count(&second_id), 0); + assert_eq!(registry.search_value_map().len(), 2); + assert_eq!(shared.recent_revision(), baseline + 1); + + assert!(!shared.unapply_all_search_values(&mut registry)); + assert_eq!(shared.recent_revision(), baseline + 1); + } + #[test] fn drop_search_updates_counts() { let mut shared = new_shared(); diff --git a/crates/app/src/session/ui/shared/searching/filters.rs b/crates/app/src/session/ui/shared/searching/filters.rs index e0daeb8af..41bb5f1f2 100644 --- a/crates/app/src/session/ui/shared/searching/filters.rs +++ b/crates/app/src/session/ui/shared/searching/filters.rs @@ -338,6 +338,19 @@ impl FiltersState { false } + /// Removes all filters from the session without deleting registry definitions. + /// + /// Returns `true` when at least one filter was removed. + pub fn unapply_all_filters(&mut self, registry: &mut FilterRegistry) -> bool { + let changed = !self.filter_entries.is_empty(); + let session_id = self.session_id; + for item in self.filter_entries.drain(..) { + registry.unapply_filter_from_session(item.id, session_id); + } + + changed + } + /// Swaps the registry definition behind an applied filter while keeping the /// session-local row state intact. /// @@ -422,6 +435,19 @@ impl FiltersState { false } + /// Removes all search values from the session without deleting registry definitions. + /// + /// Returns `true` when at least one search value was removed. + pub fn unapply_all_search_values(&mut self, registry: &mut FilterRegistry) -> bool { + let changed = !self.search_value_entries.is_empty(); + let session_id = self.session_id; + for item in self.search_value_entries.drain(..) { + registry.unapply_search_value_from_session(item.id, session_id); + } + + changed + } + /// Swaps the registry definition behind an applied search value while /// keeping the session-local row state intact. /// diff --git a/crates/app/src/session/ui/side_panel/filters.rs b/crates/app/src/session/ui/side_panel/filters.rs index a3620c6aa..0048c43a2 100644 --- a/crates/app/src/session/ui/side_panel/filters.rs +++ b/crates/app/src/session/ui/side_panel/filters.rs @@ -43,9 +43,11 @@ enum FilterPanelAction { ToggleFilter(Uuid, bool), EditFilterFlags(Uuid, FilterFlags), RemoveFilter(Uuid), + RemoveAllFilters, MoveFilterToValue(Uuid), ToggleSearchValue(Uuid, bool), RemoveSearchValue(Uuid), + RemoveAllSearchValues, MoveValueToFilter(Uuid), /// Requests the parent session to capture the current filters and charts. CapturePreset, @@ -341,6 +343,11 @@ impl FiltersUi { ui.close(); } + if ui.button("Remove All Filters").clicked() { + *side_action = Some(FilterPanelAction::RemoveAllFilters); + ui.close(); + } + let mut move_btn = ui .add_enabled( row.search_value_eligibility.is_eligible(), @@ -435,6 +442,11 @@ impl FiltersUi { ui.close(); } + if ui.button("Remove All Charts").clicked() { + *side_action = Some(FilterPanelAction::RemoveAllSearchValues); + ui.close(); + } + if ui.button("Move to Filter").clicked() { *side_action = Some(FilterPanelAction::MoveValueToFilter(row.id)); ui.close(); @@ -1076,6 +1088,20 @@ impl FiltersUi { .into_iter() .for_each(|cmd| _ = actions.try_send_command(&self.cmd_tx, cmd)); } + FilterPanelAction::RemoveAllFilters => { + self.filter_edit_state = None; + if self + .selected_item + .is_some_and(|item| matches!(item, SelectedSidebarItem::Filter(_))) + { + self.selected_item = None; + } + shared.unapply_all_filters(registry); + shared + .sync_search(registry, SearchSyncTarget::Filter) + .into_iter() + .for_each(|cmd| _ = actions.try_send_command(&self.cmd_tx, cmd)); + } FilterPanelAction::MoveFilterToValue(filter_id) => { self.clear_filter_edit_for(filter_id); let was_applied = shared.filters.is_filter_applied(&filter_id); @@ -1114,6 +1140,20 @@ impl FiltersUi { .into_iter() .for_each(|cmd| _ = actions.try_send_command(&self.cmd_tx, cmd)); } + FilterPanelAction::RemoveAllSearchValues => { + self.search_value_edit_state = None; + if self + .selected_item + .is_some_and(|item| matches!(item, SelectedSidebarItem::SearchValue(_))) + { + self.selected_item = None; + } + shared.unapply_all_search_values(registry); + shared + .sync_search(registry, SearchSyncTarget::SearchValue) + .into_iter() + .for_each(|cmd| _ = actions.try_send_command(&self.cmd_tx, cmd)); + } FilterPanelAction::MoveValueToFilter(value_id) => { self.clear_search_value_edit_for(value_id); let was_applied = shared.filters.is_search_value_applied(&value_id);