cadmus_core/view/settings_editor/
mod.rs1use crate::color::BLACK;
32use crate::context::Context;
33use crate::device::CURRENT_DEVICE;
34use crate::framebuffer::{Framebuffer, UpdateMode};
35use crate::geom::{halves, Rectangle};
36use crate::unit::scale_by_dpi;
37use crate::view::common::toggle_main_menu;
38use crate::view::filler::Filler;
39use crate::view::navigation::stack_navigation_bar::StackNavigationBar;
40use crate::view::top_bar::{TopBar, TopBarVariant};
41use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ViewId, ID_FEEDER};
42use crate::view::{SMALL_BAR_HEIGHT, THICKNESS_MEDIUM};
43
44mod bottom_bar;
45mod category;
46mod category_button;
47mod category_editor;
48mod category_navigation_bar;
49mod category_provider;
50mod library_editor;
51mod setting_row;
52mod setting_value;
53
54pub use setting_value::ToggleSettings;
55
56pub use self::bottom_bar::{BottomBarVariant, SettingsEditorBottomBar};
57pub use self::category::Category;
58pub use self::category_button::CategoryButton;
59pub use self::category_editor::CategoryEditor;
60pub use self::category_navigation_bar::CategoryNavigationBar;
61pub use self::category_provider::SettingsCategoryProvider;
62pub use self::setting_row::{Kind as RowKind, SettingRow};
63pub use self::setting_value::SettingValue;
64
65pub struct SettingsEditor {
79 id: Id,
80 rect: Rectangle,
81 children: Vec<Box<dyn View>>,
82 nav_bar_index: usize,
83 editor_index: usize,
84}
85
86impl SettingsEditor {
87 pub fn new(rect: Rectangle, rq: &mut RenderQueue, context: &mut Context) -> Self {
88 let id = ID_FEEDER.next();
89 let mut children = Vec::new();
90
91 let (bar_height, _separator_thickness, separator_top_half, separator_bottom_half) =
92 Self::calculate_dimensions();
93
94 children.push(Self::build_top_bar(
95 &rect,
96 bar_height,
97 separator_top_half,
98 context,
99 ));
100
101 children.push(Self::build_top_separator(
102 &rect,
103 bar_height,
104 separator_top_half,
105 separator_bottom_half,
106 ));
107
108 let nav_bar_rect = rect![
109 rect.min.x,
110 rect.min.y + bar_height + separator_bottom_half,
111 rect.max.x,
112 rect.min.y + bar_height + separator_bottom_half + bar_height
113 ];
114
115 let provider = SettingsCategoryProvider;
116 let mut navigation_bar =
117 StackNavigationBar::new(nav_bar_rect, rect.max.y, 1, provider, Category::General)
118 .disable_resize();
119
120 navigation_bar.set_selected(Category::General, rq, context);
121 let nav_bar_index = children.len();
122 children.push(Box::new(navigation_bar));
123
124 let content_rect = rect![
125 rect.min.x,
126 children[nav_bar_index].rect().max.y,
127 rect.max.x,
128 rect.max.y
129 ];
130
131 let category_editor = CategoryEditor::new(content_rect, Category::General, rq, context);
132
133 let editor_index = children.len();
134 children.push(Box::new(category_editor));
135
136 rq.add(RenderData::new(id, rect, UpdateMode::Gui));
137
138 SettingsEditor {
139 id,
140 rect,
141 children,
142 nav_bar_index,
143 editor_index,
144 }
145 }
146
147 fn calculate_dimensions() -> (i32, i32, i32, i32) {
148 let dpi = CURRENT_DEVICE.dpi;
149 let small_height = scale_by_dpi(SMALL_BAR_HEIGHT, dpi) as i32;
150 let separator_thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
151 let (separator_top_half, separator_bottom_half) = halves(separator_thickness);
152 let bar_height = small_height;
153
154 (
155 bar_height,
156 separator_thickness,
157 separator_top_half,
158 separator_bottom_half,
159 )
160 }
161
162 fn build_top_bar(
163 rect: &Rectangle,
164 bar_height: i32,
165 separator_top_half: i32,
166 context: &mut Context,
167 ) -> Box<dyn View> {
168 let top_bar = TopBar::new(
169 rect![
170 rect.min.x,
171 rect.min.y,
172 rect.max.x,
173 rect.min.y + bar_height - separator_top_half
174 ],
175 TopBarVariant::Back,
176 "Settings".to_string(),
177 context,
178 );
179 Box::new(top_bar) as Box<dyn View>
180 }
181
182 fn build_top_separator(
183 rect: &Rectangle,
184 bar_height: i32,
185 separator_top_half: i32,
186 separator_bottom_half: i32,
187 ) -> Box<dyn View> {
188 let separator = Filler::new(
189 rect![
190 rect.min.x,
191 rect.min.y + bar_height - separator_top_half,
192 rect.max.x,
193 rect.min.y + bar_height + separator_bottom_half
194 ],
195 BLACK,
196 );
197 Box::new(separator) as Box<dyn View>
198 }
199}
200
201impl View for SettingsEditor {
202 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _hub, _bus, rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
203 fn handle_event(
204 &mut self,
205 evt: &Event,
206 _hub: &Hub,
207 _bus: &mut Bus,
208 rq: &mut RenderQueue,
209 context: &mut Context,
210 ) -> bool {
211 match evt {
212 Event::FileChooserClosed(_) => {
213 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
214 true
215 }
216 Event::SelectSettingsCategory(category) => {
217 let nav_bar_max_y = {
218 let nav_bar = self.children[self.nav_bar_index]
219 .downcast_mut::<StackNavigationBar<SettingsCategoryProvider>>()
220 .unwrap();
221
222 nav_bar.set_selected(*category, rq, context);
223 nav_bar.rect.max.y
224 };
225
226 self.children.remove(self.editor_index);
227
228 let content_rect = rect![
229 self.rect.min.x,
230 nav_bar_max_y,
231 self.rect.max.x,
232 self.rect.max.y
233 ];
234
235 let new_editor = CategoryEditor::new(content_rect, *category, rq, context);
236 self.children
237 .insert(self.editor_index, Box::new(new_editor));
238
239 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
240
241 true
242 }
243 Event::NavigationBarResized(_) => {
244 unimplemented!("The settings navigation bar should not be resizable which means this event is not expected to be send.")
245 }
246 Event::ToggleNear(ViewId::MainMenu, rect) => {
247 toggle_main_menu(self, *rect, None, rq, context);
248 true
249 }
250 Event::Close(ViewId::MainMenu) => {
251 toggle_main_menu(self, Rectangle::default(), Some(false), rq, context);
252 true
253 }
254 Event::Close(view_id) => match view_id {
255 ViewId::MainMenu => {
256 toggle_main_menu(self, Rectangle::default(), Some(false), rq, context);
257 true
258 }
259 ViewId::FileChooser => {
260 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
261 true
262 }
263 _ => false,
264 },
265 _ => false,
266 }
267 }
268
269 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, _fb, _fonts), fields(rect = ?_rect)))]
270 fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut crate::font::Fonts) {
271 }
272
273 fn rect(&self) -> &Rectangle {
274 &self.rect
275 }
276
277 fn rect_mut(&mut self) -> &mut Rectangle {
278 &mut self.rect
279 }
280
281 fn children(&self) -> &Vec<Box<dyn View>> {
282 &self.children
283 }
284
285 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
286 &mut self.children
287 }
288
289 fn id(&self) -> Id {
290 self.id
291 }
292
293 fn is_background(&self) -> bool {
294 true
295 }
296}