cadmus_core/view/dictionary/
bottom_bar.rs1use 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}