Fillet dialog: a setback the field flags is not refused again under the dialog

The Setback field's "More than 2 mm" is the message; the kernel's refusal of
that setback shows no text under the dialog and no Create Issue (it is the
user's value to change), and its raw text stays in Copy Details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtL5Lzv2n8fyKqD61NCHSF
This commit is contained in:
jyviko
2026-10-28 14:50:05 -04:00
committed by Irakli Kourtis
parent 06fbcdfec6
commit baa44fc64b
2 changed files with 36 additions and 10 deletions
+31 -10
View File
@@ -2595,14 +2595,17 @@ fn error_rows(app: &mut SolveApp, ctx: &egui::Context, d: &mut Dialog, ui: &mut
crate::ui_kit::text(ui, &shown_error(&error), crate::ui_kit::Text::Hint("missing_pick"));
return;
}
// A fillet's refusal reads in plain words; the kernel's own text stays in the hover
// text, Copy Details and the issue report.
let plain = if matches!(d.kind, Kind::Fillet { chamfer: false, .. }) { fillet_plain_error(&error) } else { None };
// A fillet's refusal reads in plain words, or not at all where its Setback field already
// says what is wrong; the kernel's own text stays in the hover text, Copy Details and the
// issue report.
let setback_in_field = d.extra.get("setbacks").is_none();
let plain = if matches!(d.kind, Kind::Fillet { chamfer: false, .. }) { fillet_plain_error(&error, setback_in_field) } else { None };
ui.vertical(|ui| {
match &plain {
Some(plain) => {
Some(FilletRefusal::Plain(plain)) => {
crate::ui_kit::text(ui, plain, crate::ui_kit::Text::Report("error", true)).on_hover_text(shown_error(&error));
}
Some(FilletRefusal::InField) => {}
None => {
crate::ui_kit::text(ui, &shown_error(&error), crate::ui_kit::Text::Report("error", true));
}
@@ -2615,6 +2618,10 @@ fn error_rows(app: &mut SolveApp, ctx: &egui::Context, d: &mut Dialog, ui: &mut
ui.ctx().copy_text(error.clone());
}
}
// A value the field flags is the user's to change, not a failure to report.
if matches!(plain, Some(FilletRefusal::InField)) {
return;
}
let response = crate::ui_kit::button(ui, "Create Issue…", crate::ui_kit::Button::Normal);
crate::scenario::publish_handle("dialog:create_issue", response.rect.center());
if response.clicked() {
@@ -2625,11 +2632,25 @@ fn error_rows(app: &mut SolveApp, ctx: &egui::Context, d: &mut Dialog, ui: &mut
}
}
/// How the Fillet dialog shows a kernel refusal ([`fillet_plain_error`]).
#[derive(Debug, PartialEq)]
pub(crate) enum FilletRefusal {
/// In these plain words.
Plain(&'static str),
/// Not again: the Setback field already flags the value and names the least it takes.
InField,
}
/// A fillet's refusal by the kernel in plain words, for the dialog: what happened and what may
/// help, without SMLib's status codes, its check's test list or the command's parameter names.
/// `None` for a refusal that is no kernel failure (an expression, a pick), shown as it is.
pub(crate) fn fillet_plain_error(error: &str) -> Option<&'static str> {
/// help, without SMLib's status codes, its check's test list or the command's parameter names;
/// a setback short of the least is left to the Setback field (`setback_in_field`: the setback
/// came from it, so it is flagged there). `None` for a refusal that is no kernel failure (an
/// expression, a pick), shown as it is.
pub(crate) fn fillet_plain_error(error: &str, setback_in_field: bool) -> Option<FilletRefusal> {
// The kernel's refusals as the timeline records them (`KernelError`, `blend_smlib`).
if setback_in_field && error.contains("the setback must be more than") {
return Some(FilletRefusal::InField);
}
let too_large = error.contains("larger than the faces beside its edges allow");
let corner = error.contains("setback") || error.contains("G2 corner") || error.contains("corner patch");
let kernel = error.contains("SMLib")
@@ -2638,11 +2659,11 @@ pub(crate) fn fillet_plain_error(error: &str) -> Option<&'static str> {
|| error.contains("kernel internal error")
|| error.contains("the rounds would leave openings");
if too_large {
Some(tr("ui.fillet_error_too_large"))
Some(FilletRefusal::Plain(tr("ui.fillet_error_too_large")))
} else if corner && kernel {
Some(tr("ui.fillet_error_corner"))
Some(FilletRefusal::Plain(tr("ui.fillet_error_corner")))
} else if kernel {
Some(tr("ui.fillet_error_failed"))
Some(FilletRefusal::Plain(tr("ui.fillet_error_failed")))
} else {
None
}
+5
View File
@@ -1370,6 +1370,11 @@ mod tests {
let mut copy = solidcraft_engine::Session::new((*h.app.session.doc).clone());
let refused = copy.execute("solid.fillet", &parameters(&h)).unwrap_err().to_string();
assert!(refused.contains("setback must be more than the radius (2 mm)"), "the kernel refuses what the field flags: {refused}");
assert_eq!(
crate::dialogs::fillet_plain_error(&refused, true),
Some(crate::dialogs::FilletRefusal::InField),
"the field says it, not again under the dialog"
);
let least = crate::dialogs::fillet_least_setback(
&h.ctx,
&h.app.session,