cadmus_core/view/reader/
chapter_label.rs

1use 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}