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::document::open;
6use crate::font::Fonts;
7use crate::framebuffer::{Framebuffer, UpdateMode};
8use crate::geom::divide;
9use crate::geom::{halves, CycleDir, Dir, Rectangle};
10use crate::gesture::GestureEvent;
11use crate::helpers::Fingerprint;
12use crate::metadata::Info;
13use crate::settings::{FirstColumn, SecondColumn};
14use crate::unit::scale_by_dpi;
15use crate::view::filler::Filler;
16use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
17use crate::view::{BIG_BAR_HEIGHT, THICKNESS_MEDIUM};
18use lazy_static::lazy_static;
19use std::sync::Mutex;
20use std::thread;
21
22lazy_static! {
23    static ref EXCLUSIVE_ACCESS: Mutex<u8> = Mutex::new(0);
24}
25
26pub struct Shelf {
27    id: Id,
28    pub rect: Rectangle,
29    children: Vec<Box<dyn View>>,
30    pub max_lines: usize,
31    first_column: FirstColumn,
32    second_column: SecondColumn,
33    thumbnail_previews: bool,
34}
35
36impl Shelf {
37    pub fn new(
38        rect: Rectangle,
39        first_column: FirstColumn,
40        second_column: SecondColumn,
41        thumbnail_previews: bool,
42    ) -> Shelf {
43        let dpi = CURRENT_DEVICE.dpi;
44        let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32;
45        let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
46        let max_lines = ((rect.height() as i32 + thickness) / big_height) as usize;
47        Shelf {
48            id: ID_FEEDER.next(),
49            rect,
50            children: Vec::new(),
51            max_lines,
52            first_column,
53            second_column,
54            thumbnail_previews,
55        }
56    }
57
58    pub fn set_first_column(&mut self, first_column: FirstColumn) {
59        self.first_column = first_column;
60    }
61
62    pub fn set_second_column(&mut self, second_column: SecondColumn) {
63        self.second_column = second_column;
64    }
65
66    pub fn set_thumbnail_previews(&mut self, thumbnail_previews: bool) {
67        self.thumbnail_previews = thumbnail_previews;
68    }
69
70    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, hub, rq, context)))]
71    pub fn update(
72        &mut self,
73        metadata: &[Info],
74        hub: &Hub,
75        rq: &mut RenderQueue,
76        context: &Context,
77    ) {
78        self.children.clear();
79        let dpi = CURRENT_DEVICE.dpi;
80        let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32;
81        let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
82        let (small_thickness, big_thickness) = halves(thickness);
83        let max_lines = ((self.rect.height() as i32 + thickness) / big_height) as usize;
84        let book_heights = divide(self.rect.height() as i32, max_lines as i32);
85        let mut y_pos = self.rect.min.y;
86        let th = big_height;
87        let tw = 3 * th / 4;
88
89        #[cfg(feature = "tracing")]
90        let _span = tracing::info_span!("processing metadata").entered();
91        for (index, info) in metadata.iter().enumerate() {
92            #[cfg(feature = "tracing")]
93            let _span = tracing::info_span!("processing metadata entry", info = ?info).entered();
94
95            let y_min = y_pos + if index > 0 { big_thickness } else { 0 };
96            let y_max = y_pos + book_heights[index]
97                - if index < max_lines - 1 {
98                    small_thickness
99                } else {
100                    0
101                };
102
103            let preview = if self.thumbnail_previews {
104                let existing = context.library.thumbnail_preview(&info.file.path);
105                if existing.is_none() {
106                    let hub2 = hub.clone();
107                    let path = info.file.path.clone();
108                    let full_path = context.library.home.join(&info.file.path);
109                    let database = context.library.db.clone();
110                    let fp = full_path.fingerprint().ok();
111
112                    if let Some(fp) = fp {
113                        thread::spawn(move || {
114                            let _guard = EXCLUSIVE_ACCESS.lock().unwrap();
115                            if let Some(bytes) = open(full_path)
116                                .and_then(|mut doc| {
117                                    doc.preview_pixmap(
118                                        tw as f32,
119                                        th as f32,
120                                        CURRENT_DEVICE.color_samples(),
121                                    )
122                                })
123                                .and_then(|pixmap| pixmap.to_png_bytes().ok())
124                            {
125                                database.save_thumbnail(fp, &bytes).ok();
126                                hub2.send(Event::RefreshBookPreview(path)).ok();
127                            }
128                        });
129                    }
130                }
131                existing
132            } else {
133                None
134            };
135
136            let book = Book::new(
137                rect![self.rect.min.x, y_min, self.rect.max.x, y_max],
138                info.clone(),
139                index,
140                self.first_column,
141                self.second_column,
142                preview,
143            );
144            self.children.push(Box::new(book) as Box<dyn View>);
145
146            if index < max_lines - 1 {
147                let separator = Filler::new(
148                    rect![self.rect.min.x, y_max, self.rect.max.x, y_max + thickness],
149                    SEPARATOR_NORMAL,
150                );
151                self.children.push(Box::new(separator) as Box<dyn View>);
152            }
153
154            y_pos += book_heights[index];
155        }
156
157        if metadata.len() < max_lines {
158            let y_start = y_pos + if metadata.is_empty() { 0 } else { thickness };
159            let filler = Filler::new(
160                rect![self.rect.min.x, y_start, self.rect.max.x, self.rect.max.y],
161                WHITE,
162            );
163            self.children.push(Box::new(filler) as Box<dyn View>);
164        }
165
166        self.max_lines = max_lines;
167        rq.add(RenderData::new(self.id, self.rect, UpdateMode::Partial));
168    }
169}
170
171impl View for Shelf {
172    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
173    fn handle_event(
174        &mut self,
175        evt: &Event,
176        _hub: &Hub,
177        bus: &mut Bus,
178        _rq: &mut RenderQueue,
179        _context: &mut Context,
180    ) -> bool {
181        match *evt {
182            Event::Gesture(GestureEvent::Swipe { dir, start, .. }) if self.rect.includes(start) => {
183                match dir {
184                    Dir::West => {
185                        bus.push_back(Event::Page(CycleDir::Next));
186                        true
187                    }
188                    Dir::East => {
189                        bus.push_back(Event::Page(CycleDir::Previous));
190                        true
191                    }
192                    _ => false,
193                }
194            }
195            _ => false,
196        }
197    }
198
199    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _fb, _fonts, _rect), fields(rect = ?_rect)))]
200    fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {}
201
202    fn rect(&self) -> &Rectangle {
203        &self.rect
204    }
205
206    fn rect_mut(&mut self) -> &mut Rectangle {
207        &mut self.rect
208    }
209
210    fn children(&self) -> &Vec<Box<dyn View>> {
211        &self.children
212    }
213
214    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
215        &mut self.children
216    }
217
218    fn id(&self) -> Id {
219        self.id
220    }
221}