Downloading prototype works
This commit is contained in:
@@ -20,6 +20,8 @@ bench = false
|
||||
[dependencies]
|
||||
xmpd-manifest.path = "../xmpd-manifest"
|
||||
xmpd-settings.path = "../xmpd-settings"
|
||||
xmpd-cliargs.path = "../xmpd-cliargs"
|
||||
xmpd-cache.path = "../xmpd-cache"
|
||||
egui.workspace = true
|
||||
eframe.workspace = true
|
||||
tokio.workspace = true
|
||||
@@ -29,3 +31,4 @@ log.workspace = true
|
||||
egui_extras.workspace = true
|
||||
egui-aesthetix = "0.2.4"
|
||||
uuid.workspace = true
|
||||
camino.workspace = true
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use egui::{RichText, Vec2};
|
||||
use std::fmt::write;
|
||||
|
||||
use egui::{Color32, RichText, Sense, Vec2};
|
||||
use song_list_nav::SearchType;
|
||||
use xmpd_cache::DlStatus;
|
||||
use xmpd_manifest::{song::Song, store::BaseStore};
|
||||
use super::{CompGetter, CompUi};
|
||||
|
||||
@@ -80,34 +83,92 @@ impl CompUi for SongList {
|
||||
|
||||
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| {
|
||||
ui.add(
|
||||
ui.horizontal(|ui| {
|
||||
let mut clicked = ui.add(
|
||||
egui::Image::new(crate::data::NOTE_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
.fit_to_exact_size(Vec2::new(32.0, 32.0))
|
||||
);
|
||||
).clicked();
|
||||
|
||||
ui.vertical(|ui| {
|
||||
let selected_song_id = {handle_error_ui!(SongList::get()).selected_song_id};
|
||||
if selected_song_id == *sid {
|
||||
let label = if selected_song_id == *sid {
|
||||
ui.label(
|
||||
RichText::new(song.name())
|
||||
.color(theme.accent_color)
|
||||
);
|
||||
)
|
||||
} else {
|
||||
ui.label(
|
||||
RichText::new(song.name())
|
||||
.color(theme.text_color)
|
||||
);
|
||||
)
|
||||
};
|
||||
|
||||
if label.clicked() {
|
||||
clicked = true;
|
||||
}
|
||||
ui.monospace(
|
||||
RichText::new(format!("By {}", song.author()))
|
||||
.color(theme.dim_text_color)
|
||||
.size(10.0)
|
||||
);
|
||||
|
||||
});
|
||||
}).response.rect;
|
||||
if ui.interact(rct, format!("song_list_{sid:?}").into(), egui::Sense::click()).clicked() {
|
||||
handle_error_ui!(SongList::get()).selected_song_id = sid.clone();
|
||||
}
|
||||
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
||||
ui.add_space(3.0);
|
||||
let status = {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).get_cached_song_status(&sid).clone()
|
||||
};
|
||||
match status {
|
||||
Some(DlStatus::Done(p)) => {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::CHECK_ICON)
|
||||
.tint(Color32::LIGHT_GREEN)
|
||||
.sense(Sense::hover())
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
);
|
||||
|
||||
img.on_hover_ui(|ui| {
|
||||
ui.label(format!("Path: {p}"));
|
||||
});
|
||||
}
|
||||
Some(DlStatus::Downloading) => {
|
||||
ui.add(
|
||||
egui::Spinner::new()
|
||||
.color(theme.accent_color)
|
||||
.size(16.0)
|
||||
);
|
||||
}
|
||||
Some(DlStatus::Error(e)) => {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::WARN_ICON)
|
||||
.tint(Color32::LIGHT_YELLOW)
|
||||
.sense(Sense::hover())
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
);
|
||||
img.on_hover_ui(|ui| {
|
||||
ui.label(e);
|
||||
});
|
||||
}
|
||||
None => {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::DL_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(Sense::click())
|
||||
.fit_to_exact_size(Vec2::new(16.0, 16.0))
|
||||
);
|
||||
if img.clicked() {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if clicked {
|
||||
handle_error_ui!(SongList::get()).selected_song_id = sid.clone();
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::components::{CompGetter, CompUi};
|
||||
use uuid::Uuid;
|
||||
use xmpd_manifest::store::BaseStore;
|
||||
|
||||
use crate::components::{left_nav::LeftNav, CompGetter, CompUi};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SearchType {
|
||||
@@ -9,14 +12,16 @@ pub enum SearchType {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SongListNav {
|
||||
text: String
|
||||
text: String,
|
||||
}
|
||||
|
||||
component_register!(SongListNav);
|
||||
|
||||
impl CompUi for SongListNav {
|
||||
fn draw(ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
fn draw(ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let theme = xmpd_settings::Settings::get()?.theme.clone();
|
||||
let pid = {LeftNav::get()?.selected_playlist_id.clone()};
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
let search_icon = egui::Image::new(crate::data::SEARCH_ICON)
|
||||
.fit_to_exact_size(egui::Vec2::new(16.0, 16.0))
|
||||
@@ -25,6 +30,31 @@ impl CompUi for SongListNav {
|
||||
{
|
||||
ui.text_edit_singleline(&mut handle_error_ui!(SongListNav::get()).text);
|
||||
}
|
||||
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::RIGHT), |ui| {
|
||||
let img = ui.add(
|
||||
egui::Image::new(crate::data::DL_ICON)
|
||||
.tint(theme.accent_color)
|
||||
.sense(egui::Sense::click())
|
||||
.fit_to_exact_size(egui::Vec2::new(16.0, 16.0))
|
||||
);
|
||||
if img.clicked() {
|
||||
let songs: Vec<_>;
|
||||
match pid {
|
||||
Some(pid) => {
|
||||
songs = state.manifest.store().get_playlist(&pid).unwrap().songs().to_vec();
|
||||
}
|
||||
None => {
|
||||
songs = state.manifest.store().get_songs().keys().cloned().collect();
|
||||
}
|
||||
}
|
||||
for sid in &songs {
|
||||
if let Some(song) = state.manifest.store().get_song(&sid) {
|
||||
handle_error_ui!(xmpd_cache::Cache::get()).download_to_cache(sid.clone(), song.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,16 +14,19 @@ impl CompUi for TopNav {
|
||||
ui.menu_button("File", |ui| {
|
||||
if ui.button("Settings").clicked() {
|
||||
state.windows.toggle(&WindowId::Settings, true);
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
ui.menu_button("Manifest", |ui| {
|
||||
if ui.button("Save").clicked() {
|
||||
handle_error_ui!(state.manifest.save());
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
ui.menu_button("Help", |ui| {
|
||||
if ui.button("Source").clicked() {
|
||||
ui.ctx().open_url(egui::OpenUrl::new_tab("https://git.mcorangehq.xyz/XOR64/music"));
|
||||
ui.close_menu();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -6,3 +6,6 @@ pub const PREV_ICON: egui::ImageSource = egui::include_image!("../../assets/pre
|
||||
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 PAUSE_ICON: egui::ImageSource = egui::include_image!("../../assets/pause.svg");
|
||||
pub const CHECK_ICON: egui::ImageSource = egui::include_image!("../../assets/check.svg");
|
||||
pub const DL_ICON: egui::ImageSource = egui::include_image!("../../assets/download.svg");
|
||||
pub const WARN_ICON: egui::ImageSource = egui::include_image!("../../assets/warning.svg");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![feature(async_closure)]
|
||||
|
||||
use std::{path::{Path, PathBuf}, time::Duration};
|
||||
use std::time::Duration;
|
||||
use xmpd_manifest::{store::JsonStore, Manifest};
|
||||
|
||||
#[macro_use]
|
||||
@@ -15,10 +15,10 @@ const W_NAME: &str = "xmpd v2.0.0a";
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn start(manifest_path: PathBuf) -> Result<()> {
|
||||
pub fn start() -> Result<()> {
|
||||
xmpd_cache::Cache::get()?.init();
|
||||
let options = eframe::NativeOptions::default();
|
||||
let mut state = GuiState::new(&manifest_path)?;
|
||||
let theme = xmpd_settings::Settings::get()?.theme.clone();
|
||||
let mut state = GuiState::new()?;
|
||||
let res = eframe::run_simple_native(W_NAME, options, move |ctx, _frame| {
|
||||
egui_extras::install_image_loaders(ctx);
|
||||
state.windows.clone().draw_all(ctx, &mut state);
|
||||
@@ -31,20 +31,15 @@ pub fn start(manifest_path: PathBuf) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub enum Message {
|
||||
|
||||
}
|
||||
|
||||
pub struct GuiState {
|
||||
pub manifest: Manifest<JsonStore>,
|
||||
pub windows: windows::Windows
|
||||
pub windows: windows::Windows,
|
||||
}
|
||||
|
||||
impl GuiState {
|
||||
pub fn new(manifest_path: &Path) -> Result<Self> {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Self {
|
||||
manifest: Manifest::new(manifest_path)?,
|
||||
manifest: Manifest::new(&xmpd_cliargs::CLIARGS.manifest_path())?,
|
||||
windows: windows::Windows::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use xmpd_settings::theme::Theme;
|
||||
|
||||
use crate::{components::CompUi, GuiState};
|
||||
use crate::{components::{song_list, CompUi}, GuiState};
|
||||
|
||||
pub fn draw(ctx: &egui::Context, state: &mut GuiState) -> crate::Result<()> {
|
||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||
@@ -22,19 +22,22 @@ pub fn draw(ctx: &egui::Context, state: &mut GuiState) -> crate::Result<()> {
|
||||
let avail = ui.available_size();
|
||||
let main_height = avail.y * 0.91;
|
||||
|
||||
let left_nav_width = (avail.x * 0.25).clamp(0.0, 200.0);
|
||||
let song_list_width = (avail.x - left_nav_width - 35.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.set_height(main_height);
|
||||
ui.group(|ui| {
|
||||
ui.set_height(main_height);
|
||||
ui.set_max_width((avail.x * 0.25).clamp(0.0, 200.0));
|
||||
ui.set_max_width(left_nav_width);
|
||||
handle_error_ui!(crate::components::left_nav::LeftNav::draw(ui, state));
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.set_width(avail.x * 0.75);
|
||||
ui.set_width(song_list_width);
|
||||
handle_error_ui!(crate::components::song_list::song_list_nav::SongListNav::draw(ui, state));
|
||||
});
|
||||
ui.group(|ui| {
|
||||
ui.set_width(song_list_width);
|
||||
handle_error_ui!(crate::components::song_list::SongList::draw(ui, state));
|
||||
});
|
||||
});
|
||||
@@ -58,7 +61,7 @@ fn get_themed_frame(theme: &Theme) -> egui::Frame {
|
||||
egui::Frame::none()
|
||||
.fill(theme.primary_bg_color)
|
||||
.stroke(egui::Stroke::new(
|
||||
1.0,
|
||||
5.0,
|
||||
theme.secondary_bg_color,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,40 +1,80 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::Window;
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SettingsW {
|
||||
accent_color: egui::Color32,
|
||||
primary_bg_color: egui::Color32,
|
||||
secondary_bg_color: egui::Color32,
|
||||
text_color: egui::Color32,
|
||||
dim_text_color: egui::Color32,
|
||||
ytdlp_p: String,
|
||||
spotdl_p: String,
|
||||
ffmpeg_p: String,
|
||||
song_fmt: String,
|
||||
}
|
||||
|
||||
impl Default for SettingsW {
|
||||
fn default() -> Self {
|
||||
let def = xmpd_settings::theme::Theme::default();
|
||||
Self {
|
||||
accent_color: def.accent_color,
|
||||
primary_bg_color: def.primary_bg_color,
|
||||
secondary_bg_color: def.secondary_bg_color,
|
||||
text_color: def.text_color,
|
||||
dim_text_color: def.dim_text_color
|
||||
let tooling = xmpd_settings::Settings::get().unwrap().tooling.clone();
|
||||
Self {
|
||||
ytdlp_p: tooling.ytdlp_path.to_string(),
|
||||
spotdl_p: tooling.spotdl_path.to_string(),
|
||||
ffmpeg_p: tooling.ffmpeg_path.to_string(),
|
||||
song_fmt: tooling.song_format
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl Window for SettingsW {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
fn draw(&mut self, ui: &mut egui::Ui, _: &mut crate::GuiState) -> crate::Result<()> {
|
||||
let theme = &mut xmpd_settings::Settings::get()?.theme;
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.heading("Theme");
|
||||
Self::add_theme_button(&mut theme.accent_color, ui, "Accent");
|
||||
Self::add_theme_button(&mut theme.primary_bg_color, ui, "Primary BG");
|
||||
Self::add_theme_button(&mut theme.secondary_bg_color, ui, "Secondary BG");
|
||||
Self::add_theme_button(&mut theme.text_color, ui, "Text");
|
||||
Self::add_theme_button(&mut theme.dim_text_color, ui, "Dim Text");
|
||||
ui.horizontal(|ui| {
|
||||
{
|
||||
let theme = &mut handle_error_ui!(xmpd_settings::Settings::get()).theme;
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.heading("Theme");
|
||||
Self::add_theme_button(&mut theme.accent_color, ui, "Accent");
|
||||
Self::add_theme_button(&mut theme.primary_bg_color, ui, "Primary BG");
|
||||
Self::add_theme_button(&mut theme.secondary_bg_color, ui, "Secondary BG");
|
||||
Self::add_theme_button(&mut theme.text_color, ui, "Text");
|
||||
Self::add_theme_button(&mut theme.dim_text_color, ui, "Dim Text");
|
||||
if ui.button("Reset").clicked() {
|
||||
*theme = xmpd_settings::theme::Theme::default();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
{
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.heading("Tooling paths");
|
||||
Self::add_tooling_input(&mut self.ytdlp_p, ui, "stdlp");
|
||||
Self::add_tooling_input(&mut self.spotdl_p, ui, "spotdl");
|
||||
Self::add_tooling_input(&mut self.ffmpeg_p, ui, "ffmpeg");
|
||||
Self::add_tooling_input(&mut self.song_fmt, ui, "Format");
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::RIGHT), |ui| {
|
||||
if ui.button("Save").clicked() {
|
||||
let mut settings = handle_error_ui!(xmpd_settings::Settings::get());
|
||||
if let Ok(p) = camino::Utf8PathBuf::from_str(&self.ytdlp_p) {
|
||||
settings.tooling.ytdlp_path = p;
|
||||
}
|
||||
if let Ok(p) = camino::Utf8PathBuf::from_str(&self.spotdl_p) {
|
||||
settings.tooling.spotdl_path = p;
|
||||
}
|
||||
if let Ok(p) = camino::Utf8PathBuf::from_str(&self.ffmpeg_p) {
|
||||
settings.tooling.ffmpeg_path = p;
|
||||
}
|
||||
settings.tooling.song_format.clone_from(&self.song_fmt);
|
||||
handle_error_ui!(settings.save(None));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Ok(())
|
||||
@@ -48,4 +88,10 @@ impl SettingsW {
|
||||
ui.color_edit_button_srgba(rf);
|
||||
});
|
||||
}
|
||||
fn add_tooling_input(inp: &mut String, ui: &mut egui::Ui, name: &str) {
|
||||
ui.horizontal(|ui|{
|
||||
ui.label(format!("{name}: "));
|
||||
ui.text_edit_singleline(inp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user