mod auto_solve;
mod callbacks;
mod cut_sheet;
mod deep_solve;
mod loading;
mod material_lookup;
mod native_io;
mod optimize_solve;
pub mod retarget;
mod state;
mod view;
use crate::{
MainWindow,
bridge::{library::source::LibrarySource, render_thread::RenderContext},
gui::solid_preview::preview_state::{SolidLastSolved, SolidPickState, SolidPreviewState},
};
use indicatrix::geometry::meet_solver::SolvedTier;
use indicatrix_vault::db::sqlite::Database;
use slint::{ComponentHandle as _, Model as _};
use state::EditorState;
use std::{
cell::RefCell,
rc::Rc,
sync::{Arc, Mutex},
};
fn setup_solve_cancel_callback(ui: &MainWindow) {
let ui_weak = ui.as_weak();
ui.global::<crate::EditorModel>().on_solve_cancel(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
auto_solve::cancel_in_flight_solve();
let model = ui.global::<crate::EditorModel>();
model.set_solve_running(false);
model.set_solve_state("stale".into());
model.set_status_text("Solve abandoned -- click Solve when you are ready.".into());
model.set_status_is_problem(true);
});
}
fn setup_startup_restore(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
) {
let model = ui.global::<crate::EditorModel>();
let autosave = native_io::find_leftover_autosave();
let offer = autosave.clone().or_else(|| {
ui.get_recent_native_files()
.iter()
.next()
.map(|path| std::path::PathBuf::from(path.as_str()))
});
let Some(offer) = offer else {
return;
};
model.set_startup_restore_is_autosave(autosave.is_some());
model.set_startup_restore_path(offer.display().to_string().into());
let state_accept = Rc::clone(state);
let render_ctx_accept = Arc::clone(render_ctx);
let preview_accept = Arc::clone(preview_state);
let solved_accept = Arc::clone(solid_last_solved);
let ui_weak = ui.as_weak();
ui.global::<crate::EditorModel>()
.on_startup_restore_accept(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let path = ui.global::<crate::EditorModel>().get_startup_restore_path();
ui.global::<crate::EditorModel>()
.set_startup_restore_path(String::new().into());
native_io::open_recent_native_path(
&ui,
&state_accept,
&render_ctx_accept,
&preview_accept,
&solved_accept,
std::path::PathBuf::from(path.as_str()),
);
});
let ui_weak_dismiss = ui.as_weak();
ui.global::<crate::EditorModel>()
.on_startup_restore_dismiss(move || {
let Some(ui) = ui_weak_dismiss.upgrade() else {
return;
};
ui.global::<crate::EditorModel>()
.set_startup_restore_path(String::new().into());
});
}
fn setup_tier_cutoff_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::<crate::SolidPreviewModel>()
.on_tier_cutoff_changed(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let st = state.borrow();
view::submit_preview_replan(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&st,
std::collections::BTreeSet::new(),
false,
);
});
}
pub fn setup_editor_callbacks(
ui: &MainWindow,
db: &Arc<Mutex<Database>>,
source: &Arc<Mutex<LibrarySource>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &SolidLastSolved,
solid_pick_state: &SolidPickState,
) {
auto_solve::init(preview_state, solid_last_solved);
let state = Rc::new(RefCell::new(EditorState::fresh()));
view::refresh_editor_panel(ui, render_ctx, &state.borrow());
setup_startup_restore(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_new_design_create_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_load_selected_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
db,
source,
);
callbacks::setup_solve_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_undo_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_redo_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_apply_preform_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_apply_yield_inputs_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_save_tier_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_remove_tier_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_toggle_detach_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_inline_set_angle_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_nudge_angle_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_duplicate_tier_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_toggle_multi_select_callback(ui, &state);
native_io::setup_export_asc_callback(ui, &state);
native_io::setup_export_cutting_sheet_callback(ui, &state);
native_io::setup_export_diagram_callback(ui, &state);
native_io::setup_save_native_callback(ui, &state, db, source);
native_io::setup_open_native_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_adopt_meet_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_deep_solve_callback(ui, &state);
setup_solve_cancel_callback(ui);
setup_tier_cutoff_callback(ui, &state, render_ctx, preview_state, solid_last_solved);
callbacks::setup_deep_solve_cancel_callback(ui, &state);
callbacks::setup_optimize_callback(ui, &state, render_ctx);
callbacks::setup_optimize_cancel_callback(ui, &state);
callbacks::setup_optimize_apply_callback(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
);
setup_editor_secondary_callbacks(
ui,
&state,
render_ctx,
preview_state,
solid_last_solved,
solid_pick_state,
);
}
fn setup_editor_secondary_callbacks(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &SolidLastSolved,
solid_pick_state: &SolidPickState,
) {
callbacks::setup_apply_design_material_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_gear_apply_callback(ui, state);
callbacks::setup_gear_remap_confirm_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_gear_remap_cancel_callback(ui, state);
callbacks::setup_apply_symmetry_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_viewport_material_linked_changed_callback(ui, state, render_ctx);
callbacks::setup_material_suggestion_accept_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_material_suggestion_dismiss_callback(ui);
callbacks::setup_retarget_open_callback(ui, state, render_ctx);
callbacks::setup_retarget_proposal_changed_callback(ui, state, render_ctx);
callbacks::setup_retarget_apply_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
callbacks::setup_retarget_close_callback(ui, state);
callbacks::setup_tier_filter_callback(ui);
callbacks::setup_solid_facet_hover_callback(ui, solid_pick_state);
callbacks::setup_solid_facet_click_callback(ui, solid_pick_state);
callbacks::setup_solid_selected_tier_changed_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
}
pub fn apply_matching_preview_frame(ui: &MainWindow, generation: u64, solved: &[SolvedTier]) {
if let Some((design, multi_selected)) = auto_solve::take_matching_design(generation) {
view::push_solved_preview(ui, &design, solved, &multi_selected);
}
}