cadmus_core/view/home/
library_label.rs

1use 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}