use super::{SLOT, ensure_current, pending_fix, present, services};
use crate::{
EditorModel, MainWindow,
gui::{
editor::{
callbacks::{HistoryMove, refresh_after_renumbering_move},
native_io::ask_write_confirm,
relation_ui::take_cleared_relations_sentence,
},
show_toast,
},
};
use indicatrix_cut_core::ConstraintTier;
use indicatrix_editor::verdict::{FixAction, FixPlan, plan_fix};
use slint::ComponentHandle;
use std::panic::{AssertUnwindSafe, catch_unwind};
const CONFIRM_HEADING: &str = "Remove what is cut away?";
const CONFIRM_BUTTON: &str = "Remove";
const PLANNER_FAILED: &str = "The fix stopped unexpectedly. Nothing was changed.";
const EDITOR_BUSY: &str = "The editor is busy. Try the fix again.";
pub(super) fn fix_reason(ui: &MainWindow, index: i32) {
let Some(services) = services() else {
return;
};
let ready = usize::try_from(index)
.map_err(|_| "There is no such reason.".to_string())
.and_then(|index| pending_fix(index, &services.editor.borrow().design.tiers));
match ready {
Ok((action, Some(question))) => {
ask_write_confirm(
ui,
CONFIRM_HEADING,
question,
CONFIRM_BUTTON,
None,
move |ui| {
start(ui, action);
},
);
}
Ok((action, None)) => start(ui, action),
Err(message) => show_toast(ui, &message, "info"),
}
}
fn start(ui: &MainWindow, action: FixAction) {
let Some(services) = services() else {
return;
};
if let Err(message) = ensure_current(&services.editor.borrow().design.tiers) {
show_toast(ui, &message, "info");
return;
}
let design = services.editor.borrow().design.clone();
let n_d = SLOT.with(|cell| cell.borrow().n_d);
SLOT.with(|cell| cell.borrow_mut().busy = true);
present::set_busy(ui, true);
let reference = design.tiers.clone();
let ui_weak = ui.as_weak();
std::thread::spawn(move || {
let planned = catch_unwind(AssertUnwindSafe(|| plan_fix(&design, &action, n_d)))
.unwrap_or_else(|_| Err(PLANNER_FAILED.to_string()));
let _ = ui_weak.upgrade_in_event_loop(move |ui| finish_fix(&ui, &reference, planned));
});
}
fn finish_fix(ui: &MainWindow, reference: &[ConstraintTier], planned: Result<FixPlan, String>) {
SLOT.with(|cell| cell.borrow_mut().busy = false);
present::set_busy(ui, false);
let plan = match planned {
Ok(plan) => plan,
Err(message) => {
show_toast(ui, &message, "error");
return;
}
};
let Some(services) = services() else {
return;
};
let Ok(mut st) = services.editor.try_borrow_mut() else {
show_toast(ui, EDITOR_BUSY, "error");
return;
};
if st.design.tiers != reference {
drop(st);
show_toast(
ui,
"The design changed while the fix was being planned. Nothing was changed.",
"info",
);
return;
}
let FixPlan {
edit,
message,
select,
} = plan;
match st.try_apply_mapped(edit, None) {
Ok((change, renumbering)) => {
refresh_after_renumbering_move(
ui,
&services.render_ctx,
&services.preview_state,
&services.solid_last_solved,
&st,
&HistoryMove::new(change, renumbering),
);
let freed = take_cleared_relations_sentence(&mut st);
drop(st);
if let Some(tier) = select.and_then(|tier| i32::try_from(tier).ok()) {
ui.global::<EditorModel>().set_selected_tier_index(tier);
}
let mut text = format!("{message} Undo (Ctrl+Z) puts it back.");
if let Some(freed) = freed {
text.push(' ');
text.push_str(&freed);
}
show_toast(ui, &text, "success");
}
Err(error) => {
drop(st);
show_toast(ui, &error.to_string(), "error");
}
}
}