mod collated_grid; mod collation_menu; use std::cmp::Ordering; use std::fmt::Debug; use gtk4::prelude::{BoxExt, OrientableExt}; use gtk4::{Box, Orientation, ScrolledWindow}; use relm4_macros::view; use crate::data_manager::{FilmOverview, MediaOverview, SeriesOverview}; use crate::ui::collatable_container::collated_grid::CollatedMediaGrid; use crate::ui::collatable_container::collation_menu::MediaCollationMenu; use crate::ui::component::Component; use crate::ui::utility::{vertical_filler, view_expr}; use crate::utility::leak; pub trait MediaSorting: Clone + Copy + Debug + Default { fn new(property: P, direction: SortingDirection) -> Self; fn get_property(&self) -> P; fn get_direction(&self) -> SortingDirection; } pub trait MediaProperty: Clone + Copy + Debug + PartialEq {} #[derive(Clone, Copy, Debug, Default, PartialEq)] pub enum FilmProperty { #[default] Name, ReleaseDate, Runtime, } #[derive(Clone, Copy, Debug, Default, PartialEq)] pub enum SeriesProperty { #[default] Name, FirstReleaseDate, } #[derive(Clone, Copy, Debug, Default, PartialEq)] pub enum SortingDirection { #[default] Ascending, Descending, } #[derive(Clone, Copy, Debug, Default)] pub struct FilmsSorting { property: FilmProperty, direction: SortingDirection, } #[derive(Clone, Copy, Debug, Default)] pub struct SeriesSorting { property: SeriesProperty, direction: SortingDirection, } impl MediaSorting for FilmsSorting { fn new(property: FilmProperty, direction: SortingDirection) -> Self { Self { property, direction, } } fn get_property(&self) -> FilmProperty { self.property } fn get_direction(&self) -> SortingDirection { self.direction } } impl MediaSorting for SeriesSorting { fn new(property: SeriesProperty, direction: SortingDirection) -> Self { Self { property, direction, } } fn get_property(&self) -> SeriesProperty { self.property } fn get_direction(&self) -> SortingDirection { self.direction } } impl MediaProperty for FilmProperty {} impl MediaProperty for SeriesProperty {} pub struct CollatableMediaContainer { collated_grid: &'static CollatedMediaGrid, widget: Box, } impl CollatableMediaContainer { pub fn new(on_media_selected: impl Fn(A::Overview) + 'static) -> Self { let collated_grid = leak(CollatedMediaGrid::new(on_media_selected)); let collation_menu = MediaCollationMenu::new::(|sorting| collated_grid.set_sorting(sorting)); view! { widget = gtk4::Box { set_orientation: Orientation::Vertical, append: collation_menu.get_widget(), append: &view_expr! { ScrolledWindow { set_propagate_natural_height: true, set_child: Some(&vertical_filler(collated_grid.get_widget())), } }, } } Self { collated_grid, widget, } } pub async fn set_media(&self, media: Vec) { self .collated_grid .set_media(media, A::Sorting::default()) .await; } } pub trait MediaAdapter: 'static { type Overview: MediaOverview; type Sorting: MediaSorting; type Property: MediaProperty; fn compare_by( media_1: &Self::Overview, media_2: &Self::Overview, sorting: Self::Sorting, ) -> Ordering; fn get_property_descriptions() -> &'static [(Self::Property, &'static str)]; } impl Component for CollatableMediaContainer { fn get_widget(&self) -> &Box { &self.widget } } pub struct FilmsAdapter {} pub struct SeriesAdapter {} impl MediaAdapter for FilmsAdapter { type Overview = FilmOverview; type Sorting = FilmsSorting; type Property = FilmProperty; fn compare_by(film_1: &FilmOverview, film_2: &FilmOverview, sorting: FilmsSorting) -> Ordering { let ordering = match sorting.property { FilmProperty::Name => film_1.name.cmp(&film_2.name), FilmProperty::ReleaseDate => film_1.release_date.cmp(&film_2.release_date), FilmProperty::Runtime => film_1.runtime_minutes.cmp(&film_2.runtime_minutes), }; match sorting.direction { SortingDirection::Ascending => ordering, SortingDirection::Descending => ordering.reverse(), } } fn get_property_descriptions() -> &'static [(FilmProperty, &'static str)] { leak([ (FilmProperty::Name, "Name"), (FilmProperty::ReleaseDate, "Release date"), (FilmProperty::Runtime, "Runtime"), ]) } } impl MediaAdapter for SeriesAdapter { type Overview = SeriesOverview; type Sorting = SeriesSorting; type Property = SeriesProperty; fn compare_by( series_1: &SeriesOverview, series_2: &SeriesOverview, sorting: SeriesSorting, ) -> Ordering { let ordering = match sorting.property { SeriesProperty::Name => series_1.name.cmp(&series_2.name), SeriesProperty::FirstReleaseDate => series_1 .first_release_date .cmp(&series_2.first_release_date), }; match sorting.direction { SortingDirection::Ascending => ordering, SortingDirection::Descending => ordering.reverse(), } } fn get_property_descriptions() -> &'static [(SeriesProperty, &'static str)] { leak([ (SeriesProperty::Name, "Name"), (SeriesProperty::FirstReleaseDate, "First release date"), ]) } }