zoodex/src/ui/collatable_container/mod.rs

202 lines
5.3 KiB
Rust

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<P: MediaProperty>: 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<FilmProperty> 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<SeriesProperty> 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<A: MediaAdapter> {
collated_grid: &'static CollatedMediaGrid<A>,
widget: Box,
}
impl<A: MediaAdapter> CollatableMediaContainer<A> {
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::<A>(|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<A::Overview>) {
self
.collated_grid
.set_media(media, A::Sorting::default())
.await;
}
}
pub trait MediaAdapter: 'static {
type Overview: MediaOverview;
type Sorting: MediaSorting<Self::Property>;
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<A: MediaAdapter> Component for CollatableMediaContainer<A> {
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"),
])
}
}