cadmus_core/view/
presets_list.rs1use super::preset::{Preset, PresetKind};
2use super::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
3use crate::color::WHITE;
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::{CycleDir, Dir, Rectangle};
9use crate::gesture::GestureEvent;
10use crate::settings::LightPreset;
11
12pub struct PresetsList {
13 id: Id,
14 rect: Rectangle,
15 pages: Vec<Vec<Box<dyn View>>>,
16 current_page: usize,
17}
18
19impl PresetsList {
20 pub fn new(rect: Rectangle) -> PresetsList {
21 PresetsList {
22 id: ID_FEEDER.next(),
23 rect,
24 pages: Vec::new(),
25 current_page: 0,
26 }
27 }
28
29 pub fn update(&mut self, presets: &[LightPreset], rq: &mut RenderQueue, fonts: &mut Fonts) {
30 let dpi = CURRENT_DEVICE.dpi;
31 let font = font_from_style(fonts, &NORMAL_STYLE, dpi);
32 let x_height = font.x_heights.0 as i32;
33 let preset_height = 4 * x_height;
34 let padding = font.em() as i32;
35 let preset_width = font.plan(&presets[0].name(), None, None).width + padding;
36 let max_per_line = (self.rect.width() as i32 + padding) / (preset_width + padding);
37
38 self.pages.clear();
39 let mut children = Vec::new();
40
41 let presets_count = presets.len() as i32;
42 let first_line_count = max_per_line.min(presets_count);
43 let mut item_index = 0;
44 let mut index = 0;
45
46 let dx = (self.rect.width() as i32
47 - (first_line_count * preset_width + (first_line_count - 1) * padding))
48 / 2;
49
50 while index < presets_count {
51 let position = item_index % max_per_line;
52 let x = self.rect.min.x + dx + position * (preset_width + padding);
53 let preset_rect = rect![
54 x,
55 self.rect.max.y - preset_height,
56 x + preset_width,
57 self.rect.max.y
58 ];
59 let kind = if (position == 0 && index > 0)
60 || (position == max_per_line - 1 && index < presets_count - 1)
61 {
62 let dir = if position == 0 {
63 CycleDir::Previous
64 } else {
65 CycleDir::Next
66 };
67 PresetKind::Page(dir)
68 } else {
69 let name = presets[index as usize].name();
70 let kind = PresetKind::Normal(name, index as usize);
71 index += 1;
72 kind
73 };
74
75 let preset = Preset::new(preset_rect, kind);
76 children.push(Box::new(preset) as Box<dyn View>);
77 item_index += 1;
78
79 if item_index % max_per_line == 0 || index == presets_count {
80 self.pages.push(children);
81 children = Vec::new();
82 }
83 }
84
85 self.current_page = self.current_page.min(self.pages.len().saturating_sub(1));
86
87 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
88 }
89
90 pub fn set_current_page(&mut self, dir: CycleDir) {
91 match dir {
92 CycleDir::Next if self.current_page < self.pages.len() - 1 => {
93 self.current_page += 1;
94 }
95 CycleDir::Previous if self.current_page > 0 => {
96 self.current_page -= 1;
97 }
98 _ => (),
99 }
100 }
101}
102
103impl View for PresetsList {
104 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, _bus, rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
105 fn handle_event(
106 &mut self,
107 evt: &Event,
108 _hub: &Hub,
109 _bus: &mut Bus,
110 rq: &mut RenderQueue,
111 _context: &mut Context,
112 ) -> bool {
113 match *evt {
114 Event::Gesture(GestureEvent::Swipe { dir, start, .. }) if self.rect.includes(start) => {
115 match dir {
116 Dir::West => {
117 self.set_current_page(CycleDir::Next);
118 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
119 true
120 }
121 Dir::East => {
122 self.set_current_page(CycleDir::Previous);
123 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
124 true
125 }
126 _ => false,
127 }
128 }
129 Event::Page(dir) => {
130 self.set_current_page(dir);
131 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
132 true
133 }
134 _ => false,
135 }
136 }
137
138 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, _fonts, _rect), fields(rect = ?_rect)))]
139 fn render(&self, fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut Fonts) {
140 fb.draw_rectangle(&self.rect, WHITE);
141 }
142
143 fn is_background(&self) -> bool {
144 true
145 }
146
147 fn rect(&self) -> &Rectangle {
148 &self.rect
149 }
150
151 fn rect_mut(&mut self) -> &mut Rectangle {
152 &mut self.rect
153 }
154
155 fn children(&self) -> &Vec<Box<dyn View>> {
156 &self.pages[self.current_page]
157 }
158
159 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
160 &mut self.pages[self.current_page]
161 }
162
163 fn id(&self) -> Id {
164 self.id
165 }
166}