Early gui impl, basic window management

This commit is contained in:
Gvidas Juknevičius 2024-11-07 15:42:34 +02:00
parent a00486eeaf
commit 4dcd36c3d8
Signed by: MCorange
GPG Key ID: 12B1346D720B7FBB
14 changed files with 3905 additions and 24 deletions

3694
Cargo.lock generated

File diff suppressed because it is too large Load Diff

1
DEV.md Normal file
View File

@ -0,0 +1 @@
listen along feature using ws and or p2p, downloading music when connectedd if you dont have it, matched by either the url, or a global id set by server

4
manifest.json Normal file
View File

@ -0,0 +1,4 @@
{
"songs": {},
"playlists": {}
}

View File

@ -24,4 +24,8 @@ path="src/main.rs"
xmpd-cli={ path="../xmpd-cli" }
xmpd-gui={ path="../xmpd-gui" }
xmpd-manifest={ path="../xmpd-manifest" }
clap = { workspace=true }
clap.workspace=true
camino.workspace = true
anyhow.workspace = true
log.workspace = true
env_logger.workspace = true

18
xmpd-core/src/cli.rs Normal file
View File

@ -0,0 +1,18 @@
use std::path::{Path, PathBuf};
#[derive(Debug, clap::Parser)]
pub struct CliArgs {
/// Manifest path
#[arg(long, short)]
manifest: Option<camino::Utf8PathBuf>,
/// Debug mode
#[arg(long, short)]
pub debug: bool,
}
impl CliArgs {
pub fn manifest_path(&self) -> Option<PathBuf> {
Some(self.manifest.clone()?.into_std_path_buf())
}
}

17
xmpd-core/src/logger.rs Normal file
View File

@ -0,0 +1,17 @@
use log::LevelFilter;
use crate::cli::CliArgs;
pub fn init(cliargs: &CliArgs) {
let level = if cliargs.debug { LevelFilter::Debug } else { LevelFilter::Info };
env_logger::builder()
.format_timestamp(None)
.filter(Some("xmpd_core"), level)
.filter(Some("xmpd_cli"), level)
.filter(Some("xmpd_gui"), level)
.filter(Some("xmpd_manifest"), level)
.filter(Some("xmpd_dl"), level)
.init();
}

View File

@ -1,9 +1,21 @@
use std::path::{Path, PathBuf};
fn main() {
let args = std::env::args();
if args.len() > 1 {
// gui
use clap::Parser;
mod cli;
mod logger;
type Result<T> = anyhow::Result<T>;
fn main() -> Result<()> {
let cliargs = cli::CliArgs::parse();
logger::init(&cliargs);
let manifest_path;
if let Some(mp) = cliargs.manifest_path() {
manifest_path = mp;
} else {
// cli
}
manifest_path = PathBuf::from("manifest.json");
};
xmpd_gui::start(manifest_path)?;
Ok(())
}

View File

@ -18,3 +18,10 @@ crate-type = ["rlib"]
bench = false
[dependencies]
xmpd-manifest.path = "../xmpd-manifest"
egui.workspace = true
eframe.workspace = true
tokio.workspace = true
anyhow.workspace = true
lazy_static.workspace = true
log.workspace = true

View File

View File

@ -1,3 +1,51 @@
pub fn test() {
println!("Hello, world!");
#![feature(async_closure)]
use std::{path::{Path, PathBuf}, sync::mpsc, thread::JoinHandle};
use windows::WindowId;
use xmpd_manifest::{store::JsonStore, Manifest};
mod main_window;
mod windows;
const W_NAME: &str = "xmpd v2.0.0a";
type Result<T> = anyhow::Result<T>;
pub fn start(manifest_path: PathBuf) -> Result<()> {
let options = eframe::NativeOptions::default();
let mut state = GuiState::new(&manifest_path)?;
state.windows.toggle(&WindowId::Error, true);
let res = eframe::run_simple_native(W_NAME, options, move |ctx, _frame| {
state.windows.clone().draw_all(ctx, &mut state);
egui::CentralPanel::default().show(ctx, |ui| main_window::draw(ui, &mut state));
ctx.request_repaint();
});
if let Err(e) = res { // dumb err value by eframe
anyhow::bail!(e.to_string());
}
Ok(())
}
pub enum Message {
}
pub struct GuiState {
pub manifest: Manifest<JsonStore>,
pub windows: windows::Windows
}
impl GuiState {
pub fn new(manifest_path: &Path) -> Result<Self> {
Ok(Self {
manifest: Manifest::new(manifest_path)?,
windows: windows::Windows::new(),
})
}
}

View File

@ -0,0 +1,14 @@
use egui::ViewportId;
use xmpd_manifest::store::JsonStore;
use crate::GuiState;
pub fn draw(ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()> {
ui.label("Hello! this is root of main window");
if ui.button("open iwndow").clicked() {
state.windows.toggle(&crate::windows::WindowId::Error, true);
}
Ok(())
}

View File

@ -0,0 +1,14 @@
use super::Window;
#[derive(Debug, Default)]
pub struct ErrorW {
}
impl Window for ErrorW {
fn draw(&self, ui: &mut egui::Ui, state: &mut crate::GuiState) -> crate::Result<()> {
ui.label("Hello from other window!");
Ok(())
}
}

View File

@ -0,0 +1,75 @@
use std::{collections::{HashMap, HashSet}, ops::Deref, sync::{Arc, Mutex}};
use egui::{ViewportBuilder, ViewportId};
use crate::GuiState;
mod error;
lazy_static::lazy_static!(
static ref WINDOWS: Arc<Mutex<HashMap<WindowId, Box<dyn Window>>>> = Arc::new(Mutex::new(HashMap::new()));
static ref OPEN_WINDOWS: Arc<Mutex<HashSet<WindowId>>> = Arc::new(Mutex::new(HashSet::new()));
);
pub trait Window: std::fmt::Debug + Send {
fn draw(&self, ui: &mut egui::Ui, state: &mut GuiState) -> crate::Result<()>;
}
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
pub enum WindowId {
Error
}
#[derive(Debug, Clone)]
pub struct Windows {
windows: HashMap<WindowId, (ViewportId, ViewportBuilder)>,
}
impl Windows {
pub fn new() -> Self {
let mut s = Self {
windows: HashMap::new(),
};
s.add_all_windows();
s
}
pub fn add_all_windows(&mut self) {
self.add_new_window(WindowId::Error, "Error!", Box::<error::ErrorW>::default())
}
pub fn add_new_window(&mut self, id: WindowId, title: &str, cb: Box<dyn Window>) {
let builder = ViewportBuilder::default()
.with_title(title);
self.windows.insert(id.clone(), (ViewportId::from_hash_of(id.clone()), builder));
WINDOWS.lock().unwrap().insert(id, cb);
}
pub fn draw_all(&mut self, ctx: &egui::Context, state: &mut GuiState) {
for (win_id, (vp_id, builder)) in &self.windows {
if self.is_open(&win_id) {
ctx.show_viewport_immediate(vp_id.clone(), builder.clone(), |ctx, _vp_class| {
ctx.input(|inp| {
// println!("CLose requested: {}",inp.viewport().close_requested() );
self.toggle(win_id, !inp.viewport().close_requested());
});
egui::CentralPanel::default().show(ctx, |ui| {
WINDOWS.lock().unwrap().get(&win_id).unwrap().draw(ui, state)
})
});
}
}
}
pub fn toggle(&self, id: &WindowId, status: bool) {
if status {
OPEN_WINDOWS.lock().unwrap().insert(id.clone());
} else {
log::debug!("tried to kill");
OPEN_WINDOWS.lock().unwrap().remove(id);
}
}
pub fn is_open(&self, id: &WindowId) -> bool {
OPEN_WINDOWS.lock().unwrap().contains(&id)
}
}

View File

@ -10,6 +10,7 @@ pub mod query;
pub type Result<T> = anyhow::Result<T>;
#[derive(Debug, Clone)]
pub struct Manifest<ST: store::BaseStore> {
store: Box<ST>,
}