Skip to main content

cadmus_core/view/home/
shelf.rs

1use super::book::Book;
2use crate::color::{SEPARATOR_NORMAL, WHITE};
3use crate::context::Context;
4use crate::device::CURRENT_DEVICE;
5use crate::font::Fonts;
6use crate::framebuffer::{Framebuffer, UpdateMode};
7use crate::geom::divide;
8use crate::geom::{halves, CycleDir, Dir, Rectangle};
9use crate::gesture::GestureEvent;
10use crate::metadata::Info;
11use crate::settings::{FirstColumn, SecondColumn};
12use crate::unit::scale_by_dpi;
13use crate::view::filler::Filler;
14use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
15use crate::view::{BIG_BAR_HEIGHT, THICKNESS_MEDIUM};
16
17pub struct Shelf {
18    id: Id,
19    pub rect: Rectangle,
20    children: Vec<Box<dyn View>>,
21    pub max_lines: usize,
22    first_column: FirstColumn,
23    second_column: SecondColumn,
24    thumbnail_previews: bool,
25}
26
27impl Shelf {
28    pub fn new(
29        rect: Rectangle,
30        first_column: FirstColumn,
31        second_column: SecondColumn,
32        thumbnail_previews: bool,
33    ) -> Shelf {
34        let dpi = CURRENT_DEVICE.dpi;
35        let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32;
36        let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
37        let max_lines = ((rect.height() as i32 + thickness) / big_height) as usize;
38        Shelf {
39            id: ID_FEEDER.next(),
40            rect,
41            children: Vec::new(),
42            max_lines,
43            first_column,
44            second_column,
45            thumbnail_previews,
46        }
47    }
48
49    pub fn set_first_column(&mut self, first_column: FirstColumn) {
50        self.first_column = first_column;
51    }
52
53    pub fn set_second_column(&mut self, second_column: SecondColumn) {
54        self.second_column = second_column;
55    }
56
57    pub fn set_thumbnail_previews(&mut self, thumbnail_previews: bool) {
58        self.thumbnail_previews = thumbnail_previews;
59    }
60
61    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, rq, context)))]
62    pub fn update(&mut self, metadata: &[Info], rq: &mut RenderQueue, context: &Context) {
63        self.children.clear();
64        let dpi = CURRENT_DEVICE.dpi;
65        let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32;
66        let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
67        let (small_thickness, big_thickness) = halves(thickness);
68        let max_lines = ((self.rect.height() as i32 + thickness) / big_height) as usize;
69        let book_heights = divide(self.rect.height() as i32, max_lines as i32);
70        let mut y_pos = self.rect.min.y;
71
72        #[cfg(feature = "tracing")]
73        let _span = tracing::info_span!("processing metadata").entered();
74        for (index, info) in metadata.iter().enumerate() {
75            #[cfg(feature = "tracing")]
76            let _span = tracing::info_span!("processing metadata entry", info = ?info).entered();
77
78            let y_min = y_pos + if index > 0 { big_thickness } else { 0 };
79            let y_max = y_pos + book_heights[index]
80                - if index < max_lines - 1 {
81                    small_thickness
82                } else {
83                    0
84                };
85
86            let preview = if self.thumbnail_previews {
87                let existing = context.library.thumbnail_preview(&info.file.path);
88                if existing.is_none() {
89                    tracing::debug!(path = %info.file.path.display(), "no preview");
90                }
91                existing
92            } else {
93                None
94            };
95
96            let book = Book::new(
97                rect![self.rect.min.x, y_min, self.rect.max.x, y_max],
98                info.clone(),
99                index,
100                self.first_column,
101                self.second_column,
102                preview,
103            );
104            self.children.push(Box::new(book) as Box<dyn View>);
105
106            if index < max_lines - 1 {
107                let separator = Filler::new(
108                    rect![self.rect.min.x, y_max, self.rect.max.x, y_max + thickness],
109                    SEPARATOR_NORMAL,
110                );
111                self.children.push(Box::new(separator) as Box<dyn View>);
112            }
113
114            y_pos += book_heights[index];
115        }
116
117        if metadata.len() < max_lines {
118            let y_start = y_pos + if metadata.is_empty() { 0 } else { thickness };
119            let filler = Filler::new(
120                rect![self.rect.min.x, y_start, self.rect.max.x, self.rect.max.y],
121                WHITE,
122            );
123            self.children.push(Box::new(filler) as Box<dyn View>);
124        }
125
126        self.max_lines = max_lines;
127        rq.add(RenderData::new(self.id, self.rect, UpdateMode::Partial));
128    }
129}
130
131impl View for Shelf {
132    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
133    fn handle_event(
134        &mut self,
135        evt: &Event,
136        _hub: &Hub,
137        bus: &mut Bus,
138        _rq: &mut RenderQueue,
139        _context: &mut Context,
140    ) -> bool {
141        match *evt {
142            Event::Gesture(GestureEvent::Swipe { dir, start, .. }) if self.rect.includes(start) => {
143                match dir {
144                    Dir::West => {
145                        bus.push_back(Event::Page(CycleDir::Next));
146                        true
147                    }
148                    Dir::East => {
149                        bus.push_back(Event::Page(CycleDir::Previous));
150                        true
151                    }
152                    _ => false,
153                }
154            }
155            _ => false,
156        }
157    }
158
159    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _fb, _fonts, _rect), fields(rect = ?_rect)))]
160    fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {}
161
162    fn rect(&self) -> &Rectangle {
163        &self.rect
164    }
165
166    fn rect_mut(&mut self) -> &mut Rectangle {
167        &mut self.rect
168    }
169
170    fn children(&self) -> &Vec<Box<dyn View>> {
171        &self.children
172    }
173
174    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
175        &mut self.children
176    }
177
178    fn id(&self) -> Id {
179        self.id
180    }
181}