ui: mesh in the background from the mesher forms

OK on a mesher's form ran the command on the window thread: octree hex
on the bolted assembly froze the window for over a minute, with no
progress and no way to stop it. The tet and hex meshers' forms now run
their command as a background job on a copy of the session, as opening
a file and solving do: the window keeps drawing with the job's name and
Cancel, the form closes when the mesh is in and shows the error when it
is not. ui.dialog {run} stays synchronous for programmatic callers.

The new test walks a first-time user's path: Open picks a SolidCraft
design, Tet Mesh opens its form, OK with the defaults meshes the part,
and a mapped hex mesh of a holed plate says why it does not fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 08:46:50 -04:00
1 parent 8a4209a20c
commit e4e3c9a699
4 files changed
+91 -6

No files matched your search

+18 -2
View File
@@ -523,8 +523,8 @@ impl ModelApp {
/// Run the open parameter form; it closes when the command succeeds.
pub fn run_dialog(&mut self) -> Result<Value, String> {
let Some(d) = self.dialog.clone() else { return Err("no command form is open".into()) };
let r = serde_json::from_str::<Value>(&d.text).map_err(|e| format!("the parameters are not JSON: {e}")).and_then(|p| self.run(d.command, p));
let (command, params) = self.dialog_params()?;
let r = self.run(command, params);
match &r {
Ok(_) => self.dialog = None,
Err(e) => {
@@ -536,6 +536,22 @@ impl ModelApp {
r
}
/// The open form's command and parameters; parameters that are not JSON are the form's error.
pub(crate) fn dialog_params(&mut self) -> Result<(&'static str, Value), String> {
let Some(d) = self.dialog.as_mut() else { return Err("no command form is open".into()) };
match serde_json::from_str::<Value>(&d.text) {
Ok(params) => {
d.error = None;
Ok((d.command, params))
}
Err(e) => {
let e = format!("the parameters are not JSON: {e}");
d.error = Some(e.clone());
Err(e)
}
}
}
/// Probe a node of the results shown (`solve.probe`) and keep its values for the viewport.
pub fn probe_node(&mut self, node: u32) -> Result<(), String> {
let Some(view) = self.session.results_view.clone() else { return Err("no results are shown".into()) };