cadmus_core/view/
rounded_button.rs

1use super::icon::ICONS_PIXMAPS;
2use super::THICKNESS_MEDIUM;
3use super::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
4use crate::color::{TEXT_INVERTED_HARD, TEXT_NORMAL};
5use crate::context::Context;
6use crate::device::CURRENT_DEVICE;
7use crate::font::Fonts;
8use crate::framebuffer::{Framebuffer, UpdateMode};
9use crate::geom::{BorderSpec, CornerSpec, Rectangle};
10use crate::gesture::GestureEvent;
11use crate::input::{DeviceEvent, FingerStatus};
12use crate::unit::scale_by_dpi;
13
14pub struct RoundedButton {
15    id: Id,
16    rect: Rectangle,
17    children: Vec<Box<dyn View>>,
18    name: String,
19    event: Event,
20    active: bool,
21}
22
23impl RoundedButton {
24    pub fn new(name: &str, rect: Rectangle, event: Event) -> RoundedButton {
25        RoundedButton {
26            id: ID_FEEDER.next(),
27            rect,
28            children: Vec::new(),
29            name: name.to_string(),
30            event,
31            active: false,
32        }
33    }
34}
35
36impl View for RoundedButton {
37    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, bus, rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
38    fn handle_event(
39        &mut self,
40        evt: &Event,
41        _hub: &Hub,
42        bus: &mut Bus,
43        rq: &mut RenderQueue,
44        _context: &mut Context,
45    ) -> bool {
46        match *evt {
47            Event::Device(DeviceEvent::Finger {
48                status, position, ..
49            }) => match status {
50                FingerStatus::Down if self.rect.includes(position) => {
51                    self.active = true;
52                    rq.add(RenderData::new(self.id, self.rect, UpdateMode::Fast));
53                    true
54                }
55                FingerStatus::Up if self.active => {
56                    self.active = false;
57                    rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
58                    true
59                }
60                _ => false,
61            },
62            Event::Gesture(GestureEvent::Tap(center)) if self.rect.includes(center) => {
63                bus.push_back(self.event.clone());
64                true
65            }
66            _ => false,
67        }
68    }
69    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, _fonts, _rect), fields(rect = ?_rect)))]
70    fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {
71        let dpi = CURRENT_DEVICE.dpi;
72        let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as u16;
73        let button_radius = self.rect.height() as i32 / 2;
74
75        let scheme = if self.active {
76            TEXT_INVERTED_HARD
77        } else {
78            TEXT_NORMAL
79        };
80
81        let pixmap = ICONS_PIXMAPS.get(&self.name[..]).unwrap();
82        let dx = (self.rect.width() as i32 - pixmap.width as i32) / 2;
83        let dy = (self.rect.height() as i32 - pixmap.height as i32) / 2;
84        let pt = self.rect.min + pt!(dx, dy);
85
86        fb.draw_rounded_rectangle_with_border(
87            &self.rect,
88            &CornerSpec::Uniform(button_radius),
89            &BorderSpec {
90                thickness: thickness as u16,
91                color: scheme[1],
92            },
93            &scheme[0],
94        );
95
96        fb.draw_blended_pixmap(pixmap, pt, scheme[1]);
97    }
98
99    fn rect(&self) -> &Rectangle {
100        &self.rect
101    }
102
103    fn rect_mut(&mut self) -> &mut Rectangle {
104        &mut self.rect
105    }
106
107    fn children(&self) -> &Vec<Box<dyn View>> {
108        &self.children
109    }
110
111    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
112        &mut self.children
113    }
114
115    fn id(&self) -> Id {
116        self.id
117    }
118}