use super::shared::side_shown_in_mode;
use crate::{AngleItem, CuttingTableModel, MainWindow};
use slint::{ComponentHandle, Model, ModelRc};
fn visible_order_indices(angles: &ModelRc<AngleItem>, mode: i32) -> Vec<i32> {
angles
.iter()
.filter(|angle| side_shown_in_mode(angle.side, mode))
.map(|angle| angle.order_idx)
.collect()
}
#[must_use]
pub(super) fn step_row_cursor(visible: &[i32], current: i32, step: i32) -> i32 {
let Some(last) = visible.len().checked_sub(1) else {
return -1;
};
let target = match visible.iter().position(|&index| index == current) {
Some(position) => {
let wanted = i64::try_from(position)
.unwrap_or(0)
.saturating_add(i64::from(step));
usize::try_from(wanted.max(0)).map_or(0, |wanted| wanted.min(last))
}
None if step.unsigned_abs() > 1 => {
if step > 0 {
last
} else {
0
}
}
None => {
if step > 0 {
0
} else {
last
}
}
};
visible.get(target).copied().unwrap_or(-1)
}
pub(super) fn setup_row_cursor_callback(ui: &MainWindow) {
ui.global::<CuttingTableModel>()
.on_step_row(|angles, current, step, mode| {
step_row_cursor(&visible_order_indices(&angles, mode), current, step)
});
}
#[cfg(test)]
mod tests {
use super::{step_row_cursor, visible_order_indices};
use crate::AngleItem;
use slint::{ModelRc, VecModel};
fn angle(order_idx: i32, side: i32) -> AngleItem {
AngleItem {
order_idx,
side,
facet: "P1".into(),
code: "P1".into(),
angle: "0.0".into(),
index_val: "".into(),
notes: "".into(),
second_line: "".into(),
}
}
#[test]
fn an_empty_table_has_no_cursor_row() {
assert_eq!(step_row_cursor(&[], -1, 1), -1);
assert_eq!(step_row_cursor(&[], 3, -1), -1);
}
#[test]
fn down_from_the_unset_cursor_lands_on_the_first_row_and_up_on_the_last() {
let rows = [4, 5, 6];
assert_eq!(step_row_cursor(&rows, -1, 1), 4);
assert_eq!(step_row_cursor(&rows, -1, -1), 6);
}
#[test]
fn a_step_moves_one_row_and_stops_at_both_ends() {
let rows = [0, 1, 2];
assert_eq!(step_row_cursor(&rows, 0, 1), 1);
assert_eq!(step_row_cursor(&rows, 1, 1), 2);
assert_eq!(step_row_cursor(&rows, 2, 1), 2);
assert_eq!(step_row_cursor(&rows, 2, -1), 1);
assert_eq!(step_row_cursor(&rows, 0, -1), 0);
}
#[test]
fn home_and_end_reach_the_ends_from_anywhere() {
let rows = [3, 5, 8, 9];
assert_eq!(step_row_cursor(&rows, 5, 1_000_000), 9);
assert_eq!(step_row_cursor(&rows, 8, -1_000_000), 3);
assert_eq!(step_row_cursor(&rows, -1, -1_000_000), 3);
assert_eq!(step_row_cursor(&rows, -1, 1_000_000), 9);
assert_eq!(step_row_cursor(&rows, 3, i32::MAX), 9);
assert_eq!(step_row_cursor(&rows, 9, i32::MIN), 3);
}
#[test]
fn a_cursor_on_a_hidden_row_restarts_from_the_edge() {
let rows = [1, 2];
assert_eq!(step_row_cursor(&rows, 7, 1), 1);
assert_eq!(step_row_cursor(&rows, 7, -1), 2);
}
#[test]
fn steps_skip_the_rows_a_filter_hides() {
let angles = ModelRc::new(VecModel::from(vec![
angle(0, -1),
angle(1, 1),
angle(2, -1),
angle(3, 1),
angle(4, 1),
]));
let crown = visible_order_indices(&angles, 2);
assert_eq!(crown, vec![1, 3, 4]);
assert_eq!(step_row_cursor(&crown, 1, 1), 3);
assert_eq!(step_row_cursor(&crown, 3, -1), 1);
let pavilion = visible_order_indices(&angles, 1);
assert_eq!(pavilion, vec![0, 2]);
assert_eq!(step_row_cursor(&pavilion, 0, 1), 2);
assert_eq!(visible_order_indices(&angles, 0), vec![0, 1, 2, 3, 4]);
}
}