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