Settings, took care of warnings

This commit is contained in:
Gvidas Juknevičius 2024-11-15 12:33:11 +02:00
parent 14ee1e69bc
commit a060161c64
Signed by: MCorange
GPG Key ID: 12B1346D720B7FBB
26 changed files with 323 additions and 126 deletions

View File

@ -1,3 +1,8 @@
[target.aarch64-unknown-linux-gnu] [target.aarch64-unknown-linux-gnu]
linker="aarch64-linux-gnu-gcc" linker="aarch64-linux-gnu-gcc"
[env]
XMPD_MANIFEST_PATH="./manifest.json"
XMPD_SETTINGS_PATH="./settings.toml"
XMPD_CACHE_PATH="./cache"

87
Cargo.lock generated
View File

@ -1032,6 +1032,27 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "dispatch" name = "dispatch"
version = "0.2.0" version = "0.2.0"
@ -2406,6 +2427,12 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "orbclient" name = "orbclient"
version = "0.3.48" version = "0.3.48"
@ -2687,6 +2714,17 @@ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
] ]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"
@ -2920,6 +2958,15 @@ dependencies = [
"syn 2.0.87", "syn 2.0.87",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.6" version = "0.10.6"
@ -3255,11 +3302,26 @@ dependencies = [
"syn 2.0.87", "syn 2.0.87",
] ]
[[package]]
name = "toml"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.22",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.8" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
@ -3279,6 +3341,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde",
"serde_spanned",
"toml_datetime", "toml_datetime",
"winnow 0.6.20", "winnow 0.6.20",
] ]
@ -4322,19 +4386,13 @@ dependencies = [
"anyhow", "anyhow",
"camino", "camino",
"clap", "clap",
"dirs",
"env_logger", "env_logger",
"log", "log",
"xmpd-cli", "xmpd-cli",
"xmpd-gui", "xmpd-gui",
"xmpd-manifest", "xmpd-manifest",
] "xmpd-settings",
[[package]]
name = "xmpd-derive"
version = "2.0.0"
dependencies = [
"quote",
"syn 2.0.87",
] ]
[[package]] [[package]]
@ -4354,8 +4412,8 @@ dependencies = [
"log", "log",
"tokio", "tokio",
"uuid", "uuid",
"xmpd-derive",
"xmpd-manifest", "xmpd-manifest",
"xmpd-settings",
] ]
[[package]] [[package]]
@ -4369,6 +4427,17 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "xmpd-settings"
version = "2.0.0"
dependencies = [
"anyhow",
"egui 0.27.2",
"lazy_static",
"serde",
"toml",
]
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "3.15.2" version = "3.15.2"

View File

@ -4,7 +4,9 @@ members=[
"xmpd-core", "xmpd-core",
"xmpd-manifest", "xmpd-manifest",
"xmpd-gui", "xmpd-gui",
"xmpd-cli", "xmpd-dl", "xmpd-derive", #"xmpd-cli",
"xmpd-dl",
"xmpd-settings",
# "xmpd-tui" # "xmpd-tui"
] ]
@ -25,7 +27,7 @@ bitflags = { version = "2.6.0", features = ["serde"] }
camino = "1.1.6" camino = "1.1.6"
clap = { version = "4.5.4", features = ["derive"] } clap = { version = "4.5.4", features = ["derive"] }
eframe = "0.27.2" eframe = "0.27.2"
egui = { version = "0.27.2", features = ["color-hex"] } egui = { version = "0.27.2", features = ["color-hex", "serde"] }
egui_extras = { version = "0.27.2", features = ["all_loaders"] } egui_extras = { version = "0.27.2", features = ["all_loaders"] }
env_logger = "0.11.3" env_logger = "0.11.3"
futures = "0.3.30" futures = "0.3.30"
@ -44,4 +46,5 @@ tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "process"
url = { version = "2.5.0", features = ["serde"] } url = { version = "2.5.0", features = ["serde"] }
uuid = { version = "1.11.0", features = ["serde", "v4"] } uuid = { version = "1.11.0", features = ["serde", "v4"] }
windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console"] } windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console"] }
zip-extensions = "0.6.2" zip-extensions = "0.6.20"
dirs="5.0.1"

View File

@ -21,11 +21,13 @@ name="xmpd"
path="src/main.rs" path="src/main.rs"
[dependencies] [dependencies]
xmpd-cli={ path="../xmpd-cli" } xmpd-cli.path="../xmpd-cli"
xmpd-gui={ path="../xmpd-gui" } xmpd-gui.path="../xmpd-gui"
xmpd-manifest={ path="../xmpd-manifest" } xmpd-manifest.path="../xmpd-manifest"
xmpd-settings.path = "../xmpd-settings"
clap.workspace=true clap.workspace=true
camino.workspace = true camino.workspace = true
anyhow.workspace = true anyhow.workspace = true
log.workspace = true log.workspace = true
env_logger.workspace = true env_logger.workspace = true
dirs.workspace = true

View File

@ -1,18 +1,75 @@
use std::path::{Path, PathBuf}; use std::{path::PathBuf, str::FromStr};
#[derive(Debug, clap::Parser)] #[derive(Debug, clap::Parser)]
pub struct CliArgs { pub struct CliArgs {
/// Manifest path /// Manifest path
#[arg(long, short)] #[arg(long, short, default_value_t=get_default_manifest_path())]
manifest: Option<camino::Utf8PathBuf>, manifest: camino::Utf8PathBuf,
/// settings file path
#[arg(long, short, default_value_t=get_default_settings_path())]
settings: camino::Utf8PathBuf,
/// Cache dir path
#[arg(long, short, default_value_t=get_default_cache_path())]
cache: camino::Utf8PathBuf,
/// Debug mode /// Debug mode
#[arg(long, short)] #[arg(long, short)]
pub debug: bool, pub debug: bool,
} }
impl CliArgs { impl CliArgs {
pub fn manifest_path(&self) -> Option<PathBuf> { pub fn manifest_path(&self) -> PathBuf {
Some(self.manifest.clone()?.into_std_path_buf()) self.manifest.clone().into_std_path_buf()
}
pub fn settings_path(&self) -> PathBuf {
self.settings.clone().into_std_path_buf()
}
pub fn cache_path(&self) -> PathBuf {
self.cache.clone().into_std_path_buf()
} }
} }
#[allow(irrefutable_let_patterns)] // Broken?
fn get_default_settings_path() -> camino::Utf8PathBuf {
if let Ok(p) = std::env::var("XMPD_SETTINGS_PATH") {
if let Ok(p) = camino::Utf8PathBuf::from_str(&p) {
return p;
}
}
if let Some(mut p) = dirs::config_dir() {
p.push("xmpd");
p.push("config.toml");
return camino::Utf8PathBuf::from_path_buf(p).expect("Invalid os path");
}
unreachable!()
}
#[allow(irrefutable_let_patterns)] // Broken?
fn get_default_manifest_path() -> camino::Utf8PathBuf {
if let Ok(p) = std::env::var("XMPD_MANIFEST_PATH") {
if let Ok(p) = camino::Utf8PathBuf::from_str(&p) {
return p;
}
}
if let Some(mut p) = dirs::config_dir() {
p.push("xmpd");
p.push("manifest.json");
return camino::Utf8PathBuf::from_path_buf(p).expect("Invalid os path");
}
unreachable!()
}
#[allow(irrefutable_let_patterns)] // Broken?
fn get_default_cache_path() -> camino::Utf8PathBuf {
if let Ok(p) = std::env::var("XMPD_CACHE_PATH") {
if let Ok(p) = camino::Utf8PathBuf::from_str(&p) {
return p;
}
}
if let Some(mut p) = dirs::cache_dir() {
p.push("xmpd");
return camino::Utf8PathBuf::from_path_buf(p).expect("Invalid os path");
}
unreachable!()
}

View File

@ -8,10 +8,11 @@ pub fn init(cliargs: &CliArgs) {
let level = if cliargs.debug { LevelFilter::Debug } else { LevelFilter::Info }; let level = if cliargs.debug { LevelFilter::Debug } else { LevelFilter::Info };
env_logger::builder() env_logger::builder()
.format_timestamp(None) .format_timestamp(None)
.filter(Some("xmpd_core"), level) .filter(Some("xmpd"), level)
.filter(Some("xmpd_cli"), level) .filter(Some("xmpd_cli"), level)
.filter(Some("xmpd_gui"), level) .filter(Some("xmpd_gui"), level)
.filter(Some("xmpd_manifest"), level) .filter(Some("xmpd_manifest"), level)
.filter(Some("xmpd_config"), level)
.filter(Some("xmpd_dl"), level) .filter(Some("xmpd_dl"), level)
.init(); .init();
} }

View File

@ -1,5 +1,3 @@
use std::path::{Path, PathBuf};
use clap::Parser; use clap::Parser;
mod cli; mod cli;
@ -10,12 +8,8 @@ type Result<T> = anyhow::Result<T>;
fn main() -> Result<()> { fn main() -> Result<()> {
let cliargs = cli::CliArgs::parse(); let cliargs = cli::CliArgs::parse();
logger::init(&cliargs); logger::init(&cliargs);
let manifest_path; log::debug!("Cli: {cliargs:?}");
if let Some(mp) = cliargs.manifest_path() { xmpd_settings::Settings::get()?.load(Some(cliargs.settings_path()))?;
manifest_path = mp; xmpd_gui::start(cliargs.manifest_path())?;
} else {
manifest_path = PathBuf::from("manifest.json");
};
xmpd_gui::start(manifest_path)?;
Ok(()) Ok(())
} }

View File

@ -1,26 +0,0 @@
use proc_macro::TokenStream;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate proc_macro;
#[proc_macro_derive(UiComponent)]
pub fn ui_comp_impl(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let vis = &ast.vis;
let attr = &ast.attrs;
let data = &ast.data;
let generics = &ast.generics;
let gen = quote! {
impl xmp
};
gen.into()
}

View File

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View File

@ -19,7 +19,7 @@ bench = false
[dependencies] [dependencies]
xmpd-manifest.path = "../xmpd-manifest" xmpd-manifest.path = "../xmpd-manifest"
xmpd-derive.path = "../xmpd-derive" xmpd-settings.path = "../xmpd-settings"
egui.workspace = true egui.workspace = true
eframe.workspace = true eframe.workspace = true
tokio.workspace = true tokio.workspace = true

View File

@ -43,12 +43,13 @@ impl CompUi for LeftNav {
} }
fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, author: Option<&str>, song_count: usize, width: f32) { fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, author: Option<&str>, song_count: usize, width: f32) {
let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
let wdg_rect = ui.horizontal(|ui| { let wdg_rect = ui.horizontal(|ui| {
ui.set_width(width); ui.set_width(width);
ui.add_space(5.0); ui.add_space(5.0);
ui.add( ui.add(
egui::Image::new(crate::data::NOTE_ICON) egui::Image::new(crate::data::NOTE_ICON)
.tint(crate::data::C_ACCENT) .tint(theme.accent_color)
.fit_to_exact_size(egui::Vec2::new(32.0, 32.0)) .fit_to_exact_size(egui::Vec2::new(32.0, 32.0))
); );
ui.vertical(|ui| { ui.vertical(|ui| {
@ -57,7 +58,7 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, au
ui.label( ui.label(
RichText::new(title) RichText::new(title)
.size(10.0) .size(10.0)
.color(crate::data::C_ACCENT) .color(theme.accent_color)
); );
} else { } else {
ui.label( ui.label(
@ -69,13 +70,13 @@ fn add_playlist_tab(ui: &mut egui::Ui, pid: &Option<uuid::Uuid>, title: &str, au
if let Some(author) = author { if let Some(author) = author {
ui.monospace( ui.monospace(
RichText::new(format!("By {author}")) RichText::new(format!("By {author}"))
.color(crate::data::C_TEXT_DIM) .color(theme.dim_text_color)
.size(8.0) .size(8.0)
); );
} }
ui.monospace( ui.monospace(
RichText::new(format!("{song_count} songs")) RichText::new(format!("{song_count} songs"))
.color(crate::data::C_TEXT_DIM) .color(theme.dim_text_color)
.size(8.0) .size(8.0)
); );
}); });

View File

@ -1,4 +1,4 @@
use std::sync::{MutexGuard, PoisonError}; use std::sync::MutexGuard;
use crate::GuiState; use crate::GuiState;

View File

@ -13,7 +13,8 @@ component_register!(Player);
impl CompUi for Player { impl CompUi for Player {
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> { fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
let theme = xmpd_settings::Settings::get()?.theme.clone();
let avail = ui.available_size(); let avail = ui.available_size();
ui.vertical_centered_justified(|ui| { ui.vertical_centered_justified(|ui| {
ui.add_space(3.0); ui.add_space(3.0);
@ -25,7 +26,7 @@ impl CompUi for Player {
.show_value(false); .show_value(false);
ui.style_mut().spacing.slider_width = avail.x * 0.90; ui.style_mut().spacing.slider_width = avail.x * 0.90;
let s = Stroke { let s = Stroke {
color: crate::data::C_ACCENT, color: theme.accent_color,
width: 2.0 width: 2.0
}; };
ui.style_mut().visuals.widgets.inactive.fg_stroke = s; ui.style_mut().visuals.widgets.inactive.fg_stroke = s;
@ -45,15 +46,15 @@ impl CompUi for Player {
let prev = egui::Image::new(crate::data::PREV_ICON) let prev = egui::Image::new(crate::data::PREV_ICON)
.tint(crate::data::C_ACCENT) .tint(theme.accent_color)
.sense(Sense::click()) .sense(Sense::click())
.max_size(Vec2::new(16.0, 16.0)); .max_size(Vec2::new(16.0, 16.0));
let pp = egui::Image::new(pp) let pp = egui::Image::new(pp)
.tint(crate::data::C_ACCENT) .tint(theme.accent_color)
.sense(Sense::click()) .sense(Sense::click())
.max_size(Vec2::new(16.0, 16.0)); .max_size(Vec2::new(16.0, 16.0));
let next = egui::Image::new(crate::data::NEXT_ICON) let next = egui::Image::new(crate::data::NEXT_ICON)
.tint(crate::data::C_ACCENT) .tint(theme.accent_color)
.sense(Sense::click()) .sense(Sense::click())
.max_size(Vec2::new(16.0, 16.0)); .max_size(Vec2::new(16.0, 16.0));
if ui.add(prev).clicked() {} if ui.add(prev).clicked() {}

View File

@ -1,6 +1,6 @@
use egui::{Color32, RichText, Vec2}; use egui::{RichText, Vec2};
use song_list_nav::SearchType; use song_list_nav::SearchType;
use xmpd_manifest::{query::QueryType, song::Song, store::BaseStore}; use xmpd_manifest::{song::Song, store::BaseStore};
use super::{CompGetter, CompUi}; use super::{CompGetter, CompUi};
pub mod song_list_nav; pub mod song_list_nav;
@ -79,23 +79,23 @@ impl CompUi for SongList {
} }
fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) { fn display_song_tab(ui: &mut egui::Ui, sid: &uuid::Uuid, song: &Song) {
let theme = handle_error_ui!(xmpd_settings::Settings::get()).theme.clone();
let rct = ui.horizontal(|ui| { let rct = ui.horizontal(|ui| {
ui.add( ui.add(
egui::Image::new(crate::data::NOTE_ICON) egui::Image::new(crate::data::NOTE_ICON)
.tint(crate::data::C_ACCENT) .tint(theme.accent_color)
.fit_to_exact_size(Vec2::new(32.0, 32.0)) .fit_to_exact_size(Vec2::new(32.0, 32.0))
); );
ui.vertical(|ui| { ui.vertical(|ui| {
let selected_song_id = {handle_error_ui!(SongList::get()).selected_song_id}; let selected_song_id = {handle_error_ui!(SongList::get()).selected_song_id};
if selected_song_id == *sid { if selected_song_id == *sid {
ui.label(RichText::new(song.name()).color(crate::data::C_ACCENT)); ui.label(RichText::new(song.name()).color(theme.accent_color));
} else { } else {
ui.label(song.name()); ui.label(song.name());
}; };
ui.monospace( ui.monospace(
RichText::new(format!("By {}", song.author())) RichText::new(format!("By {}", song.author()))
.color(crate::data::C_TEXT_DIM) .color(theme.dim_text_color)
.size(10.0) .size(10.0)
); );
}); });

View File

@ -16,10 +16,11 @@ component_register!(SongListNav);
impl CompUi for SongListNav { impl CompUi for SongListNav {
fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> { fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
let theme = xmpd_settings::Settings::get()?.theme.clone();
ui.horizontal(|ui| { ui.horizontal(|ui| {
let search_icon = egui::Image::new(crate::data::SEARCH_ICON) let search_icon = egui::Image::new(crate::data::SEARCH_ICON)
.fit_to_exact_size(egui::Vec2::new(16.0, 16.0)) .fit_to_exact_size(egui::Vec2::new(16.0, 16.0))
.tint(crate::data::C_ACCENT); .tint(theme.accent_color);
ui.add(search_icon); ui.add(search_icon);
{ {
ui.text_edit_singleline(&mut handle_error_ui!(SongListNav::get()).text); ui.text_edit_singleline(&mut handle_error_ui!(SongListNav::get()).text);

View File

@ -1,12 +1,8 @@
use super::CompUi; use super::CompUi;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TopNav; pub struct TopNav;
component_register!(TopNav);
impl CompUi for TopNav { impl CompUi for TopNav {
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> { fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
ui.add_space(3.0); ui.add_space(3.0);
@ -14,7 +10,15 @@ impl CompUi for TopNav {
ui.add_space(3.0); ui.add_space(3.0);
egui::menu::bar(ui, |ui| { egui::menu::bar(ui, |ui| {
ui.menu_button("File", |ui| { ui.menu_button("File", |ui| {
// TBD if ui.button("Settings").clicked() {
}
});
ui.menu_button("Manifest", |ui| {
if ui.button("Save").clicked() {
handle_error_ui!(state.manifest.save());
}
}); });
ui.menu_button("Help", |ui| { ui.menu_button("Help", |ui| {
if ui.button("Source").clicked() { if ui.button("Source").clicked() {
@ -22,12 +26,7 @@ impl CompUi for TopNav {
} }
}); });
ui.menu_button("Manifest", |ui| {
if ui.button("Save").clicked() {
handle_error_ui!(state.manifest.save());
}
});
}); });
}); });

View File

@ -1,18 +1,8 @@
// pub const APP_ICON: egui::ImageSource = egui::include_image!("../../assets/app_icon.png"); // pub const APP_ICON: egui::ImageSource = egui::include_image!("../../assets/app_icon.png");
pub const APP_ICON_BYTES: &[u8] = include_bytes!("../../assets/app_icon.png"); // pub const APP_ICON_BYTES: &[u8] = include_bytes!("../../assets/app_icon.png");
pub const NOTE_ICON: egui::ImageSource = egui::include_image!("../../assets/note.svg"); pub const NOTE_ICON: egui::ImageSource = egui::include_image!("../../assets/note.svg");
pub const SEARCH_ICON: egui::ImageSource = egui::include_image!("../../assets/search.svg"); pub const SEARCH_ICON: egui::ImageSource = egui::include_image!("../../assets/search.svg");
pub const PREV_ICON: egui::ImageSource = egui::include_image!("../../assets/prev.svg"); pub const PREV_ICON: egui::ImageSource = egui::include_image!("../../assets/prev.svg");
pub const NEXT_ICON: egui::ImageSource = egui::include_image!("../../assets/next.svg"); pub const NEXT_ICON: egui::ImageSource = egui::include_image!("../../assets/next.svg");
pub const PLAY_ICON: egui::ImageSource = egui::include_image!("../../assets/play.svg"); pub const PLAY_ICON: egui::ImageSource = egui::include_image!("../../assets/play.svg");
pub const PAUSE_ICON: egui::ImageSource = egui::include_image!("../../assets/pause.svg"); pub const PAUSE_ICON: egui::ImageSource = egui::include_image!("../../assets/pause.svg");
// TODO: Replace this with config for theming
// pub const C_ACCENT: egui::Color32 = egui::Color32::from_rgb(51, 51, 119);
pub const C_ACCENT: egui::Color32 = egui::Color32::from_rgb(5, 102, 146); // #0566F6
pub const C_PRIM_BG: egui::Color32 = egui::Color32::from_rgb(31, 34, 40); // #1F2228
pub const C_SEC_BG: egui::Color32 = egui::Color32::from_rgb(47, 53, 61); // #2f353d
pub const C_TEXT: egui::Color32 = egui::Color32::from_rgb(223, 223, 223); // #dfdfdf
pub const C_TEXT_DIM: egui::Color32 = egui::Color32::from_rgb(175, 175, 175 ); // #afafaf

View File

@ -1,8 +1,6 @@
#![feature(async_closure)] #![feature(async_closure)]
use std::{path::{Path, PathBuf}, time::Duration}; use std::{path::{Path, PathBuf}, time::Duration};
use egui::TextStyle;
use windows::WindowId;
use xmpd_manifest::{store::JsonStore, Manifest}; use xmpd_manifest::{store::JsonStore, Manifest};
#[macro_use] #[macro_use]
@ -20,21 +18,21 @@ type Result<T> = anyhow::Result<T>;
pub fn start(manifest_path: PathBuf) -> Result<()> { pub fn start(manifest_path: PathBuf) -> Result<()> {
let options = eframe::NativeOptions::default(); let options = eframe::NativeOptions::default();
let mut state = GuiState::new(&manifest_path)?; let mut state = GuiState::new(&manifest_path)?;
let theme = xmpd_settings::Settings::get()?.theme.clone();
let res = eframe::run_simple_native(W_NAME, options, move |ctx, _frame| { let res = eframe::run_simple_native(W_NAME, options, move |ctx, _frame| {
egui_extras::install_image_loaders(ctx); egui_extras::install_image_loaders(ctx);
state.windows.clone().draw_all(ctx, &mut state); state.windows.clone().draw_all(ctx, &mut state);
egui::CentralPanel::default() egui::CentralPanel::default()
.frame( .frame(
egui::Frame::none() egui::Frame::none()
.fill(data::C_PRIM_BG) .fill(theme.primary_bg_color)
.stroke(egui::Stroke::new( .stroke(egui::Stroke::new(
1.0, 1.0,
data::C_SEC_BG, theme.secondary_bg_color,
)), )),
) )
.show(ctx, |ui| { .show(ctx, |ui| {
ui.style_mut().visuals.override_text_color = Some(crate::data::C_TEXT); ui.style_mut().visuals.override_text_color = Some(theme.text_color);
main_window::draw(ui, &mut state) main_window::draw(ui, &mut state)
}); });
ctx.request_repaint_after(Duration::from_millis(500)); ctx.request_repaint_after(Duration::from_millis(500));

View File

@ -1,17 +1,13 @@
use egui::ViewportId; use crate::{components::CompUi, GuiState};
use xmpd_manifest::store::JsonStore;
use crate::{components::{CompGetter, CompUi}, GuiState};
pub fn draw(ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()> { pub fn draw(ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()> {
// The central panel the region left after adding TopPanel's and SidePanel's // The central panel the region left after adding TopPanel's and SidePanel's
// ui.heading(format!("Songs ({})", self.manifest.get_song_count())); // ui.heading(format!("Songs ({})", self.manifest.get_song_count()));
let theme = xmpd_settings::Settings::get()?.theme.clone();
let avail = ui.available_size(); let avail = ui.available_size();
ui.vertical(|ui| { ui.vertical(|ui| {
handle_error_ui!(crate::components::top_nav::TopNav::draw(ui, state)); handle_error_ui!(crate::components::top_nav::TopNav::draw(ui, state));
crate::utils::super_separator(ui, crate::data::C_ACCENT, avail.x, 2.0); crate::utils::super_separator(ui, theme.accent_color, avail.x, 2.0);
let avail = ui.available_size(); let avail = ui.available_size();
let main_height = avail.y * 0.91; let main_height = avail.y * 0.91;
@ -35,15 +31,15 @@ pub fn draw(ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()> {
egui::TopBottomPanel::new(egui::panel::TopBottomSide::Bottom, "player") egui::TopBottomPanel::new(egui::panel::TopBottomSide::Bottom, "player")
.frame( .frame(
egui::Frame::none() egui::Frame::none()
.fill(crate::data::C_PRIM_BG) .fill(theme.primary_bg_color)
.stroke(egui::Stroke::new( .stroke(egui::Stroke::new(
1.0, 1.0,
crate::data::C_SEC_BG, theme.secondary_bg_color,
)), )),
) )
.show(ui.ctx(), |ui| { .show(ui.ctx(), |ui| {
ui.style_mut().visuals.override_text_color = Some(crate::data::C_TEXT); ui.style_mut().visuals.override_text_color = Some(theme.accent_color);
handle_error_ui!(crate::components::player::Player::draw(ui, state)); handle_error_ui!(crate::components::player::Player::draw(ui, state));
}); });
}); });

View File

@ -2,7 +2,6 @@
pub fn super_separator(ui: &mut egui::Ui, color: egui::Color32, width: f32, height: f32) { pub fn super_separator(ui: &mut egui::Ui, color: egui::Color32, width: f32, height: f32) {
egui::Frame::none() egui::Frame::none()
.fill(color) .fill(color)
// .stroke(egui::Stroke { color: crate::data::C_ACCENT, width: 1.0 })
.show(ui, |ui| { .show(ui, |ui| {
ui.set_width(width); ui.set_width(width);
ui.set_height(height); ui.set_height(height);

View File

@ -7,7 +7,7 @@ pub struct ErrorW {
} }
impl Window for ErrorW { impl Window for ErrorW {
fn draw(&self, ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> { fn draw(&self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
ui.label("Hello from other window!"); ui.label("Hello from other window!");
Ok(()) Ok(())
} }

View File

@ -1,8 +1,9 @@
use std::{collections::{HashMap, HashSet}, ops::Deref, sync::{Arc, Mutex}}; use std::{collections::{HashMap, HashSet}, sync::{Arc, Mutex}};
use egui::{ViewportBuilder, ViewportId}; use egui::{ViewportBuilder, ViewportId};
use crate::GuiState; use crate::GuiState;
mod error; mod error;
mod settings;
lazy_static::lazy_static!( lazy_static::lazy_static!(
static ref WINDOWS: Arc<Mutex<HashMap<WindowId, Box<dyn Window>>>> = Arc::new(Mutex::new(HashMap::new())); static ref WINDOWS: Arc<Mutex<HashMap<WindowId, Box<dyn Window>>>> = Arc::new(Mutex::new(HashMap::new()));
@ -16,6 +17,7 @@ pub trait Window: std::fmt::Debug + Send {
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)] #[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
pub enum WindowId { pub enum WindowId {
Settings,
Error Error
} }
@ -49,7 +51,6 @@ impl Windows {
if self.is_open(&win_id) { if self.is_open(&win_id) {
ctx.show_viewport_immediate(vp_id.clone(), builder.clone(), |ctx, _vp_class| { ctx.show_viewport_immediate(vp_id.clone(), builder.clone(), |ctx, _vp_class| {
ctx.input(|inp| { ctx.input(|inp| {
// println!("CLose requested: {}",inp.viewport().close_requested() );
self.toggle(win_id, !inp.viewport().close_requested()); self.toggle(win_id, !inp.viewport().close_requested());
}); });
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {

View File

@ -0,0 +1,14 @@
use super::Window;
#[derive(Debug, Clone)]
pub struct SettingsW {
}
impl Window for SettingsW {
fn draw(&self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
Ok(())
}
}

View File

@ -1,15 +1,14 @@
[package] [package]
name = "xmpd-derive" name = "xmpd-settings"
edition = "2021" edition = "2021"
version.workspace = true version.workspace = true
repository.workspace = true repository.workspace = true
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true
[lib]
proc-macro=true
[dependencies] [dependencies]
quote = "1.0.37" anyhow.workspace = true
syn = "2.0.87" egui.workspace = true
# xmpd-gui.path = "../xmpd-gui" lazy_static.workspace = true
serde.workspace = true
toml = "0.8.19"

49
xmpd-settings/src/lib.rs Normal file
View File

@ -0,0 +1,49 @@
use std::{path::PathBuf, sync::{Arc, Mutex, MutexGuard}};
use serde::{Deserialize, Serialize};
use theme::Theme;
mod theme;
lazy_static::lazy_static!(
static ref SETTINGS: Arc<Mutex<Settings>> = Arc::new(Mutex::new(Settings::default()));
);
pub type Result<T> = anyhow::Result<T>;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Settings {
#[serde(skip)]
settings_path: PathBuf,
pub theme: Theme,
}
impl Settings {
pub fn get() -> crate::Result<MutexGuard<'static, Self>> {
match SETTINGS.lock() {
Ok(l) => Ok(l),
Err(e) => Err(anyhow::anyhow!(format!("{e:?}"))),
}
}
pub fn load(&mut self, path: Option<PathBuf>) -> Result<()> {
let path = path.unwrap_or(self.settings_path.clone());
if !path.exists() {
std::fs::write(&path, "[theme]")?;
self.save(Some(path.clone()))?;
}
let data = std::fs::read_to_string(&path)?;
let data: Self = toml::from_str(&data)?;
*self = data;
self.settings_path = path;
Ok(())
}
pub fn save(&mut self, path: Option<PathBuf>) -> Result<()> {
let path = path.unwrap_or(self.settings_path.clone());
let data = toml::to_string_pretty(&self)?;
std::fs::write(&path, data)?;
self.settings_path = path;
Ok(())
}
}

View File

@ -0,0 +1,47 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Theme {
#[serde(default="Theme::default_accent_color")]
pub accent_color: egui::Color32,
#[serde(default="Theme::default_primary_bg_color")]
pub primary_bg_color: egui::Color32,
#[serde(default="Theme::default_secondary_bg_color")]
pub secondary_bg_color: egui::Color32,
#[serde(default="Theme::default_text_color")]
pub text_color: egui::Color32,
#[serde(default="Theme::default_dim_text_color")]
pub dim_text_color: egui::Color32,
}
impl Default for Theme {
fn default() -> Self {
Self {
accent_color: Self::default_accent_color(),
primary_bg_color: Self::default_primary_bg_color(),
secondary_bg_color: Self::default_secondary_bg_color(),
text_color: Self::default_text_color(),
dim_text_color: Self::default_dim_text_color(),
}
}
}
impl Theme {
fn default_accent_color() -> egui::Color32 {
egui::Color32::from_rgb(5, 102, 146) // #0566F6
}
fn default_primary_bg_color() -> egui::Color32 {
egui::Color32::from_rgb(31, 34, 40) // #1F2228
}
fn default_secondary_bg_color() -> egui::Color32 {
egui::Color32::from_rgb(47, 53, 61) // #2f353d
}
fn default_text_color() -> egui::Color32 {
egui::Color32::from_rgb(223, 223, 223) // #dfdfdf
}
fn default_dim_text_color() -> egui::Color32 {
egui::Color32::from_rgb(175, 175, 175 ) // #afafaf
}
}