diff --git a/src/ui/collatable_container/collated_grid.rs b/src/ui/collatable_container/collated_grid.rs index 822b8a3..6d2c448 100644 --- a/src/ui/collatable_container/collated_grid.rs +++ b/src/ui/collatable_container/collated_grid.rs @@ -3,6 +3,7 @@ use gtk4 :: Align :: * ; use gtk4 :: Orientation :: * ; use gtk4 :: gdk :: * ; use gtk4 :: gio :: * ; +use gtk4 :: glib :: * ; use gtk4 :: pango :: * ; use gtk4 :: pango :: Weight :: * ; use gtk4 :: prelude :: * ; @@ -15,21 +16,23 @@ use crate :: ui :: component :: * ; -pub struct CollatedMediaGrid < A : MediaAdapter > { +pub struct CollatedMediaGrid < A : MediaAdapter + 'static > { media_widget_pairs : RefCell < Vec < ( A :: Overview , Button ) > > , grid_widget : FlowBox , + on_media_selected : & 'static dyn Fn ( A :: Overview ) , } impl < A : MediaAdapter > CollatedMediaGrid { - pub fn new () -> Self { + pub fn new ( on_media_selected : impl Fn ( A :: Overview ) + 'static ) -> Self { let grid_widget = flow_box ! ( @ orientation : Horizontal ; @ homogeneous : true ; @ selection_mode : SelectionMode :: None ; ) ; let media_widget_pairs = RefCell :: new ( vec ! () ) ; + let on_media_selected = leak (on_media_selected) ; - Self { media_widget_pairs , grid_widget } + Self { media_widget_pairs , grid_widget , on_media_selected } } pub async fn set_media ( & self , media : Vec < A :: Overview > , sorting : A :: Sorting ) { @@ -37,7 +40,7 @@ impl < A : MediaAdapter > CollatedMediaGrid { let mut widgets = Vec :: new () ; for media in media . as_slice () { - widgets . push ( create_media_entry (media) . await ) ; + widgets . push ( self . create_media_entry (media) . await ) ; } self . media_widget_pairs . replace ( zip ( media , widgets ) . collect () ) ; @@ -46,6 +49,71 @@ impl < A : MediaAdapter > CollatedMediaGrid { } } + async fn create_media_entry ( & self , media : & A :: Overview ) -> Button { + button ! ( + @ css_classes : & [ "flat" , "open-collection-item-button" ] ; + @ connect_clicked : clone ! ( + # [strong] media , + # [ strong ( rename_to = on_media_selected ) ] self . on_media_selected , + move |_| on_media_selected ( media . clone () ) , + ) ; + & 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 () , + + 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 ; ) ; + media . get_name () . as_str () , + ) ) , + + 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 () , + + 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 () , + ) ) , + ) , + ) + } + pub fn set_sorting ( & self , sorting : A :: Sorting ) { self . grid_widget . remove_all () ; @@ -71,64 +139,3 @@ impl < A : MediaAdapter > CollatedMediaGrid { impl < A : MediaAdapter > Component for CollatedMediaGrid { fn get_widget ( & self ) -> & FlowBox { & self . grid_widget } } - -async fn create_media_entry < M : MediaOverview > ( media : & M ) -> Button { - button ! ( - @ css_classes : & [ "flat" , "open-collection-item-button" ] ; - @ connect_clicked : |_| todo ! () ; - & 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 () , - - 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 ; ) ; - media . get_name () . as_str () , - ) ) , - - 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 () , - - 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 () , - ) ) , - ) , - ) -} diff --git a/src/ui/collatable_container/mod.rs b/src/ui/collatable_container/mod.rs index 6d306ac..e14629c 100644 --- a/src/ui/collatable_container/mod.rs +++ b/src/ui/collatable_container/mod.rs @@ -67,8 +67,8 @@ pub struct CollatableMediaContainer < A : MediaAdapter + 'static > { } impl < A : MediaAdapter > CollatableMediaContainer { - pub fn new () -> Self { - let collated_grid = leak ( CollatedMediaGrid :: new () ) ; + 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) , ) ; diff --git a/src/ui/mod.rs b/src/ui/mod.rs index e49a1fe..737a244 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -6,41 +6,47 @@ use futures :: * ; use gtk4 :: Orientation :: * ; use gtk4 :: prelude :: * ; use libadwaita :: * ; +use libadwaita :: prelude :: * ; use libadwaita :: ViewSwitcherPolicy :: * ; use crate :: data_manager :: * ; use crate :: ui :: collatable_container :: * ; use crate :: ui :: component :: * ; use crate :: ui :: utility :: * ; +use crate :: utility :: * ; pub struct UI { - window : ApplicationWindow , + window : & 'static ApplicationWindow , films_component : CollatableMediaContainer , series_component : CollatableMediaContainer , } impl UI { pub fn new ( application : & Application ) -> UI { - let films_component = CollatableMediaContainer :: :: new () ; - let series_component = CollatableMediaContainer :: :: new () ; - let switcher = view_stack ! ( + let window = leak ( application_window ! ( + @ application : application ; + @ title : "Zoödex" ; + ) ) ; + + let films_component = CollatableMediaContainer :: :: new ( + |film| dialog ! () . present ( Some (window) ) , + ) ; + let series_component = CollatableMediaContainer :: :: new ( + |series| dialog ! () . present ( Some (window) ) , + ) ; + let switched = view_stack ! ( ( "Films" , "camera-video-symbolic" , films_component . get_widget () ) , ( "Series" , "video-display-symbolic" , series_component . get_widget () ) , ) ; let header_bar = header_bar ! ( - & view_switcher ! ( @ policy : Wide ; & switcher ) , + & view_switcher ! ( @ policy : Wide ; & switched ) , ) ; - let window = application_window ! ( - @ application : application ; - @ title : "Zoödex" ; - & toolbar_view ! ( - @ top_bar : & header_bar ; - & switcher , - ) , - ) ; + window . set_content ( Some ( + & toolbar_view ! ( @ top_bar : & header_bar ; & switched ) , + ) ) ; UI { window , films_component , series_component } } diff --git a/src/ui/utility.rs b/src/ui/utility.rs index a696aec..d77569a 100644 --- a/src/ui/utility.rs +++ b/src/ui/utility.rs @@ -187,13 +187,13 @@ macro_rules ! bin { ( $ ( @ vexpand : $ vexpand : expr ; ) ? ) => { { macro_rules ! application_window { ( @ application : $ application : expr ; @ title : $ title : expr ; - $ content : expr $ (,) ? + $ ( $ content : expr $ (,) ? ) ? ) => { { use libadwaita :: prelude :: * ; let window = libadwaita :: ApplicationWindow :: new ( $ application ) ; window . set_title ( Some ( $ title ) ) ; - window . set_content ( Some ( $ content ) ) ; + $ ( window . set_content ( Some ( $ content ) ) ; ) ? window } } } @@ -207,6 +207,11 @@ macro_rules ! toolbar_view { ( widget } } } +macro_rules ! dialog { () => { { + let widget = libadwaita :: Dialog :: new () ; + widget +} } } + macro_rules ! vertically_filling { ( $ child : expr ) => { g_box ! ( @ orientation : gtk4 :: Orientation :: Vertical ; @@ -247,6 +252,7 @@ pub (crate) use { bin , application_window , toolbar_view , + dialog , vertically_filling , pango_attributes , } ;