cadmus_core/view/reader/
chapter_label.rs1use super::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ViewId, ID_FEEDER};
2use crate::color::{BLACK, WHITE};
3use crate::context::Context;
4use crate::device::CURRENT_DEVICE;
5use crate::font::{font_from_style, Fonts, NORMAL_STYLE};
6use crate::framebuffer::{Framebuffer, UpdateMode};
7use crate::geom::Rectangle;
8use crate::gesture::GestureEvent;
9
10pub struct ChapterLabel {
11 id: Id,
12 rect: Rectangle,
13 children: Vec<Box<dyn View>>,
14 title: String,
15 progress: f32,
16}
17
18impl ChapterLabel {
19 pub fn new(rect: Rectangle, title: String, progress: f32) -> ChapterLabel {
20 ChapterLabel {
21 id: ID_FEEDER.next(),
22 rect,
23 children: Vec::new(),
24 title,
25 progress,
26 }
27 }
28
29 pub fn update(&mut self, title: String, progress: f32, rq: &mut RenderQueue) {
30 let mut render = false;
31 if self.title != title {
32 self.title = title;
33 render = true;
34 }
35 if self.progress != progress {
36 self.progress = progress;
37 render = true;
38 }
39 if render {
40 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
41 }
42 }
43}
44
45impl View for ChapterLabel {
46 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
47 fn handle_event(
48 &mut self,
49 evt: &Event,
50 _hub: &Hub,
51 bus: &mut Bus,
52 _rq: &mut RenderQueue,
53 _context: &mut Context,
54 ) -> bool {
55 match *evt {
56 Event::Gesture(GestureEvent::Tap(center)) if self.rect.includes(center) => {
57 bus.push_back(Event::Show(ViewId::TableOfContents));
58 true
59 }
60 _ => false,
61 }
62 }
63
64 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, fonts, _rect), fields(rect = ?_rect)))]
65 fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, fonts: &mut Fonts) {
66 fb.draw_rectangle(&self.rect, WHITE);
67 if !self.title.is_empty() {
68 let dpi = CURRENT_DEVICE.dpi;
69 let font = font_from_style(fonts, &NORMAL_STYLE, dpi);
70 let padding = font.em() as i32 / 2;
71 let max_width = self.rect.width().saturating_sub(2 * padding as u32) as i32;
72 let max_progress_width = max_width - font.ellipsis.width;
73 let progress_plan = font.plan(
74 &format!(" ({:.1}%)", 100.0 * self.progress),
75 Some(max_progress_width),
76 None,
77 );
78 let max_title_width = max_width - progress_plan.width;
79 let title_plan = font.plan(&self.title, Some(max_title_width), None);
80 let dx = padding + (max_width - title_plan.width - progress_plan.width) / 2;
81 let dy = (self.rect.height() as i32 - font.x_heights.0 as i32) / 2;
82 let mut pt = pt!(self.rect.min.x + dx, self.rect.max.y - dy);
83 font.render(fb, BLACK, &title_plan, pt);
84 pt.x += title_plan.width;
85 font.render(fb, BLACK, &progress_plan, pt);
86 }
87 }
88
89 fn rect(&self) -> &Rectangle {
90 &self.rect
91 }
92
93 fn rect_mut(&mut self) -> &mut Rectangle {
94 &mut self.rect
95 }
96
97 fn children(&self) -> &Vec<Box<dyn View>> {
98 &self.children
99 }
100
101 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
102 &mut self.children
103 }
104
105 fn id(&self) -> Id {
106 self.id
107 }
108}