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