cadmus_core/view/home/
bottom_bar.rs

1use super::library_label::LibraryLabel;
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::view::filler::Filler;
8use crate::view::icon::Icon;
9use crate::view::page_label::PageLabel;
10use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
11
12pub struct BottomBar {
13    id: Id,
14    rect: Rectangle,
15    children: Vec<Box<dyn View>>,
16    is_prev_disabled: bool,
17    is_next_disabled: bool,
18}
19
20impl BottomBar {
21    pub fn new(
22        rect: Rectangle,
23        current_page: usize,
24        pages_count: usize,
25        name: &str,
26        count: usize,
27        filter: bool,
28    ) -> BottomBar {
29        let id = ID_FEEDER.next();
30        let mut children = Vec::new();
31        let side = rect.height() as i32;
32        let is_prev_disabled = pages_count < 2 || current_page == 0;
33        let is_next_disabled = pages_count < 2 || current_page == pages_count - 1;
34
35        let prev_rect = rect![rect.min, rect.min + side];
36
37        if is_prev_disabled {
38            let prev_filler = Filler::new(prev_rect, WHITE);
39            children.push(Box::new(prev_filler) as Box<dyn View>);
40        } else {
41            let prev_icon = Icon::new("arrow-left", prev_rect, Event::Page(CycleDir::Previous));
42            children.push(Box::new(prev_icon) as Box<dyn View>);
43        }
44
45        let (small_half_width, big_half_width) = halves(rect.width() as i32 - 2 * side);
46        let library_label = LibraryLabel::new(
47            rect![
48                rect.min.x + side,
49                rect.min.y,
50                rect.min.x + side + small_half_width,
51                rect.max.y
52            ],
53            name,
54            count,
55            filter,
56        );
57        children.push(Box::new(library_label) as Box<dyn View>);
58
59        let page_label = PageLabel::new(
60            rect![
61                rect.max.x - side - big_half_width,
62                rect.min.y,
63                rect.max.x - side,
64                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                "arrow-right",
80                rect![rect.max - side, rect.max],
81                Event::Page(CycleDir::Next),
82            );
83            children.push(Box::new(next_icon) as Box<dyn View>);
84        }
85
86        BottomBar {
87            id,
88            rect,
89            children,
90            is_prev_disabled,
91            is_next_disabled,
92        }
93    }
94
95    pub fn update_library_label(
96        &mut self,
97        name: &str,
98        count: usize,
99        filter: bool,
100        rq: &mut RenderQueue,
101    ) {
102        let library_label = self.children[1]
103            .as_mut()
104            .downcast_mut::<LibraryLabel>()
105            .unwrap();
106        library_label.update(name, count, filter, rq);
107    }
108
109    pub fn update_page_label(
110        &mut self,
111        current_page: usize,
112        pages_count: usize,
113        rq: &mut RenderQueue,
114    ) {
115        let page_label = self.children[2]
116            .as_mut()
117            .downcast_mut::<PageLabel>()
118            .unwrap();
119        page_label.update(current_page, pages_count, rq);
120    }
121
122    pub fn update_icons(&mut self, current_page: usize, pages_count: usize, rq: &mut RenderQueue) {
123        let is_prev_disabled = pages_count < 2 || current_page == 0;
124
125        if self.is_prev_disabled != is_prev_disabled {
126            let index = 0;
127            let prev_rect = *self.child(index).rect();
128            if is_prev_disabled {
129                let prev_filler = Filler::new(prev_rect, WHITE);
130                self.children[index] = Box::new(prev_filler) as Box<dyn View>;
131            } else {
132                let prev_icon = Icon::new("arrow-left", prev_rect, Event::Page(CycleDir::Previous));
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 = Icon::new("arrow-right", next_rect, Event::Page(CycleDir::Next));
149                self.children[index] = Box::new(next_icon) as Box<dyn View>;
150            }
151            self.is_next_disabled = is_next_disabled;
152            rq.add(RenderData::new(self.id, next_rect, UpdateMode::Gui));
153        }
154    }
155}
156
157impl View for BottomBar {
158    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _evt, _hub, _bus, _rq, _context), fields(event = ?_evt), ret(level=tracing::Level::TRACE)))]
159    fn handle_event(
160        &mut self,
161        _evt: &Event,
162        _hub: &Hub,
163        _bus: &mut Bus,
164        _rq: &mut RenderQueue,
165        _context: &mut Context,
166    ) -> bool {
167        false
168    }
169
170    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _fb, _fonts, _rect), fields(rect = ?_rect)))]
171    fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {}
172
173    fn resize(&mut self, rect: Rectangle, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) {
174        let side = rect.height() as i32;
175        let prev_rect = rect![rect.min, rect.min + side];
176        self.children[0].resize(prev_rect, hub, rq, context);
177        let (small_half_width, big_half_width) = halves(rect.width() as i32 - 2 * side);
178        let library_label_rect = rect![
179            rect.min.x + side,
180            rect.min.y,
181            rect.min.x + side + small_half_width,
182            rect.max.y
183        ];
184        self.children[1].resize(library_label_rect, hub, rq, context);
185        let page_label_rect = rect![
186            rect.max.x - side - big_half_width,
187            rect.min.y,
188            rect.max.x - side,
189            rect.max.y
190        ];
191
192        self.children[2].resize(page_label_rect, hub, rq, context);
193        let next_rect = rect![rect.max - side, rect.max];
194        self.children[3].resize(next_rect, hub, rq, context);
195        self.rect = rect;
196    }
197
198    fn rect(&self) -> &Rectangle {
199        &self.rect
200    }
201
202    fn rect_mut(&mut self) -> &mut Rectangle {
203        &mut self.rect
204    }
205
206    fn children(&self) -> &Vec<Box<dyn View>> {
207        &self.children
208    }
209
210    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
211        &mut self.children
212    }
213
214    fn id(&self) -> Id {
215        self.id
216    }
217}