diff --git a/musicdb-client/src/config_gui.toml b/musicdb-client/src/config_gui.toml new file mode 100644 index 0000000..70250d2 --- /dev/null +++ b/musicdb-client/src/config_gui.toml @@ -0,0 +1,32 @@ +font = '' + +[text] +# define the text displayed in the application. +# escape sequences: +# \t: song title +# \a: album name +# \A: artist name +# \s1.0;: set the scale (to the default: 1.0) +# \h1.0;: set the height-alignment (to the default: 1.0 / align text to be on one baseline) +# \cRRGGBB: set color to this hex value. +# \cFFFFFF: default color (white) +# \: (\\ => \, \# => #, \% => %, ...) +# custom properties: +# %% +# %_word% returns the first property that includes "word" +# %>Year=% returns the end of the first property that starts with "Year=", +# so if a song has "Year=2019", this would return "2019". +# %=Value% returns something if a property "Value" is found. +# IF: +# ?### +# If is not empty, the entire block will be replaced by the value generated by . +# If is empty, the entire block will be replaced by the value generated by . +# Examples: +# ?\A#by \A## +# If we know the artist's name, write "by " followed by the name, +# if not, don't write anything (## -> is empty) +# ?\t#\t#(no title found)# +# If we know the title, write it. If not, write "(no title found)" instead. + +status_bar = '''\t +\s0.5;?\A#\c505050by \c593D6E\A##?\a#?\A# ##\c505050on \c264524\a##?%>Year=%#\c808080 (%>Year=%)##''' diff --git a/musicdb-client/src/gui.rs b/musicdb-client/src/gui.rs index 64cc698..f4c3ce5 100755 --- a/musicdb-client/src/gui.rs +++ b/musicdb-client/src/gui.rs @@ -28,7 +28,7 @@ use speedy2d::{ Graphics2D, }; -use crate::{gui_screen::GuiScreen, gui_wrappers::WithFocusHotkey}; +use crate::{gui_screen::GuiScreen, gui_wrappers::WithFocusHotkey, textcfg}; pub enum GuiEvent { Refresh, @@ -50,6 +50,7 @@ pub fn main( let mut scroll_pixels_multiplier = 1.0; let mut scroll_lines_multiplier = 3.0; let mut scroll_pages_multiplier = 0.75; + let status_bar_text; match std::fs::read_to_string(&config_file) { Ok(cfg) => { if let Ok(table) = cfg.parse::() { @@ -85,8 +86,26 @@ pub fn main( { scroll_pages_multiplier = v; } + if let Some(t) = table.get("text").and_then(|v| v.as_table()) { + if let Some(v) = t.get("status_bar").and_then(|v| v.as_str()) { + match v.parse() { + Ok(v) => status_bar_text = v, + Err(e) => { + eprintln!("[toml] `text.status_bar couldn't be parsed: {e}`"); + std::process::exit(30); + } + } + } else { + eprintln!("[toml] missing the required `text.status_bar` string value."); + std::process::exit(30); + } + } else { + eprintln!("[toml] missing the required `[text]` section!"); + std::process::exit(30); + } } else { eprintln!("Couldn't parse config file {config_file:?} as toml!"); + std::process::exit(30); } } Err(e) => { @@ -94,7 +113,9 @@ pub fn main( if let Some(p) = config_file.parent() { _ = std::fs::create_dir_all(p); } - _ = std::fs::write(&config_file, "font = ''"); + if std::fs::write(&config_file, include_bytes!("config_gui.toml")).is_ok() { + eprintln!("[info] created a default config file."); + } std::process::exit(25); } } @@ -126,9 +147,14 @@ pub fn main( scroll_pixels_multiplier, scroll_lines_multiplier, scroll_pages_multiplier, + GuiConfig { status_bar_text }, )); } +pub struct GuiConfig { + pub status_bar_text: textcfg::TextBuilder, +} + pub struct Gui { pub event_sender: UserEventSender, pub database: Arc>, @@ -150,6 +176,7 @@ pub struct Gui { pub scroll_pixels_multiplier: f64, pub scroll_lines_multiplier: f64, pub scroll_pages_multiplier: f64, + pub gui_config: Option, } impl Gui { fn new( @@ -163,6 +190,7 @@ impl Gui { scroll_pixels_multiplier: f64, scroll_lines_multiplier: f64, scroll_pages_multiplier: f64, + gui_config: GuiConfig, ) -> Self { database.lock().unwrap().update_endpoints.push( musicdb_lib::data::database::UpdateEndpoint::Custom(Box::new(move |cmd| match cmd { @@ -227,6 +255,7 @@ impl Gui { scroll_pixels_multiplier, scroll_lines_multiplier, scroll_pages_multiplier, + gui_config: Some(gui_config), } } } @@ -430,6 +459,7 @@ pub struct DrawInfo<'a> { Dragging, Option>, )>, + pub gui_config: &'a GuiConfig, } /// Generic wrapper over anything that implements GuiElemTrait @@ -817,6 +847,7 @@ impl WindowHandler for Gui { ); let mut dblock = self.database.lock().unwrap(); let mut covers = self.covers.take().unwrap(); + let cfg = self.gui_config.take().unwrap(); let mut info = DrawInfo { actions: Vec::with_capacity(0), pos: Rectangle::new(Vec2::ZERO, self.size.into_f32()), @@ -830,6 +861,7 @@ impl WindowHandler for Gui { child_has_keyboard_focus: true, line_height: self.line_height, dragging: self.dragging.take(), + gui_config: &cfg, }; self.gui.draw(&mut info, graphics); let actions = std::mem::replace(&mut info.actions, Vec::with_capacity(0)); @@ -864,6 +896,7 @@ impl WindowHandler for Gui { } // cleanup drop(info); + self.gui_config = Some(cfg); self.covers = Some(covers); drop(dblock); for a in actions { diff --git a/musicdb-client/src/gui_playback.rs b/musicdb-client/src/gui_playback.rs index d5acd08..8df2f6b 100755 --- a/musicdb-client/src/gui_playback.rs +++ b/musicdb-client/src/gui_playback.rs @@ -31,20 +31,11 @@ impl CurrentSong { pub fn new(config: GuiElemCfg) -> Self { Self { config, - children: vec![ - GuiElem::new(Label::new( - GuiElemCfg::at(Rectangle::from_tuples((0.4, 0.0), (1.0, 0.5))), - "".to_owned(), - Color::from_int_rgb(180, 180, 210), - None, - Vec2::new(0.0, 1.0), - )), - GuiElem::new(AdvancedLabel::new( - GuiElemCfg::at(Rectangle::from_tuples((0.4, 0.5), (1.0, 1.0))), - Vec2::new(0.0, 0.0), - vec![], - )), - ], + children: vec![GuiElem::new(AdvancedLabel::new( + GuiElemCfg::at(Rectangle::from_tuples((0.4, 0.0), (1.0, 1.0))), + Vec2::new(0.0, 0.5), + vec![], + ))], cover_pos: Rectangle::new(Vec2::ZERO, Vec2::ZERO), covers: VecDeque::new(), prev_song: None, @@ -140,139 +131,34 @@ impl GuiElemTrait for CurrentSong { // redraw if self.config.redraw { self.config.redraw = false; - let (name, subtext) = if let Some(song) = new_song { - if let Some(song) = info.database.get_song(&song) { - let sub = match ( - info.database.artists().get(&song.artist), - song.album - .as_ref() - .and_then(|id| info.database.albums().get(id)), - ) { - (None, None) => vec![], - (Some(artist), None) => vec![ - ( - Content::new("by ".to_owned(), Self::color_by(0.0)), - 1.0, - 1.0, - ), - ( - Content::new(artist.name.to_owned(), Self::color_artist(0.0)), - 1.0, - 1.0, - ), - ], - (None, Some(album)) => vec![ - (Content::new(String::new(), Color::TRANSPARENT), 0.0, 1.0), - ( - Content::new("on ".to_owned(), Self::color_on(0.0)), - 1.0, - 1.0, - ), - ( - Content::new(album.name.to_owned(), Self::color_album(0.0)), - 1.0, - 1.0, - ), - ], - (Some(artist), Some(album)) => vec![ - ( - Content::new("by ".to_owned(), Self::color_by(0.0)), - 1.0, - 1.0, - ), - ( - Content::new( - format!("{} ", artist.name), - Self::color_artist(0.0), - ), - 1.0, - 1.0, - ), - ( - Content::new("on ".to_owned(), Self::color_on(0.0)), - 1.0, - 1.0, - ), - ( - Content::new(album.name.to_owned(), Self::color_album(0.0)), - 1.0, - 1.0, - ), - ], - }; - (song.title.clone(), sub) - } else { - ( - "< song not in db >".to_owned(), - vec![( - Content::new( - "you may need to restart the client to resync the database" - .to_owned(), - Color::from_rgb(0.8, 0.5, 0.5), - ), - 1.0, - 1.0, - )], - ) - } - } else { - (String::new(), vec![]) - }; - *self.children[0] - .try_as_mut::