mirror of
				https://github.com/Dummi26/musicdb.git
				synced 2025-10-31 03:55:25 +01:00 
			
		
		
		
	added some comments
This commit is contained in:
		
							parent
							
								
									5add9c477e
								
							
						
					
					
						commit
						0ae0126f04
					
				| @ -204,27 +204,36 @@ pub trait GuiElemTrait { | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| /// The config for any gui element.
 | ||||
| pub struct GuiElemCfg { | ||||
|     pub enabled: bool, | ||||
|     /// if true, indicates that something (text size, screen size, ...) has changed
 | ||||
|     /// and you should probably relayout and redraw from scratch.
 | ||||
|     pub redraw: bool, | ||||
|     /// Position relative to the parent where this element should be drawn.
 | ||||
|     /// ((0, 0), (1, 1)) is the default and fills all available space.
 | ||||
|     /// ((0, 0.5), (0.5, 1)) fills the bottom left quarter.
 | ||||
|     pub pos: Rectangle, | ||||
|     /// the pixel position after the last call to draw().
 | ||||
|     /// in draw, use info.pos instead, as pixel_pos is only updated *after* draw().
 | ||||
|     /// this can act like a "previous pos" field within draw.
 | ||||
|     pub pixel_pos: Rectangle, | ||||
|     /// which mouse buttons were pressed down while the mouse was on this element and haven't been released since? (Left/Middle/Right)
 | ||||
|     pub mouse_down: (bool, bool, bool), | ||||
|     /// Set this to true to receive mouse click events when the mouse is within this element's bounds
 | ||||
|     pub mouse_events: bool, | ||||
|     /// Set this to true to receive scroll events when the mouse is within this element's bounds
 | ||||
|     pub scroll_events: bool, | ||||
|     /// allows elements to watch all keyboard events, regardless of keyboard focus.
 | ||||
|     pub keyboard_events_watch: bool, | ||||
|     /// indicates that this element can have the keyboard focus
 | ||||
|     pub keyboard_events_focus: bool, | ||||
|     /// index of the child that has keyboard focus. if usize::MAX, `self` has focus.
 | ||||
|     /// will automatically be changed when Tab is pressed. [TODO]
 | ||||
|     /// will automatically be changed when Tab is pressed (Tab skips elements with keyboard_events_focus == false)
 | ||||
|     pub keyboard_focus_index: usize, | ||||
|     /// if this is true and ResetKeyboardFocus is returned, this element may get the keyboard focus (guaranteed if no other element has this set to true)
 | ||||
|     pub request_keyboard_focus: bool, | ||||
|     /// if this is true, things can be dragged into this element via drag-n-drop
 | ||||
|     pub drag_target: bool, | ||||
| } | ||||
| impl GuiElemCfg { | ||||
| @ -281,9 +290,10 @@ pub enum GuiAction { | ||||
|     OpenMain, | ||||
|     SetIdle(bool), | ||||
|     OpenSettings(bool), | ||||
|     /// Build the GuiAction(s) later, when we have access to the Database (can turn an AlbumId into a QueueContent::Folder, etc)
 | ||||
|     Build(Box<dyn FnOnce(&mut Database) -> Vec<Self>>), | ||||
|     SendToServer(Command), | ||||
|     /// unfocuses all gui elements, then assigns keyboard focus to one with config().request_keyboard_focus == true.
 | ||||
|     /// unfocuses all gui elements, then assigns keyboard focus to one with config().request_keyboard_focus == true if there is one.
 | ||||
|     ResetKeyboardFocus, | ||||
|     SetDragging( | ||||
|         Option<( | ||||
| @ -292,6 +302,7 @@ pub enum GuiAction { | ||||
|         )>, | ||||
|     ), | ||||
|     SetLineHeight(f32), | ||||
|     /// Run a custom closure with mutable access to the Gui struct
 | ||||
|     Do(Box<dyn FnMut(&mut Gui)>), | ||||
|     Exit, | ||||
| } | ||||
| @ -301,6 +312,9 @@ pub enum Dragging { | ||||
|     Song(SongId), | ||||
|     Queue(Queue), | ||||
| } | ||||
| 
 | ||||
| /// GuiElems have access to this within draw.
 | ||||
| /// Except for `actions`, they should not change any of these values - GuiElem::draw will handle everything automatically.
 | ||||
| pub struct DrawInfo<'a> { | ||||
|     pub actions: Vec<GuiAction>, | ||||
|     pub pos: Rectangle, | ||||
| @ -316,6 +330,7 @@ pub struct DrawInfo<'a> { | ||||
|     pub line_height: f32, | ||||
| } | ||||
| 
 | ||||
| /// Generic wrapper over anything that implements GuiElemTrait
 | ||||
| pub struct GuiElem { | ||||
|     pub inner: Box<dyn GuiElemTrait>, | ||||
| } | ||||
|  | ||||
| @ -7,6 +7,13 @@ use crate::{ | ||||
|     gui_text::Label, | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| Some basic structs to use everywhere. | ||||
| Mostly containers for other GuiElems. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| /// A simple container for zero, one, or multiple child GuiElems. Can optionally fill the background with a color.
 | ||||
| #[derive(Clone)] | ||||
| pub struct Panel { | ||||
|  | ||||
| @ -14,6 +14,13 @@ use crate::{ | ||||
|     gui_wrappers::WithFocusHotkey, | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| This is responsible for showing the library, | ||||
| with Regex search and drag-n-drop. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct LibraryBrowser { | ||||
|     config: GuiElemCfg, | ||||
|  | ||||
| @ -9,6 +9,13 @@ use crate::{ | ||||
|     gui_text::Label, | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| Components for the StatusBar. | ||||
| This file could probably have a better name. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct CurrentSong { | ||||
|     config: GuiElemCfg, | ||||
|  | ||||
| @ -20,6 +20,15 @@ use crate::{ | ||||
|     gui_text::Label, | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| 
 | ||||
| This is responsible for showing the current queue, | ||||
| with drag-n-drop only if the mouse leaves the element before it is released, | ||||
| because simple clicks have to be GoTo events. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct QueueViewer { | ||||
|     config: GuiElemCfg, | ||||
|  | ||||
| @ -12,7 +12,15 @@ use crate::{ | ||||
|     gui_text::Label, | ||||
| }; | ||||
| 
 | ||||
| /// calculates f(p) (f(x) = 3x^2 - 2x^3)):
 | ||||
| /* | ||||
| 
 | ||||
| The root gui element. | ||||
| Contains the Library, Queue, StatusBar, and sometimes Settings elements. | ||||
| Resizes these elements to show/hide the settings menu and to smoothly switch to/from idle mode. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| /// calculates f(p), where f(x) = 3x^2 - 2x^3, because
 | ||||
| /// f(0) = 0
 | ||||
| /// f(0.5) = 0.5
 | ||||
| /// f(1) = 1
 | ||||
| @ -36,9 +44,6 @@ pub struct GuiScreen { | ||||
|     pub prev_mouse_pos: Vec2, | ||||
| } | ||||
| impl GuiScreen { | ||||
|     fn i_statusbar() -> usize { | ||||
|         0 | ||||
|     } | ||||
|     pub fn new( | ||||
|         config: GuiElemCfg, | ||||
|         line_height: f32, | ||||
|  | ||||
| @ -10,6 +10,13 @@ use speedy2d::{ | ||||
| 
 | ||||
| use crate::gui::{GuiAction, GuiElem, GuiElemCfg, GuiElemTrait}; | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| Some basic structs to use everywhere, | ||||
| except they are all text-related. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct Label { | ||||
|     config: GuiElemCfg, | ||||
|  | ||||
| @ -18,7 +18,9 @@ use super::{ | ||||
| }; | ||||
| 
 | ||||
| pub struct Database { | ||||
|     /// the path to the file used to save/load the data
 | ||||
|     db_file: PathBuf, | ||||
|     /// the path to the directory containing the actual music and cover image files
 | ||||
|     pub lib_directory: PathBuf, | ||||
|     artists: HashMap<ArtistId, Artist>, | ||||
|     albums: HashMap<AlbumId, Album>, | ||||
| @ -26,13 +28,18 @@ pub struct Database { | ||||
|     covers: HashMap<CoverId, DatabaseLocation>, | ||||
|     // TODO! make sure this works out for the server AND clients
 | ||||
|     // cover_cache: HashMap<CoverId, Vec<u8>>,
 | ||||
|     // These will be used for autosave once that gets implemented
 | ||||
|     db_data_file_change_first: Option<Instant>, | ||||
|     db_data_file_change_last: Option<Instant>, | ||||
|     pub queue: Queue, | ||||
|     /// if the database receives an update, it will inform all of its clients so they can stay in sync.
 | ||||
|     /// this is a list containing all the clients.
 | ||||
|     pub update_endpoints: Vec<UpdateEndpoint>, | ||||
|     /// true if a song is/should be playing
 | ||||
|     pub playing: bool, | ||||
|     pub command_sender: Option<mpsc::Sender<Command>>, | ||||
| } | ||||
| // for custom server implementations, this enum should allow you to deal with updates from any context (writers such as tcp streams, sync/async mpsc senders, or via closure as a fallback)
 | ||||
| pub enum UpdateEndpoint { | ||||
|     Bytes(Box<dyn Write + Sync + Send>), | ||||
|     CmdChannel(mpsc::Sender<Arc<Command>>), | ||||
| @ -41,6 +48,7 @@ pub enum UpdateEndpoint { | ||||
| } | ||||
| 
 | ||||
| impl Database { | ||||
|     /// TODO!
 | ||||
|     fn panic(&self, msg: &str) -> ! { | ||||
|         // custom panic handler
 | ||||
|         // make a backup
 | ||||
| @ -50,6 +58,7 @@ impl Database { | ||||
|     pub fn get_path(&self, location: &DatabaseLocation) -> PathBuf { | ||||
|         self.lib_directory.join(&location.rel_path) | ||||
|     } | ||||
|     // NOTE: just use `songs` directly? not sure yet...
 | ||||
|     pub fn get_song(&self, song: &SongId) -> Option<&Song> { | ||||
|         self.songs.get(song) | ||||
|     } | ||||
| @ -72,6 +81,7 @@ impl Database { | ||||
|         } | ||||
|         id | ||||
|     } | ||||
|     /// used internally
 | ||||
|     pub fn add_song_new_nomagic(&mut self, mut song: Song) -> SongId { | ||||
|         for key in 0.. { | ||||
|             if !self.songs.contains_key(&key) { | ||||
| @ -89,6 +99,7 @@ impl Database { | ||||
|         let id = self.add_artist_new_nomagic(artist); | ||||
|         id | ||||
|     } | ||||
|     /// used internally
 | ||||
|     fn add_artist_new_nomagic(&mut self, mut artist: Artist) -> ArtistId { | ||||
|         for key in 0.. { | ||||
|             if !self.artists.contains_key(&key) { | ||||
| @ -110,6 +121,7 @@ impl Database { | ||||
|         } | ||||
|         id | ||||
|     } | ||||
|     /// used internally
 | ||||
|     fn add_album_new_nomagic(&mut self, mut album: Album) -> AlbumId { | ||||
|         for key in 0.. { | ||||
|             if !self.albums.contains_key(&key) { | ||||
|  | ||||
| @ -17,11 +17,13 @@ pub type ArtistId = u64; | ||||
| pub type CoverId = u64; | ||||
| 
 | ||||
| #[derive(Clone, Default, Debug)] | ||||
| /// general data for songs, albums and artists
 | ||||
| pub struct GeneralData { | ||||
|     pub tags: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| /// the location of a file relative to the lib directory, often Artist/Album/Song.ext or similar
 | ||||
| pub struct DatabaseLocation { | ||||
|     pub rel_path: PathBuf, | ||||
| } | ||||
|  | ||||
| @ -81,6 +81,8 @@ pub fn run_server( | ||||
|     sender_sender: Option<tokio::sync::mpsc::Sender<mpsc::Sender<Command>>>, | ||||
| ) { | ||||
|     let mut player = Player::new().unwrap(); | ||||
|     // commands sent to this will be handeled later in this function in an infinite loop.
 | ||||
|     // these commands are sent to the database asap.
 | ||||
|     let (command_sender, command_receiver) = mpsc::channel(); | ||||
|     if let Some(s) = sender_sender { | ||||
|         s.blocking_send(command_sender.clone()).unwrap(); | ||||
| @ -91,6 +93,7 @@ pub fn run_server( | ||||
|             Ok(v) => { | ||||
|                 let command_sender = command_sender.clone(); | ||||
|                 let db = Arc::clone(&database); | ||||
|                 // each connection gets its own thread, but they will be idle most of the time (waiting for data on the tcp stream)
 | ||||
|                 thread::spawn(move || loop { | ||||
|                     if let Ok((mut connection, con_addr)) = v.accept() { | ||||
|                         eprintln!("[info] TCP connection accepted from {con_addr}."); | ||||
| @ -100,10 +103,15 @@ pub fn run_server( | ||||
|                             // sync database
 | ||||
|                             let mut db = db.lock().unwrap(); | ||||
|                             db.init_connection(&mut connection)?; | ||||
|                             // keep the client in sync:
 | ||||
|                             // the db will send all updates to the client once it is added to update_endpoints
 | ||||
|                             db.update_endpoints.push(UpdateEndpoint::Bytes(Box::new( | ||||
|                                 // try_clone is used here to split a TcpStream into Writer and Reader
 | ||||
|                                 connection.try_clone().unwrap(), | ||||
|                             ))); | ||||
|                             // drop the mutex lock
 | ||||
|                             drop(db); | ||||
|                             // read updates from the tcp stream and send them to the database, exit on EOF or Err
 | ||||
|                             loop { | ||||
|                                 if let Ok(command) = Command::from_bytes(&mut connection) { | ||||
|                                     command_sender.send(command).unwrap(); | ||||
| @ -121,6 +129,8 @@ pub fn run_server( | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     // for now, update the player 10 times a second so it can detect when a song has finished and start a new one.
 | ||||
|     // TODO: player should send a NextSong update to the mpsc::Sender to wake up this thread
 | ||||
|     let dur = Duration::from_secs_f32(0.1); | ||||
|     loop { | ||||
|         player.update(&mut database.lock().unwrap()); | ||||
|  | ||||
| @ -25,6 +25,7 @@ use musicdb_lib::data::database::Database; | ||||
| 
 | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     // parse args
 | ||||
|     let mut args = std::env::args().skip(1); | ||||
|     let mut tcp_addr = None; | ||||
|     let mut web_addr = None; | ||||
| @ -148,20 +149,7 @@ this help was shown because no arguments were provided." | ||||
|         ); | ||||
|         exit(1); | ||||
|     }; | ||||
|     // database.add_song_new(Song::new(
 | ||||
|     //     "Amaranthe/Manifest/02 Make It Better.mp3".into(),
 | ||||
|     //     "Make It Better".to_owned(),
 | ||||
|     //     None,
 | ||||
|     //     None,
 | ||||
|     //     vec![],
 | ||||
|     //     None,
 | ||||
|     // ));
 | ||||
|     // let mut player = Player::new();
 | ||||
|     // eprintln!("[info] database.songs: {:?}", database.songs());
 | ||||
|     // database.save_database(Some("/tmp/dbfile".into())).unwrap();
 | ||||
|     // eprintln!("{}", database.get_song(&0).unwrap());
 | ||||
|     // database.queue.add_to_end(QueueContent::Song(1).into());
 | ||||
|     // player.update_and_restart_playing_song(&database);
 | ||||
|     // database can be shared by multiple threads using Arc<Mutex<_>>
 | ||||
|     let database = Arc::new(Mutex::new(database)); | ||||
|     if tcp_addr.is_some() || web_addr.is_some() { | ||||
|         if let Some(addr) = web_addr { | ||||
| @ -177,8 +165,4 @@ this help was shown because no arguments were provided." | ||||
|     } else { | ||||
|         eprintln!("nothing to do, not starting the server."); | ||||
|     } | ||||
|     // std::io::stdin().read_line(&mut String::new()).unwrap();
 | ||||
|     // dbg!(Update::from_bytes(&mut BufReader::new(
 | ||||
|     //     TcpStream::connect("127.0.0.1:26314".parse::<SocketAddr>().unwrap()).unwrap()
 | ||||
|     // )));
 | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mark
						Mark