1use crate::{
22 core::{
23 pool::Handle, reflect::prelude::*, type_traits::prelude::*, uuid_provider,
24 visitor::prelude::*, PhantomDataSendSync,
25 },
26 define_constructor,
27 inspector::{
28 editors::{
29 PropertyEditorBuildContext, PropertyEditorDefinition,
30 PropertyEditorDefinitionContainer, PropertyEditorInstance,
31 PropertyEditorMessageContext, PropertyEditorTranslationContext,
32 },
33 make_expander_container, CollectionChanged, FieldKind, InspectorEnvironment,
34 InspectorError, PropertyChanged,
35 },
36 inspector::{make_property_margin, PropertyFilter},
37 message::{MessageDirection, UiMessage},
38 stack_panel::StackPanelBuilder,
39 widget::{Widget, WidgetBuilder},
40 BuildContext, Control, Thickness, UiNode, UserInterface,
41};
42
43use fyrox_graph::BaseSceneGraph;
44use std::sync::Arc;
45use std::{
46 any::TypeId,
47 fmt::Debug,
48 ops::{Deref, DerefMut},
49};
50
51#[derive(Clone, Debug, PartialEq, Visit, Reflect, Default)]
52pub struct Item {
53 pub editor_instance: PropertyEditorInstance,
54}
55
56#[derive(Debug, PartialEq, Clone)]
57pub enum ArrayEditorMessage {
58 ItemChanged { index: usize, message: UiMessage },
59}
60
61impl ArrayEditorMessage {
62 define_constructor!(ArrayEditorMessage:ItemChanged => fn item_changed(index: usize, message: UiMessage), layout: false);
63}
64
65#[derive(Clone, Debug, Visit, Reflect, ComponentProvider)]
66#[reflect(derived_type = "UiNode")]
67pub struct ArrayEditor {
68 pub widget: Widget,
69 pub items: Vec<Item>,
70}
71
72crate::define_widget_deref!(ArrayEditor);
73
74uuid_provider!(ArrayEditor = "5c6e4785-8e2d-441f-8478-523900394b93");
75
76impl Control for ArrayEditor {
77 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
78 self.widget.handle_routed_message(ui, message);
79
80 if let Some(index) = self
81 .items
82 .iter()
83 .position(|i| i.editor_instance.editor() == message.destination())
84 {
85 ui.send_message(ArrayEditorMessage::item_changed(
86 self.handle,
87 MessageDirection::FromWidget,
88 index,
89 message.clone(),
90 ));
91 }
92 }
93}
94
95pub struct ArrayEditorBuilder<'a, T, I>
96where
97 T: Reflect,
98 I: IntoIterator<Item = &'a T>,
99{
100 widget_builder: WidgetBuilder,
101 collection: Option<I>,
102 environment: Option<Arc<dyn InspectorEnvironment>>,
103 definition_container: Option<Arc<PropertyEditorDefinitionContainer>>,
104 layer_index: usize,
105 generate_property_string_values: bool,
106 filter: PropertyFilter,
107}
108
109fn create_item_views(items: &[Item]) -> Vec<Handle<UiNode>> {
110 items
111 .iter()
112 .map(|item| match item.editor_instance {
113 PropertyEditorInstance::Simple { editor } => editor,
114 PropertyEditorInstance::Custom { container, .. } => container,
115 })
116 .collect::<Vec<_>>()
117}
118
119fn create_items<'a, 'b, T, I>(
120 iter: I,
121 environment: Option<Arc<dyn InspectorEnvironment>>,
122 definition_container: Arc<PropertyEditorDefinitionContainer>,
123 property_info: &FieldRef<'a, 'b>,
124 ctx: &mut BuildContext,
125 sync_flag: u64,
126 layer_index: usize,
127 generate_property_string_values: bool,
128 filter: PropertyFilter,
129 name_column_width: f32,
130 base_path: String,
131 has_parent_object: bool,
132) -> Result<Vec<Item>, InspectorError>
133where
134 T: Reflect,
135 I: IntoIterator<Item = &'a T>,
136{
137 let mut items = Vec::new();
138
139 for (index, item) in iter.into_iter().enumerate() {
140 if let Some(definition) = definition_container.definitions().get(&TypeId::of::<T>()) {
141 let name = format!("{}[{index}]", property_info.name);
142 let display_name = format!("{}[{index}]", property_info.display_name);
143
144 let metadata = FieldMetadata {
145 name: &name,
146 display_name: &display_name,
147 read_only: property_info.read_only,
148 immutable_collection: property_info.immutable_collection,
149 min_value: property_info.min_value,
150 max_value: property_info.max_value,
151 step: property_info.step,
152 precision: property_info.precision,
153 tag: property_info.tag,
154 doc: property_info.doc,
155 };
156
157 let proxy_property_info = FieldRef {
158 metadata: &metadata,
159 value: item,
160 };
161
162 let editor =
163 definition
164 .property_editor
165 .create_instance(PropertyEditorBuildContext {
166 build_context: ctx,
167 property_info: &proxy_property_info,
168 environment: environment.clone(),
169 definition_container: definition_container.clone(),
170 sync_flag,
171 layer_index: layer_index + 1,
172 generate_property_string_values,
173 filter: filter.clone(),
174 name_column_width,
175 base_path: format!("{base_path}[{index}]"),
176 has_parent_object,
177 })?;
178
179 if let PropertyEditorInstance::Simple { editor } = editor {
180 ctx[editor].set_margin(make_property_margin(layer_index + 1));
181 }
182
183 items.push(Item {
184 editor_instance: editor,
185 });
186 } else {
187 return Err(InspectorError::Custom(format!(
188 "Missing property editor of type {}",
189 std::any::type_name::<T>()
190 )));
191 }
192 }
193
194 Ok(items)
195}
196
197impl<'a, T, I> ArrayEditorBuilder<'a, T, I>
198where
199 T: Reflect,
200 I: IntoIterator<Item = &'a T>,
201{
202 pub fn new(widget_builder: WidgetBuilder) -> Self {
203 Self {
204 widget_builder,
205 collection: None,
206 environment: None,
207 definition_container: None,
208 layer_index: 0,
209 generate_property_string_values: false,
210 filter: Default::default(),
211 }
212 }
213
214 pub fn with_collection(mut self, collection: I) -> Self {
215 self.collection = Some(collection);
216 self
217 }
218
219 pub fn with_environment(mut self, environment: Option<Arc<dyn InspectorEnvironment>>) -> Self {
220 self.environment = environment;
221 self
222 }
223
224 pub fn with_generate_property_string_values(
225 mut self,
226 generate_property_string_values: bool,
227 ) -> Self {
228 self.generate_property_string_values = generate_property_string_values;
229 self
230 }
231
232 pub fn with_definition_container(
233 mut self,
234 definition_container: Arc<PropertyEditorDefinitionContainer>,
235 ) -> Self {
236 self.definition_container = Some(definition_container);
237 self
238 }
239
240 pub fn with_layer_index(mut self, layer_index: usize) -> Self {
241 self.layer_index = layer_index;
242 self
243 }
244
245 pub fn with_filter(mut self, filter: PropertyFilter) -> Self {
246 self.filter = filter;
247 self
248 }
249
250 pub fn build(
251 self,
252 ctx: &mut BuildContext,
253 property_info: &FieldRef<'a, '_>,
254 sync_flag: u64,
255 name_column_width: f32,
256 base_path: String,
257 has_parent_object: bool,
258 ) -> Result<Handle<UiNode>, InspectorError> {
259 let definition_container = self
260 .definition_container
261 .unwrap_or_else(|| Arc::new(PropertyEditorDefinitionContainer::with_default_editors()));
262
263 let environment = self.environment;
264 let items = if let Some(collection) = self.collection {
265 create_items(
266 collection,
267 environment,
268 definition_container,
269 property_info,
270 ctx,
271 sync_flag,
272 self.layer_index + 1,
273 self.generate_property_string_values,
274 self.filter,
275 name_column_width,
276 base_path,
277 has_parent_object,
278 )?
279 } else {
280 Vec::new()
281 };
282
283 let panel =
284 StackPanelBuilder::new(WidgetBuilder::new().with_children(create_item_views(&items)))
285 .build(ctx);
286
287 let ce = ArrayEditor {
288 widget: self.widget_builder.with_child(panel).build(ctx),
289 items,
290 };
291
292 Ok(ctx.add_node(UiNode::new(ce)))
293 }
294}
295
296#[derive(Debug)]
297pub struct ArrayPropertyEditorDefinition<T, const N: usize>
298where
299 T: Reflect,
300{
301 #[allow(dead_code)]
302 phantom: PhantomDataSendSync<T>,
303}
304
305impl<T, const N: usize> ArrayPropertyEditorDefinition<T, N>
306where
307 T: Reflect,
308{
309 pub fn new() -> Self {
310 Self::default()
311 }
312}
313
314impl<T, const N: usize> Default for ArrayPropertyEditorDefinition<T, N>
315where
316 T: Reflect,
317{
318 fn default() -> Self {
319 Self {
320 phantom: Default::default(),
321 }
322 }
323}
324
325impl<T, const N: usize> PropertyEditorDefinition for ArrayPropertyEditorDefinition<T, N>
326where
327 T: Reflect,
328{
329 fn value_type_id(&self) -> TypeId {
330 TypeId::of::<[T; N]>()
331 }
332
333 fn create_instance(
334 &self,
335 ctx: PropertyEditorBuildContext,
336 ) -> Result<PropertyEditorInstance, InspectorError> {
337 let value = ctx.property_info.cast_value::<[T; N]>()?;
338
339 let editor;
340 let container = make_expander_container(
341 ctx.layer_index,
342 ctx.property_info.display_name,
343 ctx.property_info.doc,
344 Handle::NONE,
345 {
346 editor = ArrayEditorBuilder::new(
347 WidgetBuilder::new().with_margin(Thickness::uniform(1.0)),
348 )
349 .with_collection(value.iter())
350 .with_environment(ctx.environment.clone())
351 .with_layer_index(ctx.layer_index + 1)
352 .with_definition_container(ctx.definition_container.clone())
353 .with_generate_property_string_values(ctx.generate_property_string_values)
354 .with_filter(ctx.filter)
355 .build(
356 ctx.build_context,
357 ctx.property_info,
358 ctx.sync_flag,
359 ctx.name_column_width,
360 ctx.base_path.clone(),
361 ctx.has_parent_object,
362 )?;
363 editor
364 },
365 ctx.name_column_width,
366 ctx.build_context,
367 );
368
369 Ok(PropertyEditorInstance::Custom { container, editor })
370 }
371
372 fn create_message(
373 &self,
374 ctx: PropertyEditorMessageContext,
375 ) -> Result<Option<UiMessage>, InspectorError> {
376 let PropertyEditorMessageContext {
377 sync_flag,
378 instance,
379 ui,
380 layer_index,
381 generate_property_string_values,
382 property_info,
383 filter,
384 definition_container,
385 environment,
386 name_column_width,
387 base_path,
388 has_parent_object,
389 } = ctx;
390
391 let instance_ref = if let Some(instance) = ui.node(instance).cast::<ArrayEditor>() {
392 instance
393 } else {
394 return Err(InspectorError::Custom(
395 "Property editor is not ArrayEditor!".to_string(),
396 ));
397 };
398
399 let value = property_info.cast_value::<[T; N]>()?;
400
401 if let Some(definition) = definition_container.definitions().get(&TypeId::of::<T>()) {
402 for (index, (item, obj)) in instance_ref
403 .items
404 .clone()
405 .iter()
406 .zip(value.iter())
407 .enumerate()
408 {
409 let name = format!("{}[{index}]", property_info.name);
410 let display_name = format!("{}[{index}]", property_info.display_name);
411
412 let metadata = FieldMetadata {
413 name: &name,
414 display_name: &display_name,
415 read_only: property_info.read_only,
416 immutable_collection: property_info.immutable_collection,
417 min_value: property_info.min_value,
418 max_value: property_info.max_value,
419 step: property_info.step,
420 precision: property_info.precision,
421 tag: property_info.tag,
422 doc: property_info.doc,
423 };
424
425 let proxy_property_info = FieldRef {
426 metadata: &metadata,
427 value: obj,
428 };
429
430 if let Some(message) =
431 definition
432 .property_editor
433 .create_message(PropertyEditorMessageContext {
434 property_info: &proxy_property_info,
435 environment: environment.clone(),
436 definition_container: definition_container.clone(),
437 sync_flag,
438 instance: item.editor_instance.editor(),
439 layer_index: layer_index + 1,
440 ui,
441 generate_property_string_values,
442 filter: filter.clone(),
443 name_column_width,
444 base_path: format!("{base_path}[{index}]"),
445 has_parent_object,
446 })?
447 {
448 ui.send_message(message.with_flags(ctx.sync_flag))
449 }
450 }
451 }
452
453 Ok(None)
454 }
455
456 fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
457 if ctx.message.direction() == MessageDirection::FromWidget {
458 if let Some(ArrayEditorMessage::ItemChanged { index, message }) = ctx.message.data() {
459 if let Some(definition) = ctx
460 .definition_container
461 .definitions()
462 .get(&TypeId::of::<T>())
463 {
464 return Some(PropertyChanged {
465 name: ctx.name.to_string(),
466
467 value: FieldKind::Collection(Box::new(CollectionChanged::ItemChanged {
468 index: *index,
469 property: definition
470 .property_editor
471 .translate_message(PropertyEditorTranslationContext {
472 environment: ctx.environment.clone(),
473 name: "",
474 message,
475 definition_container: ctx.definition_container.clone(),
476 })?
477 .value,
478 })),
479 });
480 }
481 }
482 }
483 None
484 }
485}