cadmus_core/view/home/
library_label.rs1use crate::color::{BLACK, WHITE};
2use crate::context::Context;
3use crate::device::CURRENT_DEVICE;
4use crate::font::{font_from_style, Fonts, NORMAL_STYLE};
5use crate::framebuffer::{Framebuffer, UpdateMode};
6use crate::geom::Rectangle;
7use crate::gesture::GestureEvent;
8use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ViewId, ID_FEEDER};
9
10pub struct LibraryLabel {
11 id: Id,
12 rect: Rectangle,
13 children: Vec<Box<dyn View>>,
14 name: String,
15 count: usize,
16 filter: bool,
17}
18
19impl LibraryLabel {
20 pub fn new(rect: Rectangle, name: &str, count: usize, filter: bool) -> LibraryLabel {
21 LibraryLabel {
22 id: ID_FEEDER.next(),
23 rect,
24 children: Vec::new(),
25 name: name.to_string(),
26 count,
27 filter,
28 }
29 }
30
31 pub fn update(&mut self, name: &str, count: usize, filter: bool, rq: &mut RenderQueue) {
32 let mut render = false;
33 if self.name != name {
34 self.name = name.to_string();
35 render = true;
36 }
37 if self.count != count {
38 self.count = count;
39 render = true;
40 }
41 if self.filter != filter {
42 self.filter = filter;
43 render = true;
44 }
45 if render {
46 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
47 }
48 }
49
50 fn text(&self) -> String {
51 let subject = if self.filter {
52 if self.count != 1 {
53 "matches"
54 } else {
55 "match"
56 }
57 } else {
58 if self.count != 1 {
59 "books"
60 } else {
61 "book"
62 }
63 };
64
65 if self.count == 0 {
66 format!("{} (No {})", self.name, subject)
67 } else {
68 format!("{} ({} {})", self.name, self.count, subject)
69 }
70 }
71}
72
73impl View for LibraryLabel {
74 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
75 fn handle_event(
76 &mut self,
77 evt: &Event,
78 _hub: &Hub,
79 bus: &mut Bus,
80 _rq: &mut RenderQueue,
81 _context: &mut Context,
82 ) -> bool {
83 match *evt {
84 Event::Gesture(GestureEvent::Tap(center)) if self.rect.includes(center) => {
85 bus.push_back(Event::ToggleNear(ViewId::LibraryMenu, self.rect));
86 true
87 }
88 _ => false,
89 }
90 }
91
92 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, fonts, _rect), fields(rect = ?_rect)))]
93 fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, fonts: &mut Fonts) {
94 let dpi = CURRENT_DEVICE.dpi;
95 let font = font_from_style(fonts, &NORMAL_STYLE, dpi);
96 let padding = font.em() as i32 / 2;
97 let max_width = self.rect.width().saturating_sub(2 * padding as u32) as i32;
98 let plan = font.plan(&self.text(), Some(max_width), None);
99 let dx = padding + (max_width - plan.width) / 2;
100 let dy = (self.rect.height() as i32 - font.x_heights.0 as i32) / 2;
101 let pt = pt!(self.rect.min.x + dx, self.rect.max.y - dy);
102 fb.draw_rectangle(&self.rect, WHITE);
103 font.render(fb, BLACK, &plan, pt);
104 }
105
106 fn rect(&self) -> &Rectangle {
107 &self.rect
108 }
109
110 fn rect_mut(&mut self) -> &mut Rectangle {
111 &mut self.rect
112 }
113
114 fn children(&self) -> &Vec<Box<dyn View>> {
115 &self.children
116 }
117
118 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
119 &mut self.children
120 }
121
122 fn id(&self) -> Id {
123 self.id
124 }
125}