Early gui impl, basic window management
This commit is contained in:
parent
a00486eeaf
commit
4dcd36c3d8
3694
Cargo.lock
generated
3694
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
1
DEV.md
Normal file
1
DEV.md
Normal 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
4
manifest.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"songs": {},
|
||||||
|
"playlists": {}
|
||||||
|
}
|
|
@ -24,4 +24,8 @@ path="src/main.rs"
|
||||||
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" }
|
||||||
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
18
xmpd-core/src/cli.rs
Normal 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
17
xmpd-core/src/logger.rs
Normal 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();
|
||||||
|
}
|
|
@ -1,9 +1,21 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
fn main() {
|
use clap::Parser;
|
||||||
let args = std::env::args();
|
|
||||||
if args.len() > 1 {
|
mod cli;
|
||||||
// gui
|
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 {
|
} else {
|
||||||
// cli
|
manifest_path = PathBuf::from("manifest.json");
|
||||||
}
|
};
|
||||||
|
xmpd_gui::start(manifest_path)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,3 +18,10 @@ crate-type = ["rlib"]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[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
|
||||||
|
|
0
xmpd-gui/src/components/mod.rs
Normal file
0
xmpd-gui/src/components/mod.rs
Normal file
|
@ -1,3 +1,51 @@
|
||||||
pub fn test() {
|
#![feature(async_closure)]
|
||||||
println!("Hello, world!");
|
|
||||||
|
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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
14
xmpd-gui/src/main_window.rs
Normal file
14
xmpd-gui/src/main_window.rs
Normal 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(())
|
||||||
|
}
|
14
xmpd-gui/src/windows/error.rs
Normal file
14
xmpd-gui/src/windows/error.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
75
xmpd-gui/src/windows/mod.rs
Normal file
75
xmpd-gui/src/windows/mod.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ pub mod query;
|
||||||
|
|
||||||
pub type Result<T> = anyhow::Result<T>;
|
pub type Result<T> = anyhow::Result<T>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Manifest<ST: store::BaseStore> {
|
pub struct Manifest<ST: store::BaseStore> {
|
||||||
store: Box<ST>,
|
store: Box<ST>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user