cadmus_core/view/home/
library_label.rs1use crate::color::{BLACK, WHITE};
2use crate::context::Context;
3use crate::device::CURRENT_DEVICE;
4use crate::font::{Fonts, NORMAL_STYLE, font_from_style};
5use crate::framebuffer::{Framebuffer, UpdateMode};
6use crate::geom::Rectangle;
7use crate::gesture::GestureEvent;
8use crate::view::{Bus, Event, Hub, ID_FEEDER, Id, RenderData, RenderQueue, View, ViewId};
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 { "matches" } else { "match" }
53 } else {
54 if self.count != 1 { "books" } else { "book" }
55 };
56
57 if self.count == 0 {
58 format!("{} (No {})", self.name, subject)
59 } else {
60 format!("{} ({} {})", self.name, self.count, subject)
61 }
62 }
63}
64
65impl View for LibraryLabel {
66 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _hub, bus, _rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
67 fn handle_event(
68 &mut self,
69 evt: &Event,
70 _hub: &Hub,
71 bus: &mut Bus,
72 _rq: &mut RenderQueue,
73 _context: &mut Context,
74 ) -> bool {
75 match *evt {
76 Event::Gesture(GestureEvent::Tap(center)) if self.rect.includes(center) => {
77 bus.push_back(Event::ToggleNear(ViewId::LibraryMenu, self.rect));
78 true
79 }
80 _ => false,
81 }
82 }
83
84 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, fb, fonts, _rect), fields(rect = ?_rect)))]
85 fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, fonts: &mut Fonts) {
86 let dpi = CURRENT_DEVICE.dpi;
87 let font = font_from_style(fonts, &NORMAL_STYLE, dpi);
88 let padding = font.em() as i32 / 2;
89 let max_width = self.rect.width().saturating_sub(2 * padding as u32) as i32;
90 let plan = font.plan(&self.text(), Some(max_width), None);
91 let dx = padding + (max_width - plan.width) / 2;
92 let dy = (self.rect.height() as i32 - font.x_heights.0 as i32) / 2;
93 let pt = pt!(self.rect.min.x + dx, self.rect.max.y - dy);
94 fb.draw_rectangle(&self.rect, WHITE);
95 font.render(fb, BLACK, &plan, pt);
96 }
97
98 fn rect(&self) -> &Rectangle {
99 &self.rect
100 }
101
102 fn rect_mut(&mut self) -> &mut Rectangle {
103 &mut self.rect
104 }
105
106 fn children(&self) -> &Vec<Box<dyn View>> {
107 &self.children
108 }
109
110 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
111 &mut self.children
112 }
113
114 fn id(&self) -> Id {
115 self.id
116 }
117}