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}