cadmus_core/view/
button.rs

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