use super::{
RETARGET_ASYNC,
material::{target_display_name, target_material_selection},
};
use crate::{
CompareModel, MainWindow, RetargetModel,
bridge::render_thread::RenderContext,
gui::{
editor::{
retarget::{self, RetargetProposal},
stale::{self, ResultKind},
stall_guard::stall_guard,
state::EditorState,
view,
},
render::render_visibility::recompute_tab_visible,
show_toast,
solid_preview::preview_state::SolidPreviewState,
},
};
use indicatrix_cut_core::{Edit, EditError, MaterialSelection};
use slint::ComponentHandle;
use std::{
cell::RefCell,
collections::BTreeSet,
rc::Rc,
sync::{Arc, Mutex, atomic::Ordering as AtomicOrdering},
};
pub(super) enum RetargetApplyError {
Stale,
Edit(EditError),
}
pub(super) fn apply_pending_retarget(
state: &mut EditorState,
pending: (RetargetProposal, u64),
material_change: Option<MaterialSelection>,
) -> Result<usize, RetargetApplyError> {
let (proposal, started_generation) = pending;
if state.generation.load(AtomicOrdering::Relaxed) != started_generation {
return Err(RetargetApplyError::Stale);
}
let angle_edit = retarget::apply(&state.design, &proposal);
let applied = proposal.rows.len();
let edit = match material_change {
Some(material) => Edit::Batch(vec![angle_edit, Edit::SetMaterial { material }]),
None => angle_edit,
};
state
.apply(edit)
.map(|()| applied)
.map_err(RetargetApplyError::Edit)
}
pub(in crate::gui::editor) fn setup_retarget_apply_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
) {
let state = Rc::clone(state);
let render_ctx = Arc::clone(render_ctx);
let preview_state = Arc::clone(preview_state);
let solid_last_solved = Arc::clone(solid_last_solved);
let ui_weak = ui.as_weak();
ui.global::<RetargetModel>().on_apply(move || {
stall_guard("retarget_on_apply", || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let mut st = state.borrow_mut();
let pending = st
.pending_retarget
.take()
.or_else(|| RETARGET_ASYNC.with(|cell| cell.borrow_mut().pending.take()));
let Some(pending) = pending else {
return;
};
stale::clear(ResultKind::Retarget);
let custom = render_ctx
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.custom_materials
.as_ref()
.clone();
let combo_index = ui.global::<RetargetModel>().get_target_material_index();
let ri_text = ui.global::<RetargetModel>().get_target_ri_override_text();
let material_selection =
target_material_selection(&st.design, &custom, combo_index, &ri_text);
let target_name = target_display_name(&material_selection);
let material_change =
(material_selection != st.design.material).then_some(material_selection);
match apply_pending_retarget(&mut st, pending, material_change) {
Ok(applied) => {
drop(st);
view::refresh_all_now(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&state,
false,
);
ui.global::<RetargetModel>().set_is_open(false);
release_modal_viewport(&ui, &render_ctx);
show_toast(
&ui,
&format!("Retargeted {applied} tier(s) for {target_name}."),
"success",
);
}
Err(RetargetApplyError::Stale) => {
show_toast(
&ui,
"The design changed since this retarget proposal was built -- \
re-open Retarget for material.",
"error",
);
}
Err(RetargetApplyError::Edit(e)) => {
show_toast(&ui, &e.to_string(), "error");
}
}
});
});
}
fn release_modal_viewport(ui: &MainWindow, render_ctx: &Arc<Mutex<RenderContext>>) {
ui.global::<CompareModel>().invoke_close_embedded();
recompute_tab_visible(ui, render_ctx);
}
pub(in crate::gui::editor) fn setup_retarget_close_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
) {
let state = Rc::clone(state);
let render_ctx = Arc::clone(render_ctx);
let preview_state = Arc::clone(preview_state);
let solid_last_solved = Arc::clone(solid_last_solved);
let ui_weak = ui.as_weak();
ui.global::<RetargetModel>().on_close(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
RETARGET_ASYNC.with(|cell| cell.borrow_mut().cancel_and_supersede());
state.borrow_mut().pending_retarget = None;
stale::clear(ResultKind::Retarget);
ui.global::<RetargetModel>().set_is_open(false);
release_modal_viewport(&ui, &render_ctx);
let st = state.borrow();
revert_ghost_to_live_design(&ui, &render_ctx, &preview_state, &solid_last_solved, &st);
});
}
pub(super) fn revert_ghost_to_live_design(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
st: &EditorState,
) {
view::submit_preview_replan(
ui,
render_ctx,
preview_state,
solid_last_solved,
st,
BTreeSet::new(),
false,
);
}