diff --git a/data/eye-closed-symbolic.svg b/data/eye-closed-symbolic.svg new file mode 100644 index 0000000..26417e9 --- /dev/null +++ b/data/eye-closed-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/data/zoodex.gresource.xml b/data/zoodex.gresource.xml index 9d21967..4b79334 100644 --- a/data/zoodex.gresource.xml +++ b/data/zoodex.gresource.xml @@ -2,6 +2,8 @@ application.css + + eye-closed-symbolic.svg eye-outline-filled-symbolic.svg diff --git a/src/ui/components/collatable_media_grid/collatable_film_grid.rs b/src/ui/components/collatable_media_grid/collatable_film_grid.rs index 8da5f15..763d2ff 100644 --- a/src/ui/components/collatable_media_grid/collatable_film_grid.rs +++ b/src/ui/components/collatable_media_grid/collatable_film_grid.rs @@ -41,6 +41,9 @@ impl SimpleComponent for CollatableFilmGrid { FilmCollationMenuOutput::SortBy(sorting, direction) => { FilmGridInput::SortBy(sorting, direction) } + FilmCollationMenuOutput::ApplyWatchedFilter(watched_filter) => { + FilmGridInput::ApplyWatchedFilter(watched_filter) + } }, ); diff --git a/src/ui/components/media_collation_menu/film_collation_menu.rs b/src/ui/components/media_collation_menu/film_collation_menu.rs index be32ef0..7a3e997 100644 --- a/src/ui/components/media_collation_menu/film_collation_menu.rs +++ b/src/ui/components/media_collation_menu/film_collation_menu.rs @@ -1,28 +1,35 @@ use gtk4::prelude::{ - BoxExt, ButtonExt, EntryExt, ListBoxRowExt, ObjectExt, OrientableExt, PopoverExt, WidgetExt, + BoxExt, ButtonExt, EntryExt, ListBoxRowExt, ObjectExt, OrientableExt, PopoverExt, + ToggleButtonExt, WidgetExt, }; -use gtk4::{Align, Button, Entry, Label, ListBox, MenuButton, Orientation, Popover}; +use gtk4::{Align, Button, Entry, Label, ListBox, MenuButton, Orientation, Popover, ToggleButton}; use relm4::{ComponentParts, ComponentSender, SimpleComponent, component}; use crate::ui::components::sorting_popover_entry::sorting_popover_entry; +use crate::ui::filtering::WatchedFilter; use crate::ui::sorting::{FilmsSorting, SortingDirection}; +use crate::ui::widget_extensions::ComponentSenderExt; pub struct FilmCollationMenu { sorted_by: FilmsSorting, sort_direction: SortingDirection, + watched_filter: Option, } #[derive(Debug)] pub enum FilmCollationMenuInput { SortBy(FilmsSorting), ToggleSortOrder, + ToggleWatchedFilter, + ToggleUnwatchedFilter, } #[derive(Debug)] pub enum FilmCollationMenuOutput { SortBy(FilmsSorting, SortingDirection), + ApplyWatchedFilter(Option), } #[component(pub)] @@ -89,12 +96,33 @@ impl SimpleComponent for FilmCollationMenu { }, }, }, + Entry { set_width_request: 230, set_placeholder_text: Some("Search"), set_secondary_icon_name: Some("system-search-symbolic"), set_secondary_icon_sensitive: false, }, + + gtk4::Box { + set_orientation: Orientation::Horizontal, + set_css_classes: &["linked"], + + ToggleButton { + set_icon_name: "eye-outline-filled-symbolic", + #[watch] + #[block_signal(watched_toggled)] + set_active: model.watched_filter == Some(WatchedFilter::OnlyWatched), + connect_toggled => FilmCollationMenuInput::ToggleWatchedFilter @watched_toggled, + }, + ToggleButton { + set_icon_name: "eye-closed-symbolic", + #[watch] + #[block_signal(unwatched_toggled)] + set_active: model.watched_filter == Some(WatchedFilter::OnlyUnwatched), + connect_toggled => FilmCollationMenuInput::ToggleUnwatchedFilter @unwatched_toggled, + }, + }, } } @@ -106,6 +134,7 @@ impl SimpleComponent for FilmCollationMenu { let model = FilmCollationMenu { sorted_by: FilmsSorting::Name, sort_direction: SortingDirection::Ascending, + watched_filter: None, }; let widgets = view_output!(); ComponentParts { model, widgets } @@ -120,7 +149,7 @@ impl SimpleComponent for FilmCollationMenu { FilmCollationMenuInput::SortBy(sorting) => { self.sorted_by = sorting; self.sort_direction = SortingDirection::Ascending; - sender.output_sender().emit(FilmCollationMenuOutput::SortBy( + sender.emit_output(FilmCollationMenuOutput::SortBy( sorting, SortingDirection::Ascending, )); @@ -130,11 +159,37 @@ impl SimpleComponent for FilmCollationMenu { SortingDirection::Ascending => SortingDirection::Descending, SortingDirection::Descending => SortingDirection::Ascending, }; - sender.output_sender().emit(FilmCollationMenuOutput::SortBy( + sender.emit_output(FilmCollationMenuOutput::SortBy( self.sorted_by, self.sort_direction, )); } + FilmCollationMenuInput::ToggleWatchedFilter => { + match self.watched_filter { + Some(WatchedFilter::OnlyWatched) => { + self.watched_filter = None; + } + _ => { + self.watched_filter = Some(WatchedFilter::OnlyWatched); + } + } + sender.emit_output(FilmCollationMenuOutput::ApplyWatchedFilter( + self.watched_filter, + )); + } + FilmCollationMenuInput::ToggleUnwatchedFilter => { + match self.watched_filter { + Some(WatchedFilter::OnlyUnwatched) => { + self.watched_filter = None; + } + _ => { + self.watched_filter = Some(WatchedFilter::OnlyUnwatched); + } + } + sender.emit_output(FilmCollationMenuOutput::ApplyWatchedFilter( + self.watched_filter, + )); + } } } } diff --git a/src/ui/components/media_collation_menu/series_collation_menu.rs b/src/ui/components/media_collation_menu/series_collation_menu.rs index 478b8a7..c998282 100644 --- a/src/ui/components/media_collation_menu/series_collation_menu.rs +++ b/src/ui/components/media_collation_menu/series_collation_menu.rs @@ -6,6 +6,7 @@ use relm4::{ComponentParts, ComponentSender, SimpleComponent, component}; use crate::ui::components::sorting_popover_entry::sorting_popover_entry; use crate::ui::sorting::{SeriesSorting, SortingDirection}; +use crate::ui::widget_extensions::ComponentSenderExt; @@ -118,24 +119,20 @@ impl SimpleComponent for SeriesCollationMenu { SeriesCollationMenuInput::SortBy(sorting) => { self.sorted_by = sorting; self.sort_direction = SortingDirection::Ascending; - sender - .output_sender() - .emit(SeriesCollationMenuOutput::SortBy( - sorting, - SortingDirection::Ascending, - )); + sender.emit_output(SeriesCollationMenuOutput::SortBy( + sorting, + SortingDirection::Ascending, + )); } SeriesCollationMenuInput::ToggleSortOrder => { self.sort_direction = match self.sort_direction { SortingDirection::Ascending => SortingDirection::Descending, SortingDirection::Descending => SortingDirection::Ascending, }; - sender - .output_sender() - .emit(SeriesCollationMenuOutput::SortBy( - self.sorted_by, - self.sort_direction, - )); + sender.emit_output(SeriesCollationMenuOutput::SortBy( + self.sorted_by, + self.sort_direction, + )); } } } diff --git a/src/ui/components/media_details/film_details.rs b/src/ui/components/media_details/film_details.rs index 5914f17..99837d7 100644 --- a/src/ui/components/media_details/film_details.rs +++ b/src/ui/components/media_details/film_details.rs @@ -4,6 +4,7 @@ use gtk4::{Align, Button, IconSize, Image, Justification, Label, Orientation, To use relm4::{Component, ComponentParts, ComponentSender, RelmWidgetExt, component}; use crate::persist::data_manager::{DataManager, DataManagerError}; +use crate::ui::widget_extensions::ComponentSenderExt; use crate::views::overview::FilmOverview; @@ -97,9 +98,7 @@ impl Component for FilmDetails { match message { FilmDetailsInput::WatchedStatusChanged(watched) => { self.film_overview.watched = watched; - sender - .output_sender() - .emit(FilmDetailsOutput::WatchedStatusChanged(watched)); + sender.emit_output(FilmDetailsOutput::WatchedStatusChanged(watched)); sender.oneshot_command(clone!( #[strong(rename_to = uuid)] diff --git a/src/ui/components/media_grid/film_grid.rs b/src/ui/components/media_grid/film_grid.rs index 5397968..c9b8c3a 100644 --- a/src/ui/components/media_grid/film_grid.rs +++ b/src/ui/components/media_grid/film_grid.rs @@ -6,6 +6,7 @@ use relm4::{Component, ComponentParts, ComponentSender, component}; use crate::persist::data_manager::{DataManager, DataManagerError}; use crate::ui::components::media_grid_item::FilmGridItem; use crate::ui::factory_sorting::sort_factory_vec; +use crate::ui::filtering::WatchedFilter; use crate::ui::sorting::{ FilmsSorting, SortingDirection, cmp_films_by_name, cmp_films_by_rel_date, cmp_films_by_runtime, }; @@ -20,6 +21,7 @@ pub struct FilmGrid { #[derive(Debug)] pub enum FilmGridInput { SortBy(FilmsSorting, SortingDirection), + ApplyWatchedFilter(Option), } #[derive(Debug)] @@ -96,6 +98,7 @@ impl Component for FilmGrid { } }); } + FilmGridInput::ApplyWatchedFilter(_watched_filter) => {} } } diff --git a/src/ui/filtering.rs b/src/ui/filtering.rs new file mode 100644 index 0000000..8f00bf5 --- /dev/null +++ b/src/ui/filtering.rs @@ -0,0 +1,5 @@ +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum WatchedFilter { + OnlyWatched, + OnlyUnwatched, +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index c92a42f..31ba8b6 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,5 +1,6 @@ mod components; mod factory_sorting; +mod filtering; mod sorting; mod widget_extensions; diff --git a/src/ui/widget_extensions.rs b/src/ui/widget_extensions.rs index a3405dd..d01fc87 100644 --- a/src/ui/widget_extensions.rs +++ b/src/ui/widget_extensions.rs @@ -3,6 +3,7 @@ use gtk4::prelude::{BoxExt, IsA}; use gtk4::{Label, Widget}; use libadwaita::Dialog; use libadwaita::prelude::AdwDialogExt; +use relm4::{Component, ComponentSender}; @@ -66,3 +67,15 @@ impl AttrListExt for AttrList { self.insert(AttrFontDesc::new(&font_desc)); } } + + + +pub trait ComponentSenderExt { + fn emit_output(&self, message: C::Output); +} + +impl ComponentSenderExt for ComponentSender { + fn emit_output(&self, message: C::Output) { + self.output_sender().emit(message); + } +}