mirror of
				https://github.com/johrpan/musicus.git
				synced 2025-10-26 11:47:25 +01:00 
			
		
		
		
	Add player details screen
This commit is contained in:
		
							parent
							
								
									8e952fb6e2
								
							
						
					
					
						commit
						eb77c97f9b
					
				
					 24 changed files with 792 additions and 43 deletions
				
			
		|  | @ -13,6 +13,7 @@ | ||||||
|         <file preprocess="xml-stripblanks">ui/person_screen.ui</file> |         <file preprocess="xml-stripblanks">ui/person_screen.ui</file> | ||||||
|         <file preprocess="xml-stripblanks">ui/person_selector.ui</file> |         <file preprocess="xml-stripblanks">ui/person_selector.ui</file> | ||||||
|         <file preprocess="xml-stripblanks">ui/player_bar.ui</file> |         <file preprocess="xml-stripblanks">ui/player_bar.ui</file> | ||||||
|  |         <file preprocess="xml-stripblanks">ui/player_screen.ui</file> | ||||||
|         <file preprocess="xml-stripblanks">ui/poe_list.ui</file> |         <file preprocess="xml-stripblanks">ui/poe_list.ui</file> | ||||||
|         <file preprocess="xml-stripblanks">ui/preferences.ui</file> |         <file preprocess="xml-stripblanks">ui/preferences.ui</file> | ||||||
|         <file preprocess="xml-stripblanks">ui/recording_editor.ui</file> |         <file preprocess="xml-stripblanks">ui/recording_editor.ui</file> | ||||||
|  |  | ||||||
							
								
								
									
										305
									
								
								res/ui/player_screen.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								res/ui/player_screen.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,305 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- Generated with glade 3.38.1 --> | ||||||
|  | <interface> | ||||||
|  |   <requires lib="gtk+" version="3.24"/> | ||||||
|  |   <requires lib="libhandy" version="1.0"/> | ||||||
|  |   <object class="GtkImage" id="play_image"> | ||||||
|  |     <property name="visible">True</property> | ||||||
|  |     <property name="can-focus">False</property> | ||||||
|  |     <property name="icon-name">media-playback-start-symbolic</property> | ||||||
|  |   </object> | ||||||
|  |   <object class="GtkAdjustment" id="position"> | ||||||
|  |     <property name="upper">1</property> | ||||||
|  |     <property name="step-increment">0.01</property> | ||||||
|  |     <property name="page-increment">0.05</property> | ||||||
|  |   </object> | ||||||
|  |   <object class="GtkBox" id="widget"> | ||||||
|  |     <property name="visible">True</property> | ||||||
|  |     <property name="can-focus">False</property> | ||||||
|  |     <property name="orientation">vertical</property> | ||||||
|  |     <child> | ||||||
|  |       <object class="HdyHeaderBar"> | ||||||
|  |         <property name="visible">True</property> | ||||||
|  |         <property name="can-focus">False</property> | ||||||
|  |         <property name="title" translatable="yes">Player</property> | ||||||
|  |         <property name="show-close-button">True</property> | ||||||
|  |         <child> | ||||||
|  |           <object class="GtkButton" id="back_button"> | ||||||
|  |             <property name="visible">True</property> | ||||||
|  |             <property name="can-focus">True</property> | ||||||
|  |             <property name="receives-default">True</property> | ||||||
|  |             <child> | ||||||
|  |               <object class="GtkImage"> | ||||||
|  |                 <property name="visible">True</property> | ||||||
|  |                 <property name="can-focus">False</property> | ||||||
|  |                 <property name="icon-name">go-previous-symbolic</property> | ||||||
|  |               </object> | ||||||
|  |             </child> | ||||||
|  |           </object> | ||||||
|  |         </child> | ||||||
|  |       </object> | ||||||
|  |       <packing> | ||||||
|  |         <property name="expand">False</property> | ||||||
|  |         <property name="fill">True</property> | ||||||
|  |         <property name="position">0</property> | ||||||
|  |       </packing> | ||||||
|  |     </child> | ||||||
|  |     <child> | ||||||
|  |       <object class="GtkScrolledWindow"> | ||||||
|  |         <property name="visible">True</property> | ||||||
|  |         <property name="can-focus">True</property> | ||||||
|  |         <child> | ||||||
|  |           <object class="GtkViewport"> | ||||||
|  |             <property name="visible">True</property> | ||||||
|  |             <property name="can-focus">False</property> | ||||||
|  |             <property name="shadow-type">none</property> | ||||||
|  |             <child> | ||||||
|  |               <object class="HdyClamp"> | ||||||
|  |                 <property name="visible">True</property> | ||||||
|  |                 <property name="can-focus">False</property> | ||||||
|  |                 <property name="margin-left">12</property> | ||||||
|  |                 <property name="margin-right">12</property> | ||||||
|  |                 <property name="margin-start">12</property> | ||||||
|  |                 <property name="margin-end">12</property> | ||||||
|  |                 <property name="margin-top">18</property> | ||||||
|  |                 <property name="margin-bottom">12</property> | ||||||
|  |                 <property name="maximum-size">800</property> | ||||||
|  |                 <child> | ||||||
|  |                   <object class="GtkBox"> | ||||||
|  |                     <property name="visible">True</property> | ||||||
|  |                     <property name="can-focus">False</property> | ||||||
|  |                     <property name="orientation">vertical</property> | ||||||
|  |                     <property name="spacing">12</property> | ||||||
|  |                     <child> | ||||||
|  |                       <object class="GtkBox"> | ||||||
|  |                         <property name="visible">True</property> | ||||||
|  |                         <property name="can-focus">False</property> | ||||||
|  |                         <property name="spacing">12</property> | ||||||
|  |                         <child> | ||||||
|  |                           <object class="GtkBox"> | ||||||
|  |                             <property name="visible">True</property> | ||||||
|  |                             <property name="can-focus">False</property> | ||||||
|  |                             <property name="valign">center</property> | ||||||
|  |                             <property name="spacing">6</property> | ||||||
|  |                             <child> | ||||||
|  |                               <object class="GtkButton" id="previous_button"> | ||||||
|  |                                 <property name="visible">True</property> | ||||||
|  |                                 <property name="sensitive">False</property> | ||||||
|  |                                 <property name="can-focus">True</property> | ||||||
|  |                                 <property name="receives-default">True</property> | ||||||
|  |                                 <child> | ||||||
|  |                                   <object class="GtkImage"> | ||||||
|  |                                     <property name="visible">True</property> | ||||||
|  |                                     <property name="can-focus">False</property> | ||||||
|  |                                     <property name="icon-name">media-skip-backward-symbolic</property> | ||||||
|  |                                   </object> | ||||||
|  |                                 </child> | ||||||
|  |                               </object> | ||||||
|  |                               <packing> | ||||||
|  |                                 <property name="expand">False</property> | ||||||
|  |                                 <property name="fill">True</property> | ||||||
|  |                                 <property name="position">0</property> | ||||||
|  |                               </packing> | ||||||
|  |                             </child> | ||||||
|  |                             <child> | ||||||
|  |                               <object class="GtkButton" id="play_button"> | ||||||
|  |                                 <property name="visible">True</property> | ||||||
|  |                                 <property name="can-focus">True</property> | ||||||
|  |                                 <property name="receives-default">True</property> | ||||||
|  |                                 <child> | ||||||
|  |                                   <object class="GtkImage" id="pause_image"> | ||||||
|  |                                     <property name="visible">True</property> | ||||||
|  |                                     <property name="can-focus">False</property> | ||||||
|  |                                     <property name="icon-name">media-playback-pause-symbolic</property> | ||||||
|  |                                   </object> | ||||||
|  |                                 </child> | ||||||
|  |                               </object> | ||||||
|  |                               <packing> | ||||||
|  |                                 <property name="expand">False</property> | ||||||
|  |                                 <property name="fill">True</property> | ||||||
|  |                                 <property name="position">1</property> | ||||||
|  |                               </packing> | ||||||
|  |                             </child> | ||||||
|  |                             <child> | ||||||
|  |                               <object class="GtkButton" id="next_button"> | ||||||
|  |                                 <property name="visible">True</property> | ||||||
|  |                                 <property name="sensitive">False</property> | ||||||
|  |                                 <property name="can-focus">True</property> | ||||||
|  |                                 <property name="receives-default">True</property> | ||||||
|  |                                 <child> | ||||||
|  |                                   <object class="GtkImage"> | ||||||
|  |                                     <property name="visible">True</property> | ||||||
|  |                                     <property name="can-focus">False</property> | ||||||
|  |                                     <property name="icon-name">media-skip-forward-symbolic</property> | ||||||
|  |                                   </object> | ||||||
|  |                                 </child> | ||||||
|  |                               </object> | ||||||
|  |                               <packing> | ||||||
|  |                                 <property name="expand">False</property> | ||||||
|  |                                 <property name="fill">True</property> | ||||||
|  |                                 <property name="position">2</property> | ||||||
|  |                               </packing> | ||||||
|  |                             </child> | ||||||
|  |                           </object> | ||||||
|  |                           <packing> | ||||||
|  |                             <property name="expand">False</property> | ||||||
|  |                             <property name="fill">True</property> | ||||||
|  |                             <property name="position">0</property> | ||||||
|  |                           </packing> | ||||||
|  |                         </child> | ||||||
|  |                         <child> | ||||||
|  |                           <object class="GtkBox"> | ||||||
|  |                             <property name="visible">True</property> | ||||||
|  |                             <property name="can-focus">False</property> | ||||||
|  |                             <property name="orientation">vertical</property> | ||||||
|  |                             <child> | ||||||
|  |                               <object class="GtkLabel" id="title_label"> | ||||||
|  |                                 <property name="visible">True</property> | ||||||
|  |                                 <property name="can-focus">False</property> | ||||||
|  |                                 <property name="halign">start</property> | ||||||
|  |                                 <property name="label" translatable="yes">Title</property> | ||||||
|  |                                 <property name="ellipsize">end</property> | ||||||
|  |                                 <attributes> | ||||||
|  |                                   <attribute name="weight" value="bold"/> | ||||||
|  |                                 </attributes> | ||||||
|  |                               </object> | ||||||
|  |                               <packing> | ||||||
|  |                                 <property name="expand">False</property> | ||||||
|  |                                 <property name="fill">True</property> | ||||||
|  |                                 <property name="position">0</property> | ||||||
|  |                               </packing> | ||||||
|  |                             </child> | ||||||
|  |                             <child> | ||||||
|  |                               <object class="GtkLabel" id="subtitle_label"> | ||||||
|  |                                 <property name="visible">True</property> | ||||||
|  |                                 <property name="can-focus">False</property> | ||||||
|  |                                 <property name="halign">start</property> | ||||||
|  |                                 <property name="label" translatable="yes">Subtitle</property> | ||||||
|  |                                 <property name="ellipsize">end</property> | ||||||
|  |                               </object> | ||||||
|  |                               <packing> | ||||||
|  |                                 <property name="expand">False</property> | ||||||
|  |                                 <property name="fill">True</property> | ||||||
|  |                                 <property name="position">1</property> | ||||||
|  |                               </packing> | ||||||
|  |                             </child> | ||||||
|  |                           </object> | ||||||
|  |                           <packing> | ||||||
|  |                             <property name="expand">True</property> | ||||||
|  |                             <property name="fill">True</property> | ||||||
|  |                             <property name="position">1</property> | ||||||
|  |                           </packing> | ||||||
|  |                         </child> | ||||||
|  |                         <child> | ||||||
|  |                           <object class="GtkButton" id="stop_button"> | ||||||
|  |                             <property name="visible">True</property> | ||||||
|  |                             <property name="can-focus">True</property> | ||||||
|  |                             <property name="receives-default">True</property> | ||||||
|  |                             <property name="valign">center</property> | ||||||
|  |                             <child> | ||||||
|  |                               <object class="GtkImage"> | ||||||
|  |                                 <property name="visible">True</property> | ||||||
|  |                                 <property name="can-focus">False</property> | ||||||
|  |                                 <property name="icon-name">media-playback-stop-symbolic</property> | ||||||
|  |                               </object> | ||||||
|  |                             </child> | ||||||
|  |                           </object> | ||||||
|  |                           <packing> | ||||||
|  |                             <property name="expand">False</property> | ||||||
|  |                             <property name="fill">True</property> | ||||||
|  |                             <property name="position">2</property> | ||||||
|  |                           </packing> | ||||||
|  |                         </child> | ||||||
|  |                       </object> | ||||||
|  |                       <packing> | ||||||
|  |                         <property name="expand">False</property> | ||||||
|  |                         <property name="fill">True</property> | ||||||
|  |                         <property name="position">0</property> | ||||||
|  |                       </packing> | ||||||
|  |                     </child> | ||||||
|  |                     <child> | ||||||
|  |                       <object class="GtkBox"> | ||||||
|  |                         <property name="visible">True</property> | ||||||
|  |                         <property name="can-focus">False</property> | ||||||
|  |                         <property name="spacing">6</property> | ||||||
|  |                         <child> | ||||||
|  |                           <object class="GtkLabel" id="position_label"> | ||||||
|  |                             <property name="visible">True</property> | ||||||
|  |                             <property name="can-focus">False</property> | ||||||
|  |                             <property name="label" translatable="yes">0:00</property> | ||||||
|  |                           </object> | ||||||
|  |                           <packing> | ||||||
|  |                             <property name="expand">False</property> | ||||||
|  |                             <property name="fill">True</property> | ||||||
|  |                             <property name="position">0</property> | ||||||
|  |                           </packing> | ||||||
|  |                         </child> | ||||||
|  |                         <child> | ||||||
|  |                           <object class="GtkScale" id="position_scale"> | ||||||
|  |                             <property name="visible">True</property> | ||||||
|  |                             <property name="can-focus">True</property> | ||||||
|  |                             <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> | ||||||
|  |                             <property name="adjustment">position</property> | ||||||
|  |                             <property name="upper-stepper-sensitivity">off</property> | ||||||
|  |                             <property name="round-digits">1</property> | ||||||
|  |                             <property name="draw-value">False</property> | ||||||
|  |                           </object> | ||||||
|  |                           <packing> | ||||||
|  |                             <property name="expand">True</property> | ||||||
|  |                             <property name="fill">True</property> | ||||||
|  |                             <property name="position">1</property> | ||||||
|  |                           </packing> | ||||||
|  |                         </child> | ||||||
|  |                         <child> | ||||||
|  |                           <object class="GtkLabel" id="duration_label"> | ||||||
|  |                             <property name="visible">True</property> | ||||||
|  |                             <property name="can-focus">False</property> | ||||||
|  |                             <property name="label" translatable="yes">0:00</property> | ||||||
|  |                           </object> | ||||||
|  |                           <packing> | ||||||
|  |                             <property name="expand">False</property> | ||||||
|  |                             <property name="fill">True</property> | ||||||
|  |                             <property name="position">2</property> | ||||||
|  |                           </packing> | ||||||
|  |                         </child> | ||||||
|  |                       </object> | ||||||
|  |                       <packing> | ||||||
|  |                         <property name="expand">False</property> | ||||||
|  |                         <property name="fill">True</property> | ||||||
|  |                         <property name="position">1</property> | ||||||
|  |                       </packing> | ||||||
|  |                     </child> | ||||||
|  |                     <child> | ||||||
|  |                       <object class="GtkFrame" id="frame"> | ||||||
|  |                         <property name="visible">True</property> | ||||||
|  |                         <property name="can-focus">False</property> | ||||||
|  |                         <property name="label-xalign">0</property> | ||||||
|  |                         <property name="shadow-type">in</property> | ||||||
|  |                         <child> | ||||||
|  |                           <placeholder/> | ||||||
|  |                         </child> | ||||||
|  |                         <child type="label_item"> | ||||||
|  |                           <placeholder/> | ||||||
|  |                         </child> | ||||||
|  |                       </object> | ||||||
|  |                       <packing> | ||||||
|  |                         <property name="expand">False</property> | ||||||
|  |                         <property name="fill">True</property> | ||||||
|  |                         <property name="position">2</property> | ||||||
|  |                       </packing> | ||||||
|  |                     </child> | ||||||
|  |                   </object> | ||||||
|  |                 </child> | ||||||
|  |               </object> | ||||||
|  |             </child> | ||||||
|  |           </object> | ||||||
|  |         </child> | ||||||
|  |       </object> | ||||||
|  |       <packing> | ||||||
|  |         <property name="expand">True</property> | ||||||
|  |         <property name="fill">True</property> | ||||||
|  |         <property name="position">1</property> | ||||||
|  |       </packing> | ||||||
|  |     </child> | ||||||
|  |   </object> | ||||||
|  | </interface> | ||||||
|  | @ -49,6 +49,11 @@ where | ||||||
|             for (index, ensemble) in ensembles.iter().enumerate() { |             for (index, ensemble) in ensembles.iter().enumerate() { | ||||||
|                 let label = gtk::Label::new(Some(&ensemble.name)); |                 let label = gtk::Label::new(Some(&ensemble.name)); | ||||||
|                 label.set_halign(gtk::Align::Start); |                 label.set_halign(gtk::Align::Start); | ||||||
|  |                 label.set_margin_start(6); | ||||||
|  |                 label.set_margin_end(6); | ||||||
|  |                 label.set_margin_top(6); | ||||||
|  |                 label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|                 let row = SelectorRow::new(index.try_into().unwrap(), &label); |                 let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|                 row.show_all(); |                 row.show_all(); | ||||||
|                 clone.list.insert(&row, -1); |                 clone.list.insert(&row, -1); | ||||||
|  |  | ||||||
|  | @ -49,6 +49,11 @@ where | ||||||
|             for (index, instrument) in instruments.iter().enumerate() { |             for (index, instrument) in instruments.iter().enumerate() { | ||||||
|                 let label = gtk::Label::new(Some(&instrument.name)); |                 let label = gtk::Label::new(Some(&instrument.name)); | ||||||
|                 label.set_halign(gtk::Align::Start); |                 label.set_halign(gtk::Align::Start); | ||||||
|  |                 label.set_margin_start(6); | ||||||
|  |                 label.set_margin_end(6); | ||||||
|  |                 label.set_margin_top(6); | ||||||
|  |                 label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|                 let row = SelectorRow::new(index.try_into().unwrap(), &label); |                 let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|                 row.show_all(); |                 row.show_all(); | ||||||
|                 clone.list.insert(&row, -1); |                 clone.list.insert(&row, -1); | ||||||
|  |  | ||||||
|  | @ -140,6 +140,11 @@ impl PartEditor { | ||||||
|         for (index, instrument) in self.instruments.borrow().iter().enumerate() { |         for (index, instrument) in self.instruments.borrow().iter().enumerate() { | ||||||
|             let label = gtk::Label::new(Some(&instrument.name)); |             let label = gtk::Label::new(Some(&instrument.name)); | ||||||
|             label.set_halign(gtk::Align::Start); |             label.set_halign(gtk::Align::Start); | ||||||
|  |             label.set_margin_start(6); | ||||||
|  |             label.set_margin_end(6); | ||||||
|  |             label.set_margin_top(6); | ||||||
|  |             label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|             let row = SelectorRow::new(index.try_into().unwrap(), &label); |             let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|             row.show_all(); |             row.show_all(); | ||||||
|             self.instrument_list.insert(&row, -1); |             self.instrument_list.insert(&row, -1); | ||||||
|  |  | ||||||
|  | @ -163,6 +163,11 @@ where | ||||||
|         for (index, performer) in self.performers.borrow().iter().enumerate() { |         for (index, performer) in self.performers.borrow().iter().enumerate() { | ||||||
|             let label = gtk::Label::new(Some(&performer.get_title())); |             let label = gtk::Label::new(Some(&performer.get_title())); | ||||||
|             label.set_halign(gtk::Align::Start); |             label.set_halign(gtk::Align::Start); | ||||||
|  |             label.set_margin_start(6); | ||||||
|  |             label.set_margin_end(6); | ||||||
|  |             label.set_margin_top(6); | ||||||
|  |             label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|             let row = SelectorRow::new(index.try_into().unwrap(), &label); |             let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|             row.show_all(); |             row.show_all(); | ||||||
|             self.performer_list.insert(&row, -1); |             self.performer_list.insert(&row, -1); | ||||||
|  |  | ||||||
|  | @ -110,6 +110,10 @@ impl RecordingSelectorPersonScreen { | ||||||
|             |work: &WorkDescription| { |             |work: &WorkDescription| { | ||||||
|                 let label = gtk::Label::new(Some(&work.title)); |                 let label = gtk::Label::new(Some(&work.title)); | ||||||
|                 label.set_halign(gtk::Align::Start); |                 label.set_halign(gtk::Align::Start); | ||||||
|  |                 label.set_margin_start(6); | ||||||
|  |                 label.set_margin_end(6); | ||||||
|  |                 label.set_margin_top(6); | ||||||
|  |                 label.set_margin_bottom(6); | ||||||
|                 label.upcast() |                 label.upcast() | ||||||
|             }, |             }, | ||||||
|             |_| true, |             |_| true, | ||||||
|  | @ -213,6 +217,7 @@ impl RecordingSelectorWorkScreen { | ||||||
|                 performers_label.set_halign(gtk::Align::Start); |                 performers_label.set_halign(gtk::Align::Start); | ||||||
| 
 | 
 | ||||||
|                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); |                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                 vbox.set_border_width(6); | ||||||
|                 vbox.add(&work_label); |                 vbox.add(&work_label); | ||||||
|                 vbox.add(&performers_label); |                 vbox.add(&performers_label); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -71,6 +71,7 @@ impl TracksEditor { | ||||||
|                 file_name_label.set_halign(gtk::Align::Start); |                 file_name_label.set_halign(gtk::Align::Start); | ||||||
| 
 | 
 | ||||||
|                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); |                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                 vbox.set_border_width(6); | ||||||
|                 vbox.add(&title_label); |                 vbox.add(&title_label); | ||||||
|                 vbox.add(&file_name_label); |                 vbox.add(&file_name_label); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -341,6 +341,11 @@ where | ||||||
|         for (index, instrument) in self.instruments.borrow().iter().enumerate() { |         for (index, instrument) in self.instruments.borrow().iter().enumerate() { | ||||||
|             let label = gtk::Label::new(Some(&instrument.name)); |             let label = gtk::Label::new(Some(&instrument.name)); | ||||||
|             label.set_halign(gtk::Align::Start); |             label.set_halign(gtk::Align::Start); | ||||||
|  |             label.set_margin_start(6); | ||||||
|  |             label.set_margin_end(6); | ||||||
|  |             label.set_margin_top(6); | ||||||
|  |             label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|             let row = SelectorRow::new(index.try_into().unwrap(), &label); |             let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|             row.show_all(); |             row.show_all(); | ||||||
|             self.instrument_list.insert(&row, -1); |             self.instrument_list.insert(&row, -1); | ||||||
|  | @ -365,6 +370,10 @@ where | ||||||
|         for (index, part) in self.structure.borrow().iter().enumerate() { |         for (index, part) in self.structure.borrow().iter().enumerate() { | ||||||
|             let label = gtk::Label::new(Some(&part.get_title())); |             let label = gtk::Label::new(Some(&part.get_title())); | ||||||
|             label.set_halign(gtk::Align::Start); |             label.set_halign(gtk::Align::Start); | ||||||
|  |             label.set_margin_start(6); | ||||||
|  |             label.set_margin_end(6); | ||||||
|  |             label.set_margin_top(6); | ||||||
|  |             label.set_margin_bottom(6); | ||||||
| 
 | 
 | ||||||
|             if part.is_part() { |             if part.is_part() { | ||||||
|                 label.set_margin_start(6); |                 label.set_margin_start(6); | ||||||
|  |  | ||||||
|  | @ -142,6 +142,11 @@ where | ||||||
|                 for (index, person) in persons.iter().enumerate() { |                 for (index, person) in persons.iter().enumerate() { | ||||||
|                     let label = gtk::Label::new(Some(&person.name_lf())); |                     let label = gtk::Label::new(Some(&person.name_lf())); | ||||||
|                     label.set_halign(gtk::Align::Start); |                     label.set_halign(gtk::Align::Start); | ||||||
|  |                     label.set_margin_start(6); | ||||||
|  |                     label.set_margin_end(6); | ||||||
|  |                     label.set_margin_top(6); | ||||||
|  |                     label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|                     let row = SelectorRow::new(index.try_into().unwrap(), &label); |                     let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|                     row.show_all(); |                     row.show_all(); | ||||||
|                     self.person_list.insert(&row, -1); |                     self.person_list.insert(&row, -1); | ||||||
|  | @ -207,6 +212,11 @@ where | ||||||
|                 for (index, work) in works.iter().enumerate() { |                 for (index, work) in works.iter().enumerate() { | ||||||
|                     let label = gtk::Label::new(Some(&work.title)); |                     let label = gtk::Label::new(Some(&work.title)); | ||||||
|                     label.set_halign(gtk::Align::Start); |                     label.set_halign(gtk::Align::Start); | ||||||
|  |                     label.set_margin_start(6); | ||||||
|  |                     label.set_margin_end(6); | ||||||
|  |                     label.set_margin_top(6); | ||||||
|  |                     label.set_margin_bottom(6); | ||||||
|  | 
 | ||||||
|                     let row = SelectorRow::new(index.try_into().unwrap(), &label); |                     let row = SelectorRow::new(index.try_into().unwrap(), &label); | ||||||
|                     row.show_all(); |                     row.show_all(); | ||||||
|                     self.work_list.insert(&row, -1); |                     self.work_list.insert(&row, -1); | ||||||
|  |  | ||||||
|  | @ -57,6 +57,7 @@ sources = files( | ||||||
|   'screens/ensemble_screen.rs', |   'screens/ensemble_screen.rs', | ||||||
|   'screens/mod.rs', |   'screens/mod.rs', | ||||||
|   'screens/person_screen.rs', |   'screens/person_screen.rs', | ||||||
|  |   'screens/player_screen.rs', | ||||||
|   'screens/recording_screen.rs', |   'screens/recording_screen.rs', | ||||||
|   'screens/work_screen.rs', |   'screens/work_screen.rs', | ||||||
|   'widgets/list.rs', |   'widgets/list.rs', | ||||||
|  |  | ||||||
|  | @ -19,11 +19,11 @@ pub struct Player { | ||||||
|     current_item: Cell<Option<usize>>, |     current_item: Cell<Option<usize>>, | ||||||
|     current_track: Cell<Option<usize>>, |     current_track: Cell<Option<usize>>, | ||||||
|     playing: Cell<bool>, |     playing: Cell<bool>, | ||||||
|     playlist_cb: RefCell<Option<Box<dyn Fn(Vec<PlaylistItem>) -> ()>>>, |     playlist_cbs: RefCell<Vec<Box<dyn Fn(Vec<PlaylistItem>) -> ()>>>, | ||||||
|     track_cb: RefCell<Option<Box<dyn Fn(usize, usize) -> ()>>>, |     track_cbs: RefCell<Vec<Box<dyn Fn(usize, usize) -> ()>>>, | ||||||
|     duration_cb: RefCell<Option<Box<dyn Fn(u64) -> ()>>>, |     duration_cbs: RefCell<Vec<Box<dyn Fn(u64) -> ()>>>, | ||||||
|     playing_cb: RefCell<Option<Box<dyn Fn(bool) -> ()>>>, |     playing_cbs: RefCell<Vec<Box<dyn Fn(bool) -> ()>>>, | ||||||
|     position_cb: RefCell<Option<Box<dyn Fn(u64) -> ()>>>, |     position_cbs: RefCell<Vec<Box<dyn Fn(u64) -> ()>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Player { | impl Player { | ||||||
|  | @ -42,11 +42,11 @@ impl Player { | ||||||
|             current_item: Cell::new(None), |             current_item: Cell::new(None), | ||||||
|             current_track: Cell::new(None), |             current_track: Cell::new(None), | ||||||
|             playing: Cell::new(false), |             playing: Cell::new(false), | ||||||
|             playlist_cb: RefCell::new(None), |             playlist_cbs: RefCell::new(Vec::new()), | ||||||
|             track_cb: RefCell::new(None), |             track_cbs: RefCell::new(Vec::new()), | ||||||
|             duration_cb: RefCell::new(None), |             duration_cbs: RefCell::new(Vec::new()), | ||||||
|             playing_cb: RefCell::new(None), |             playing_cbs: RefCell::new(Vec::new()), | ||||||
|             position_cb: RefCell::new(None), |             position_cbs: RefCell::new(Vec::new()), | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         let clone = fragile::Fragile::new(result.clone()); |         let clone = fragile::Fragile::new(result.clone()); | ||||||
|  | @ -56,8 +56,8 @@ impl Player { | ||||||
|                 clone.next().unwrap(); |                 clone.next().unwrap(); | ||||||
|             } else { |             } else { | ||||||
|                 clone.player.stop(); |                 clone.player.stop(); | ||||||
| 
 |                 clone.playing.replace(false); | ||||||
|                 if let Some(cb) = &*clone.playing_cb.borrow() { |                 for cb in &*clone.playing_cbs.borrow() { | ||||||
|                     cb(false); |                     cb(false); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -65,14 +65,14 @@ impl Player { | ||||||
| 
 | 
 | ||||||
|         let clone = fragile::Fragile::new(result.clone()); |         let clone = fragile::Fragile::new(result.clone()); | ||||||
|         player.connect_position_updated(move |_, position| { |         player.connect_position_updated(move |_, position| { | ||||||
|             if let Some(cb) = &*clone.get().position_cb.borrow() { |             for cb in &*clone.get().position_cbs.borrow() { | ||||||
|                 cb(position.mseconds().unwrap()); |                 cb(position.mseconds().unwrap()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         let clone = fragile::Fragile::new(result.clone()); |         let clone = fragile::Fragile::new(result.clone()); | ||||||
|         player.connect_duration_changed(move |_, duration| { |         player.connect_duration_changed(move |_, duration| { | ||||||
|             if let Some(cb) = &*clone.get().duration_cb.borrow() { |             for cb in &*clone.get().duration_cbs.borrow() { | ||||||
|                 cb(duration.mseconds().unwrap()); |                 cb(duration.mseconds().unwrap()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  | @ -80,24 +80,24 @@ impl Player { | ||||||
|         result |         result | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_playlist_cb<F: Fn(Vec<PlaylistItem>) -> () + 'static>(&self, cb: F) { |     pub fn add_playlist_cb<F: Fn(Vec<PlaylistItem>) -> () + 'static>(&self, cb: F) { | ||||||
|         self.playlist_cb.replace(Some(Box::new(cb))); |         self.playlist_cbs.borrow_mut().push(Box::new(cb)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_track_cb<F: Fn(usize, usize) -> () + 'static>(&self, cb: F) { |     pub fn add_track_cb<F: Fn(usize, usize) -> () + 'static>(&self, cb: F) { | ||||||
|         self.track_cb.replace(Some(Box::new(cb))); |         self.track_cbs.borrow_mut().push(Box::new(cb)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_duration_cb<F: Fn(u64) -> () + 'static>(&self, cb: F) { |     pub fn add_duration_cb<F: Fn(u64) -> () + 'static>(&self, cb: F) { | ||||||
|         self.duration_cb.replace(Some(Box::new(cb))); |         self.duration_cbs.borrow_mut().push(Box::new(cb)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_playing_cb<F: Fn(bool) -> () + 'static>(&self, cb: F) { |     pub fn add_playing_cb<F: Fn(bool) -> () + 'static>(&self, cb: F) { | ||||||
|         self.playing_cb.replace(Some(Box::new(cb))); |         self.playing_cbs.borrow_mut().push(Box::new(cb)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn set_position_cb<F: Fn(u64) -> () + 'static>(&self, cb: F) { |     pub fn add_position_cb<F: Fn(u64) -> () + 'static>(&self, cb: F) { | ||||||
|         self.position_cb.replace(Some(Box::new(cb))); |         self.position_cbs.borrow_mut().push(Box::new(cb)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn get_playlist(&self) -> Vec<PlaylistItem> { |     pub fn get_playlist(&self) -> Vec<PlaylistItem> { | ||||||
|  | @ -135,7 +135,7 @@ impl Player { | ||||||
|                 was_empty |                 was_empty | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             if let Some(cb) = &*self.playlist_cb.borrow() { |             for cb in &*self.playlist_cbs.borrow() { | ||||||
|                 cb(self.playlist.borrow().clone()); |                 cb(self.playlist.borrow().clone()); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -144,7 +144,7 @@ impl Player { | ||||||
|                 self.player.play(); |                 self.player.play(); | ||||||
|                 self.playing.set(true); |                 self.playing.set(true); | ||||||
| 
 | 
 | ||||||
|                 if let Some(cb) = &*self.playing_cb.borrow() { |                 for cb in &*self.playing_cbs.borrow() { | ||||||
|                     cb(true); |                     cb(true); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -158,19 +158,23 @@ impl Player { | ||||||
|             self.player.pause(); |             self.player.pause(); | ||||||
|             self.playing.set(false); |             self.playing.set(false); | ||||||
| 
 | 
 | ||||||
|             if let Some(cb) = &*self.playing_cb.borrow() { |             for cb in &*self.playing_cbs.borrow() { | ||||||
|                 cb(false); |                 cb(false); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             self.player.play(); |             self.player.play(); | ||||||
|             self.playing.set(true); |             self.playing.set(true); | ||||||
| 
 | 
 | ||||||
|             if let Some(cb) = &*self.playing_cb.borrow() { |             for cb in &*self.playing_cbs.borrow() { | ||||||
|                 cb(true); |                 cb(true); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn seek(&self, ms: u64) { | ||||||
|  |         self.player.seek(gstreamer::ClockTime::from_mseconds(ms)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub fn has_previous(&self) -> bool { |     pub fn has_previous(&self) -> bool { | ||||||
|         if let Some(current_item) = self.current_item.get() { |         if let Some(current_item) = self.current_item.get() { | ||||||
|             if let Some(current_track) = self.current_track.get() { |             if let Some(current_track) = self.current_track.get() { | ||||||
|  | @ -266,7 +270,7 @@ impl Player { | ||||||
|         self.current_item.set(Some(current_item)); |         self.current_item.set(Some(current_item)); | ||||||
|         self.current_track.set(Some(current_track)); |         self.current_track.set(Some(current_track)); | ||||||
| 
 | 
 | ||||||
|         if let Some(cb) = &*self.track_cb.borrow() { |         for cb in &*self.track_cbs.borrow() { | ||||||
|             cb(current_item, current_track); |             cb(current_item, current_track); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -280,11 +284,11 @@ impl Player { | ||||||
|         self.current_track.set(None); |         self.current_track.set(None); | ||||||
|         self.playlist.replace(Vec::new()); |         self.playlist.replace(Vec::new()); | ||||||
| 
 | 
 | ||||||
|         if let Some(cb) = &*self.playing_cb.borrow() { |         for cb in &*self.playing_cbs.borrow() { | ||||||
|             cb(false); |             cb(false); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if let Some(cb) = &*self.playlist_cb.borrow() { |         for cb in &*self.playlist_cbs.borrow() { | ||||||
|             cb(Vec::new()); |             cb(Vec::new()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -63,6 +63,7 @@ impl EnsembleScreen { | ||||||
|                 performers_label.set_halign(gtk::Align::Start); |                 performers_label.set_halign(gtk::Align::Start); | ||||||
| 
 | 
 | ||||||
|                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); |                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                 vbox.set_border_width(6); | ||||||
|                 vbox.add(&work_label); |                 vbox.add(&work_label); | ||||||
|                 vbox.add(&performers_label); |                 vbox.add(&performers_label); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,9 @@ pub use ensemble_screen::*; | ||||||
| pub mod person_screen; | pub mod person_screen; | ||||||
| pub use person_screen::*; | pub use person_screen::*; | ||||||
| 
 | 
 | ||||||
|  | pub mod player_screen; | ||||||
|  | pub use player_screen::*; | ||||||
|  | 
 | ||||||
| pub mod work_screen; | pub mod work_screen; | ||||||
| pub use work_screen::*; | pub use work_screen::*; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -58,6 +58,10 @@ impl PersonScreen { | ||||||
|             |work: &WorkDescription| { |             |work: &WorkDescription| { | ||||||
|                 let label = gtk::Label::new(Some(&work.title)); |                 let label = gtk::Label::new(Some(&work.title)); | ||||||
|                 label.set_halign(gtk::Align::Start); |                 label.set_halign(gtk::Align::Start); | ||||||
|  |                 label.set_margin_start(6); | ||||||
|  |                 label.set_margin_end(6); | ||||||
|  |                 label.set_margin_top(6); | ||||||
|  |                 label.set_margin_bottom(6); | ||||||
|                 label.upcast() |                 label.upcast() | ||||||
|             }, |             }, | ||||||
|             clone!(@strong search_entry => move |work: &WorkDescription| { |             clone!(@strong search_entry => move |work: &WorkDescription| { | ||||||
|  | @ -81,6 +85,7 @@ impl PersonScreen { | ||||||
|                 performers_label.set_halign(gtk::Align::Start); |                 performers_label.set_halign(gtk::Align::Start); | ||||||
| 
 | 
 | ||||||
|                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); |                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                 vbox.set_border_width(6); | ||||||
|                 vbox.add(&work_label); |                 vbox.add(&work_label); | ||||||
|                 vbox.add(&performers_label); |                 vbox.add(&performers_label); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										336
									
								
								src/screens/player_screen.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								src/screens/player_screen.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,336 @@ | ||||||
|  | use crate::player::*; | ||||||
|  | use crate::widgets::*; | ||||||
|  | use gettextrs::gettext; | ||||||
|  | use glib::clone; | ||||||
|  | use gtk::prelude::*; | ||||||
|  | use gtk_macros::get_widget; | ||||||
|  | use std::cell::{Cell, RefCell}; | ||||||
|  | use std::rc::Rc; | ||||||
|  | 
 | ||||||
|  | struct PlaylistElement { | ||||||
|  |     pub item: usize, | ||||||
|  |     pub track: usize, | ||||||
|  |     pub title: String, | ||||||
|  |     pub subtitle: Option<String>, | ||||||
|  |     pub playable: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct PlayerScreen { | ||||||
|  |     pub widget: gtk::Box, | ||||||
|  |     title_label: gtk::Label, | ||||||
|  |     subtitle_label: gtk::Label, | ||||||
|  |     previous_button: gtk::Button, | ||||||
|  |     play_button: gtk::Button, | ||||||
|  |     next_button: gtk::Button, | ||||||
|  |     position_label: gtk::Label, | ||||||
|  |     position: gtk::Adjustment, | ||||||
|  |     duration_label: gtk::Label, | ||||||
|  |     play_image: gtk::Image, | ||||||
|  |     pause_image: gtk::Image, | ||||||
|  |     list: Rc<List<PlaylistElement>>, | ||||||
|  |     player: Rc<RefCell<Option<Rc<Player>>>>, | ||||||
|  |     seeking: Rc<Cell<bool>>, | ||||||
|  |     current_item: Rc<Cell<usize>>, | ||||||
|  |     current_track: Rc<Cell<usize>>, | ||||||
|  |     back_cb: Rc<RefCell<Option<Box<dyn Fn() -> ()>>>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PlayerScreen { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/player_screen.ui"); | ||||||
|  | 
 | ||||||
|  |         get_widget!(builder, gtk::Box, widget); | ||||||
|  |         get_widget!(builder, gtk::Button, back_button); | ||||||
|  |         get_widget!(builder, gtk::Label, title_label); | ||||||
|  |         get_widget!(builder, gtk::Label, subtitle_label); | ||||||
|  |         get_widget!(builder, gtk::Button, previous_button); | ||||||
|  |         get_widget!(builder, gtk::Button, play_button); | ||||||
|  |         get_widget!(builder, gtk::Button, next_button); | ||||||
|  |         get_widget!(builder, gtk::Button, stop_button); | ||||||
|  |         get_widget!(builder, gtk::Label, position_label); | ||||||
|  |         get_widget!(builder, gtk::Scale, position_scale); | ||||||
|  |         get_widget!(builder, gtk::Adjustment, position); | ||||||
|  |         get_widget!(builder, gtk::Label, duration_label); | ||||||
|  |         get_widget!(builder, gtk::Image, play_image); | ||||||
|  |         get_widget!(builder, gtk::Image, pause_image); | ||||||
|  |         get_widget!(builder, gtk::Frame, frame); | ||||||
|  | 
 | ||||||
|  |         let back_cb = Rc::new(RefCell::new(None::<Box<dyn Fn() -> ()>>)); | ||||||
|  | 
 | ||||||
|  |         back_button.connect_clicked(clone!(@strong back_cb => move |_| { | ||||||
|  |             if let Some(cb) = &*back_cb.borrow() { | ||||||
|  |                 cb(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         let player = Rc::new(RefCell::new(None::<Rc<Player>>)); | ||||||
|  |         let seeking = Rc::new(Cell::new(false)); | ||||||
|  | 
 | ||||||
|  |         previous_button.connect_clicked(clone!(@strong player => move |_| { | ||||||
|  |             if let Some(player) = &*player.borrow() { | ||||||
|  |                 player.previous().unwrap(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         play_button.connect_clicked(clone!(@strong player => move |_| { | ||||||
|  |             if let Some(player) = &*player.borrow() { | ||||||
|  |                 player.play_pause(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         next_button.connect_clicked(clone!(@strong player => move |_| { | ||||||
|  |             if let Some(player) = &*player.borrow() { | ||||||
|  |                 player.next().unwrap(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         stop_button.connect_clicked(clone!(@strong player, @strong back_cb => move |_| { | ||||||
|  |             if let Some(player) = &*player.borrow() { | ||||||
|  |                 if let Some(cb) = &*back_cb.borrow() { | ||||||
|  |                     cb(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 player.clear(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         position_scale.connect_button_press_event(clone!(@strong seeking => move |_, _| { | ||||||
|  |             seeking.replace(true); | ||||||
|  |             Inhibit(false) | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         position_scale.connect_button_release_event( | ||||||
|  |             clone!(@strong seeking, @strong position, @strong player => move |_, _| { | ||||||
|  |                 if let Some(player) = &*player.borrow() { | ||||||
|  |                     player.seek(position.get_value() as u64); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 seeking.replace(false); | ||||||
|  |                 Inhibit(false) | ||||||
|  |             }), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         position_scale.connect_value_changed( | ||||||
|  |             clone!(@strong seeking, @strong position, @strong position_label => move |_| { | ||||||
|  |                 if seeking.get() { | ||||||
|  |                     let ms = position.get_value() as u64; | ||||||
|  |                     let min = ms / 60000; | ||||||
|  |                     let sec = (ms % 60000) / 1000; | ||||||
|  |                     position_label.set_text(&format!("{}:{:02}", min, sec)); | ||||||
|  |                 } | ||||||
|  |             }), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         let current_item = Rc::new(Cell::<usize>::new(0)); | ||||||
|  |         let current_track = Rc::new(Cell::<usize>::new(0)); | ||||||
|  |         let list = List::new( | ||||||
|  |             clone!( | ||||||
|  |                 @strong current_item, | ||||||
|  |                 @strong current_track | ||||||
|  |                 => move |element: &PlaylistElement| { | ||||||
|  |                     let title_label = gtk::Label::new(Some(&element.title)); | ||||||
|  |                     title_label.set_ellipsize(pango::EllipsizeMode::End); | ||||||
|  |                     title_label.set_halign(gtk::Align::Start); | ||||||
|  |                     let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                     vbox.add(&title_label); | ||||||
|  |                     if let Some(subtitle) = &element.subtitle { | ||||||
|  |                         let subtitle_label = gtk::Label::new(Some(&subtitle)); | ||||||
|  |                         subtitle_label.set_ellipsize(pango::EllipsizeMode::End); | ||||||
|  |                         subtitle_label.set_halign(gtk::Align::Start); | ||||||
|  |                         subtitle_label.set_opacity(0.5); | ||||||
|  |                         vbox.add(&subtitle_label); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 6); | ||||||
|  |                     hbox.set_border_width(6); | ||||||
|  | 
 | ||||||
|  |                     if element.playable { | ||||||
|  |                         let image = gtk::Image::new(); | ||||||
|  | 
 | ||||||
|  |                         if element.item == current_item.get() && element.track == current_track.get() { | ||||||
|  |                             image.set_from_icon_name( | ||||||
|  |                                 Some("media-playback-start-symbolic"), | ||||||
|  |                                 gtk::IconSize::Button, | ||||||
|  |                             ); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         hbox.add(&image); | ||||||
|  |                     } else if element.item > 0 { | ||||||
|  |                         hbox.set_margin_top(18); | ||||||
|  |                     } | ||||||
|  |                     hbox.add(&vbox); | ||||||
|  |                     hbox.upcast() | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             |_| true, | ||||||
|  |             "", | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         list.set_selected(clone!(@strong player => move |element| { | ||||||
|  |             if let Some(player) = &*player.borrow() { | ||||||
|  |                 player.set_track(element.item, element.track).unwrap(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         frame.add(&list.widget); | ||||||
|  | 
 | ||||||
|  |         Self { | ||||||
|  |             widget, | ||||||
|  |             title_label, | ||||||
|  |             subtitle_label, | ||||||
|  |             previous_button, | ||||||
|  |             play_button, | ||||||
|  |             next_button, | ||||||
|  |             position_label, | ||||||
|  |             position, | ||||||
|  |             duration_label, | ||||||
|  |             play_image, | ||||||
|  |             pause_image, | ||||||
|  |             list, | ||||||
|  |             player, | ||||||
|  |             seeking, | ||||||
|  |             current_item, | ||||||
|  |             current_track, | ||||||
|  |             back_cb, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_player(&self, player: Option<Rc<Player>>) { | ||||||
|  |         self.player.replace(player.clone()); | ||||||
|  | 
 | ||||||
|  |         if let Some(player) = player { | ||||||
|  |             let playlist = Rc::new(RefCell::new(Vec::<PlaylistItem>::new())); | ||||||
|  | 
 | ||||||
|  |             player.add_playlist_cb(clone!( | ||||||
|  |                 @strong player, | ||||||
|  |                 @strong self.previous_button as previous_button, | ||||||
|  |                 @strong self.next_button as next_button, | ||||||
|  |                 @strong self.list as list, | ||||||
|  |                 @strong playlist | ||||||
|  |                 => move |new_playlist| { | ||||||
|  |                     playlist.replace(new_playlist); | ||||||
|  |                     previous_button.set_sensitive(player.has_previous()); | ||||||
|  |                     next_button.set_sensitive(player.has_next()); | ||||||
|  | 
 | ||||||
|  |                     let mut elements = Vec::new(); | ||||||
|  |                     for (item_index, item) in playlist.borrow().iter().enumerate() { | ||||||
|  |                         elements.push(PlaylistElement { | ||||||
|  |                             item: item_index, | ||||||
|  |                             track: 0, | ||||||
|  |                             title: item.recording.work.get_title(), | ||||||
|  |                             subtitle: Some(item.recording.get_performers()), | ||||||
|  |                             playable: false, | ||||||
|  |                         }); | ||||||
|  | 
 | ||||||
|  |                         for (track_index, track) in item.tracks.iter().enumerate() { | ||||||
|  |                             let mut parts = Vec::<String>::new(); | ||||||
|  |                             for part in &track.work_parts { | ||||||
|  |                                 parts.push(item.recording.work.parts[*part].title.clone()); | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|  |                             let title = if parts.is_empty() { | ||||||
|  |                                 gettext("Unknown") | ||||||
|  |                             } else { | ||||||
|  |                                 parts.join(", ") | ||||||
|  |                             }; | ||||||
|  | 
 | ||||||
|  |                             elements.push(PlaylistElement { | ||||||
|  |                                 item: item_index, | ||||||
|  |                                 track: track_index, | ||||||
|  |                                 title: title, | ||||||
|  |                                 subtitle: None, | ||||||
|  |                                 playable: true, | ||||||
|  |                             }); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     list.show_items(elements); | ||||||
|  |                 } | ||||||
|  |             )); | ||||||
|  | 
 | ||||||
|  |             player.add_track_cb(clone!( | ||||||
|  |                 @strong player, | ||||||
|  |                 @strong playlist, | ||||||
|  |                 @strong self.previous_button as previous_button, | ||||||
|  |                 @strong self.next_button as next_button, | ||||||
|  |                 @strong self.title_label as title_label, | ||||||
|  |                 @strong self.subtitle_label as subtitle_label, | ||||||
|  |                 @strong self.position_label as position_label, | ||||||
|  |                 @strong self.current_item as self_item, | ||||||
|  |                 @strong self.current_track as self_track, | ||||||
|  |                 @strong self.list as list | ||||||
|  |                 => move |current_item, current_track| { | ||||||
|  |                     previous_button.set_sensitive(player.has_previous()); | ||||||
|  |                     next_button.set_sensitive(player.has_next()); | ||||||
|  | 
 | ||||||
|  |                     let item = &playlist.borrow()[current_item]; | ||||||
|  |                     let track = &item.tracks[current_track]; | ||||||
|  | 
 | ||||||
|  |                     let mut parts = Vec::<String>::new(); | ||||||
|  |                     for part in &track.work_parts { | ||||||
|  |                         parts.push(item.recording.work.parts[*part].title.clone()); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     let mut title = item.recording.work.get_title(); | ||||||
|  |                     if !parts.is_empty() { | ||||||
|  |                         title = format!("{}: {}", title, parts.join(", ")); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     title_label.set_text(&title); | ||||||
|  |                     subtitle_label.set_text(&item.recording.get_performers()); | ||||||
|  |                     position_label.set_text("0:00"); | ||||||
|  | 
 | ||||||
|  |                     self_item.replace(current_item); | ||||||
|  |                     self_track.replace(current_track); | ||||||
|  |                     list.update(); | ||||||
|  |                 } | ||||||
|  |             )); | ||||||
|  | 
 | ||||||
|  |             player.add_duration_cb(clone!( | ||||||
|  |                 @strong self.duration_label as duration_label, | ||||||
|  |                 @strong self.position as position | ||||||
|  |                 => move |ms| { | ||||||
|  |                     let min = ms / 60000; | ||||||
|  |                     let sec = (ms % 60000) / 1000; | ||||||
|  |                     duration_label.set_text(&format!("{}:{:02}", min, sec)); | ||||||
|  |                     position.set_upper(ms as f64); | ||||||
|  |                 } | ||||||
|  |             )); | ||||||
|  | 
 | ||||||
|  |             player.add_playing_cb(clone!( | ||||||
|  |                 @strong self.play_button as play_button, | ||||||
|  |                 @strong self.play_image as play_image, | ||||||
|  |                 @strong self.pause_image as pause_image | ||||||
|  |                 => move |playing| { | ||||||
|  |                     if let Some(child) = play_button.get_child() { | ||||||
|  |                         play_button.remove( &child); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     play_button.add(if playing { | ||||||
|  |                         &pause_image | ||||||
|  |                     } else { | ||||||
|  |                         &play_image | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |             )); | ||||||
|  | 
 | ||||||
|  |             player.add_position_cb(clone!( | ||||||
|  |                 @strong self.position_label as position_label, | ||||||
|  |                 @strong self.position as position, | ||||||
|  |                 @strong self.seeking as seeking | ||||||
|  |                 => move |ms| { | ||||||
|  |                     if !seeking.get() { | ||||||
|  |                         let min = ms / 60000; | ||||||
|  |                         let sec = (ms % 60000) / 1000; | ||||||
|  |                         position_label.set_text(&format!("{}:{:02}", min, sec)); | ||||||
|  |                         position.set_value(ms as f64); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             )); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_back_cb<F: Fn() -> () + 'static>(&self, cb: F) { | ||||||
|  |         self.back_cb.replace(Some(Box::new(cb))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -75,6 +75,7 @@ impl RecordingScreen { | ||||||
|                 file_name_label.set_halign(gtk::Align::Start); |                 file_name_label.set_halign(gtk::Align::Start); | ||||||
| 
 | 
 | ||||||
|                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); |                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                 vbox.set_border_width(6); | ||||||
|                 vbox.add(&title_label); |                 vbox.add(&title_label); | ||||||
|                 vbox.add(&file_name_label); |                 vbox.add(&file_name_label); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ impl WorkScreen { | ||||||
|                 performers_label.set_halign(gtk::Align::Start); |                 performers_label.set_halign(gtk::Align::Start); | ||||||
| 
 | 
 | ||||||
|                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); |                 let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); | ||||||
|  |                 vbox.set_border_width(6); | ||||||
|                 vbox.add(&work_label); |                 vbox.add(&work_label); | ||||||
|                 vbox.add(&performers_label); |                 vbox.add(&performers_label); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -88,12 +88,23 @@ where | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn select_index(&self, index: usize) { |     pub fn select_index(&self, index: usize) { | ||||||
|         self.widget.select_row(self.widget.get_row_at_index(index.try_into().unwrap()).as_ref()); |         self.widget.select_row( | ||||||
|  |             self.widget | ||||||
|  |                 .get_row_at_index(index.try_into().unwrap()) | ||||||
|  |                 .as_ref(), | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn show_items(&self, items: Vec<T>) { |     pub fn show_items(&self, items: Vec<T>) { | ||||||
|         self.items.replace(items); |         self.items.replace(items); | ||||||
|  |         self.update(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn invalidate_filter(&self) { | ||||||
|  |         self.widget.invalidate_filter(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn update(&self) { | ||||||
|         for child in self.widget.get_children() { |         for child in self.widget.get_children() { | ||||||
|             self.widget.remove(&child); |             self.widget.remove(&child); | ||||||
|         } |         } | ||||||
|  | @ -105,10 +116,6 @@ where | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn invalidate_filter(&self) { |  | ||||||
|         self.widget.invalidate_filter(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn clear_selection(&self) { |     pub fn clear_selection(&self) { | ||||||
|         self.widget.unselect_all(); |         self.widget.unselect_all(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -27,6 +27,10 @@ impl PersonList { | ||||||
|             |person: &Person| { |             |person: &Person| { | ||||||
|                 let label = gtk::Label::new(Some(&person.name_lf())); |                 let label = gtk::Label::new(Some(&person.name_lf())); | ||||||
|                 label.set_halign(gtk::Align::Start); |                 label.set_halign(gtk::Align::Start); | ||||||
|  |                 label.set_margin_start(6); | ||||||
|  |                 label.set_margin_end(6); | ||||||
|  |                 label.set_margin_top(6); | ||||||
|  |                 label.set_margin_bottom(6); | ||||||
|                 label.upcast() |                 label.upcast() | ||||||
|             }, |             }, | ||||||
|             clone!(@strong search_entry => move |person: &Person| { |             clone!(@strong search_entry => move |person: &Person| { | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ pub struct PlayerBar { | ||||||
|     play_image: gtk::Image, |     play_image: gtk::Image, | ||||||
|     pause_image: gtk::Image, |     pause_image: gtk::Image, | ||||||
|     player: Rc<RefCell<Option<Rc<Player>>>>, |     player: Rc<RefCell<Option<Rc<Player>>>>, | ||||||
|  |     playlist_cb: Rc<RefCell<Option<Box<dyn Fn() -> ()>>>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PlayerBar { | impl PlayerBar { | ||||||
|  | @ -31,10 +32,12 @@ impl PlayerBar { | ||||||
|         get_widget!(builder, gtk::Button, next_button); |         get_widget!(builder, gtk::Button, next_button); | ||||||
|         get_widget!(builder, gtk::Label, position_label); |         get_widget!(builder, gtk::Label, position_label); | ||||||
|         get_widget!(builder, gtk::Label, duration_label); |         get_widget!(builder, gtk::Label, duration_label); | ||||||
|  |         get_widget!(builder, gtk::Button, playlist_button); | ||||||
|         get_widget!(builder, gtk::Image, play_image); |         get_widget!(builder, gtk::Image, play_image); | ||||||
|         get_widget!(builder, gtk::Image, pause_image); |         get_widget!(builder, gtk::Image, pause_image); | ||||||
| 
 | 
 | ||||||
|         let player = Rc::new(RefCell::new(None::<Rc<Player>>)); |         let player = Rc::new(RefCell::new(None::<Rc<Player>>)); | ||||||
|  |         let playlist_cb = Rc::new(RefCell::new(None::<Box<dyn Fn() -> ()>>)); | ||||||
| 
 | 
 | ||||||
|         previous_button.connect_clicked(clone!(@strong player => move |_| { |         previous_button.connect_clicked(clone!(@strong player => move |_| { | ||||||
|             if let Some(player) = &*player.borrow() { |             if let Some(player) = &*player.borrow() { | ||||||
|  | @ -54,6 +57,12 @@ impl PlayerBar { | ||||||
|             } |             } | ||||||
|         })); |         })); | ||||||
| 
 | 
 | ||||||
|  |         playlist_button.connect_clicked(clone!(@strong playlist_cb => move |_| { | ||||||
|  |             if let Some(cb) = &*playlist_cb.borrow() { | ||||||
|  |                 cb(); | ||||||
|  |             } | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|         Self { |         Self { | ||||||
|             widget, |             widget, | ||||||
|             title_label, |             title_label, | ||||||
|  | @ -66,6 +75,7 @@ impl PlayerBar { | ||||||
|             play_image, |             play_image, | ||||||
|             pause_image, |             pause_image, | ||||||
|             player: player, |             player: player, | ||||||
|  |             playlist_cb: playlist_cb, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -75,7 +85,7 @@ impl PlayerBar { | ||||||
|         if let Some(player) = player { |         if let Some(player) = player { | ||||||
|             let playlist = Rc::new(RefCell::new(Vec::<PlaylistItem>::new())); |             let playlist = Rc::new(RefCell::new(Vec::<PlaylistItem>::new())); | ||||||
| 
 | 
 | ||||||
|             player.set_playlist_cb(clone!( |             player.add_playlist_cb(clone!( | ||||||
|                 @strong player, |                 @strong player, | ||||||
|                 @strong self.widget as widget, |                 @strong self.widget as widget, | ||||||
|                 @strong self.previous_button as previous_button, |                 @strong self.previous_button as previous_button, | ||||||
|  | @ -89,7 +99,7 @@ impl PlayerBar { | ||||||
|                 } |                 } | ||||||
|             )); |             )); | ||||||
| 
 | 
 | ||||||
|             player.set_track_cb(clone!( |             player.add_track_cb(clone!( | ||||||
|                 @strong player, |                 @strong player, | ||||||
|                 @strong playlist, |                 @strong playlist, | ||||||
|                 @strong self.previous_button as previous_button, |                 @strong self.previous_button as previous_button, | ||||||
|  | @ -120,7 +130,7 @@ impl PlayerBar { | ||||||
|                 } |                 } | ||||||
|             )); |             )); | ||||||
| 
 | 
 | ||||||
|             player.set_duration_cb(clone!( |             player.add_duration_cb(clone!( | ||||||
|                 @strong self.duration_label as duration_label |                 @strong self.duration_label as duration_label | ||||||
|                 => move |ms| { |                 => move |ms| { | ||||||
|                     let min = ms / 60000; |                     let min = ms / 60000; | ||||||
|  | @ -129,7 +139,7 @@ impl PlayerBar { | ||||||
|                 } |                 } | ||||||
|             )); |             )); | ||||||
| 
 | 
 | ||||||
|             player.set_playing_cb(clone!( |             player.add_playing_cb(clone!( | ||||||
|                 @strong self.play_button as play_button, |                 @strong self.play_button as play_button, | ||||||
|                 @strong self.play_image as play_image, |                 @strong self.play_image as play_image, | ||||||
|                 @strong self.pause_image as pause_image |                 @strong self.pause_image as pause_image | ||||||
|  | @ -146,7 +156,7 @@ impl PlayerBar { | ||||||
|                 } |                 } | ||||||
|             )); |             )); | ||||||
| 
 | 
 | ||||||
|             player.set_position_cb(clone!( |             player.add_position_cb(clone!( | ||||||
|                 @strong self.position_label as position_label |                 @strong self.position_label as position_label | ||||||
|                 => move |ms| { |                 => move |ms| { | ||||||
|                     let min = ms / 60000; |                     let min = ms / 60000; | ||||||
|  | @ -158,4 +168,8 @@ impl PlayerBar { | ||||||
|             self.widget.set_reveal_child(false); |             self.widget.set_reveal_child(false); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set_playlist_cb<F: Fn() -> () + 'static>(&self, cb: F) { | ||||||
|  |         self.playlist_cb.replace(Some(Box::new(cb))); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,6 +42,10 @@ impl PoeList { | ||||||
|             |poe: &PersonOrEnsemble| { |             |poe: &PersonOrEnsemble| { | ||||||
|                 let label = gtk::Label::new(Some(&poe.get_title())); |                 let label = gtk::Label::new(Some(&poe.get_title())); | ||||||
|                 label.set_halign(gtk::Align::Start); |                 label.set_halign(gtk::Align::Start); | ||||||
|  |                 label.set_margin_start(6); | ||||||
|  |                 label.set_margin_end(6); | ||||||
|  |                 label.set_margin_top(6); | ||||||
|  |                 label.set_margin_bottom(6); | ||||||
|                 label.upcast() |                 label.upcast() | ||||||
|             }, |             }, | ||||||
|             clone!(@strong search_entry => move |poe: &PersonOrEnsemble| { |             clone!(@strong search_entry => move |poe: &PersonOrEnsemble| { | ||||||
|  |  | ||||||
|  | @ -90,7 +90,6 @@ impl ObjectImpl for SelectorRowPriv { | ||||||
|         self.parent_constructed(object); |         self.parent_constructed(object); | ||||||
| 
 | 
 | ||||||
|         let row = object.downcast_ref::<SelectorRow>().unwrap(); |         let row = object.downcast_ref::<SelectorRow>().unwrap(); | ||||||
|         row.set_border_width(6); |  | ||||||
| 
 | 
 | ||||||
|         let child = self.child.borrow(); |         let child = self.child.borrow(); | ||||||
|         match child.as_ref() { |         match child.as_ref() { | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ pub struct Window { | ||||||
|     poe_list: Rc<PoeList>, |     poe_list: Rc<PoeList>, | ||||||
|     navigator: Rc<Navigator>, |     navigator: Rc<Navigator>, | ||||||
|     player_bar: PlayerBar, |     player_bar: PlayerBar, | ||||||
|  |     player_screen: PlayerScreen, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Window { | impl Window { | ||||||
|  | @ -38,6 +39,9 @@ impl Window { | ||||||
|         let backend = Rc::new(Backend::new()); |         let backend = Rc::new(Backend::new()); | ||||||
|         backend.clone().init(); |         backend.clone().init(); | ||||||
| 
 | 
 | ||||||
|  |         let player_screen = PlayerScreen::new(); | ||||||
|  |         stack.add_named(&player_screen.widget, "player_screen"); | ||||||
|  | 
 | ||||||
|         let poe_list = PoeList::new(backend.clone()); |         let poe_list = PoeList::new(backend.clone()); | ||||||
|         let navigator = Navigator::new(&empty_screen); |         let navigator = Navigator::new(&empty_screen); | ||||||
|         navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || { |         navigator.set_back_cb(clone!(@strong leaflet, @strong sidebar_box => move || { | ||||||
|  | @ -56,6 +60,7 @@ impl Window { | ||||||
|             poe_list, |             poe_list, | ||||||
|             navigator, |             navigator, | ||||||
|             player_bar, |             player_bar, | ||||||
|  |             player_screen, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         result.window.set_application(Some(app)); |         result.window.set_application(Some(app)); | ||||||
|  | @ -85,6 +90,18 @@ impl Window { | ||||||
|             })).show(); |             })).show(); | ||||||
|         })); |         })); | ||||||
| 
 | 
 | ||||||
|  |         result | ||||||
|  |             .player_bar | ||||||
|  |             .set_playlist_cb(clone!(@strong result => move || { | ||||||
|  |                 result.stack.set_visible_child_name("player_screen"); | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|  |         result | ||||||
|  |             .player_screen | ||||||
|  |             .set_back_cb(clone!(@strong result => move || { | ||||||
|  |                 result.stack.set_visible_child_name("content"); | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|         action!( |         action!( | ||||||
|             result.window, |             result.window, | ||||||
|             "preferences", |             "preferences", | ||||||
|  | @ -298,7 +315,8 @@ impl Window { | ||||||
|                         clone.poe_list.clone().reload(); |                         clone.poe_list.clone().reload(); | ||||||
| 
 | 
 | ||||||
|                         let player = clone.backend.get_player().unwrap(); |                         let player = clone.backend.get_player().unwrap(); | ||||||
|                         clone.player_bar.set_player(Some(player)); |                         clone.player_bar.set_player(Some(player.clone())); | ||||||
|  |                         clone.player_screen.set_player(Some(player)); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Elias Projahn
						Elias Projahn