From f2b03265bac68bf2a0955f91ac410faaf7ff5aff Mon Sep 17 00:00:00 2001 From: Halit Can Date: Wed, 15 Jul 2026 16:17:43 +0300 Subject: [PATCH] feat(iced): add filter instant preview --- hcie-iced-app/crates/hcie-iced-gui/src/app.rs | 43 +++++++++++++++++++ .../crates/hcie-iced-gui/src/dock/view.rs | 2 +- .../hcie-iced-gui/src/panels/filters.rs | 13 ++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/hcie-iced-app/crates/hcie-iced-gui/src/app.rs b/hcie-iced-app/crates/hcie-iced-gui/src/app.rs index 9fb6ab3..7aad2c7 100644 --- a/hcie-iced-app/crates/hcie-iced-gui/src/app.rs +++ b/hcie-iced-app/crates/hcie-iced-gui/src/app.rs @@ -350,6 +350,9 @@ pub enum Message { FilterApply, FilterReset, FilterParamChanged(String, serde_json::Value), + FilterPreviewToggle(bool), + FilterPreviewApply, + FilterPreviewRestore, // ── Dialogs ───────────────────────────────────────── DialogOpen(ActiveDialog), @@ -1768,6 +1771,46 @@ impl HcieIcedApp { self.filter_params = serde_json::json!({}); } + Message::FilterPreviewToggle(enabled) => { + self.filter_preview_active = enabled; + if enabled { + // Start a new preview snapshot so restore can undo it + self.documents[self.active_doc].engine.filter_preview_begin(); + // Apply the current filter with current params to show live preview + if let Some(filter) = self.selected_filter { + let filter_id = filter_type_to_id(filter); + self.documents[self.active_doc].engine.apply_filter_preview(filter_id, self.filter_params.clone()); + self.refresh_composite_if_needed(); + return Task::perform(async {}, |_| Message::CompositeRefresh); + } + } else { + // Restore original pixels + self.documents[self.active_doc].engine.filter_preview_restore(); + self.refresh_composite_if_needed(); + return Task::perform(async {}, |_| Message::CompositeRefresh); + } + } + + Message::FilterPreviewApply => { + if let Some(filter) = self.selected_filter { + let filter_id = filter_type_to_id(filter); + self.documents[self.active_doc].engine.apply_filter(filter_id, self.filter_params.clone()); + self.refresh_composite_if_needed(); + self.filter_preview_active = false; + self.selected_filter = None; + self.filter_params = serde_json::json!({}); + return Task::perform(async {}, |_| Message::CompositeRefresh); + } + } + + Message::FilterPreviewRestore => { + if self.filter_preview_active { + self.documents[self.active_doc].engine.filter_preview_restore(); + self.refresh_composite_if_needed(); + return Task::perform(async {}, |_| Message::CompositeRefresh); + } + } + // ── Dialogs ─────────────────────────────────── Message::DialogOpen(dialog) => { self.active_dialog = dialog; diff --git a/hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs b/hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs index b30fa04..a433f1c 100644 --- a/hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs +++ b/hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs @@ -66,7 +66,7 @@ pub fn dock_view<'a>( ) } PaneType::Filters => { - crate::panels::filters::view(app.selected_filter, &app.filter_params, colors) + crate::panels::filters::view(app.selected_filter, &app.filter_params, app.filter_preview_active, colors) } PaneType::ColorPicker => { crate::color_picker::view(&app.fg_color, &app.bg_color, &app.recent_colors, app.color_tab) diff --git a/hcie-iced-app/crates/hcie-iced-gui/src/panels/filters.rs b/hcie-iced-app/crates/hcie-iced-gui/src/panels/filters.rs index 4bf040f..a62d6bd 100644 --- a/hcie-iced-app/crates/hcie-iced-gui/src/panels/filters.rs +++ b/hcie-iced-app/crates/hcie-iced-gui/src/panels/filters.rs @@ -5,7 +5,7 @@ use crate::app::Message; use crate::panels::styles; use crate::theme::ThemeColors; use hcie_engine_api::FilterType; -use iced::widget::{button, column, container, horizontal_rule, row, scrollable, text}; +use iced::widget::{button, checkbox, column, container, horizontal_rule, row, scrollable, text}; use iced::{Element, Length}; /// Filter category with its filters. @@ -88,11 +88,12 @@ const FILTER_CATEGORIES: &[FilterCategory] = &[ /// Build the filters panel. /// /// Shows the filter category list on top, and when a filter is selected, -/// displays its parameter editing panel below the list. The Apply/Reset -/// buttons are always at the bottom. +/// displays its parameter editing panel below the list. The preview checkbox, +/// Apply, and Reset buttons are always at the bottom. pub fn view( selected_filter: Option, filter_params: &serde_json::Value, + filter_preview_active: bool, colors: ThemeColors, ) -> Element<'static, Message> { let mut filter_list = column![].spacing(2); @@ -145,7 +146,11 @@ pub fn view( .on_press(Message::FilterReset) .padding([6, 12]); - let buttons = row![apply_btn, reset_btn].spacing(8); + let preview_check = checkbox("Preview", filter_preview_active) + .on_toggle(Message::FilterPreviewToggle) + .size(11); + + let buttons = row![preview_check, apply_btn, reset_btn].spacing(8); // When a filter is selected, show its parameter panel below the list. let param_section = match selected_filter {