use brep_render::engine_state::EngineState;
use eframe::egui;
use std::collections::HashMap;
pub(super) const FIELDS: [(&str, &str); 6] =
[("x", "X"), ("y", "Y"), ("z", "Z"), ("rx", "X"), ("ry", "Y"), ("rz", "Z")];
#[derive(Default)]
pub(crate) struct MoveCard {
fields: [String; 6],
pub(super) hits: HashMap<String, egui::Rect>,
}
impl MoveCard {
pub(super) fn draw(&mut self, ctx: &egui::Context, rect: egui::Rect, state: &mut EngineState) {
self.hits.clear();
if !state.component_move_armed() {
self.fields = Default::default();
return;
}
let group = state.component_move_group();
let title = match group.as_slice() {
[one] => {
let name = state.component_info(one).map(|info| info.part_name).filter(|name| !name.is_empty());
format!("Move {}", name.unwrap_or_else(|| one.clone()))
}
many => format!("Move {} components", many.len()),
};
let axes = if group.len() > 1 { "World axes, about the group's centre" } else { "Along the gizmo's axes, about its centre" };
let dragging = state.component_move_dragging();
let report = state.component_move_typed_report();
let mut commit: Option<(usize, String)> = None;
let hits = &mut self.hits;
let fields = &mut self.fields;
let shown = egui::Area::new(egui::Id::new("brep-move-card"))
.order(egui::Order::Foreground)
.pivot(egui::Align2::LEFT_TOP)
.fixed_pos(rect.left_top() + egui::vec2(8.0, 8.0))
.show(ctx, |ui| {
egui::Frame::popup(ui.style()).show(ui, |ui| {
ui.strong(title);
ui.weak(axes);
egui::Grid::new("brep-move-card-grid").num_columns(5).spacing([6.0, 4.0]).show(ui, |ui| {
for (row, (label, unit)) in [("Move", "mm"), ("Turn", "\u{00b0}")].into_iter().enumerate() {
ui.label(label);
for column in 0..3 {
let index = row * 3 + column;
let (id, axis) = FIELDS[index];
let field = ui.add_enabled(
!dragging,
egui::TextEdit::singleline(&mut fields[index]).hint_text(axis).desired_width(56.0),
);
if field.lost_focus() && ui.input(|input| input.key_pressed(egui::Key::Enter)) {
commit = Some((index, fields[index].clone()));
}
hits.insert(format!("field:{id}"), field.rect);
}
ui.label(unit);
ui.end_row();
}
});
if let Some(report) = &report {
let color = if !report.written {
ui.visuals().error_fg_color
} else if report.complete() {
ui.visuals().text_color()
} else {
ui.visuals().warn_fg_color
};
ui.label(egui::RichText::new(report.summary()).color(color));
}
});
});
self.hits.insert("card".into(), shown.response.rect);
if let Some((index, text)) = commit {
self.fields[index].clear();
apply(state, FIELDS[index].0, &text);
}
}
}
fn apply(state: &mut EngineState, field: &str, text: &str) {
let text = text.trim();
if text.is_empty() {
return;
}
let Ok(value) = text.replace(',', ".").parse::<f64>() else {
state.push_notice(format!("Move: {text:?} is not a number."));
return;
};
if value == 0.0 {
return;
}
if let Err(error) = state.component_move_typed(field, value) {
state.push_notice(format!("Move: {error}."));
}
}