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}