mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-03-10 14:13:53 +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…
Reference in New Issue
Block a user