Save / load app config

This commit is contained in:
Connor Slade
2024-07-27 00:15:24 -04:00
parent 2b319019fd
commit ae5a1b2f14
12 changed files with 181 additions and 71 deletions

49
Cargo.lock generated
View File

@@ -1398,6 +1398,15 @@ dependencies = [
"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-next"
version = "2.0.0"
@@ -1408,6 +1417,18 @@ dependencies = [
"dirs-sys-next",
]
[[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]]
name = "dirs-sys-next"
version = "0.1.2"
@@ -1493,6 +1514,7 @@ dependencies = [
"pollster",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.2",
"serde",
"static_assertions",
"thiserror",
"wasm-bindgen",
@@ -2803,6 +2825,7 @@ dependencies = [
"clone-macro",
"common",
"const_format",
"dirs",
"eframe",
"egui",
"egui-modal",
@@ -2820,8 +2843,10 @@ dependencies = [
"rand 0.8.5",
"remote_send",
"rfd",
"serde",
"serde_json",
"slicer",
"toml",
"tracing",
"tracing-subscriber",
"wgpu",
@@ -3358,6 +3383,12 @@ version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "orbclient"
version = "0.3.47"
@@ -4204,9 +4235,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
dependencies = [
"serde",
]
@@ -4630,21 +4661,21 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.14"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.14",
"toml_edit 0.22.17",
]
[[package]]
name = "toml_datetime"
version = "0.6.6"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
dependencies = [
"serde",
]
@@ -4673,9 +4704,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.14"
version = "0.22.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16"
dependencies = [
"indexmap",
"serde",

View File

@@ -11,7 +11,8 @@ chrono = "0.4.38"
clone-macro = "0.1.0"
const_format = "0.2.32"
criterion = "0.5.1"
eframe = { version = "0.27.2", features = ["wgpu"] }
dirs = "5.0.1"
eframe = { version = "0.27.2", features = ["wgpu", "serde"] }
egui = "0.27.2"
egui_dock = "0.12.0"
egui_tracing = "0.2.2"
@@ -35,6 +36,7 @@ serde_json = "1.0.120"
serde_repr = "0.1.19"
soon = { git = "https://github.com/connorslade/misc" }
stl_io = "0.7.0"
toml = "0.8.16"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
wgpu = "0.19.4"
wgpu = "0.19.4"

View File

@@ -53,7 +53,7 @@
- [ ] Verify printer capabilities before uploading / printing
- [ ] Move remote print module to remote send?
- [ ] Add some optional alert sound for print completion
- [ ] Create config struct thats saved / loaded on shutdown / startup
- [x] Create config struct thats saved / loaded on shutdown / startup
- [ ] Publish goo_format crate
- [ ] Allow uploading / printing a local .goo file
- [x] Load normals from model files

View File

@@ -9,6 +9,7 @@ bytemuck.workspace = true
chrono.workspace = true
clone-macro.workspace = true
const_format.workspace = true
dirs.workspace = true
eframe.workspace = true
egui_dock.workspace = true
egui_tracing.workspace = true
@@ -25,6 +26,8 @@ plexus.workspace = true
rand.workspace = true
rfd.workspace = true
serde_json.workspace = true
serde.workspace = true
toml.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
wgpu.workspace = true

View File

@@ -1,7 +1,8 @@
use std::{sync::Arc, thread, time::Instant};
use std::{path::PathBuf, sync::Arc, thread, time::Instant};
use clone_macro::clone;
use eframe::Theme;
use egui::Visuals;
use egui_dock::{DockState, NodeIndex};
use egui_modal::{DialogBuilder, Icon, Modal};
use egui_tracing::EventCollector;
@@ -9,12 +10,14 @@ use image::imageops::FilterType;
use nalgebra::{Vector2, Vector3};
use parking_lot::RwLock;
use slicer::{slicer::Slicer, Pos};
use tracing::info;
use tracing::{info, warn};
use crate::{
config::Config,
remote_print::RemotePrint,
render::{camera::Camera, pipelines::model::RenderStyle, rendered_mesh::RenderedMesh},
render::{camera::Camera, rendered_mesh::RenderedMesh},
slice_operation::{SliceOperation, SliceResult},
ui_state::UiState,
windows::{self, Tab},
};
use common::config::{ExposureConfig, SliceConfig};
@@ -33,31 +36,7 @@ pub struct App {
pub meshes: Arc<RwLock<Vec<RenderedMesh>>>,
pub slice_operation: Option<SliceOperation>,
pub remote_print: RemotePrint,
}
#[derive(Default)]
pub struct UiState {
pub event_collector: EventCollector,
pub working_address: String,
pub send_print_completion: bool,
pub remote_print_connecting: RemotePrintConnectStatus,
}
#[derive(Default, PartialEq, Eq)]
pub enum RemotePrintConnectStatus {
#[default]
None,
Connecting,
Scanning,
}
pub struct Config {
pub render_style: RenderStyle,
pub grid_size: f32,
pub theme: Theme,
pub alert_print_completion: bool,
pub init_remote_print_at_startup: bool,
pub network_timeout: f32,
pub config_dir: PathBuf,
}
pub struct FpsTracker {
@@ -73,6 +52,15 @@ impl App {
let [_old_node, new_node] = surface.split_below(new_node, 0.5, vec![Tab::SliceConfig]);
surface.split_below(new_node, 0.5, vec![Tab::Workspace, Tab::RemotePrint]);
let config_dir = dirs::config_dir().unwrap().join("mslicer");
let config = match Config::load(&config_dir) {
Ok(config) => config,
Err(err) => {
warn!("Failed to load config, using defaults: {}", err);
Config::default()
}
};
Self {
dock_state,
modal: None,
@@ -80,14 +68,7 @@ impl App {
event_collector,
..Default::default()
},
config: Config {
render_style: RenderStyle::Rended,
theme: Theme::Dark,
grid_size: 12.16,
alert_print_completion: false,
init_remote_print_at_startup: false,
network_timeout: 5.0,
},
config,
camera: Camera::default(),
slice_config: SliceConfig {
platform_resolution: Vector2::new(11_520, 5_120),
@@ -108,6 +89,7 @@ impl App {
meshes: Arc::new(RwLock::new(Vec::new())),
slice_operation: None,
remote_print: RemotePrint::uninitialized(),
config_dir,
}
}
@@ -205,17 +187,32 @@ impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
ctx.request_repaint();
self.fps.update();
self.remote_print.tick(&mut self.modal, &mut self.state);
match self.config.theme {
Theme::Dark => ctx.set_visuals(Visuals::dark()),
Theme::Light => ctx.set_visuals(Visuals::light()),
}
match &mut self.modal {
Some(modal) => modal.show_dialog(),
None => self.modal = Some(Modal::new(ctx, "modal")),
}
self.remote_print.tick(&mut self.modal, &mut self.state);
windows::ui(self, ctx);
}
}
impl Drop for App {
fn drop(&mut self) {
if let Err(err) = self.config.save(&self.config_dir) {
warn!("Failed to save config: {}", err);
return;
}
info!("Successfully saved config");
}
}
impl FpsTracker {
fn new() -> Self {
Self {

62
mslicer/src/config.rs Normal file
View File

@@ -0,0 +1,62 @@
use std::{
fs,
net::{IpAddr, Ipv4Addr},
path::Path,
};
use anyhow::Result;
use eframe::Theme;
use serde::{Deserialize, Serialize};
use tracing::info;
use crate::render::pipelines::model::RenderStyle;
#[derive(Serialize, Deserialize)]
pub struct Config {
pub render_style: RenderStyle,
pub grid_size: f32,
pub theme: Theme,
pub alert_print_completion: bool,
pub init_remote_print_at_startup: bool,
pub network_timeout: f32,
pub network_broadcast_address: IpAddr,
}
impl Config {
pub fn load(config_dir: &Path) -> Result<Self> {
let config_file = config_dir.join("config.toml");
Ok(if config_file.exists() {
let file = fs::read(&config_file)?;
let string = String::from_utf8_lossy(&file);
let config = toml::from_str(&string)?;
info!("Successfully loaded config file");
config
} else {
info!("No config file found, using defaults");
Self::default()
})
}
pub fn save(&self, config_dir: &Path) -> Result<()> {
fs::create_dir_all(config_dir)?;
let config_file = config_dir.join("config.toml");
let string = toml::to_string(self)?;
fs::write(config_file, string)?;
Ok(())
}
}
impl Default for Config {
fn default() -> Self {
Self {
render_style: RenderStyle::Rended,
theme: Theme::Dark,
grid_size: 12.16,
alert_print_completion: false,
init_remote_print_at_startup: false,
network_timeout: 5.0,
network_broadcast_address: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255)),
}
}
}

View File

@@ -12,9 +12,11 @@ const TEXTURE_FORMAT: TextureFormat = TextureFormat::Bgra8Unorm;
mod app;
mod components;
mod config;
mod remote_print;
mod render;
mod slice_operation;
mod ui_state;
mod windows;
use app::App;

View File

@@ -1,6 +1,6 @@
use std::{
io::ErrorKind,
net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket},
net::{IpAddr, SocketAddr, TcpListener, UdpSocket},
str::FromStr,
sync::Arc,
thread::{self, JoinHandle},
@@ -23,7 +23,7 @@ use remote_send::{
Response,
};
use crate::app::{RemotePrintConnectStatus, UiState};
use crate::ui_state::{RemotePrintConnectStatus, UiState};
pub struct RemotePrint {
services: Option<Arc<Services>>,
@@ -157,12 +157,12 @@ impl RemotePrint {
Ok(())
}
pub fn scan_for_printers(&mut self) {
pub fn scan_for_printers(&mut self, broadcast: IpAddr) {
self.jobs.push(AsyncJob::new(
thread::spawn(clone!(
[{ self.printers } as printers, { self.services } as services],
move || {
scan_for_printers(services.unwrap(), printers)
scan_for_printers(services.unwrap(), printers, broadcast)
.context("Error scanning for printers.")
}
)),
@@ -254,13 +254,15 @@ fn add_printer(
Ok(())
}
fn scan_for_printers(services: Arc<Services>, printers: Arc<Mutex<Vec<Printer>>>) -> Result<()> {
const BROADCAST: IpAddr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255));
info!("Scanning for printers on {BROADCAST}");
fn scan_for_printers(
services: Arc<Services>,
printers: Arc<Mutex<Vec<Printer>>>,
broadcast: IpAddr,
) -> Result<()> {
info!("Scanning for printers on {broadcast}");
services
.udp
.send_to(b"M99999", SocketAddr::new(BROADCAST, 3000))?;
.send_to(b"M99999", SocketAddr::new(broadcast, 3000))?;
let mut buffer = [0; 1024];
loop {

View File

@@ -2,6 +2,7 @@ use egui::epaint::util::OrderedFloat;
use egui_wgpu::ScreenDescriptor;
use encase::{ShaderType, UniformBuffer};
use nalgebra::{Matrix4, Vector3, Vector4};
use serde::{Deserialize, Serialize};
use wgpu::{
util::{BufferInitDescriptor, DeviceExt},
BindGroup, BindGroupEntry, BindGroupLayout, BlendState, BufferUsages, ColorTargetState,
@@ -39,7 +40,7 @@ struct ModelUniforms {
render_style: u32,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum RenderStyle {
Normals,

17
mslicer/src/ui_state.rs Normal file
View File

@@ -0,0 +1,17 @@
use egui_tracing::EventCollector;
#[derive(Default)]
pub struct UiState {
pub event_collector: EventCollector,
pub working_address: String,
pub send_print_completion: bool,
pub remote_print_connecting: RemotePrintConnectStatus,
}
#[derive(Default, PartialEq, Eq)]
pub enum RemotePrintConnectStatus {
#[default]
None,
Connecting,
Scanning,
}

View File

@@ -8,7 +8,7 @@ use egui::{
use notify_rust::Notification;
use remote_send::status::{FileTransferStatus, PrintInfoStatus};
use crate::app::{App, RemotePrintConnectStatus};
use crate::{app::App, ui_state::RemotePrintConnectStatus};
enum Action {
None,
@@ -212,7 +212,8 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
let height = scan.rect.height();
if scan.clicked() {
app.state.remote_print_connecting = RemotePrintConnectStatus::Scanning;
app.remote_print.scan_for_printers();
app.remote_print
.scan_for_printers(app.config.network_broadcast_address);
}
ui.add_sized(vec2(2.0, height), Separator::default());

View File

@@ -1,5 +1,5 @@
use eframe::Theme;
use egui::{ComboBox, Context, Ui, Visuals};
use egui::{ComboBox, Context, Ui};
use crate::{
app::App,
@@ -7,7 +7,7 @@ use crate::{
render::pipelines::model::RenderStyle,
};
pub fn ui(app: &mut App, ui: &mut Ui, ctx: &Context) {
pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
ComboBox::new("render_style", "Render Style")
.selected_text(app.config.render_style.name())
.show_ui(ui, |ui| {
@@ -19,7 +19,6 @@ pub fn ui(app: &mut App, ui: &mut Ui, ctx: &Context) {
ui.selectable_value(&mut app.config.render_style, RenderStyle::Rended, "Rended");
});
let last_theme = app.config.theme;
ComboBox::new("theme", "Theme")
.selected_text(match app.config.theme {
Theme::Dark => "Dark",
@@ -30,13 +29,6 @@ pub fn ui(app: &mut App, ui: &mut Ui, ctx: &Context) {
ui.selectable_value(&mut app.config.theme, Theme::Light, "Light");
});
if last_theme != app.config.theme {
match app.config.theme {
Theme::Dark => ctx.set_visuals(Visuals::dark()),
Theme::Light => ctx.set_visuals(Visuals::light()),
}
}
dragger(ui, "Grid Size", &mut app.config.grid_size, |x| x.speed(0.1));
ui.collapsing("Camera", |ui| {