cadmus_core/view/navigation/providers/
directory.rs

1use crate::context::Context;
2use crate::device::CURRENT_DEVICE;
3use crate::font::Fonts;
4use crate::geom::Point;
5use crate::unit::scale_by_dpi;
6use crate::view::home::directories_bar::DirectoriesBar;
7use crate::view::navigation::stack_navigation_bar::NavigationProvider;
8use crate::view::{View, SMALL_BAR_HEIGHT, THICKNESS_MEDIUM};
9use std::collections::BTreeSet;
10use std::path::{Path, PathBuf};
11
12#[derive(Debug, Clone, Copy, Default)]
13pub struct DirectoryNavigationProvider;
14
15impl DirectoryNavigationProvider {
16    #[inline]
17    fn guess_bar_size(dirs: &BTreeSet<PathBuf>) -> usize {
18        (dirs.iter().map(|dir| dir.as_os_str().len()).sum::<usize>() / 300).clamp(1, 4)
19    }
20}
21
22impl NavigationProvider for DirectoryNavigationProvider {
23    type LevelKey = PathBuf;
24    type LevelData = BTreeSet<PathBuf>;
25    type Bar = DirectoriesBar;
26
27    fn selected_leaf_key(&self, selected: &Self::LevelKey) -> Self::LevelKey {
28        selected.clone()
29    }
30
31    /// Determines the appropriate directory level for navigation bar traversal.
32    ///
33    /// If the selected directory has no subdirectories and is not the library home,
34    /// returns the parent directory to ensure the navigation bar displays a level
35    /// with navigable content. Otherwise, returns the selected directory itself.
36    ///
37    /// # Arguments
38    ///
39    /// * `selected` - The currently selected directory path
40    /// * `context` - Application context containing library information
41    ///
42    /// # Returns
43    ///
44    /// The directory path to use for bar traversal navigation
45    fn leaf_for_bar_traversal(
46        &self,
47        selected: &Self::LevelKey,
48        context: &Context,
49    ) -> Self::LevelKey {
50        let (_, dirs) = context.library.list(selected, None, true);
51        if dirs.is_empty() && *selected != context.library.home {
52            selected
53                .parent()
54                .map(|p| p.to_path_buf())
55                .unwrap_or_else(|| selected.clone())
56        } else {
57            selected.clone()
58        }
59    }
60
61    fn parent(&self, current: &Self::LevelKey) -> Option<Self::LevelKey> {
62        current.parent().map(|p| p.to_path_buf())
63    }
64
65    fn is_ancestor(&self, ancestor: &Self::LevelKey, descendant: &Self::LevelKey) -> bool {
66        descendant.starts_with(ancestor)
67    }
68
69    fn is_root(&self, key: &Self::LevelKey, context: &Context) -> bool {
70        *key == context.library.home
71    }
72
73    fn fetch_level_data(&self, key: &Self::LevelKey, context: &mut Context) -> Self::LevelData {
74        let (_, dirs) = context.library.list(key, None, true);
75        dirs
76    }
77
78    fn estimate_line_count(&self, _key: &Self::LevelKey, data: &Self::LevelData) -> usize {
79        Self::guess_bar_size(data)
80    }
81
82    fn create_bar(&self, rect: crate::geom::Rectangle, key: &Self::LevelKey) -> Self::Bar {
83        DirectoriesBar::new(rect, key)
84    }
85
86    fn bar_key(&self, bar: &Self::Bar) -> Self::LevelKey {
87        bar.path.clone()
88    }
89
90    fn update_bar(
91        &self,
92        bar: &mut Self::Bar,
93        data: &Self::LevelData,
94        selected: &Self::LevelKey,
95        fonts: &mut Fonts,
96    ) {
97        bar.update_content(data, Path::new(selected), fonts);
98    }
99
100    fn update_bar_selection(&self, bar: &mut Self::Bar, selected: &Self::LevelKey) {
101        bar.update_selected(Path::new(selected));
102    }
103
104    fn resize_bar_by(&self, bar: &mut Self::Bar, delta_y: i32, fonts: &mut Fonts) -> i32 {
105        let rectangle = *bar.rect();
106        let dpi = CURRENT_DEVICE.dpi;
107        let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
108        let min_height = scale_by_dpi(SMALL_BAR_HEIGHT, dpi) as i32 - thickness;
109
110        let y_max = (rectangle.max.y + delta_y).max(rectangle.min.y + min_height);
111        let resized = y_max - rectangle.max.y;
112
113        bar.rect_mut().max.y = y_max;
114
115        let dirs = bar.dirs();
116        let path = bar.path.clone();
117        bar.update_content(&dirs, path.as_path(), fonts);
118
119        resized
120    }
121
122    fn shift_bar(&self, bar: &mut Self::Bar, delta: Point) {
123        bar.shift(delta);
124    }
125}