use super::{auto_solve, edit_intent, native_io, stall_guard, state::EditorState, view};
use crate::{
MainWindow,
bridge::render_thread::RenderContext,
gui::{
solid_preview::{cut_slider, preview_state::SolidPreviewState},
tutorial_events::raise,
},
};
use indicatrix_editor::{
edit_intent::DRAIN_INTERVAL,
guide::viewing_events::{DIAGRAM_PANEL_ENLARGED, cut_slider_event},
};
use slint::{ComponentHandle as _, Model as _};
use std::{
cell::RefCell,
rc::Rc,
sync::{Arc, Mutex},
};
pub(super) 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);
});
}
pub(super) fn setup_startup_restore(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
) {
if let Some(path) = crate::gui::startup_file::take_requested_open() {
open_requested_design(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
path,
);
return;
}
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());
});
let ui_weak_delete = ui.as_weak();
ui.global::<crate::EditorModel>()
.on_startup_restore_delete(move || {
let Some(ui) = ui_weak_delete.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::delete_leftover_autosave(std::path::Path::new(path.as_str()));
});
}
fn open_requested_design(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
path: std::path::PathBuf,
) {
let ui_weak = ui.as_weak();
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);
slint::Timer::single_shot(std::time::Duration::from_millis(150), move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
native_io::open_recent_native_path(
&ui,
&state,
&render_ctx,
&preview_state,
&solid_last_solved,
path,
);
});
}
struct CutReplan {
ui_weak: slint::Weak<MainWindow>,
state: Rc<RefCell<EditorState>>,
render_ctx: Arc<Mutex<RenderContext>>,
preview_state: Arc<SolidPreviewState>,
solid_last_solved: view::SolidLastSolved,
}
fn run_cut_replan(replan: &Rc<CutReplan>, retries_left: u8) {
let Some(ui) = replan.ui_weak.upgrade() else {
return;
};
let Ok(st) = replan.state.try_borrow() else {
if let Some(next) = cut_slider::retries_after_busy(retries_left) {
let again = Rc::clone(replan);
slint::Timer::single_shot(DRAIN_INTERVAL, move || run_cut_replan(&again, next));
}
return;
};
view::submit_preview_replan(
&ui,
&replan.render_ctx,
&replan.preview_state,
&replan.solid_last_solved,
&st,
std::collections::BTreeSet::new(),
false,
);
}
pub(super) 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 replan = Rc::new(CutReplan {
ui_weak: ui.as_weak(),
state: Rc::clone(state),
render_ctx: Arc::clone(render_ctx),
preview_state: Arc::clone(preview_state),
solid_last_solved: Arc::clone(solid_last_solved),
});
let intent_queue = edit_intent::EditIntentQueue::new(move |_intent| {
run_cut_replan(&replan, cut_slider::REPLAN_RETRIES);
});
let enlarged_queue = Rc::clone(&intent_queue);
let ui_weak_enlarged = ui.as_weak();
ui.global::<crate::SolidPreviewModel>()
.on_diagram_enlarged_panel_changed(move || {
stall_guard::stall_guard("on_diagram_enlarged_panel_changed", || {
let Some(ui) = ui_weak_enlarged.upgrade() else {
return;
};
let model = ui.global::<crate::SolidPreviewModel>();
let count = model.get_tier_cutoff();
enlarged_queue.post(edit_intent::EditIntent::CutOff { count });
if model.get_diagram_enlarged_panel() >= 0 {
raise(&ui, DIAGRAM_PANEL_ENLARGED);
}
});
});
let ui_weak = ui.as_weak();
ui.global::<crate::SolidPreviewModel>()
.on_tier_cutoff_changed(move || {
stall_guard::stall_guard("on_tier_cutoff_changed", || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let count = ui.global::<crate::SolidPreviewModel>().get_tier_cutoff();
intent_queue.post(edit_intent::EditIntent::CutOff { count });
raise(&ui, cut_slider_event(count));
});
});
}