diff --git a/src/collection.rs b/src/collection.rs index f24638b..caedf71 100644 --- a/src/collection.rs +++ b/src/collection.rs @@ -17,7 +17,7 @@ pub struct Collection { pub video_file_path : Option , } -pub struct Series { +# [ derive (Clone) ] pub struct Series { pub uuid : String , pub name : String , pub original_name : Option , diff --git a/src/main.rs b/src/main.rs index 7aa8678..cebe0aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ fn main () -> ExitCode { } fn on_activate ( app : & Application ) { - let mut ui = UI :: new (app) ; + let ui = UI :: new (app) ; let collection_handle = spawn_blocking ( || read_collection_file () . unwrap () diff --git a/src/ui/collatable_container.rs b/src/ui/collatable_container.rs index d369474..15c54a3 100644 --- a/src/ui/collatable_container.rs +++ b/src/ui/collatable_container.rs @@ -1,4 +1,4 @@ -use { gtk4 :: { * , prelude :: * } , std :: cell :: * } ; +use gtk4 :: { * , prelude :: * } ; use crate :: { collection :: * , @@ -8,63 +8,55 @@ use crate :: { -pub enum FilmsSortedBy { Name , ReleaseDate , Runtime } -pub enum SeriesSortedBy { Name , FirstReleaseDate } - pub struct CollatableFilmsContainer { - films : & 'static RefCell < Vec > , - flow_box : & 'static CollatedFilmsGrid , + collated_grid : & 'static CollatedFilmsGrid , widget : gtk4 :: Box , } pub struct CollatableSeriesContainer { - series : Vec , - flow_box : CollatedSeriesGrid , + collated_grid : & 'static CollatedSeriesGrid , widget : gtk4 :: Box , } impl CollatableFilmsContainer { pub fn new ( films : Vec ) -> Self { - let films = leak ( RefCell :: new (films) ) ; - let flow_box = leak ( CollatedFilmsGrid :: new ( films . borrow () . as_slice () ) ) ; + let collated_grid = leak ( CollatedFilmsGrid :: new ( films , FilmsSortedBy :: Name ) ) ; let widget = create_vertical_box ! ( & create_film_collection_menu ( |sorted_by| { - flow_box . set_films ( films . borrow () . as_slice () ) ; + collated_grid . set_sorting (sorted_by) ; } ) , - & create_collection_scrolled_window ( flow_box . get_widget () ) , + & create_collection_scrolled_window ( collated_grid . get_widget () ) , ) ; - Self { films , flow_box , widget } + Self { collated_grid , widget } } pub fn set_films ( & self , films : Vec ) { - * self . films . borrow_mut () = films ; - self . flow_box . set_films ( self . films . borrow () . as_slice () ) ; + self . collated_grid . set_films ( films , FilmsSortedBy :: Name ) ; } pub fn get_widget ( & self ) -> & gtk4 :: Box { & self . widget } } impl CollatableSeriesContainer { pub fn new ( series : Vec ) -> Self { - let flow_box = CollatedSeriesGrid :: new ( series . as_slice () ) ; + let collated_grid = leak ( CollatedSeriesGrid :: new ( series , SeriesSortedBy :: Name ) ) ; let widget = create_vertical_box ! ( & create_series_collection_menu ( |sorted_by| { - // TODO + collated_grid . set_sorting (sorted_by) ; } ) , - & create_collection_scrolled_window ( flow_box . get_widget () ) , + & create_collection_scrolled_window ( collated_grid . get_widget () ) , ) ; - Self { series, flow_box , widget } + Self { collated_grid , widget } } - pub fn set_series ( & mut self , series : Vec ) { - self . series = series ; - self . flow_box . set_series ( self . series . as_slice () ) ; + pub fn set_series ( & self , series : Vec ) { + self . collated_grid . set_series ( series , SeriesSortedBy :: Name ) ; } pub fn get_widget ( & self ) -> & gtk4 :: Box { & self . widget } } -fn create_collection_scrolled_window ( child : & impl IsA ) -> ScrolledWindow { +fn create_collection_scrolled_window ( child : & FlowBox ) -> ScrolledWindow { ScrolledWindow :: builder () . child ( & create_vertical_filler_container (child) ) . propagate_natural_height (true) diff --git a/src/ui/collated_grid.rs b/src/ui/collated_grid.rs index c12dde8..4be14c6 100644 --- a/src/ui/collated_grid.rs +++ b/src/ui/collated_grid.rs @@ -4,66 +4,145 @@ use { Align :: * , Orientation :: * , gdk :: Texture , - glib :: * , prelude :: * , } , - std :: path :: * , + std :: { cell :: * , iter :: * , path :: * } , } ; use crate :: collection :: * ; -pub struct CollatedFilmsGrid { widget : FlowBox } -pub struct CollatedSeriesGrid { widget : FlowBox } +pub enum FilmsSortedBy { Name , ReleaseDate , Runtime } +pub enum SeriesSortedBy { Name , FirstReleaseDate } + +pub struct CollatedFilmsGrid { + film_widget_pairs : RefCell < Vec < ( Film , Box ) > > , + grid_widget : FlowBox , +} +pub struct CollatedSeriesGrid { + series_widget_pairs : RefCell < Vec < ( Series , Box ) > > , + grid_widget : FlowBox , +} impl CollatedFilmsGrid { - pub fn new ( films : & [Film] ) -> Self { - let widget = create_collection_flow_box () ; + pub fn new ( films : Vec , sorting : FilmsSortedBy ) -> Self { + let grid_widget = create_flow_box () ; + let film_widgets = films . iter () + . map (create_film_entry) + . collect :: < Vec <_> > () ; + let film_widget_pairs = RefCell :: new ( zip ( films , film_widgets ) + . collect :: < Vec <_> > () ) ; - for film in films { - widget . append ( & create_film_item (film) ) ; + let film_widget_pairs_sorted = sort_film_widget_pairs ( + film_widget_pairs . borrow () . as_slice () , sorting ) ; + for ( _ , film_widget ) in & film_widget_pairs_sorted { + grid_widget . append (film_widget) ; } - Self { widget } + Self { film_widget_pairs , grid_widget } } - pub fn set_films ( & self , films : & [Film] ) { - self . widget . remove_all () ; - for film in films { - let widget = self . widget . clone () ; - let film = film . clone () ; + pub fn set_films ( & self , films : Vec , sorting : FilmsSortedBy ) { + let film_widgets = films . iter () + . map (create_film_entry) + . collect :: < Vec <_> > () ; + * self . film_widget_pairs . borrow_mut () = zip ( films , film_widgets ) + . collect :: < Vec <_> > () ; - spawn_future_local ( async move { - widget . append ( & create_film_item ( & film ) ) ; - } ) ; + let film_widget_pairs_sorted = sort_film_widget_pairs ( + self . film_widget_pairs . borrow () . as_slice () , sorting ) ; + for ( _ , film_widget ) in & film_widget_pairs_sorted { + self . grid_widget . append (film_widget) ; } } - pub fn get_widget ( & self ) -> & FlowBox { & self . widget } + pub fn set_sorting ( & self , sorting : FilmsSortedBy ) { + self . grid_widget . remove_all () ; + + let film_widget_pairs_sorted = sort_film_widget_pairs ( + self . film_widget_pairs . borrow () . as_slice () , sorting ) ; + for ( _ , film_widget ) in & film_widget_pairs_sorted { + self . grid_widget . append (film_widget) ; + } + } + + pub fn get_widget ( & self ) -> & FlowBox { & self . grid_widget } } impl CollatedSeriesGrid { - pub fn new ( series : & [Series] ) -> Self { - let widget = create_collection_flow_box () ; + pub fn new ( series : Vec , sorting : SeriesSortedBy ) -> Self { + let grid_widget = create_flow_box () ; + let series_widgets = series . iter () + . map (create_series_entry) + . collect :: < Vec <_> > () ; + let series_widget_pairs = RefCell :: new ( zip ( series , series_widgets ) + . collect :: < Vec <_> > () ) ; - for series in series { - widget . append ( & create_series_item (series) ) ; + let series_widget_pairs_sorted = sort_series_widget_pairs ( + series_widget_pairs . borrow () . as_slice () , sorting ) ; + for ( _ , series_widget ) in & series_widget_pairs_sorted { + grid_widget . append (series_widget) ; } - Self { widget } + Self { series_widget_pairs , grid_widget } } - pub fn set_series ( & self , series : & [Series] ) { - self . widget . remove_all () ; - for series in series { - self . widget . append ( & create_series_item (series) ) ; + pub fn set_series ( & self , series : Vec , sorting : SeriesSortedBy ) { + let series_widgets = series . iter () + . map (create_series_entry) + . collect :: < Vec <_> > () ; + * self . series_widget_pairs . borrow_mut () = zip ( series , series_widgets ) + . collect :: < Vec <_> > () ; + + let series_widget_pairs_sorted = sort_series_widget_pairs ( + self . series_widget_pairs . borrow () . as_slice () , sorting ) ; + for ( _ , series_widget ) in & series_widget_pairs_sorted { + self . grid_widget . append (series_widget) ; } } - pub fn get_widget ( & self ) -> & FlowBox { & self . widget } + pub fn set_sorting ( & self , sorting : SeriesSortedBy ) { + self . grid_widget . remove_all () ; + + let series_widget_pairs_sorted = sort_series_widget_pairs ( + self . series_widget_pairs . borrow () . as_slice () , sorting ) ; + for ( _ , series_widget ) in & series_widget_pairs_sorted { + self . grid_widget . append (series_widget) ; + } + } + + pub fn get_widget ( & self ) -> & FlowBox { & self . grid_widget } } -fn create_collection_flow_box () -> FlowBox { +fn sort_film_widget_pairs ( + film_widget_pairs : & [ ( Film , Box ) ] , + sorting : FilmsSortedBy , +) -> Vec < ( Film , Box ) > { + let mut sorted = Vec :: from (film_widget_pairs) ; + + sorted . sort_by ( | ( film_1 , _ ) , ( film_2 , _ ) | match sorting { + FilmsSortedBy :: Name => film_1 . name . cmp ( & film_2 . name ) , + FilmsSortedBy :: ReleaseDate => film_1 . release_date . cmp ( & film_2 . release_date ) , + FilmsSortedBy :: Runtime => film_1 . runtime_minutes . cmp ( & film_2 . runtime_minutes ) , + } ) ; + + sorted +} +fn sort_series_widget_pairs ( + series_widget_pairs : & [ ( Series, Box ) ] , + sorting : SeriesSortedBy , +) -> Vec < ( Series , Box ) > { + let mut sorted = Vec :: from (series_widget_pairs) ; + + sorted . sort_by ( | ( series_1 , _ ) , ( series_2 , _ ) | match sorting { + SeriesSortedBy :: Name => series_1 . name . cmp ( & series_2 . name ) , + SeriesSortedBy :: FirstReleaseDate => todo ! () , + } ) ; + + sorted +} + +fn create_flow_box () -> FlowBox { FlowBox :: builder () . orientation (Horizontal) . homogeneous (true) @@ -71,7 +150,7 @@ fn create_collection_flow_box () -> FlowBox { . build () } -pub fn create_film_item ( film : & Film ) -> gtk4 :: Box { +pub fn create_film_entry ( film : & Film ) -> Box { create_collection_item ( film . name . as_str () , film . original_name . as_deref () , @@ -79,7 +158,7 @@ pub fn create_film_item ( film : & Film ) -> gtk4 :: Box { & create_film_details (film) , ) } -pub fn create_series_item ( series : & Series ) -> gtk4 :: Box { +pub fn create_series_entry ( series : & Series ) -> Box { create_collection_item ( series . name . as_str () , series . original_name . as_deref () , @@ -92,9 +171,9 @@ fn create_collection_item ( name : & str , original_name : Option < & str > , poster_file_path : Option < & Path > , - details_widget : & gtk4 :: Box , -) -> gtk4 :: Box { - let container = gtk4 :: Box :: builder () + details_widget : & Box , +) -> Box { + let container = Box :: builder () . orientation (Vertical) . margin_top (20) . margin_bottom (20) @@ -138,8 +217,8 @@ fn create_collection_item ( container } -fn create_film_details ( film : & Film ) -> gtk4 :: Box { - let container = gtk4 :: Box :: builder () +fn create_film_details ( film : & Film ) -> Box { + let container = Box :: builder () . orientation (Horizontal) . halign (Center) . spacing (20) @@ -154,8 +233,8 @@ fn create_film_details ( film : & Film ) -> gtk4 :: Box { container } -fn create_series_details ( series : & Series ) -> gtk4 :: Box { - let container = gtk4 :: Box :: builder () +fn create_series_details ( series : & Series ) -> Box { + let container = Box :: builder () . orientation (Horizontal) . halign (Center) . spacing (20) diff --git a/src/ui/collation_menu.rs b/src/ui/collation_menu.rs index 54c70a6..f948e63 100644 --- a/src/ui/collation_menu.rs +++ b/src/ui/collation_menu.rs @@ -8,12 +8,12 @@ use { libadwaita :: * , } ; -use crate :: ui :: { collatable_container :: * , utility :: * } ; +use crate :: ui :: { collated_grid :: * , utility :: * } ; -pub fn create_film_collection_menu ( on_sort : F ) -> gtk4 :: Box -where F : Fn (FilmsSortedBy) + 'static { +pub fn create_film_collection_menu < F : Fn (FilmsSortedBy) + 'static > ( on_sort : F ) +-> gtk4 :: Box { let container = gtk4 :: Box :: builder () . orientation (Horizontal) . halign (Center) @@ -26,8 +26,8 @@ where F : Fn (FilmsSortedBy) + 'static { container } - -pub fn create_series_collection_menu ( on_sort : fn (SeriesSortedBy) ) -> gtk4 :: Box { +pub fn create_series_collection_menu < F : Fn (SeriesSortedBy) + 'static > ( on_sort : F ) +-> gtk4 :: Box { let container = gtk4 :: Box :: builder () . orientation (Horizontal) . halign (Center) @@ -48,8 +48,8 @@ fn create_sort_button ( sort_menu : & Popover ) -> SplitButton { . build () } -fn create_films_sort_menu ( on_sort : F ) -> Popover -where F : Fn (FilmsSortedBy) + 'static { +fn create_films_sort_menu < F : Fn (FilmsSortedBy) + 'static > ( on_sort : F ) +-> Popover { let container = ListBox :: new () ; container . append ( & create_sort_menu_entry ( "Name" , false , true ) ) ; @@ -70,8 +70,8 @@ where F : Fn (FilmsSortedBy) + 'static { . css_classes ( ["menu"] ) . build () } - -fn create_series_sort_menu ( on_sort : fn (SeriesSortedBy) ) -> Popover { +fn create_series_sort_menu < F : Fn (SeriesSortedBy) + 'static > ( on_sort : F ) +-> Popover { let container = ListBox :: new () ; container . append ( & create_sort_menu_entry ( "Name" , false , true ) ) ; @@ -91,7 +91,9 @@ fn create_series_sort_menu ( on_sort : fn (SeriesSortedBy) ) -> Popover { . build () } -fn create_sort_menu_entry ( label : & str , reverse : bool , selected : bool ) -> ListBoxRow { +fn create_sort_menu_entry ( + label : & str , reverse : bool , selected : bool , +) -> ListBoxRow { let icon = match reverse { false => Image :: from_icon_name ("view-sort-ascending-symbolic") , true => Image :: from_icon_name ("view-sort-descending-symbolic") , diff --git a/src/ui/mod.rs b/src/ui/mod.rs index d80ddf8..4a9b288 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -35,7 +35,7 @@ impl UI { pub fn show_window ( & self ) { self . window . set_visible (true) } - pub fn render_collection ( & mut self , collection : Collection ) { + pub fn render_collection ( & self , collection : Collection ) { self . films_container . set_films ( collection . films ) ; self . series_container . set_series ( collection . series ) ; }