cadmus_core/view/dictionary/
bottom_bar.rs

1use crate::color::WHITE;
2use crate::context::Context;
3use crate::font::Fonts;
4use crate::framebuffer::{Framebuffer, UpdateMode};
5use crate::geom::{CycleDir, Rectangle};
6use crate::gesture::GestureEvent;
7use crate::input::DeviceEvent;
8use crate::view::filler::Filler;
9use crate::view::icon::Icon;
10use crate::view::label::Label;
11use crate::view::{Align, Bus, Event, Hub, Id, RenderData, RenderQueue, View, ViewId, ID_FEEDER};
12
13pub struct BottomBar {
14    id: Id,
15    rect: Rectangle,
16    children: Vec<Box<dyn View>>,
17    has_prev: bool,
18    has_next: bool,
19}
20
21impl BottomBar {
22    pub fn new(rect: Rectangle, name: &str, has_prev: bool, has_next: bool) -> BottomBar {
23        let id = ID_FEEDER.next();
24        let mut children = Vec::new();
25        let side = rect.height() as i32;
26
27        let prev_rect = rect![rect.min, rect.min + side];
28
29        if has_prev {
30            let prev_icon = Icon::new("arrow-left", prev_rect, Event::Page(CycleDir::Previous));
31            children.push(Box::new(prev_icon) as Box<dyn View>);
32        } else {
33            let prev_filler = Filler::new(prev_rect, WHITE);
34            children.push(Box::new(prev_filler) as Box<dyn View>);
35        }
36
37        let name_rect = rect![
38            pt!(rect.min.x + side, rect.min.y),
39            pt!(rect.max.x - side, rect.max.y)
40        ];
41        let name_label = Label::new(name_rect, name.to_string(), Align::Center)
42            .event(Some(Event::ToggleNear(ViewId::SearchTargetMenu, name_rect)))
43            .hold_event(Some(Event::EditLanguages));
44        children.push(Box::new(name_label) as Box<dyn View>);
45
46        let next_rect = rect![rect.max - side, rect.max];
47
48        if has_next {
49            let next_icon = Icon::new(
50                "arrow-right",
51                rect![rect.max - side, rect.max],
52                Event::Page(CycleDir::Next),
53            );
54            children.push(Box::new(next_icon) as Box<dyn View>);
55        } else {
56            let next_filler = Filler::new(next_rect, WHITE);
57            children.push(Box::new(next_filler) as Box<dyn View>);
58        }
59
60        BottomBar {
61            id,
62            rect,
63            children,
64            has_prev,
65            has_next,
66        }
67    }
68
69    pub fn update_icons(&mut self, has_prev: bool, has_next: bool, rq: &mut RenderQueue) {
70        if self.has_prev != has_prev {
71            let index = 0;
72            let prev_rect = *self.child(index).rect();
73            if has_prev {
74                let prev_icon = Icon::new("arrow-left", prev_rect, Event::Page(CycleDir::Previous));
75                self.children[index] = Box::new(prev_icon) as Box<dyn View>;
76            } else {
77                let prev_filler = Filler::new(prev_rect, WHITE);
78                self.children[index] = Box::new(prev_filler) as Box<dyn View>;
79            }
80            self.has_prev = has_prev;
81            rq.add(RenderData::new(self.id, prev_rect, UpdateMode::Gui));
82        }
83
84        if self.has_next != has_next {
85            let index = self.len() - 1;
86            let next_rect = *self.child(index).rect();
87            if has_next {
88                let next_icon = Icon::new("arrow-right", next_rect, Event::Page(CycleDir::Next));
89                self.children[index] = Box::new(next_icon) as Box<dyn View>;
90            } else {
91                let next_filler = Filler::new(next_rect, WHITE);
92                self.children[index] = Box::new(next_filler) as Box<dyn View>;
93            }
94            self.has_next = has_next;
95            rq.add(RenderData::new(self.id, next_rect, UpdateMode::Gui));
96        }
97    }
98
99    pub fn update_name(&mut self, text: &str, rq: &mut RenderQueue) {
100        let name_label = self.child_mut(1).downcast_mut::<Label>().unwrap();
101        name_label.update(text, rq);
102    }
103}
104
105impl View for BottomBar {
106    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, _bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
107    fn handle_event(
108        &mut self,
109        evt: &Event,
110        _hub: &Hub,
111        _bus: &mut Bus,
112        _rq: &mut RenderQueue,
113        _context: &mut Context,
114    ) -> bool {
115        match *evt {
116            Event::Gesture(GestureEvent::Tap(center))
117            | Event::Gesture(GestureEvent::HoldFingerShort(center, ..))
118                if self.rect.includes(center) =>
119            {
120                true
121            }
122            Event::Device(DeviceEvent::Finger { position, .. }) if self.rect.includes(position) => {
123                true
124            }
125            _ => false,
126        }
127    }
128
129    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _fb, _fonts, _rect), fields(rect = ?_rect)))]
130    fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {}
131
132    fn resize(&mut self, rect: Rectangle, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) {
133        let side = rect.height() as i32;
134        let prev_rect = rect![rect.min, rect.min + side];
135        self.children[0].resize(prev_rect, hub, rq, context);
136        let name_rect = rect![
137            pt!(rect.min.x + side, rect.min.y),
138            pt!(rect.max.x - side, rect.max.y)
139        ];
140        self.children[1].resize(name_rect, hub, rq, context);
141        let next_rect = rect![rect.max - side, rect.max];
142        self.children[2].resize(next_rect, hub, rq, context);
143        self.rect = rect;
144    }
145
146    fn rect(&self) -> &Rectangle {
147        &self.rect
148    }
149
150    fn rect_mut(&mut self) -> &mut Rectangle {
151        &mut self.rect
152    }
153
154    fn children(&self) -> &Vec<Box<dyn View>> {
155        &self.children
156    }
157
158    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
159        &mut self.children
160    }
161
162    fn id(&self) -> Id {
163        self.id
164    }
165}