cadmus_core/view/
intermission.rs

1use super::{Bus, Event, Hub, Id, RenderQueue, View, ID_FEEDER};
2use crate::color::{TEXT_INVERTED_HARD, TEXT_NORMAL};
3use crate::context::Context;
4use crate::device::CURRENT_DEVICE;
5use crate::document::{open, Location};
6use crate::font::{font_from_style, Fonts, DISPLAY_STYLE};
7use crate::framebuffer::Framebuffer;
8use crate::geom::Rectangle;
9use crate::metadata::{sort, BookQuery, SortMethod};
10use crate::settings::{IntermKind, IntermissionDisplay};
11use std::path::PathBuf;
12
13pub struct Intermission {
14    id: Id,
15    rect: Rectangle,
16    children: Vec<Box<dyn View>>,
17    message: Message,
18    halt: bool,
19}
20
21pub enum Message {
22    Text(String),
23    Image(PathBuf),
24    Cover(PathBuf),
25}
26
27impl Intermission {
28    pub fn new(rect: Rectangle, kind: IntermKind, context: &Context) -> Intermission {
29        let message = match &context.settings.intermissions[kind] {
30            IntermissionDisplay::Logo => Message::Text(kind.text().to_string()),
31            IntermissionDisplay::Cover => {
32                let query = BookQuery {
33                    reading: Some(true),
34                    ..Default::default()
35                };
36                let (mut files, _) =
37                    context
38                        .library
39                        .list(&context.library.home, Some(&query), false);
40                sort(&mut files, SortMethod::Opened, true);
41                if !files.is_empty() {
42                    Message::Cover(context.library.home.join(&files[0].file.path))
43                } else {
44                    Message::Text(kind.text().to_string())
45                }
46            }
47            IntermissionDisplay::Image(path) => Message::Image(path.clone()),
48        };
49        Intermission {
50            id: ID_FEEDER.next(),
51            rect,
52            children: Vec::new(),
53            message,
54            halt: kind == IntermKind::PowerOff,
55        }
56    }
57}
58
59impl View for Intermission {
60    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _evt, _hub, _bus, _rq, _context), fields(event = ?_evt), ret(level=tracing::Level::TRACE)))]
61    fn handle_event(
62        &mut self,
63        _evt: &Event,
64        _hub: &Hub,
65        _bus: &mut Bus,
66        _rq: &mut RenderQueue,
67        _context: &mut Context,
68    ) -> bool {
69        true
70    }
71
72    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, fonts, _rect), fields(rect = ?_rect)))]
73    fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, fonts: &mut Fonts) {
74        let scheme = if self.halt {
75            TEXT_INVERTED_HARD
76        } else {
77            TEXT_NORMAL
78        };
79
80        fb.draw_rectangle(&self.rect, scheme[0]);
81
82        match self.message {
83            Message::Text(ref text) => {
84                let dpi = CURRENT_DEVICE.dpi;
85
86                let font = font_from_style(fonts, &DISPLAY_STYLE, dpi);
87                let padding = font.em() as i32;
88                let max_width = self.rect.width() as i32 - 3 * padding;
89                let mut plan = font.plan(text, None, None);
90
91                if plan.width > max_width {
92                    let scale = max_width as f32 / plan.width as f32;
93                    let size = (scale * DISPLAY_STYLE.size as f32) as u32;
94                    font.set_size(size, dpi);
95                    plan = font.plan(text, None, None);
96                }
97
98                let x_height = font.x_heights.0 as i32;
99
100                let dx = (self.rect.width() as i32 - plan.width) / 2;
101                let dy = (self.rect.height() as i32) / 3;
102
103                font.render(fb, scheme[1], &plan, pt!(dx, dy));
104
105                let mut doc = open("icons/dodecahedron.svg").unwrap();
106                let (width, height) = doc.dims(0).unwrap();
107                let scale = (plan.width as f32 / width.max(height) as f32) / 4.0;
108                let (pixmap, _) = doc.pixmap(Location::Exact(0), scale, 1).unwrap();
109                let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2;
110                let dy = dy + 2 * x_height;
111                let pt = self.rect.min + pt!(dx, dy);
112
113                fb.draw_blended_pixmap(&pixmap, pt, scheme[1]);
114            }
115            Message::Image(ref path) => {
116                if let Some(mut doc) = open(path) {
117                    if let Some((width, height)) = doc.dims(0) {
118                        let w_ratio = self.rect.width() as f32 / width;
119                        let h_ratio = self.rect.height() as f32 / height;
120                        let scale = w_ratio.min(h_ratio);
121                        if let Some((pixmap, _)) =
122                            doc.pixmap(Location::Exact(0), scale, CURRENT_DEVICE.color_samples())
123                        {
124                            let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2;
125                            let dy = (self.rect.height() as i32 - pixmap.height as i32) / 2;
126                            let pt = self.rect.min + pt!(dx, dy);
127                            fb.draw_pixmap(&pixmap, pt);
128                            if fb.inverted() {
129                                let rect = pixmap.rect() + pt;
130                                fb.invert_region(&rect);
131                            }
132                        }
133                    }
134                }
135            }
136            Message::Cover(ref path) => {
137                if let Some(mut doc) = open(path) {
138                    if let Some(pixmap) = doc.preview_pixmap(
139                        self.rect.width() as f32,
140                        self.rect.height() as f32,
141                        CURRENT_DEVICE.color_samples(),
142                    ) {
143                        let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2;
144                        let dy = (self.rect.height() as i32 - pixmap.height as i32) / 2;
145                        let pt = self.rect.min + pt!(dx, dy);
146                        fb.draw_pixmap(&pixmap, pt);
147                        if fb.inverted() {
148                            let rect = pixmap.rect() + pt;
149                            fb.invert_region(&rect);
150                        }
151                    }
152                }
153            }
154        }
155    }
156
157    fn might_rotate(&self) -> bool {
158        false
159    }
160
161    fn rect(&self) -> &Rectangle {
162        &self.rect
163    }
164
165    fn rect_mut(&mut self) -> &mut Rectangle {
166        &mut self.rect
167    }
168
169    fn children(&self) -> &Vec<Box<dyn View>> {
170        &self.children
171    }
172
173    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
174        &mut self.children
175    }
176
177    fn id(&self) -> Id {
178        self.id
179    }
180}