rvimage 0.8.3

A remote image viewer with a labeling tool
Documentation
use egui::Ui;
use rvimage_domain::RvResult;

use crate::{
    control::Control,
    file_util::PathPair,
    paths_selector::PathsSelector,
    sort_params::{SortParams, SortType},
    tools_data::{AnnotationsMap, BboxToolData, BrushToolData, InstanceAnnotate, ToolsDataMap},
};

#[derive(Default)]
pub(super) struct Counts {
    pub n_files_filtered_info: Option<String>,
    pub n_files_annotated_info: Option<String>,
}
pub fn n_instance_annotated_images<T>(
    annotations_map: &AnnotationsMap<T>,
    paths: &[&PathPair],
) -> usize
where
    T: InstanceAnnotate,
{
    paths
        .iter()
        .filter(|p| {
            if let Some((anno, _)) = annotations_map.get_pp(p) {
                !anno.elts().is_empty()
            } else {
                false
            }
        })
        .count()
}
#[allow(clippy::too_many_arguments)]
pub fn labels_and_sorting(
    ui: &mut Ui,
    filename_sort_type: &mut SortParams,
    ctrl: &mut Control,
    tools_data_map: &mut ToolsDataMap,
    stats: &mut Counts,
) -> RvResult<()> {
    if ui.button("Export filtered filelist").clicked() {
        let export_path = rfd::FileDialog::new()
            .add_filter("json", &["json"])
            .set_file_name("filtered_filelist.json")
            .save_file();
        if let Some(ep) = export_path {
            ctrl.paths_navigator
                .export_filtered_filelist(ep, &ctrl.data.filter_buffer)?
        }
    }
    let mut clicked_nat = false;
    let mut clicked_alp = false;
    ui.horizontal(|ui| {
        ui.label("Sort");
        clicked_nat = ui
            .radio_value(&mut filename_sort_type.kind, SortType::Natural, "naturally")
            .clicked();
        clicked_alp = ui
            .radio_value(
                &mut filename_sort_type.kind,
                SortType::Alphabetical,
                "alphabetically",
            )
            .clicked();
    });
    #[derive(PartialEq)]
    enum TmpSortBy {
        Filename,
        RelativePath,
    }
    let mut tmp_sort_by = if filename_sort_type.sort_by_filename {
        TmpSortBy::Filename
    } else {
        TmpSortBy::RelativePath
    };
    let mut clicked_relative = false;
    let mut clicked_filename = false;
    ui.horizontal(|ui| {
        ui.label("Sort by");
        clicked_relative = ui
            .radio_value(&mut tmp_sort_by, TmpSortBy::RelativePath, "Relative path")
            .clicked();
        clicked_filename = ui
            .radio_value(&mut tmp_sort_by, TmpSortBy::Filename, "Filename")
            .clicked();
    });
    filename_sort_type.sort_by_filename = matches!(tmp_sort_by, TmpSortBy::Filename);
    if clicked_nat || clicked_alp || clicked_relative || clicked_filename {
        ctrl.reload(Some(*filename_sort_type))?;
    }
    if let Some(info) = &stats.n_files_filtered_info {
        ui.label(egui::RichText::new(info).monospace());
    }
    if let Some(info) = &stats.n_files_annotated_info {
        ui.label(egui::RichText::new(info).monospace());
    }
    let get_file_info = |ps: &PathsSelector| {
        let n_files_filtered = ps.len_filtered();
        Some(format!("{n_files_filtered} files"))
    };
    let get_annotation_info = |ps: &PathsSelector| {
        let len_n_filtered = (ps.len_filtered().checked_ilog10().unwrap_or(0) + 1) as usize;
        let paths = &ps.filtered_file_paths();
        tools_data_map
            .iter()
            .flat_map(|(active_tool_name, data)| {
                let n = data.specifics.apply(
                    |d: &BboxToolData| Ok(n_instance_annotated_images(&d.annotations_map, paths)),
                    |d: &BrushToolData| Ok(n_instance_annotated_images(&d.annotations_map, paths)),
                );
                n.ok().map(|n| {
                    format!(
                        "{n:width1$} have {active_tool_name:width2$} annos",
                        width1 = len_n_filtered,
                        width2 = 5
                    )
                })
            })
            .reduce(|s1, s2| format!("{s1}\n{s2}"))
    };
    if let Some(ps) = ctrl.paths_navigator.paths_selector() {
        if stats.n_files_filtered_info.is_none() {
            stats.n_files_filtered_info = get_file_info(ps);
        }
        if stats.n_files_annotated_info.is_none() {
            stats.n_files_annotated_info = get_annotation_info(ps);
        }
        if ui.button("Re-compute Stats").clicked() {
            stats.n_files_filtered_info = get_file_info(ps);
            stats.n_files_annotated_info = get_annotation_info(ps);
        }
    } else {
        stats.n_files_filtered_info = None;
        stats.n_files_annotated_info = None;
    }
    Ok(())
}