cadmus_core/view/reader/
bottom_bar.rs

1use super::chapter_label::ChapterLabel;
2use crate::color::WHITE;
3use crate::context::Context;
4use crate::document::{Document, Neighbors, TocEntry};
5use crate::font::Fonts;
6use crate::framebuffer::{Framebuffer, UpdateMode};
7use crate::geom::{halves, CycleDir, Rectangle};
8use crate::gesture::GestureEvent;
9use crate::input::DeviceEvent;
10use crate::view::filler::Filler;
11use crate::view::icon::Icon;
12use crate::view::page_label::PageLabel;
13use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
14
15pub struct BottomBar {
16    id: Id,
17    rect: Rectangle,
18    children: Vec<Box<dyn View>>,
19    is_prev_disabled: bool,
20    is_next_disabled: bool,
21}
22
23impl BottomBar {
24    pub fn new(
25        rect: Rectangle,
26        doc: &mut dyn Document,
27        toc: Option<Vec<TocEntry>>,
28        current_page: usize,
29        pages_count: usize,
30        neighbors: &Neighbors,
31        synthetic: bool,
32    ) -> BottomBar {
33        let id = ID_FEEDER.next();
34        let mut children = Vec::new();
35        let side = rect.height() as i32;
36        let is_prev_disabled = neighbors.previous_page.is_none();
37        let is_next_disabled = neighbors.next_page.is_none();
38
39        let prev_rect = rect![rect.min, rect.min + side];
40
41        if is_prev_disabled {
42            let prev_filler = Filler::new(prev_rect, WHITE);
43            children.push(Box::new(prev_filler) as Box<dyn View>);
44        } else {
45            let prev_icon = Icon::new("arrow-left", prev_rect, Event::Page(CycleDir::Previous));
46            children.push(Box::new(prev_icon) as Box<dyn View>);
47        }
48
49        let (small_half_width, big_half_width) = halves(rect.width() as i32 - 2 * side);
50
51        let chapter_rect = rect![
52            pt!(rect.min.x + side, rect.min.y),
53            pt!(rect.min.x + side + small_half_width, rect.max.y)
54        ];
55
56        let rtoc = toc.or_else(|| doc.toc());
57        let chapter = rtoc.as_ref().and_then(|toc| doc.chapter(current_page, toc));
58        let title = chapter.map(|(c, _)| c.title.clone()).unwrap_or_default();
59        let progress = chapter.map(|(_, p)| p).unwrap_or_default();
60        let chapter_label = ChapterLabel::new(chapter_rect, title, progress);
61        children.push(Box::new(chapter_label) as Box<dyn View>);
62
63        let page_label = PageLabel::new(
64            rect![
65                pt!(rect.max.x - side - big_half_width, rect.min.y),
66                pt!(rect.max.x - side, rect.max.y)
67            ],
68            current_page,
69            pages_count,
70            synthetic,
71        );
72        children.push(Box::new(page_label) as Box<dyn View>);
73
74        let next_rect = rect![rect.max - side, rect.max];
75
76        if is_next_disabled {
77            let next_filler = Filler::new(next_rect, WHITE);
78            children.push(Box::new(next_filler) as Box<dyn View>);
79        } else {
80            let next_icon = Icon::new(
81                "arrow-right",
82                rect![rect.max - side, rect.max],
83                Event::Page(CycleDir::Next),
84            );
85            children.push(Box::new(next_icon) as Box<dyn View>);
86        }
87
88        BottomBar {
89            id,
90            rect,
91            children,
92            is_prev_disabled,
93            is_next_disabled,
94        }
95    }
96
97    pub fn update_chapter_label(&mut self, title: String, progress: f32, rq: &mut RenderQueue) {
98        let chapter_label = self.child_mut(1).downcast_mut::<ChapterLabel>().unwrap();
99        chapter_label.update(title, progress, rq);
100    }
101
102    pub fn update_page_label(
103        &mut self,
104        current_page: usize,
105        pages_count: usize,
106        rq: &mut RenderQueue,
107    ) {
108        let page_label = self.child_mut(2).downcast_mut::<PageLabel>().unwrap();
109        page_label.update(current_page, pages_count, rq);
110    }
111
112    pub fn update_icons(&mut self, neighbors: &Neighbors, rq: &mut RenderQueue) {
113        let is_prev_disabled = neighbors.previous_page.is_none();
114
115        if self.is_prev_disabled != is_prev_disabled {
116            let index = 0;
117            let prev_rect = *self.child(index).rect();
118            if is_prev_disabled {
119                let prev_filler = Filler::new(prev_rect, WHITE);
120                self.children[index] = Box::new(prev_filler) as Box<dyn View>;
121            } else {
122                let prev_icon = Icon::new("arrow-left", prev_rect, Event::Page(CycleDir::Previous));
123                self.children[index] = Box::new(prev_icon) as Box<dyn View>;
124            }
125            self.is_prev_disabled = is_prev_disabled;
126            rq.add(RenderData::new(self.id, prev_rect, UpdateMode::Gui));
127        }
128
129        let is_next_disabled = neighbors.next_page.is_none();
130
131        if self.is_next_disabled != is_next_disabled {
132            let index = self.len() - 1;
133            let next_rect = *self.child(index).rect();
134            if is_next_disabled {
135                let next_filler = Filler::new(next_rect, WHITE);
136                self.children[index] = Box::new(next_filler) as Box<dyn View>;
137            } else {
138                let next_icon = Icon::new("arrow-right", next_rect, Event::Page(CycleDir::Next));
139                self.children[index] = Box::new(next_icon) as Box<dyn View>;
140            }
141            self.is_next_disabled = is_next_disabled;
142            rq.add(RenderData::new(self.id, next_rect, UpdateMode::Gui));
143        }
144    }
145}
146
147impl View for BottomBar {
148    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, _bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
149    fn handle_event(
150        &mut self,
151        evt: &Event,
152        _hub: &Hub,
153        _bus: &mut Bus,
154        _rq: &mut RenderQueue,
155        _context: &mut Context,
156    ) -> bool {
157        match *evt {
158            Event::Gesture(GestureEvent::Tap(center))
159            | Event::Gesture(GestureEvent::HoldFingerShort(center, ..))
160                if self.rect.includes(center) =>
161            {
162                true
163            }
164            Event::Gesture(GestureEvent::Swipe { start, end, .. })
165                if self.rect.includes(start) && self.rect.includes(end) =>
166            {
167                true
168            }
169            Event::Device(DeviceEvent::Finger { position, .. }) if self.rect.includes(position) => {
170                true
171            }
172            _ => false,
173        }
174    }
175
176    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _fb, _fonts, _rect), fields(rect = ?_rect)))]
177    fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {}
178
179    fn resize(&mut self, rect: Rectangle, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) {
180        let side = rect.height() as i32;
181        let (small_half_width, big_half_width) = halves(rect.width() as i32 - 2 * side);
182        let prev_rect = rect![rect.min, rect.min + side];
183        self.children[0].resize(prev_rect, hub, rq, context);
184        let chapter_rect = rect![
185            pt!(rect.min.x + side, rect.min.y),
186            pt!(rect.min.x + side + small_half_width, rect.max.y)
187        ];
188        self.children[1].resize(chapter_rect, hub, rq, context);
189        let page_label_rect = rect![
190            pt!(rect.max.x - side - big_half_width, rect.min.y),
191            pt!(rect.max.x - side, rect.max.y)
192        ];
193        self.children[2].resize(page_label_rect, hub, rq, context);
194        let next_rect = rect![rect.max - side, rect.max];
195        self.children[3].resize(next_rect, hub, rq, context);
196        self.rect = rect;
197    }
198
199    fn rect(&self) -> &Rectangle {
200        &self.rect
201    }
202
203    fn rect_mut(&mut self) -> &mut Rectangle {
204        &mut self.rect
205    }
206
207    fn children(&self) -> &Vec<Box<dyn View>> {
208        &self.children
209    }
210
211    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
212        &mut self.children
213    }
214
215    fn id(&self) -> Id {
216        self.id
217    }
218}