use crate::{
EditorModel, MainWindow, RetargetModel, RetargetRowItem,
gui::{
editor::{
native_io,
state::{EditorState, design_label_text},
view,
},
show_toast,
},
};
use indicatrix::geometry::meet_solver::SolvedTier;
use indicatrix_cut_core::{Design, TierDelta, diff_tiers};
use indicatrix_editor::snapshot::DesignSnapshot;
use slint::{Color, ComponentHandle, ModelRc, VecModel};
use std::{cell::RefCell, rc::Rc, sync::Arc};
thread_local! {
static DESIGN_SNAPSHOT: RefCell<Option<DesignSnapshot>> = const { RefCell::new(None) };
}
#[must_use]
pub(in crate::gui::editor) fn snapshot_for_compare() -> Option<(Design, String)> {
DESIGN_SNAPSHOT.with(|cell| {
cell.borrow()
.as_ref()
.map(|snapshot| (snapshot.design.clone(), snapshot.label.clone()))
})
}
pub(super) use indicatrix_editor::snapshot::diff_row_view;
#[must_use]
pub(super) fn diff_rows_from_deltas(deltas: &[TierDelta]) -> Vec<RetargetRowItem> {
deltas
.iter()
.map(diff_row_view)
.map(|v| RetargetRowItem {
tier_index: i32::try_from(v.tier_index).unwrap_or(i32::MAX),
block: "".into(),
name: v.name.into(),
old_angle: v.old_angle.into(),
new_angle: v.new_angle.into(),
margin: v.mast_delta.into(),
risk_label: v.status_label.into(),
risk_color: Color::from_rgb_u8(v.status_rgb.0, v.status_rgb.1, v.status_rgb.2),
})
.collect()
}
fn store_design_snapshot(
ui: &MainWindow,
design: &Design,
solved: Option<Vec<SolvedTier>>,
label: &str,
) {
DESIGN_SNAPSHOT.with(|cell| {
*cell.borrow_mut() = Some(DesignSnapshot {
design: design.clone(),
solved,
label: label.to_string(),
});
});
ui.global::<EditorModel>().set_has_snapshot(true);
show_toast(ui, &format!("Snapshot taken: \"{label}\"."), "success");
}
fn show_compare_to_snapshot(
ui: &MainWindow,
snapshot: &DesignSnapshot,
design: &Design,
current_label: &str,
current_solved: Option<&[SolvedTier]>,
) {
let deltas = diff_tiers(
&snapshot.design.tiers,
snapshot.solved.as_deref(),
&design.tiers,
current_solved,
);
let rows = diff_rows_from_deltas(&deltas);
ui.global::<RetargetModel>()
.set_compare_rows(ModelRc::new(VecModel::from(rows)));
ui.global::<RetargetModel>().set_compare_label(
format!("\"{}\" vs. current (\"{current_label}\")", snapshot.label).into(),
);
ui.global::<RetargetModel>().set_compare_open(true);
}
pub(in crate::gui::editor) fn setup_snapshot_callbacks(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
solid_last_solved: &view::SolidLastSolved,
) {
{
let state = Rc::clone(state);
let solid_last_solved = Arc::clone(solid_last_solved);
let ui_weak = ui.as_weak();
ui.global::<EditorModel>().on_snapshot_design(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let (design, label, cached) = {
let st = state.borrow();
let cached = solid_last_solved
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone()
.filter(|(_, s)| s.len() == st.design.tiers.len())
.map(|(_, s)| s);
(
Arc::new(st.design.clone()),
design_label_text(st.asc_filename.as_deref()),
cached,
)
};
if let Some(solved) = cached {
store_design_snapshot(&ui, &design, Some(solved), &label);
return;
}
native_io::resolve_solved_then(&ui, design, move |ui, design, solved| {
store_design_snapshot(ui, &design, solved.ok(), &label);
});
});
}
{
let state = Rc::clone(state);
let solid_last_solved = Arc::clone(solid_last_solved);
let ui_weak = ui.as_weak();
ui.global::<EditorModel>().on_compare_to_snapshot(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let snapshot = DESIGN_SNAPSHOT.with(|cell| cell.borrow().clone());
let Some(snapshot) = snapshot else {
show_toast(
&ui,
"No snapshot taken yet -- use Snapshot Design first.",
"error",
);
return;
};
let (design, current_label, cached) = {
let st = state.borrow();
let cached = solid_last_solved
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone()
.filter(|(_, s)| s.len() == st.design.tiers.len())
.map(|(_, s)| s);
(
Arc::new(st.design.clone()),
design_label_text(st.asc_filename.as_deref()),
cached,
)
};
if let Some(current_solved) = &cached {
show_compare_to_snapshot(
&ui,
&snapshot,
&design,
¤t_label,
Some(current_solved),
);
return;
}
native_io::resolve_solved_then(&ui, design, move |ui, design, solved| {
show_compare_to_snapshot(
ui,
&snapshot,
&design,
¤t_label,
solved.ok().as_deref(),
);
});
});
}
let ui_weak = ui.as_weak();
ui.global::<RetargetModel>().on_compare_close(move || {
if let Some(ui) = ui_weak.upgrade() {
ui.global::<RetargetModel>().set_compare_open(false);
}
});
}