Persist panel layout

This commit is contained in:
Connor Slade
2025-02-16 16:06:00 -05:00
parent 3b123d4db1
commit 74ac8b88a2
7 changed files with 59 additions and 11 deletions

21
Cargo.lock generated
View File

@@ -23,6 +23,10 @@ name = "accesskit"
version = "0.12.3" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b"
dependencies = [
"enumn",
"serde",
]
[[package]] [[package]]
name = "accesskit_consumer" name = "accesskit_consumer"
@@ -119,6 +123,7 @@ dependencies = [
"cfg-if", "cfg-if",
"getrandom", "getrandom",
"once_cell", "once_cell",
"serde",
"version_check", "version_check",
"zerocopy", "zerocopy",
] ]
@@ -1509,6 +1514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"serde",
] ]
[[package]] [[package]]
@@ -1559,6 +1565,7 @@ dependencies = [
"epaint", "epaint",
"log", "log",
"nohash-hasher", "nohash-hasher",
"serde",
] ]
[[package]] [[package]]
@@ -1613,6 +1620,7 @@ dependencies = [
"duplicate", "duplicate",
"egui", "egui",
"paste", "paste",
"serde",
] ]
[[package]] [[package]]
@@ -1660,6 +1668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"serde",
] ]
[[package]] [[package]]
@@ -1721,6 +1730,17 @@ dependencies = [
"syn 2.0.66", "syn 2.0.66",
] ]
[[package]]
name = "enumn"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
dependencies = [
"proc-macro2 1.0.85",
"quote 1.0.36",
"syn 2.0.66",
]
[[package]] [[package]]
name = "epaint" name = "epaint"
version = "0.27.2" version = "0.27.2"
@@ -1735,6 +1755,7 @@ dependencies = [
"log", "log",
"nohash-hasher", "nohash-hasher",
"parking_lot", "parking_lot",
"serde",
] ]
[[package]] [[package]]

View File

@@ -18,7 +18,7 @@ eframe = { version = "0.27.2", features = ["wgpu", "serde"] }
egui = "0.27.2" egui = "0.27.2"
egui-phosphor = { git = "https://github.com/connorslade/egui-phosphor" } egui-phosphor = { git = "https://github.com/connorslade/egui-phosphor" }
egui-wgpu = "0.27.2" egui-wgpu = "0.27.2"
egui_dock = "0.12.0" egui_dock = { version = "0.12.0", features = ["serde"] }
egui_tracing = "0.2.2" egui_tracing = "0.2.2"
encase = { version = "0.8.0", features = ["nalgebra"] } encase = { version = "0.8.0", features = ["nalgebra"] }
image = "0.25.1" image = "0.25.1"

View File

@@ -95,3 +95,5 @@
- [x] Visulize mesh outside of bounging box - [x] Visulize mesh outside of bounging box
- [ ] Save panel layout - [ ] Save panel layout
- [x] Random triangle color render mode - [x] Random triangle color render mode
- [ ] Dont clone Config every frame for no reason
- [ ] Add button to reset UI

View File

@@ -4,9 +4,17 @@ This document covers a variety of topics that don't fit into any other category.
## Remote Print HTTP Status Proxy ## Remote Print HTTP Status Proxy
Part of the process to upload a model to a printer with remote print is to serve the .goo file on a http server, then send the download link to the printer over MQTT. Because remote print already has to run a HTTP server, this option exposes an api at `0.0.0.0:<http_port>/status`. Each time remote print starts all server port are randomized and printed to the log (check the console or the Log panel). Part of the process to upload a model to a printer with remote print is to serve the .goo file on a http server, then send the download link to the printer over MQTT. Because remote print already has to run a HTTP server, this option exposes an API at `0.0.0.0:<http_port>/status`. Each time remote print starts, all server port are randomized and printed to the log (check the console or the Log panel).
The status route returns an array of printers, each with the following format. ### Scriptable Widget
Before I explain how the API works, here is a cool thing you can use it for (if you have an iPhone). There is an app called [Scriptable](https://scriptable.app) the lets you write scripts for your phone in JavaScript. I have already written one that shows a widget with the status of the current print job. It needs to access the API server so if you want to access it outside your home network you will need to use some proxy service like ngrok.
Anyway you can download the code for the widget from GitHub [here](https://gist.github.com/connorslade/5ee51da075fb0d7295f7a85cc774a5e0). Just make a new script in the app, paste the code in, then you can make a Scriptable widget, edit it, and choose the script you made.
### The API
The status route returns a JSON array of printers, each with the following format.
``` ```
struct Printer { struct Printer {

View File

@@ -6,10 +6,11 @@ use std::{
use anyhow::Result; use anyhow::Result;
use eframe::Theme; use eframe::Theme;
use egui_dock::Tree;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::{info, warn}; use tracing::{info, warn};
use crate::render::pipelines::model::RenderStyle; use crate::{render::pipelines::model::RenderStyle, windows::Tab};
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
#[serde(default)] #[serde(default)]
@@ -18,6 +19,7 @@ pub struct Config {
pub grid_size: f32, pub grid_size: f32,
pub theme: Theme, pub theme: Theme,
pub recent_projects: Vec<PathBuf>, pub recent_projects: Vec<PathBuf>,
pub panels: Option<Tree<Tab>>,
// Remote print settings // Remote print settings
pub alert_print_completion: bool, pub alert_print_completion: bool,
@@ -74,6 +76,7 @@ impl Default for Config {
theme: Theme::Dark, theme: Theme::Dark,
recent_projects: Vec::new(), recent_projects: Vec::new(),
panels: None,
alert_print_completion: false, alert_print_completion: false,
init_remote_print_at_startup: false, init_remote_print_at_startup: false,

View File

@@ -1,5 +1,6 @@
use std::{ use std::{
io::{BufRead, Seek}, io::{BufRead, Seek},
mem,
path::PathBuf, path::PathBuf,
sync::Arc, sync::Arc,
thread, thread,
@@ -10,7 +11,7 @@ use clone_macro::clone;
use const_format::concatcp; use const_format::concatcp;
use eframe::Theme; use eframe::Theme;
use egui::{Vec2, Visuals}; use egui::{Vec2, Visuals};
use egui_dock::{DockState, NodeIndex}; use egui_dock::{DockState, NodeIndex, Tree};
use egui_phosphor::regular::CARET_RIGHT; use egui_phosphor::regular::CARET_RIGHT;
use egui_tracing::EventCollector; use egui_tracing::EventCollector;
use egui_wgpu::RenderState; use egui_wgpu::RenderState;
@@ -71,15 +72,26 @@ impl App {
pub fn new( pub fn new(
render_state: RenderState, render_state: RenderState,
config_dir: PathBuf, config_dir: PathBuf,
config: Config, mut config: Config,
event_collector: EventCollector, event_collector: EventCollector,
) -> Self { ) -> Self {
let mut dock_state = DockState::new(vec![Tab::Viewport]); let mut dock_state = DockState::new(vec![Tab::Viewport]);
let surface = dock_state.main_surface_mut(); let surface = dock_state.main_surface_mut();
let [_old_node, new_node] = surface.split_left(NodeIndex::root(), 0.20, vec![Tab::Models]);
if let Some(past_state) = &mut config.panels {
*surface = mem::take(past_state);
} else {
surface.split_right(NodeIndex::root(), 0.7, vec![Tab::About]);
let [_old_node, new_node] =
surface.split_left(NodeIndex::root(), 0.2, vec![Tab::Models]);
let [_old_node, new_node] = let [_old_node, new_node] =
surface.split_below(new_node, 0.5, vec![Tab::SliceConfig, Tab::Supports]); surface.split_below(new_node, 0.5, vec![Tab::SliceConfig, Tab::Supports]);
surface.split_below(new_node, 0.5, vec![Tab::Workspace, Tab::RemotePrint]); surface.split_below(new_node, 0.5, vec![Tab::Workspace, Tab::RemotePrint]);
}
if surface.find_tab(&Tab::Viewport).is_none() {
*surface = Tree::new(vec![Tab::Viewport]);
}
Self { Self {
render_state, render_state,
@@ -246,6 +258,7 @@ impl eframe::App for App {
impl Drop for App { impl Drop for App {
fn drop(&mut self) { fn drop(&mut self) {
self.config.panels = Some(self.dock_state.main_surface().clone());
if let Err(err) = self.config.save(&self.config_dir) { if let Err(err) = self.config.save(&self.config_dir) {
warn!("Failed to save config: {}", err); warn!("Failed to save config: {}", err);
return; return;

View File

@@ -4,6 +4,7 @@ use egui_dock::{DockArea, NodeIndex, SurfaceIndex, TabViewer};
use egui_wgpu::Callback; use egui_wgpu::Callback;
use nalgebra::Matrix4; use nalgebra::Matrix4;
use parking_lot::MappedRwLockWriteGuard; use parking_lot::MappedRwLockWriteGuard;
use serde::{Deserialize, Serialize};
use crate::{app::App, render::workspace::WorkspaceRenderCallback}; use crate::{app::App, render::workspace::WorkspaceRenderCallback};
@@ -22,7 +23,7 @@ struct Tabs<'a> {
ctx: &'a Context, ctx: &'a Context,
} }
#[derive(Hash, PartialEq, Eq)] #[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
pub enum Tab { pub enum Tab {
About, About,
Logs, Logs,