rvlib/control/
paths_navigator.rs

1use std::{fmt::Debug, path::Path};
2
3use super::filter::FilterExpr;
4use crate::{
5    file_util::PathPair,
6    paths_selector::PathsSelector,
7    result::{ignore_error, trace_ok_err},
8    sort_params::{SortParams, SortType},
9    tools_data::ToolsDataMap,
10};
11use exmex::prelude::*;
12use rvimage_domain::RvResult;
13
14fn next(file_selected_idx: usize, files_len: usize) -> usize {
15    if file_selected_idx < files_len - 1 {
16        file_selected_idx + 1
17    } else {
18        files_len - 1
19    }
20}
21
22fn prev(file_selected_idx: usize, files_len: usize) -> usize {
23    if file_selected_idx >= files_len {
24        files_len - 1
25    } else if file_selected_idx > 0 {
26        file_selected_idx - 1
27    } else {
28        0
29    }
30}
31#[derive(Default)]
32pub struct PathsNavigator {
33    file_label_selected_idx: Option<usize>,
34    paths_selector: Option<PathsSelector>,
35    scroll_to_selected_label: bool,
36}
37impl PathsNavigator {
38    pub fn new(
39        mut paths_selector: Option<PathsSelector>,
40        sort_params: SortParams,
41    ) -> RvResult<Self> {
42        if let Some(ps) = &mut paths_selector {
43            match sort_params.kind {
44                SortType::Natural => ps.natural_sort(sort_params.sort_by_filename)?,
45                SortType::Alphabetical => ps.alphabetical_sort(sort_params.sort_by_filename)?,
46            }
47        };
48        Ok(Self {
49            file_label_selected_idx: None,
50            paths_selector,
51            scroll_to_selected_label: false,
52        })
53    }
54    fn pn(&mut self, f: fn(usize, usize) -> usize) {
55        if let Some(idx) = self.file_label_selected_idx {
56            if let Some(ps) = &self.paths_selector {
57                self.file_label_selected_idx = Some(f(idx, ps.len_filtered()));
58                self.scroll_to_selected_label = true;
59            }
60        }
61    }
62    pub fn next(&mut self) {
63        self.pn(next);
64    }
65    pub fn prev(&mut self) {
66        self.pn(prev);
67    }
68    pub fn file_label_selected_idx(&self) -> Option<usize> {
69        self.file_label_selected_idx
70    }
71    pub fn len_filtered(&self) -> Option<usize> {
72        self.paths_selector.as_ref().map(|ps| ps.len_filtered())
73    }
74    pub fn scroll_to_selected_label(&self) -> bool {
75        self.scroll_to_selected_label
76    }
77    pub fn activate_scroll_to_selected_label(&mut self) {
78        self.scroll_to_selected_label = true;
79    }
80    pub fn deactivate_scroll_to_selected_label(&mut self) {
81        self.scroll_to_selected_label = false;
82    }
83    /// makes sure the idx actually exists
84    pub fn select_label_idx(&mut self, filtered_label_idx: Option<usize>) {
85        if let (Some(idx), Some(ps)) = (filtered_label_idx, self.paths_selector()) {
86            if idx < ps.len_filtered() {
87                self.file_label_selected_idx = Some(idx);
88            }
89        }
90    }
91    pub fn export_filtered_filelist<P>(&self, export_path: P, filter_str: &str) -> RvResult<()>
92    where
93        P: AsRef<Path> + Debug,
94    {
95        if let Some(ps) = self.paths_selector() {
96            ps.export_filtered(export_path, filter_str)?
97        };
98        Ok(())
99    }
100
101    fn idx_of_file_label(&self, file_label: &str) -> Option<usize> {
102        match self.paths_selector() {
103            Some(ps) => ps.filteredidx_of_file_label(file_label),
104            None => None,
105        }
106    }
107
108    pub fn select_file_label(&mut self, file_label: &str) {
109        self.select_label_idx(self.idx_of_file_label(file_label));
110    }
111
112    pub fn paths_selector(&self) -> Option<&PathsSelector> {
113        self.paths_selector.as_ref()
114    }
115
116    fn filter_by_pred(&mut self, filter_predicate: impl FnMut(&str) -> bool) -> RvResult<()> {
117        if let Some(ps) = &mut self.paths_selector {
118            let unfiltered_idx_before_filter =
119                if let Some(filtered_idx) = self.file_label_selected_idx {
120                    self.scroll_to_selected_label = true;
121                    let (unfiltered_idx, _) = ps.filtered_idx_file_label_pairs(filtered_idx);
122                    Some(unfiltered_idx)
123                } else {
124                    None
125                };
126            ps.filter(filter_predicate)?;
127            self.file_label_selected_idx = match unfiltered_idx_before_filter {
128                Some(unfiltered_idx) => ps
129                    .filtered_iter()
130                    .enumerate()
131                    .find(|(_, (uidx, _))| *uidx == unfiltered_idx)
132                    .map(|(fidx, _)| fidx),
133                None => None,
134            };
135        }
136        Ok(())
137    }
138
139    pub fn filter(
140        &mut self,
141        s: &str,
142        tools_data_map: &ToolsDataMap,
143        active_tool_name: Option<&str>,
144    ) -> RvResult<()> {
145        if let Some(filter_pred) =
146            ignore_error(FilterExpr::parse(s).and_then(|expr| expr.eval(&[])))
147        {
148            let filter_pred_wrapper = |path: &str| {
149                trace_ok_err(filter_pred.apply(path, Some(tools_data_map), active_tool_name))
150                    .unwrap_or(true)
151            };
152            self.filter_by_pred(filter_pred_wrapper)?;
153        } else {
154            let trimmed = s.trim();
155            let filter_pred = |path: &str| {
156                if path.is_empty() {
157                    true
158                } else {
159                    path.contains(trimmed)
160                }
161            };
162            self.filter_by_pred(filter_pred)?;
163        }
164        Ok(())
165    }
166
167    pub fn folder_label(&self) -> Option<&str> {
168        self.paths_selector().as_ref().map(|ps| ps.folder_label())
169    }
170
171    pub fn file_path(&self, file_idx: usize) -> Option<&PathPair> {
172        self.paths_selector()
173            .as_ref()
174            .and_then(|ps| ps.file_selected_path(file_idx))
175    }
176}
177
178#[test]
179fn test_prev_next() {
180    assert_eq!(next(3, 4), 3);
181    assert_eq!(next(2, 4), 3);
182    assert_eq!(next(5, 4), 3);
183    assert_eq!(next(1, 4), 2);
184    assert_eq!(prev(3, 4), 2);
185    assert_eq!(prev(2, 3), 1);
186    assert_eq!(prev(3, 3), 2);
187    assert_eq!(prev(4, 3), 2);
188    assert_eq!(prev(9, 3), 2);
189}