Fully deduplicate shared logic between films and series
This commit is contained in:
parent
c3dfa5b459
commit
c3e2bd0f69
10 changed files with 275 additions and 452 deletions
|
|
@ -8,7 +8,6 @@ use gtk4 :: pango :: Weight :: * ;
|
|||
use gtk4 :: prelude :: * ;
|
||||
use std :: cell :: * ;
|
||||
use std :: iter :: * ;
|
||||
use std :: path :: Path ;
|
||||
use std :: path :: PathBuf ;
|
||||
|
||||
use crate :: ui :: collatable_container :: * ;
|
||||
|
|
@ -16,210 +15,120 @@ use crate :: ui :: component :: * ;
|
|||
|
||||
|
||||
|
||||
pub struct CollatedFilmsGrid {
|
||||
film_widget_pairs : RefCell < Vec < ( FilmOverview , Button ) > > ,
|
||||
grid_widget : FlowBox ,
|
||||
}
|
||||
pub struct CollatedSeriesGrid {
|
||||
series_widget_pairs : RefCell < Vec < ( SeriesOverview , Button ) > > ,
|
||||
pub struct CollatedMediaGrid < A : MediaAdapter > {
|
||||
media_widget_pairs : RefCell < Vec < ( A :: Overview , Button ) > > ,
|
||||
grid_widget : FlowBox ,
|
||||
}
|
||||
|
||||
impl CollatedFilmsGrid {
|
||||
impl < A : MediaAdapter > CollatedMediaGrid <A> {
|
||||
pub fn new () -> Self {
|
||||
let grid_widget = flow_box ! (
|
||||
@ orientation : Horizontal ;
|
||||
@ homogeneous : true ;
|
||||
@ selection_mode : SelectionMode :: None ;
|
||||
) ;
|
||||
let film_widget_pairs = RefCell :: new ( vec ! () ) ;
|
||||
let media_widget_pairs = RefCell :: new ( vec ! () ) ;
|
||||
|
||||
Self { film_widget_pairs , grid_widget }
|
||||
Self { media_widget_pairs , grid_widget }
|
||||
}
|
||||
|
||||
pub async fn set_films ( & self , films : Vec <FilmOverview> , sorting : FilmsSorting ) {
|
||||
pub async fn set_media ( & self , media : Vec < A :: Overview > , sorting : A :: Sorting ) {
|
||||
// TODO: Check if we should use `MainContext :: invoke_local` here
|
||||
|
||||
let mut widgets = Vec :: new () ;
|
||||
for film in films . as_slice () {
|
||||
widgets . push ( create_film_entry (film) . await ) ;
|
||||
for media in media . as_slice () {
|
||||
widgets . push ( create_media_entry (media) . await ) ;
|
||||
}
|
||||
self . film_widget_pairs . replace ( zip ( films , widgets ) . collect () ) ;
|
||||
self . media_widget_pairs . replace ( zip ( media , widgets ) . collect () ) ;
|
||||
|
||||
for ( _ , film_widget ) in self . sort_film_widget_pairs (sorting) {
|
||||
self . grid_widget . append ( & film_widget ) ;
|
||||
for ( _ , widget ) in self . sort_media_widget_pairs (sorting) {
|
||||
self . grid_widget . append ( & widget ) ;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sorting ( & self , sorting : FilmsSorting ) {
|
||||
pub fn set_sorting ( & self , sorting : A :: Sorting ) {
|
||||
self . grid_widget . remove_all () ;
|
||||
|
||||
for ( _ , film_widget ) in self . sort_film_widget_pairs (sorting) {
|
||||
self . grid_widget . append ( & film_widget ) ;
|
||||
for ( _ , widget ) in self . sort_media_widget_pairs (sorting) {
|
||||
self . grid_widget . append ( & widget ) ;
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_film_widget_pairs ( & self , sorting : FilmsSorting ) -> Vec < ( FilmOverview , Button ) > {
|
||||
fn sort_media_widget_pairs ( & self , sorting : A :: Sorting ) -> Vec < ( A :: Overview , Button ) > {
|
||||
let mut sorted = Vec :: from (
|
||||
self . film_widget_pairs . borrow () . as_slice () ) ;
|
||||
|
||||
sorted . sort_by ( | ( film_1 , _ ) , ( film_2 , _ ) | 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 ) ,
|
||||
} ) ;
|
||||
if sorting . direction == SortingDirection :: Descending { sorted . reverse () }
|
||||
|
||||
// See it, say it, ...
|
||||
sorted
|
||||
}
|
||||
}
|
||||
impl CollatedSeriesGrid {
|
||||
pub fn new () -> Self {
|
||||
let grid_widget = flow_box ! (
|
||||
@ orientation : Horizontal ;
|
||||
@ homogeneous : true ;
|
||||
@ selection_mode : SelectionMode :: None ;
|
||||
self . media_widget_pairs . borrow () . as_slice () ,
|
||||
) ;
|
||||
let series_widget_pairs = RefCell :: new ( vec ! () ) ;
|
||||
|
||||
Self { series_widget_pairs , grid_widget }
|
||||
}
|
||||
|
||||
pub async fn set_series ( & self , series : Vec <SeriesOverview> , sorting : SeriesSorting ) {
|
||||
let mut widgets = Vec :: new () ;
|
||||
for series in series . as_slice () {
|
||||
widgets . push ( create_series_entry (series) . await ) ;
|
||||
}
|
||||
self . series_widget_pairs . replace ( zip ( series , widgets ) . collect () ) ;
|
||||
|
||||
for ( _ , series_widget ) in self . sort_series_widget_pairs (sorting) {
|
||||
self . grid_widget . append ( & series_widget ) ;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sorting ( & self , sorting : SeriesSorting ) {
|
||||
self . grid_widget . remove_all () ;
|
||||
|
||||
for ( _ , series_widget ) in self . sort_series_widget_pairs (sorting) {
|
||||
self . grid_widget . append ( & series_widget ) ;
|
||||
}
|
||||
}
|
||||
|
||||
fn sort_series_widget_pairs ( & self , sorting : SeriesSorting ) -> Vec < ( SeriesOverview , Button ) > {
|
||||
let mut sorted = Vec :: from (
|
||||
self . series_widget_pairs . borrow () . as_slice () ) ;
|
||||
|
||||
sorted . sort_by ( | ( series_1 , _ ) , ( series_2 , _ ) | 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 ) ,
|
||||
} ) ;
|
||||
if sorting . direction == SortingDirection :: Descending { sorted . reverse () }
|
||||
sorted . sort_by ( | ( media_1 , _ ) , ( media_2 , _ ) |
|
||||
A :: compare_by ( media_1 , media_2 , sorting ) ,
|
||||
) ;
|
||||
|
||||
// See it, say it, ...
|
||||
sorted
|
||||
}
|
||||
}
|
||||
|
||||
impl Component <FlowBox> for CollatedFilmsGrid {
|
||||
fn get_widget ( & self ) -> & FlowBox { & self . grid_widget }
|
||||
}
|
||||
impl Component <FlowBox> for CollatedSeriesGrid {
|
||||
impl < A : MediaAdapter > Component for CollatedMediaGrid <A> {
|
||||
fn get_widget ( & self ) -> & FlowBox { & self . grid_widget }
|
||||
}
|
||||
|
||||
async fn create_film_entry ( film : & FilmOverview ) -> Button {
|
||||
async fn create_media_entry < M : MediaOverview > ( media : & M ) -> Button {
|
||||
button ! (
|
||||
@ css_classes : & [ "flat" , "open-collection-item-button" ] ;
|
||||
@ connect_clicked : |_| todo ! () ;
|
||||
& create_collection_item (
|
||||
film . name . as_str () ,
|
||||
film . original_name . as_deref () ,
|
||||
film . poster_file_path . as_deref () ,
|
||||
& create_film_details (film) ,
|
||||
) . await ,
|
||||
)
|
||||
}
|
||||
async fn create_series_entry ( series : & SeriesOverview ) -> Button {
|
||||
button ! (
|
||||
@ css_classes : & [ "flat" , "open-collection-item-button" ] ;
|
||||
@ connect_clicked : |_| todo ! () ;
|
||||
& create_collection_item (
|
||||
series . name . as_str () ,
|
||||
series . original_name . as_deref () ,
|
||||
series . poster_file_path . as_deref () ,
|
||||
& create_series_details (series) ,
|
||||
) . await ,
|
||||
)
|
||||
}
|
||||
& g_box ! (
|
||||
@ option_children ;
|
||||
@ orientation : Vertical ;
|
||||
@ margin_top : 20 ;
|
||||
@ margin_bottom : 20 ;
|
||||
|
||||
async fn create_collection_item (
|
||||
name : & str ,
|
||||
original_name : Option < & str > ,
|
||||
poster_file_path : Option < & Path > ,
|
||||
details_widget : & Box ,
|
||||
) -> Box {
|
||||
g_box ! (
|
||||
@ option_children ;
|
||||
@ orientation : Vertical ;
|
||||
@ margin_top : 20 ;
|
||||
@ margin_bottom : 20 ;
|
||||
match media . get_poster_file_path () {
|
||||
Some (poster_file_path) => {
|
||||
let poster_file_path = PathBuf :: from (poster_file_path) ; // God forbid `Path` would work with `clone ! ()`
|
||||
let poster_texture = spawn_blocking ( move ||
|
||||
Texture :: from_filename (poster_file_path) . unwrap ()
|
||||
) . await . unwrap () ;
|
||||
Some ( image ! (
|
||||
@ paintable : & poster_texture ;
|
||||
@ width_request : 300 ;
|
||||
@ height_request : 300 ;
|
||||
@ margin_bottom : 10 ;
|
||||
) )
|
||||
} ,
|
||||
None => None ,
|
||||
} . as_ref () ,
|
||||
|
||||
match poster_file_path {
|
||||
Some (poster_file_path) => {
|
||||
let poster_file_path = PathBuf :: from (poster_file_path) ; // God forbid `Path` would work with `clone ! ()`
|
||||
let poster_texture = spawn_blocking ( move ||
|
||||
Texture :: from_filename (poster_file_path) . unwrap ()
|
||||
) . await . unwrap () ;
|
||||
Some ( image ! (
|
||||
@ paintable : & poster_texture ;
|
||||
@ width_request : 300 ;
|
||||
@ height_request : 300 ;
|
||||
@ margin_bottom : 10 ;
|
||||
) )
|
||||
} ,
|
||||
None => None ,
|
||||
} . as_ref () ,
|
||||
|
||||
Some ( & label ! (
|
||||
@ justify : Justification :: Center ;
|
||||
@ wrap : true ;
|
||||
@ max_width_chars : 1 ; // Not the actual limit, used instead to wrap more aggressively
|
||||
@ attributes : & pango_attributes ! ( @ scale : SCALE_LARGE ; @ weight : Bold ; ) ;
|
||||
name ,
|
||||
) ) ,
|
||||
|
||||
match original_name {
|
||||
Some (original_name) => Some ( label ! (
|
||||
Some ( & label ! (
|
||||
@ justify : Justification :: Center ;
|
||||
@ wrap : true ;
|
||||
@ max_width_chars : 1 ; // Not the actual limit, used instead to wrap more aggressively
|
||||
original_name ,
|
||||
@ attributes : & pango_attributes ! ( @ scale : SCALE_LARGE ; @ weight : Bold ; ) ;
|
||||
media . get_name () . as_str () ,
|
||||
) ) ,
|
||||
None => None ,
|
||||
} . as_ref () ,
|
||||
|
||||
Some (details_widget) ,
|
||||
)
|
||||
}
|
||||
match media . get_original_name () {
|
||||
Some (original_name) => Some ( label ! (
|
||||
@ justify : Justification :: Center ;
|
||||
@ wrap : true ;
|
||||
@ max_width_chars : 1 ; // Not the actual limit, used instead to wrap more aggressively
|
||||
original_name . as_str () ,
|
||||
) ) ,
|
||||
None => None ,
|
||||
} . as_ref () ,
|
||||
|
||||
fn create_film_details ( film : & FilmOverview ) -> Box {
|
||||
g_box ! (
|
||||
@ orientation : Horizontal ;
|
||||
@ halign : Center ;
|
||||
@ spacing : 20 ;
|
||||
& label ! ( film . release_date . as_str () ) ,
|
||||
& label ! ( format ! ( "{}m" , film . runtime_minutes ) . as_str () ) ,
|
||||
)
|
||||
}
|
||||
fn create_series_details ( series : & SeriesOverview ) -> Box {
|
||||
g_box ! (
|
||||
@ orientation : Horizontal ;
|
||||
@ halign : Center ;
|
||||
@ spacing : 20 ;
|
||||
& label ! ( series . first_release_date . as_str () ) ,
|
||||
Some ( & g_box ! (
|
||||
@ option_children ;
|
||||
@ orientation : Horizontal ;
|
||||
@ halign : Center ;
|
||||
@ spacing : 20 ;
|
||||
Some ( & label ! ( media . get_release_date () . as_str () ) ) ,
|
||||
match media . get_runtime_minutes () {
|
||||
Some (runtime_minutes) => Some (
|
||||
label ! ( format ! ( "{}m" , runtime_minutes ) . as_str () ) ,
|
||||
) ,
|
||||
None => None ,
|
||||
} . as_ref () ,
|
||||
) ) ,
|
||||
) ,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue