use std::{
cell::RefCell,
collections::BTreeSet,
rc::Rc,
sync::{Arc, Mutex},
};
use indicatrix_editor::session::RemoveTierError;
use slint::{ComponentHandle, Model};
use super::{
adopt::{setup_adopt_all_callback, setup_adopt_selected_callback, setup_pin_to_mast_callback},
concave_tier::{
Services, concave_at, duplicate_concave_now, move_concave_now, remove_concave_now,
},
facet_editing::{
setup_facet_add_callback, setup_facet_remove_callback, setup_facet_toggle_detach_callback,
setup_highlight_tooth_callback, setup_tier_mirror_indices_callback,
setup_tier_rotate_indices_callback,
},
facet_overlay::resubmit_facet_overlay,
misc::{adjust_selection_after_remove, bump_form_reset_pulse},
tier_generation::{
setup_generate_step_series_callback, setup_mirror_tier_to_other_block_callback,
},
};
use crate::{
EditorModel, EditorTierItem, MainWindow,
bridge::render_thread::RenderContext,
gui::{
editor::{
auto_solve,
native_io::ask_write_confirm,
relation_ui::{setup_relation_callbacks, take_cleared_relations_sentence},
state::{EditorState, apply_multi_selection, push_multi_selected_count, push_tiers},
view::{SolidLastSolved, refresh_editor_panel_stale, submit_preview_replan},
},
show_toast,
solid_preview::preview_state::SolidPreviewState,
},
};
pub(in crate::gui::editor) fn setup_remove_tier_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_remove_tier(move |index: i32| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let Ok(position) = usize::try_from(index) else {
return;
};
let concave = concave_at(&state.borrow(), index);
if let Some(concave_index) = concave {
remove_concave_now(
&ui,
&Services {
state: &state,
render_ctx: &render_ctx,
preview_state: &preview_state,
solid_last_solved: &solid_last_solved,
},
concave_index,
index,
);
return;
}
let index = position;
remove_tier_now(
&ui,
&state,
&render_ctx,
&preview_state,
&solid_last_solved,
index,
false,
);
});
}
fn remove_tier_now(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &SolidLastSolved,
index: usize,
cascade: bool,
) {
let mut st = state.borrow_mut();
match st.remove_tier_with(index, cascade) {
Ok(removed) => {
refresh_editor_panel_stale(ui, render_ctx, &st, &BTreeSet::new());
adjust_selection_after_remove(ui, index as i32);
submit_preview_replan(
ui,
render_ctx,
preview_state,
solid_last_solved,
&st,
BTreeSet::new(),
false,
);
let freed = take_cleared_relations_sentence(&mut st);
drop(st);
let plural = if removed.facet_count == 1 { "" } else { "s" };
let mut message = format!(
"Removed {} ({} facet{plural}), Undo",
removed.name, removed.facet_count
);
if let Some(freed) = freed {
message.push_str(". ");
message.push_str(&freed);
}
show_toast(ui, &message, "info");
}
Err(error @ RemoveTierError::HasDependants { .. }) if !cascade => {
drop(st);
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);
ask_write_confirm(
ui,
"Remove anyway?",
error.to_string(),
"Remove anyway",
None,
move |ui| {
remove_tier_now(
ui,
&state,
&render_ctx,
&preview_state,
&solid_last_solved,
index,
true,
);
},
);
}
Err(error) => {
drop(st);
show_toast(ui, &error.to_string(), "error");
}
}
}
pub(in crate::gui::editor) fn setup_duplicate_tier_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_duplicate_tier(move |index: i32| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let Ok(position) = usize::try_from(index) else {
return;
};
let concave = concave_at(&state.borrow(), index);
if let Some(concave_index) = concave {
duplicate_concave_now(
&ui,
&Services {
state: &state,
render_ctx: &render_ctx,
preview_state: &preview_state,
solid_last_solved: &solid_last_solved,
},
concave_index,
);
return;
}
let index = position;
let mut st = state.borrow_mut();
match st.duplicate_tier(index) {
Ok(None) => {}
Ok(Some(duplicated)) => {
let new_index = duplicated.new_index;
refresh_editor_panel_stale(&ui, &render_ctx, &st, &BTreeSet::from([new_index]));
submit_preview_replan(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&st,
BTreeSet::from([new_index]),
false,
);
drop(st);
ui.global::<EditorModel>()
.set_selected_tier_index(new_index as i32);
show_toast(
&ui,
&format!(
"Duplicated {} as {}",
duplicated.source_label, duplicated.duplicate_label
),
"info",
);
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
});
}
pub(in crate::gui::editor) fn setup_toggle_detach_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &SolidLastSolved,
) {
auto_solve::stash_render_ctx(render_ctx);
let state_toggle = Rc::clone(state);
let render_ctx_toggle = Arc::clone(render_ctx);
let preview_state_toggle = Arc::clone(preview_state);
let solid_last_solved_toggle = Arc::clone(solid_last_solved);
let ui_weak = ui.as_weak();
ui.global::<EditorModel>()
.on_toggle_detach(move |index: i32| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
if index < 0 {
return;
}
let mut st = state_toggle.borrow_mut();
let index = index as usize;
match st.toggle_detach(index) {
Ok(None) => {}
Ok(Some(toggled)) => {
refresh_editor_panel_stale(
&ui,
&render_ctx_toggle,
&st,
&BTreeSet::from([index]),
);
submit_preview_replan(
&ui,
&render_ctx_toggle,
&preview_state_toggle,
&solid_last_solved_toggle,
&st,
BTreeSet::from([index]),
false,
);
drop(st);
if !toggled.label.is_empty() {
let verb = if toggled.detached {
"Detached"
} else {
"Reattached"
};
show_toast(&ui, &format!("{verb} {}", toggled.label), "info");
}
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
});
setup_move_tier_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_complete_orbit_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_clear_multi_select_callback(ui, state);
setup_remove_multi_selected_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_facet_remove_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_facet_toggle_detach_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_facet_add_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_tier_rotate_indices_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_tier_mirror_indices_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_adopt_all_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_adopt_selected_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_pin_to_mast_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_highlight_tooth_callback(ui, state);
setup_generate_step_series_callback(ui, state, render_ctx, preview_state, solid_last_solved);
setup_relation_callbacks(ui, state, render_ctx, preview_state, solid_last_solved);
setup_mirror_tier_to_other_block_callback(
ui,
state,
render_ctx,
preview_state,
solid_last_solved,
);
}
fn setup_move_tier_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_move_tier(move |index: i32, direction: i32| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let Ok(position) = usize::try_from(index) else {
return;
};
let concave = concave_at(&state.borrow(), index);
if let Some(concave_index) = concave {
move_concave_now(
&ui,
&Services {
state: &state,
render_ctx: &render_ctx,
preview_state: &preview_state,
solid_last_solved: &solid_last_solved,
},
concave_index,
direction,
);
return;
}
let index = position;
let mut st = state.borrow_mut();
match st.move_tier(index, direction) {
Ok(None) => {}
Ok(Some(moved)) => {
let target = moved.target;
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,
);
drop(st);
ui.global::<EditorModel>()
.set_selected_tier_index(target as i32);
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
});
}
fn setup_complete_orbit_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_complete_orbit(move |index: i32| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let Ok(index) = usize::try_from(index) else {
return;
};
let mut st = state.borrow_mut();
match st.complete_orbit(index) {
Ok(false) => {}
Ok(true) => {
refresh_editor_panel_stale(&ui, &render_ctx, &st, &BTreeSet::from([index]));
submit_preview_replan(
&ui,
&render_ctx,
&preview_state,
&solid_last_solved,
&st,
BTreeSet::from([index]),
false,
);
}
Err(e) => show_toast(&ui, &e.to_string(), "error"),
}
});
}
fn setup_clear_multi_select_callback(ui: &MainWindow, state: &Rc<RefCell<EditorState>>) {
let state = Rc::clone(state);
let ui_weak = ui.as_weak();
ui.global::<EditorModel>().on_clear_multi_select(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
let mut st = state.borrow_mut();
if st.multi_selected.is_empty() {
return;
}
st.multi_selected.clear();
let mut rows: Vec<EditorTierItem> = ui.global::<EditorModel>().get_tiers().iter().collect();
apply_multi_selection(&mut rows, &st.multi_selected);
push_tiers(&ui, rows);
push_multi_selected_count(&ui, 0);
if let Some(preview_state) = auto_solve::preview_state() {
resubmit_facet_overlay(&preview_state, |overlay| overlay.multi_selected.clear());
}
});
}
fn setup_remove_multi_selected_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_remove_multi_selected(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
remove_multi_selected_now(
&ui,
&state,
&render_ctx,
&preview_state,
&solid_last_solved,
false,
);
});
}
fn remove_multi_selected_now(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &SolidLastSolved,
cascade: bool,
) {
let mut st = state.borrow_mut();
match st.remove_multi_selected_with(cascade) {
Ok(0) => {}
Ok(removed_count) => {
refresh_editor_panel_stale(ui, render_ctx, &st, &BTreeSet::new());
submit_preview_replan(
ui,
render_ctx,
preview_state,
solid_last_solved,
&st,
BTreeSet::new(),
false,
);
let freed = take_cleared_relations_sentence(&mut st);
drop(st);
ui.global::<EditorModel>().set_selected_tier_index(-1);
bump_form_reset_pulse(ui);
let mut message = format!("Removed {removed_count} tier(s).");
if let Some(freed) = freed {
message.push(' ');
message.push_str(&freed);
}
show_toast(ui, &message, "info");
}
Err(error @ RemoveTierError::HasDependants { .. }) if !cascade => {
drop(st);
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);
ask_write_confirm(
ui,
"Remove anyway?",
error.to_string(),
"Remove anyway",
None,
move |ui| {
remove_multi_selected_now(
ui,
&state,
&render_ctx,
&preview_state,
&solid_last_solved,
true,
);
},
);
}
Err(error) => {
drop(st);
show_toast(ui, &error.to_string(), "error");
}
}
}