use super::multi_diff::{MultiViewSearchState, SortDirection};
use crate::tui::views::MatrixPanel;
pub struct MatrixState {
pub selected_row: usize,
pub selected_col: usize,
pub sbom_count: usize,
pub active_panel: MatrixPanel,
pub search: MultiViewSearchState,
pub sort_by: MatrixSortBy,
pub sort_direction: SortDirection,
pub threshold: SimilarityThreshold,
pub focus_mode: bool,
pub focus_row: Option<usize>,
pub focus_col: Option<usize>,
pub show_pair_diff: bool,
pub show_export_options: bool,
pub show_clustering_details: bool,
pub highlight_row_col: bool,
pub selected_cluster: usize,
pub total_clusters: usize,
}
impl MatrixState {
pub fn new() -> Self {
Self {
selected_row: 0,
selected_col: 1,
sbom_count: 0,
active_panel: MatrixPanel::Matrix,
search: MultiViewSearchState::new(),
sort_by: MatrixSortBy::default(),
sort_direction: SortDirection::default(),
threshold: SimilarityThreshold::default(),
focus_mode: false,
focus_row: None,
focus_col: None,
show_pair_diff: false,
show_export_options: false,
show_clustering_details: false,
highlight_row_col: true,
selected_cluster: 0,
total_clusters: 0,
}
}
pub fn new_with_size(count: usize) -> Self {
Self {
selected_row: 0,
selected_col: usize::from(count > 1),
sbom_count: count,
active_panel: MatrixPanel::Matrix,
search: MultiViewSearchState::new(),
sort_by: MatrixSortBy::default(),
sort_direction: SortDirection::default(),
threshold: SimilarityThreshold::default(),
focus_mode: false,
focus_row: None,
focus_col: None,
show_pair_diff: false,
show_export_options: false,
show_clustering_details: false,
highlight_row_col: true,
selected_cluster: 0,
total_clusters: 0,
}
}
pub const fn move_up(&mut self) {
if self.selected_row > 0 {
self.selected_row -= 1;
}
}
pub const fn move_down(&mut self) {
if self.sbom_count > 0 && self.selected_row < self.sbom_count - 1 {
self.selected_row += 1;
}
}
pub const fn move_left(&mut self) {
if self.selected_col > 0 {
self.selected_col -= 1;
}
}
pub const fn move_right(&mut self) {
if self.sbom_count > 0 && self.selected_col < self.sbom_count - 1 {
self.selected_col += 1;
}
}
pub const fn toggle_panel(&mut self) {
self.active_panel = match self.active_panel {
MatrixPanel::Matrix => MatrixPanel::Details,
MatrixPanel::Details => MatrixPanel::Matrix,
};
}
pub const fn toggle_sort(&mut self) {
self.sort_by = self.sort_by.next();
}
pub const fn toggle_sort_direction(&mut self) {
self.sort_direction.toggle();
}
pub const fn toggle_threshold(&mut self) {
self.threshold = self.threshold.next();
}
pub const fn toggle_focus_mode(&mut self) {
self.focus_mode = !self.focus_mode;
if self.focus_mode {
self.focus_row = Some(self.selected_row);
self.focus_col = Some(self.selected_col);
} else {
self.focus_row = None;
self.focus_col = None;
}
}
pub const fn focus_on_row(&mut self, row: usize) {
self.focus_mode = true;
self.focus_row = Some(row);
self.focus_col = None;
}
pub const fn focus_on_col(&mut self, col: usize) {
self.focus_mode = true;
self.focus_row = None;
self.focus_col = Some(col);
}
pub const fn clear_focus(&mut self) {
self.focus_mode = false;
self.focus_row = None;
self.focus_col = None;
}
pub const fn toggle_pair_diff(&mut self) {
if self.selected_row != self.selected_col {
self.show_pair_diff = !self.show_pair_diff;
}
}
pub const fn close_pair_diff(&mut self) {
self.show_pair_diff = false;
}
pub const fn toggle_export_options(&mut self) {
self.show_export_options = !self.show_export_options;
}
pub const fn close_export_options(&mut self) {
self.show_export_options = false;
}
pub const fn toggle_clustering_details(&mut self) {
self.show_clustering_details = !self.show_clustering_details;
}
pub const fn close_clustering_details(&mut self) {
self.show_clustering_details = false;
}
pub const fn toggle_row_col_highlight(&mut self) {
self.highlight_row_col = !self.highlight_row_col;
}
pub const fn select_next_cluster(&mut self) {
if self.total_clusters > 0 && self.selected_cluster < self.total_clusters - 1 {
self.selected_cluster += 1;
}
}
pub const fn select_prev_cluster(&mut self) {
if self.selected_cluster > 0 {
self.selected_cluster -= 1;
}
}
pub fn passes_threshold(&self, similarity: f64) -> bool {
match self.threshold {
SimilarityThreshold::None => true,
SimilarityThreshold::High => similarity >= 0.9,
SimilarityThreshold::Medium => similarity >= 0.7,
SimilarityThreshold::Low => similarity < 0.5,
}
}
}
impl Default for MatrixState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SimilarityThreshold {
#[default]
None,
High,
Medium,
Low,
}
impl SimilarityThreshold {
pub const fn label(self) -> &'static str {
match self {
Self::None => "All",
Self::High => ">= 90%",
Self::Medium => ">= 70%",
Self::Low => "< 50%",
}
}
pub const fn next(self) -> Self {
match self {
Self::None => Self::High,
Self::High => Self::Medium,
Self::Medium => Self::Low,
Self::Low => Self::None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MatrixSortBy {
#[default]
Name,
AvgSimilarity,
ComponentCount,
Cluster,
}
impl MatrixSortBy {
pub const fn label(self) -> &'static str {
match self {
Self::Name => "Name",
Self::AvgSimilarity => "Avg Similarity",
Self::ComponentCount => "Components",
Self::Cluster => "Cluster",
}
}
pub const fn next(self) -> Self {
match self {
Self::Name => Self::AvgSimilarity,
Self::AvgSimilarity => Self::ComponentCount,
Self::ComponentCount => Self::Cluster,
Self::Cluster => Self::Name,
}
}
}