sbom-tools 0.1.18

Semantic SBOM diff and analysis tool
Documentation
//! Mouse event handlers.

use crate::tui::{App, AppMode};
use crossterm::event::{MouseButton, MouseEvent, MouseEventKind};

pub fn handle_mouse_event(app: &mut App, mouse: MouseEvent) {
    // Clear status message on any mouse action
    app.clear_status_message();

    match mouse.kind {
        MouseEventKind::ScrollUp => {
            if app.active_tab == crate::tui::TabKind::Source {
                app.source_state_mut().select_prev();
            } else {
                app.select_up();
            }
        }
        MouseEventKind::ScrollDown => {
            if app.active_tab == crate::tui::TabKind::Source {
                app.source_state_mut().select_next();
            } else {
                app.select_down();
            }
        }
        MouseEventKind::Down(MouseButton::Left) => {
            let x = mouse.column;
            let y = mouse.row;

            // Close overlays on click
            if app.has_overlay() {
                app.close_overlays();
                return;
            }

            // Tab bar is typically in the first 2 rows
            if y <= 2 {
                // Estimate tab positions based on typical widths
                let tab_width = 13;
                let tab_index = (x as usize) / tab_width;
                match tab_index {
                    0 => app.select_tab(crate::tui::TabKind::Summary),
                    1 => app.select_tab(crate::tui::TabKind::Components),
                    2 => app.select_tab(crate::tui::TabKind::Dependencies),
                    3 => app.select_tab(crate::tui::TabKind::Licenses),
                    4 => app.select_tab(crate::tui::TabKind::Vulnerabilities),
                    5 => app.select_tab(crate::tui::TabKind::Quality),
                    6 if app.mode == AppMode::Diff => {
                        app.select_tab(crate::tui::TabKind::SideBySide);
                    }
                    _ => {}
                }
                return;
            }

            // Handle click on list items
            // Layout: header (2 rows) + filter bar (3 rows) + content
            // Content area starts around row 5, with 1-row header inside tables
            let content_start_row = 6u16; // After tabs + filter bar + table header

            if y >= content_start_row {
                let clicked_index = (y - content_start_row) as usize;
                handle_list_click(app, clicked_index, x);
            }
        }
        MouseEventKind::Down(MouseButton::Right) => {
            // Right-click closes overlays
            if app.has_overlay() {
                app.close_overlays();
            }
        }
        _ => {}
    }
}

/// Handle a click on a list item
pub(super) fn handle_list_click(app: &mut App, clicked_index: usize, _x: u16) {
    match app.active_tab {
        crate::tui::TabKind::Components => {
            if clicked_index < app.components_state().total {
                app.components_state_mut().selected = clicked_index;
            }
        }
        crate::tui::TabKind::Vulnerabilities => {
            if clicked_index < app.vulnerabilities_state().total {
                app.vulnerabilities_state_mut().selected = clicked_index;
            }
        }
        crate::tui::TabKind::Licenses => {
            if clicked_index < app.licenses_state().total {
                app.licenses_state_mut().selected = clicked_index;
            }
        }
        crate::tui::TabKind::Dependencies => {
            if clicked_index < app.dependencies_state().total {
                app.dependencies_state_mut().selected = clicked_index;
            }
        }
        crate::tui::TabKind::Quality => {
            // Quality view may have selectable items
            if clicked_index < app.quality_state().total_recommendations {
                app.quality_state_mut().selected_recommendation = clicked_index;
            }
        }
        crate::tui::TabKind::Source => {
            // Determine which panel from x position (50/50 split)
            let panel = app.source_state_mut().active_panel_mut();
            let max = match panel.view_mode {
                crate::tui::app_states::SourceViewMode::Tree => {
                    panel.ensure_flat_cache();
                    panel.cached_flat_items.len()
                }
                crate::tui::app_states::SourceViewMode::Raw => panel.raw_lines.len(),
            };
            let idx = panel.scroll_offset + clicked_index;
            if idx < max {
                panel.selected = idx;
            }
        }
        _ => {}
    }
}

// ============================================================================
// Cross-View Helper Functions
// ============================================================================

/// Switch to a different multi-comparison view
pub(super) fn switch_to_view(app: &mut App, view: crate::tui::app_states::MultiViewType) {
    match view {
        crate::tui::app_states::MultiViewType::MultiDiff => {
            app.mode = AppMode::MultiDiff;
            app.set_status_message("Switched to Multi-Diff Dashboard".to_string());
        }
        crate::tui::app_states::MultiViewType::Timeline => {
            app.mode = AppMode::Timeline;
            app.set_status_message("Switched to Timeline View".to_string());
        }
        crate::tui::app_states::MultiViewType::Matrix => {
            app.mode = AppMode::Matrix;
            app.set_status_message("Switched to Matrix Comparison".to_string());
        }
    }
}