use std::{
cell::RefCell,
collections::BTreeSet,
rc::Rc,
sync::{Arc, Mutex},
};
use indicatrix_cut_core::Edit;
use slint::{ComponentHandle, Model, SharedString};
use crate::{
EditorModel, MainWindow,
bridge::render_thread::RenderContext,
gui::{
editor::{
guide,
state::{
EditorState, body_colour_from_index, parse_design_material_form,
tiers_incomplete_under_proposed_symmetry,
},
view::{SolidLastSolved, refresh_editor_panel_stale, submit_preview_replan},
},
show_toast,
solid_preview::preview_state::SolidPreviewState,
},
};
pub(super) use indicatrix_editor::loading::ri_override_for_material_pick;
pub(in crate::gui::editor) fn setup_apply_design_material_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &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::<EditorModel>().on_apply_design_material(
move |combo_index: i32, ri_override_text: SharedString, body_colour_index: i32| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let options: Vec<String> = ui
.global::<EditorModel>()
.get_material_combo_options()
.iter()
.map(|s| s.to_string())
.collect();
let mut st = state.borrow_mut();
match parse_design_material_form(
combo_index,
&ri_override_text,
&options,
&st.design.material,
) {
Ok(material) => {
let mut material =
material.with_body_colour(body_colour_from_index(body_colour_index));
if material.refractive_index_override.is_none() {
material.refractive_index_override = ri_override_for_material_pick(
material.name.as_deref(),
st.design.material.name.as_deref(),
st.design.meta.refractive_index,
);
}
match st.apply(Edit::SetMaterial { material }) {
Ok(()) => {
refresh_editor_panel_stale(&ui, &render_ctx, &st, &BTreeSet::new());
ui.global::<EditorModel>()
.set_material_suggestion_name("".into());
ui.global::<EditorModel>()
.set_material_suggestion_text("".into());
guide::check_progress(&ui, &st);
submit_preview_replan(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&st,
BTreeSet::new(),
false,
);
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
}
Err(e) => show_toast(&ui, &e, "error"),
}
},
);
}
pub(in crate::gui::editor) fn setup_material_guess_set_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &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::<EditorModel>()
.on_set_material_from_guess(move |name: SharedString| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let mut st = state.borrow_mut();
let mut material = st.design.material.clone();
material.name = Some(name.to_string());
match st.apply(Edit::SetMaterial { material }) {
Ok(()) => {
refresh_editor_panel_stale(&ui, &render_ctx, &st, &BTreeSet::new());
ui.global::<EditorModel>()
.set_material_suggestion_name("".into());
ui.global::<EditorModel>()
.set_material_suggestion_text("".into());
submit_preview_replan(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&st,
BTreeSet::new(),
false,
);
show_toast(&ui, &format!("Material set to {name}."), "success");
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
});
}
pub(in crate::gui::editor) fn setup_apply_symmetry_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &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 state_preview = Rc::clone(&state);
let ui_weak = ui.as_weak();
ui.global::<EditorModel>().on_apply_symmetry(
move |symmetry_order_text: SharedString, mirror: bool| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let symmetry_order: u32 = match symmetry_order_text.trim().parse() {
Ok(v) if v >= 1 => v,
_ => {
show_toast(
&ui,
"Symmetry order must be a positive whole number.",
"error",
);
return;
}
};
let mut st = state.borrow_mut();
let gear_teeth = st.design.meta.gear_teeth;
match st.apply(Edit::SetSchedule {
gear_teeth,
symmetry_order,
mirror,
}) {
Ok(()) => {
refresh_editor_panel_stale(
&ui,
&render_ctx,
&st,
&(0..st.design.tiers.len()).collect(),
);
submit_preview_replan(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&st,
BTreeSet::new(),
true,
);
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
},
);
let ui_weak_preview = ui.as_weak();
ui.global::<EditorModel>().on_request_symmetry_preview(
move |symmetry_order_text: SharedString, mirror: bool| {
let Some(ui) = ui_weak_preview.upgrade() else {
return;
};
let Ok(symmetry_order) = symmetry_order_text.trim().parse::<u32>() else {
ui.global::<EditorModel>()
.set_symmetry_preview_text(String::new().into());
return;
};
if symmetry_order == 0 {
ui.global::<EditorModel>()
.set_symmetry_preview_text(String::new().into());
return;
}
let st = state_preview.borrow();
let incomplete =
tiers_incomplete_under_proposed_symmetry(&st.design, symmetry_order, mirror);
let text = if incomplete == 0 {
"No tiers would become incomplete orbits.".to_string()
} else {
let plural = if incomplete == 1 { "" } else { "s" };
format!("{incomplete} tier{plural} would become incomplete orbits.")
};
ui.global::<EditorModel>()
.set_symmetry_preview_text(text.into());
},
);
}