use crate::{EditorTierItem, IndexChipItem, MainWindow};
use indicatrix_editor::view_model::{IndexChip, TierRow, TierRowKind};
use slint::{ComponentHandle, Model, ModelRc, VecModel};
pub(in crate::gui::editor) fn tier_item_from_row(row: TierRow) -> EditorTierItem {
EditorTierItem {
index: row.index,
angle_deg: row.angle_deg.into(),
angle_full: row.angle_full.into(),
name: row.name.into(),
indices: row.indices.into(),
indices_full: row.indices_full.into(),
constraint_kind: row.constraint_kind,
constraint_text: row.constraint_text.into(),
mast: row.mast.into(),
mast_mm: row.mast_mm.into(),
mast_full: row.mast_full.into(),
strategy: row.strategy.into(),
strategy_is_uncertain: row.strategy_is_uncertain,
strategy_detail: row.strategy_detail.into(),
needs_anchor: row.needs_anchor,
imported_meet_text: row.imported_meet_text.into(),
orbit_status: row.orbit_status.into(),
orbit_incomplete: row.orbit_incomplete,
is_detached: row.is_detached,
block: row.block.into(),
code: row.code.into(),
margin_text: row.margin_text.into(),
risk_level: row.risk_level,
meet_partners_text: row.meet_partners_text.into(),
warning_text: row.warning_text.into(),
multi_selected: row.multi_selected,
proposed_angle: row.proposed_angle.into(),
kind: i32::from(row.kind == TierRowKind::Concave),
tool_line: row.tool_line.into(),
relation_text: row.relation_text.into(),
}
}
pub(in crate::gui::editor) fn tier_items_from_rows(rows: Vec<TierRow>) -> Vec<EditorTierItem> {
let flat_count = rows
.iter()
.filter(|row| row.kind == TierRowKind::Flat)
.count();
rows.into_iter()
.map(|row| {
let concave_index = row.index;
let mut item = tier_item_from_row(row);
if item.kind == 1 {
item.index = (flat_count as i32).saturating_add(concave_index);
}
item
})
.collect()
}
#[must_use]
pub(in crate::gui::editor) fn concave_tier_index(
flat_count: usize,
concave_count: usize,
position: i32,
) -> Option<usize> {
usize::try_from(position)
.ok()?
.checked_sub(flat_count)
.filter(|&index| index < concave_count)
}
#[must_use]
pub(in crate::gui::editor) fn index_chip_items(
indices: &[f64],
detached: &[f64],
) -> Vec<IndexChipItem> {
indicatrix_editor::view_model::row_format::index_chip_items(indices, detached)
.into_iter()
.map(|chip: IndexChip| IndexChipItem {
position: chip.position,
label: chip.label.into(),
detached: chip.detached,
})
.collect()
}
pub(in crate::gui::editor) fn apply_multi_selection(
rows: &mut [EditorTierItem],
multi_selected: &std::collections::BTreeSet<usize>,
) {
for row in rows {
row.multi_selected =
usize::try_from(row.index).is_ok_and(|index| multi_selected.contains(&index));
}
}
pub(in crate::gui::editor) fn push_multi_selected_count(ui: &MainWindow, count: usize) {
ui.global::<crate::EditorModel>()
.set_multi_selected_count(i32::try_from(count).unwrap_or(i32::MAX));
}
pub(in crate::gui::editor) fn push_tiers(ui: &MainWindow, rows: Vec<EditorTierItem>) {
push_rows(
&ui.global::<crate::EditorModel>().get_tiers(),
rows,
|model| {
ui.global::<crate::EditorModel>().set_tiers(model);
},
);
ui.global::<crate::EditorModel>().invoke_recompute_dirty();
}
pub(in crate::gui::editor) fn push_rows<T: Clone + 'static>(
current: &ModelRc<T>,
rows: Vec<T>,
set: impl FnOnce(ModelRc<T>),
) {
if current.row_count() == rows.len() {
for (index, row) in rows.into_iter().enumerate() {
current.set_row_data(index, row);
}
} else {
set(ModelRc::new(VecModel::from(rows)));
}
}
#[cfg(test)]
mod concave_row_tests {
use super::*;
fn row(kind: TierRowKind, index: i32, name: &str) -> TierRow {
TierRow {
index,
name: name.to_owned(),
kind,
tool_line: if kind == TierRowKind::Concave {
"CYL +10.00\u{b0}".to_owned()
} else {
String::new()
},
..TierRow::default()
}
}
#[test]
fn a_concave_row_carries_its_table_position_so_it_never_collides_with_a_flat_tier() {
let rows = vec![
row(TierRowKind::Flat, 0, "P1"),
row(TierRowKind::Flat, 1, "C1"),
row(TierRowKind::Concave, 0, "Groove"),
row(TierRowKind::Concave, 1, "Dimple"),
];
let items = tier_items_from_rows(rows);
let positions: Vec<i32> = items.iter().map(|item| item.index).collect();
assert_eq!(positions, [0, 1, 2, 3], "one position per table row");
let kinds: Vec<i32> = items.iter().map(|item| item.kind).collect();
assert_eq!(kinds, [0, 0, 1, 1]);
assert_eq!(items[2].tool_line.as_str(), "CYL +10.00\u{b0}");
assert_eq!(items[0].tool_line.as_str(), "");
}
#[test]
fn a_rows_code_reaches_the_table_item() {
let mut flat = row(TierRowKind::Flat, 0, "Pavilion Main");
flat.code = "P1".to_owned();
let mut concave = row(TierRowKind::Concave, 0, "Groove");
concave.code = "P3".to_owned();
let items = tier_items_from_rows(vec![flat, concave]);
assert_eq!(items[0].code.as_str(), "P1");
assert_eq!(items[1].code.as_str(), "P3");
}
#[test]
fn a_planar_row_list_maps_exactly_as_before() {
let rows = vec![
row(TierRowKind::Flat, 0, "P1"),
row(TierRowKind::Flat, 1, "C1"),
];
let items = tier_items_from_rows(rows);
assert_eq!(
items.iter().map(|item| item.index).collect::<Vec<_>>(),
[0, 1]
);
assert!(items.iter().all(|item| item.kind == 0));
}
#[test]
fn a_position_past_the_flat_tiers_names_the_concave_tier_and_nothing_else_does() {
assert_eq!(concave_tier_index(2, 2, 2), Some(0));
assert_eq!(concave_tier_index(2, 2, 3), Some(1));
assert_eq!(concave_tier_index(2, 2, 1), None, "a flat position");
assert_eq!(concave_tier_index(2, 2, 4), None, "beyond the table");
assert_eq!(concave_tier_index(2, 2, -1), None, "no selection");
assert_eq!(
concave_tier_index(2, 0, 2),
None,
"a planar design has none"
);
}
}