Files
OneCAD/crates/ui-egui/src/dialogs_thread.rs
T

96 lines
3.9 KiB
Rust

//! Metric thread choices use the document's sizing table and the existing designation
//! parameter. Browsing choices does not edit the model or freeze an automatic feature.
use solidcraft_engine::doc::{metric_thread_sizes, parse_metric_thread};
use crate::dialogs::{check, combo, row_label};
use crate::i18n::tr;
/// Common pitch presets, not an exhaustive catalogue of standard diameter/pitch pairs.
/// The coarse and current pitches are included separately; custom pairs remain editable.
const PITCHES: &[f64] = &[0.2, 0.25, 0.35, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0];
/// Returns (Enter pressed, automatic sizing requested).
pub(crate) fn rows(ui: &mut egui::Ui, designation: &mut String, suggested: &mut Option<String>) -> (bool, bool) {
row_label(ui, "Profile");
ui.label(tr("ISO metric"));
ui.end_row();
row_label(ui, "Automatic size");
let before = designation.trim().is_empty() || suggested.as_deref() == Some(designation.as_str());
let mut automatic = before;
ui.add_enabled_ui(!designation.trim().is_empty(), |ui| check(ui, &mut automatic));
let reset = automatic && !before;
if automatic != before {
*suggested = None;
if automatic {
designation.clear();
}
}
ui.end_row();
let current = parse_metric_thread(designation);
let mut diameters: Vec<f64> = metric_thread_sizes().iter().map(|(d, _)| *d).collect();
if let Some((d, _)) = current
&& !diameters.contains(&d)
{
diameters.push(d);
diameters.sort_by(f64::total_cmp);
}
row_label(ui, "Size");
let labels: Vec<String> = diameters.iter().map(|d| format!("{d} mm")).collect();
let mut selected = current.and_then(|(d, _)| diameters.iter().position(|n| *n == d)).unwrap_or(diameters.len());
let before = selected;
let mut labels: Vec<&str> = labels.iter().map(String::as_str).collect();
if current.is_none() {
labels.push("Select size");
}
combo(ui, "thread_diameter", &labels, &mut selected);
// A new diameter starts with its coarse pitch. The current custom diameter, if any,
// is already selected and retains its explicit pitch.
if selected != before
&& let Some(d) = diameters.get(selected)
&& let Some((_, coarse)) = metric_thread_sizes().iter().find(|(n, _)| n == d)
{
*designation = format!("M{d}x{coarse}");
*suggested = None;
}
ui.end_row();
let current = parse_metric_thread(designation);
row_label(ui, "Pitch");
ui.add_enabled_ui(current.is_some(), |ui| {
let mut pitches = Vec::new();
if let Some((d, p)) = current {
let coarse = metric_thread_sizes().iter().find(|(n, _)| *n == d).map(|(_, p)| *p);
pitches.extend(PITCHES.iter().copied().filter(|p| *p < d && *p <= coarse.unwrap_or(d)));
pitches.extend(coarse);
pitches.push(p);
}
pitches.sort_by(|a, b| b.total_cmp(a));
pitches.dedup();
let labels: Vec<String> = pitches.iter().map(|p| format!("{p} mm")).collect();
let labels: Vec<&str> = labels.iter().map(String::as_str).collect();
let mut selected = current.and_then(|(_, p)| pitches.iter().position(|n| *n == p)).unwrap_or(0);
let before = selected;
combo(ui, "thread_pitch", &labels, &mut selected);
if selected != before
&& let (Some((d, _)), Some(p)) = (current, pitches.get(selected))
{
*designation = format!("M{d}x{p}");
*suggested = None;
}
});
ui.end_row();
row_label(ui, "Designation");
let response = ui.add(egui::TextEdit::singleline(designation).hint_text(tr("fits the face, e.g. M8")));
crate::scenario::publish_handle("dialog:thread_size", response.rect.center());
if response.changed() {
*suggested = None;
}
let enter = response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
ui.end_row();
(enter, reset)
}