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}