cadmus_core/view/settings_editor/
setting_row.rs1use super::super::label::Label;
2use super::super::Align;
3use super::super::{Bus, Event, Hub, Id, RenderQueue, View, ID_FEEDER};
4use super::kinds::{SettingIdentity, SettingKind};
5use super::setting_value::SettingValue;
6use crate::context::Context;
7use crate::framebuffer::Framebuffer;
8use crate::geom::Rectangle;
9use crate::settings::Settings;
10
11pub struct SettingRow {
20 id: Id,
21 rect: Rectangle,
22 children: Vec<Box<dyn View>>,
23 identity: SettingIdentity,
25}
26
27impl SettingRow {
28 pub fn new(
29 kind: impl SettingKind + 'static,
30 rect: Rectangle,
31 settings: &Settings,
32 fonts: &mut crate::font::Fonts,
33 ) -> SettingRow {
34 let mut children = Vec::new();
35
36 let half_width = rect.width() as i32 / 2;
37 let label_rect = rect![rect.min.x, rect.min.y, rect.min.x + half_width, rect.max.y];
38 let value_rect = rect![rect.min.x + half_width, rect.min.y, rect.max.x, rect.max.y];
39 let hold_event = kind.hold_event(rect);
40
41 let label_text = kind.label(settings);
42 let identity = kind.identity();
43
44 let label =
45 Label::new(label_rect, label_text, Align::Left(50)).hold_event(hold_event.clone());
46 children.push(Box::new(label) as Box<dyn View>);
47
48 let setting_value =
49 SettingValue::new(kind, value_rect, settings, fonts).hold_event(hold_event);
50 children.push(Box::new(setting_value) as Box<dyn View>);
51
52 SettingRow {
53 id: ID_FEEDER.next(),
54 rect,
55 children,
56 identity,
57 }
58 }
59}
60
61impl View for SettingRow {
62 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _hub, _bus, rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
63 fn handle_event(
64 &mut self,
65 evt: &Event,
66 _hub: &Hub,
67 _bus: &mut Bus,
68 rq: &mut RenderQueue,
69 _context: &mut Context,
70 ) -> bool {
71 match evt {
72 Event::UpdateLibrary(index, ref library) => {
73 if let SettingIdentity::LibraryInfo(our_index) = self.identity {
74 if *index == our_index {
75 if let Some(name_view) = self.children.get_mut(0) {
76 if let Some(name_label) = name_view.as_any_mut().downcast_mut::<Label>()
77 {
78 name_label.update(&library.name, rq);
79 return true;
80 }
81 }
82 }
83 }
84 false
85 }
86 _ => false,
87 }
88 }
89
90 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _fb, _fonts), fields(rect = ?_rect)))]
91 fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut crate::font::Fonts) {
92 }
93
94 fn rect(&self) -> &Rectangle {
95 &self.rect
96 }
97
98 fn rect_mut(&mut self) -> &mut Rectangle {
99 &mut self.rect
100 }
101
102 fn children(&self) -> &Vec<Box<dyn View>> {
103 &self.children
104 }
105
106 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
107 &mut self.children
108 }
109
110 fn id(&self) -> Id {
111 self.id
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::context::test_helpers::create_test_context;
119 use crate::gesture::GestureEvent;
120 use crate::settings::LibrarySettings;
121 use crate::view::settings_editor::kinds::library::LibraryInfo;
122 use std::collections::VecDeque;
123 use std::path::PathBuf;
124 use std::sync::mpsc::channel;
125
126 fn create_test_settings() -> Settings {
127 let mut settings = Settings::default();
128 settings.libraries.clear();
129 settings.libraries.push(LibrarySettings {
130 name: "Test Library 0".to_string(),
131 path: PathBuf::from("/tmp/lib0"),
132 ..Default::default()
133 });
134 settings.libraries.push(LibrarySettings {
135 name: "Test Library 1".to_string(),
136 path: PathBuf::from("/tmp/lib1"),
137 ..Default::default()
138 });
139 settings
140 }
141
142 #[test]
143 fn test_update_library_event_updates_matching_row() {
144 let mut context = create_test_context();
145 let settings = create_test_settings();
146 let rect = rect![0, 0, 400, 60];
147
148 let mut row = SettingRow::new(LibraryInfo(0), rect, &settings, &mut context.fonts);
149
150 let (hub, _receiver) = channel();
151 let mut bus = VecDeque::new();
152 let mut rq = RenderQueue::new();
153
154 let updated_library = LibrarySettings {
155 name: "Updated Library Name".to_string(),
156 path: PathBuf::from("/tmp/updated"),
157 ..Default::default()
158 };
159
160 let event = Event::UpdateLibrary(0, Box::new(updated_library));
161 let handled = row.handle_event(&event, &hub, &mut bus, &mut rq, &mut context);
162
163 assert!(handled);
164 assert!(!rq.is_empty());
165 }
166
167 #[test]
168 fn test_update_library_event_ignores_non_matching() {
169 let mut context = create_test_context();
170 let settings = create_test_settings();
171 let rect = rect![0, 0, 400, 60];
172
173 let mut row = SettingRow::new(LibraryInfo(0), rect, &settings, &mut context.fonts);
174
175 let (hub, _receiver) = channel();
176 let mut bus = VecDeque::new();
177 let mut rq = RenderQueue::new();
178
179 let updated_library = LibrarySettings {
180 name: "Updated Library 1".to_string(),
181 path: PathBuf::from("/tmp/lib1_updated"),
182 ..Default::default()
183 };
184
185 let event = Event::UpdateLibrary(1, Box::new(updated_library));
186 let handled = row.handle_event(&event, &hub, &mut bus, &mut rq, &mut context);
187
188 assert!(!handled);
189 assert!(rq.is_empty());
190 }
191
192 #[test]
193 fn test_hold_finger_short_outside_label_rect_is_not_handled() {
194 let mut context = create_test_context();
195 let settings = create_test_settings();
196 let rect = rect![0, 0, 400, 60];
197
198 let mut row: Box<dyn View> = Box::new(SettingRow::new(
199 LibraryInfo(0),
200 rect,
201 &settings,
202 &mut context.fonts,
203 ));
204
205 let (hub, _receiver) = channel();
206 let mut bus = VecDeque::new();
207 let mut rq = RenderQueue::new();
208
209 let point = crate::geom::Point::new(500, 100);
211 let event = Event::Gesture(GestureEvent::HoldFingerShort(point, 0));
212
213 crate::view::handle_event(row.as_mut(), &event, &hub, &mut bus, &mut rq, &mut context);
214
215 assert!(
216 bus.is_empty(),
217 "No event should be pushed for out-of-bounds hold"
218 );
219 }
220}