1use crate::{
2 border::BorderBuilder,
3 core::{inspect::Inspect, pool::Handle},
4 decorator::DecoratorBuilder,
5 define_constructor,
6 dropdown_list::{DropdownList, DropdownListBuilder, DropdownListMessage},
7 inspector::{
8 editors::{
9 PropertyEditorBuildContext, PropertyEditorDefinition,
10 PropertyEditorDefinitionContainer, PropertyEditorInstance,
11 PropertyEditorMessageContext,
12 },
13 make_expander_container, FieldKind, Inspector, InspectorBuilder, InspectorContext,
14 InspectorEnvironment, InspectorError, InspectorMessage, PropertyChanged,
15 },
16 message::{MessageDirection, UiMessage},
17 text::TextBuilder,
18 widget::{Widget, WidgetBuilder},
19 BuildContext, Control, HorizontalAlignment, Thickness, UiNode, UserInterface,
20 VerticalAlignment,
21};
22use std::str::FromStr;
23use std::{
24 any::{Any, TypeId},
25 fmt::{Debug, Formatter},
26 ops::{Deref, DerefMut},
27 rc::Rc,
28};
29use strum::VariantNames;
30
31const LOCAL_SYNC_FLAG: u64 = 0xFF;
32
33pub trait InspectableEnum: Debug + Inspect + 'static {}
34
35impl<T: Debug + Inspect + 'static> InspectableEnum for T {}
36
37#[derive(Debug, Clone, PartialEq)]
38pub enum EnumPropertyEditorMessage {
39 Variant(usize),
40 PropertyChanged(PropertyChanged),
41}
42
43impl EnumPropertyEditorMessage {
44 define_constructor!(EnumPropertyEditorMessage:Variant => fn variant(usize), layout: false);
45 define_constructor!(EnumPropertyEditorMessage:PropertyChanged => fn property_changed(PropertyChanged), layout: false);
46}
47
48pub struct EnumPropertyEditor<T: InspectableEnum> {
49 widget: Widget,
50 variant_selector: Handle<UiNode>,
51 inspector: Handle<UiNode>,
52 definition: EnumPropertyEditorDefinition<T>,
53 definition_container: Rc<PropertyEditorDefinitionContainer>,
54 environment: Option<Rc<dyn InspectorEnvironment>>,
55 sync_flag: u64,
56 layer_index: usize,
57}
58
59impl<T: InspectableEnum> Debug for EnumPropertyEditor<T> {
60 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61 writeln!(f, "EnumPropertyEditor")
62 }
63}
64
65impl<T: InspectableEnum> Clone for EnumPropertyEditor<T> {
66 fn clone(&self) -> Self {
67 Self {
68 widget: self.widget.clone(),
69 variant_selector: self.variant_selector,
70 inspector: self.inspector,
71 definition: self.definition.clone(),
72 definition_container: self.definition_container.clone(),
73 environment: self.environment.clone(),
74 sync_flag: self.sync_flag,
75 layer_index: self.layer_index,
76 }
77 }
78}
79
80impl<T: InspectableEnum> Deref for EnumPropertyEditor<T> {
81 type Target = Widget;
82
83 fn deref(&self) -> &Self::Target {
84 &self.widget
85 }
86}
87
88impl<T: InspectableEnum> DerefMut for EnumPropertyEditor<T> {
89 fn deref_mut(&mut self) -> &mut Self::Target {
90 &mut self.widget
91 }
92}
93
94impl<T: InspectableEnum> Control for EnumPropertyEditor<T> {
95 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
96 if type_id == TypeId::of::<Self>() {
97 Some(self)
98 } else {
99 None
100 }
101 }
102
103 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
104 self.widget.handle_routed_message(ui, message);
105
106 if let Some(EnumPropertyEditorMessage::Variant(variant)) =
107 message.data::<EnumPropertyEditorMessage>()
108 {
109 if message.destination() == self.handle {
110 let variant = (self.definition.variant_generator)(*variant);
111
112 let ctx = InspectorContext::from_object(
113 &variant,
114 &mut ui.build_ctx(),
115 self.definition_container.clone(),
116 self.environment.clone(),
117 self.sync_flag,
118 self.layer_index,
119 );
120
121 ui.send_message(InspectorMessage::context(
122 self.inspector,
123 MessageDirection::ToWidget,
124 ctx,
125 ));
126 }
127 } else if let Some(InspectorMessage::PropertyChanged(property_changed)) =
128 message.data::<InspectorMessage>()
129 {
130 if message.destination() == self.inspector
131 && message.direction() == MessageDirection::FromWidget
132 {
133 ui.send_message(EnumPropertyEditorMessage::property_changed(
134 self.handle,
135 MessageDirection::FromWidget,
136 property_changed.clone(),
137 ))
138 }
139 }
140 }
141
142 fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
143 if message.direction() == MessageDirection::FromWidget
144 && message.destination() == self.variant_selector
145 && message.flags != LOCAL_SYNC_FLAG
146 {
147 if let Some(DropdownListMessage::SelectionChanged(Some(index))) =
148 message.data::<DropdownListMessage>()
149 {
150 ui.send_message(EnumPropertyEditorMessage::variant(
151 self.handle,
152 MessageDirection::ToWidget,
153 *index,
154 ));
155 }
156 }
157 }
158}
159
160pub struct EnumPropertyEditorBuilder {
161 widget_builder: WidgetBuilder,
162 definition_container: Option<Rc<PropertyEditorDefinitionContainer>>,
163 environment: Option<Rc<dyn InspectorEnvironment>>,
164 sync_flag: u64,
165 variant_selector: Handle<UiNode>,
166 layer_index: usize,
167}
168
169impl EnumPropertyEditorBuilder {
170 pub fn new(widget_builder: WidgetBuilder) -> Self {
171 Self {
172 widget_builder,
173 definition_container: None,
174 environment: None,
175 sync_flag: 0,
176 variant_selector: Handle::NONE,
177 layer_index: 0,
178 }
179 }
180
181 pub fn with_definition_container(
182 mut self,
183 definition_container: Rc<PropertyEditorDefinitionContainer>,
184 ) -> Self {
185 self.definition_container = Some(definition_container);
186 self
187 }
188
189 pub fn with_sync_flag(mut self, sync_flag: u64) -> Self {
190 self.sync_flag = sync_flag;
191 self
192 }
193
194 pub fn with_environment(mut self, environment: Option<Rc<dyn InspectorEnvironment>>) -> Self {
195 self.environment = environment;
196 self
197 }
198
199 pub fn with_variant_selector(mut self, variant_selector: Handle<UiNode>) -> Self {
200 self.variant_selector = variant_selector;
201 self
202 }
203
204 pub fn with_layer_index(mut self, layer_index: usize) -> Self {
205 self.layer_index = layer_index;
206 self
207 }
208
209 pub fn build<T: InspectableEnum>(
210 self,
211 ctx: &mut BuildContext,
212 definition: &EnumPropertyEditorDefinition<T>,
213 value: &T,
214 ) -> Handle<UiNode> {
215 let definition_container = self
216 .definition_container
217 .unwrap_or_else(|| Rc::new(PropertyEditorDefinitionContainer::new()));
218
219 let context = InspectorContext::from_object(
220 value,
221 ctx,
222 definition_container.clone(),
223 self.environment.clone(),
224 self.sync_flag,
225 self.layer_index,
226 );
227
228 let inspector = InspectorBuilder::new(WidgetBuilder::new())
229 .with_context(context)
230 .build(ctx);
231
232 let editor = EnumPropertyEditor {
233 widget: self
234 .widget_builder
235 .with_preview_messages(true)
236 .with_child(inspector)
237 .build(),
238 variant_selector: self.variant_selector,
239 inspector,
240 definition: definition.clone(),
241 definition_container,
242 environment: self.environment,
243 sync_flag: self.sync_flag,
244 layer_index: self.layer_index,
245 };
246
247 ctx.add_node(UiNode::new(editor))
248 }
249}
250
251pub struct EnumPropertyEditorDefinition<T: InspectableEnum> {
252 pub variant_generator: fn(usize) -> T,
253 pub index_generator: fn(&T) -> usize,
254 pub names_generator: fn() -> Vec<String>,
255}
256
257impl<T: InspectableEnum + Default> EnumPropertyEditorDefinition<T> {
258 pub fn new_optional() -> EnumPropertyEditorDefinition<Option<T>> {
259 EnumPropertyEditorDefinition {
260 variant_generator: |i| match i {
261 0 => None,
262 1 => Some(Default::default()),
263 _ => unreachable!(),
264 },
265 index_generator: |v| match v {
266 None => 0,
267 Some(_) => 1,
268 },
269 names_generator: || vec!["None".to_string(), "Some".to_string()],
270 }
271 }
272}
273
274impl<T, E: Debug> EnumPropertyEditorDefinition<T>
275where
276 T: InspectableEnum + VariantNames + AsRef<str> + FromStr<Err = E> + Debug,
277{
278 pub fn new() -> Self {
279 Self {
280 variant_generator: |i| T::from_str(T::VARIANTS[i]).unwrap(),
281 index_generator: |in_var| {
282 T::VARIANTS
283 .iter()
284 .position(|v| v == &in_var.as_ref())
285 .unwrap()
286 },
287 names_generator: || T::VARIANTS.iter().map(|v| v.to_string()).collect(),
288 }
289 }
290}
291
292impl<T: InspectableEnum> Clone for EnumPropertyEditorDefinition<T> {
293 fn clone(&self) -> Self {
294 Self {
295 variant_generator: self.variant_generator,
296 index_generator: self.index_generator,
297 names_generator: self.names_generator,
298 }
299 }
300}
301
302impl<T> Debug for EnumPropertyEditorDefinition<T>
303where
304 T: InspectableEnum,
305{
306 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
307 write!(f, "EnumPropertyEditorDefinition")
308 }
309}
310
311impl<T> PropertyEditorDefinition for EnumPropertyEditorDefinition<T>
312where
313 T: InspectableEnum,
314{
315 fn value_type_id(&self) -> TypeId {
316 TypeId::of::<T>()
317 }
318
319 fn create_instance(
320 &self,
321 ctx: PropertyEditorBuildContext,
322 ) -> Result<PropertyEditorInstance, InspectorError> {
323 let value = ctx.property_info.cast_value::<T>()?;
324 let names = (self.names_generator)();
325
326 let variant_selector = DropdownListBuilder::new(
327 WidgetBuilder::new()
328 .on_column(1)
329 .with_margin(Thickness::uniform(1.0)),
330 )
331 .with_selected((self.index_generator)(value))
332 .with_items(
333 names
334 .into_iter()
335 .map(|name| {
336 DecoratorBuilder::new(BorderBuilder::new(
337 WidgetBuilder::new().with_height(26.0).with_child(
338 TextBuilder::new(WidgetBuilder::new())
339 .with_vertical_text_alignment(VerticalAlignment::Center)
340 .with_horizontal_text_alignment(HorizontalAlignment::Center)
341 .with_text(name)
342 .build(ctx.build_context),
343 ),
344 ))
345 .build(ctx.build_context)
346 })
347 .collect::<Vec<_>>(),
348 )
349 .with_close_on_selection(true)
350 .build(ctx.build_context);
351
352 let editor;
353 let container = make_expander_container(
354 ctx.layer_index,
355 ctx.property_info.display_name,
356 variant_selector,
357 {
358 editor = EnumPropertyEditorBuilder::new(WidgetBuilder::new())
359 .with_variant_selector(variant_selector)
360 .with_layer_index(ctx.layer_index + 1)
361 .with_definition_container(ctx.definition_container.clone())
362 .with_environment(ctx.environment.clone())
363 .with_sync_flag(ctx.sync_flag)
364 .build(ctx.build_context, self, value);
365 editor
366 },
367 ctx.build_context,
368 );
369
370 Ok(PropertyEditorInstance::Custom { container, editor })
371 }
372
373 fn create_message(
374 &self,
375 ctx: PropertyEditorMessageContext,
376 ) -> Result<Option<UiMessage>, InspectorError> {
377 let value = ctx.property_info.cast_value::<T>()?;
378
379 let instance_ref = ctx
380 .ui
381 .node(ctx.instance)
382 .cast::<EnumPropertyEditor<T>>()
383 .expect("Must be EnumPropertyEditor!");
384
385 let variant_selector_ref = ctx
386 .ui
387 .node(instance_ref.variant_selector)
388 .cast::<DropdownList>()
389 .expect("Must be a DropDownList");
390
391 let variant_index = (self.index_generator)(value);
392 if Some(variant_index) != variant_selector_ref.selection() {
393 let environment = ctx
394 .ui
395 .node(instance_ref.inspector)
396 .cast::<Inspector>()
397 .expect("Must be Inspector!")
398 .context()
399 .environment
400 .clone();
401
402 let mut selection_message = DropdownListMessage::selection(
403 instance_ref.variant_selector,
404 MessageDirection::ToWidget,
405 Some(variant_index),
406 );
407 selection_message.flags = LOCAL_SYNC_FLAG;
408 ctx.ui.send_message(selection_message);
409
410 let inspector = instance_ref.inspector;
411
412 let context = InspectorContext::from_object(
413 value,
414 &mut ctx.ui.build_ctx(),
415 ctx.definition_container.clone(),
416 environment,
417 ctx.sync_flag,
418 ctx.layer_index + 1,
419 );
420
421 Ok(Some(InspectorMessage::context(
422 inspector,
423 MessageDirection::ToWidget,
424 context,
425 )))
426 } else {
427 let layer_index = ctx.layer_index;
428 let inspector_ctx = ctx
429 .ui
430 .node(instance_ref.inspector)
431 .cast::<Inspector>()
432 .expect("Must be Inspector!")
433 .context()
434 .clone();
435
436 if let Err(e) = inspector_ctx.sync(value, ctx.ui, layer_index + 1) {
437 Err(InspectorError::Group(e))
438 } else {
439 Ok(None)
440 }
441 }
442 }
443
444 fn translate_message(
445 &self,
446 name: &str,
447 owner_type_id: TypeId,
448 message: &UiMessage,
449 ) -> Option<PropertyChanged> {
450 if let Some(msg) = message.data::<EnumPropertyEditorMessage>() {
451 return match msg {
452 EnumPropertyEditorMessage::PropertyChanged(property_changed) => {
453 Some(PropertyChanged {
454 name: name.to_string(),
455 owner_type_id,
456 value: FieldKind::Inspectable(Box::new(property_changed.clone())),
457 })
458 }
459 EnumPropertyEditorMessage::Variant(index) => Some(PropertyChanged {
460 name: name.to_string(),
461 owner_type_id,
462 value: FieldKind::object((self.variant_generator)(*index)),
463 }),
464 };
465 }
466
467 None
468 }
469}