Skip to main content

cadmus_core/view/home/
library_label.rs

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