cadmus_core/view/reader/
results_bar.rs

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