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