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