rg3d_ui/inspector/editors/
array.rs

1use crate::{
2    border::BorderBuilder,
3    brush::Brush,
4    core::{color::Color, inspect::Inspect, pool::Handle},
5    expander::ExpanderBuilder,
6    inspector::{
7        editors::{
8            PropertyEditorBuildContext, PropertyEditorDefinition,
9            PropertyEditorDefinitionContainer, PropertyEditorInstance,
10            PropertyEditorMessageContext,
11        },
12        CollectionChanged, FieldKind, Inspector, InspectorBuilder, InspectorContext,
13        InspectorEnvironment, InspectorError, InspectorMessage, PropertyChanged,
14    },
15    message::{MessageDirection, UiMessage},
16    stack_panel::StackPanelBuilder,
17    text::TextBuilder,
18    widget::{Widget, WidgetBuilder},
19    BuildContext, Control, Thickness, UiNode, UserInterface, VerticalAlignment,
20};
21use std::{
22    any::{Any, TypeId},
23    fmt::Debug,
24    marker::PhantomData,
25    ops::{Deref, DerefMut},
26    rc::Rc,
27};
28
29#[derive(Clone, Debug, PartialEq)]
30pub struct Item {
31    inspector: Handle<UiNode>,
32}
33
34#[derive(Clone, Debug)]
35pub struct ArrayEditor {
36    widget: Widget,
37    items: Vec<Item>,
38}
39
40crate::define_widget_deref!(ArrayEditor);
41
42impl Control for ArrayEditor {
43    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
44        if type_id == TypeId::of::<Self>() {
45            Some(self)
46        } else {
47            None
48        }
49    }
50
51    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
52        self.widget.handle_routed_message(ui, message);
53
54        if let Some(InspectorMessage::PropertyChanged(p)) = message.data::<InspectorMessage>() {
55            if let Some(index) = self
56                .items
57                .iter()
58                .position(|i| i.inspector == message.destination())
59            {
60                ui.send_message(CollectionChanged::item_changed(
61                    self.handle,
62                    MessageDirection::FromWidget,
63                    index,
64                    p.clone(),
65                ))
66            }
67        }
68    }
69}
70
71pub struct ArrayEditorBuilder<'a, T, I>
72where
73    T: Inspect + 'static,
74    I: IntoIterator<Item = &'a T>,
75{
76    widget_builder: WidgetBuilder,
77    collection: Option<I>,
78    environment: Option<Rc<dyn InspectorEnvironment>>,
79    definition_container: Option<Rc<PropertyEditorDefinitionContainer>>,
80    layer_index: usize,
81}
82
83fn create_item_views(items: &[Item], ctx: &mut BuildContext) -> Vec<Handle<UiNode>> {
84    items
85        .iter()
86        .enumerate()
87        .map(|(n, item)| {
88            BorderBuilder::new(
89                WidgetBuilder::new()
90                    .with_child(
91                        ExpanderBuilder::new(WidgetBuilder::new())
92                            .with_header(
93                                TextBuilder::new(WidgetBuilder::new())
94                                    .with_vertical_text_alignment(VerticalAlignment::Center)
95                                    .with_text(format!("Item {}", n))
96                                    .build(ctx),
97                            )
98                            .with_content(item.inspector)
99                            .build(ctx),
100                    )
101                    .with_foreground(Brush::Solid(Color::opaque(130, 130, 130))),
102            )
103            .build(ctx)
104        })
105        .collect::<Vec<_>>()
106}
107
108fn create_items<'a, T, I>(
109    iter: I,
110    environment: Option<Rc<dyn InspectorEnvironment>>,
111    definition_container: Rc<PropertyEditorDefinitionContainer>,
112    ctx: &mut BuildContext,
113    sync_flag: u64,
114    layer_index: usize,
115) -> Vec<Item>
116where
117    T: Inspect + 'static,
118    I: IntoIterator<Item = &'a T>,
119{
120    iter.into_iter()
121        .map(|entry| {
122            let inspector_context = InspectorContext::from_object(
123                entry,
124                ctx,
125                definition_container.clone(),
126                environment.clone(),
127                sync_flag,
128                layer_index,
129            );
130
131            let inspector = InspectorBuilder::new(WidgetBuilder::new())
132                .with_context(inspector_context)
133                .build(ctx);
134
135            Item { inspector }
136        })
137        .collect::<Vec<_>>()
138}
139
140impl<'a, T, I> ArrayEditorBuilder<'a, T, I>
141where
142    T: Inspect + 'static,
143    I: IntoIterator<Item = &'a T>,
144{
145    pub fn new(widget_builder: WidgetBuilder) -> Self {
146        Self {
147            widget_builder,
148            collection: None,
149            environment: None,
150            definition_container: None,
151            layer_index: 0,
152        }
153    }
154
155    pub fn with_collection(mut self, collection: I) -> Self {
156        self.collection = Some(collection);
157        self
158    }
159
160    pub fn with_environment(mut self, environment: Option<Rc<dyn InspectorEnvironment>>) -> Self {
161        self.environment = environment;
162        self
163    }
164
165    pub fn with_definition_container(
166        mut self,
167        definition_container: Rc<PropertyEditorDefinitionContainer>,
168    ) -> Self {
169        self.definition_container = Some(definition_container);
170        self
171    }
172
173    pub fn with_layer_index(mut self, layer_index: usize) -> Self {
174        self.layer_index = layer_index;
175        self
176    }
177
178    pub fn build(self, ctx: &mut BuildContext, sync_flag: u64) -> Handle<UiNode> {
179        let definition_container = self
180            .definition_container
181            .unwrap_or_else(|| Rc::new(PropertyEditorDefinitionContainer::new()));
182
183        let environment = self.environment;
184        let items = self
185            .collection
186            .map(|collection| {
187                create_items(
188                    collection,
189                    environment,
190                    definition_container,
191                    ctx,
192                    sync_flag,
193                    self.layer_index,
194                )
195            })
196            .unwrap_or_default();
197
198        let panel = StackPanelBuilder::new(
199            WidgetBuilder::new().with_children(create_item_views(&items, ctx)),
200        )
201        .build(ctx);
202
203        let ce = ArrayEditor {
204            widget: self.widget_builder.with_child(panel).build(),
205            items,
206        };
207
208        ctx.add_node(UiNode::new(ce))
209    }
210}
211
212#[derive(Debug)]
213pub struct ArrayPropertyEditorDefinition<T, const N: usize>
214where
215    T: Inspect + Debug + 'static,
216{
217    phantom: PhantomData<T>,
218}
219
220impl<T, const N: usize> ArrayPropertyEditorDefinition<T, N>
221where
222    T: Inspect + Debug + 'static,
223{
224    pub fn new() -> Self {
225        Self::default()
226    }
227}
228
229impl<T, const N: usize> Default for ArrayPropertyEditorDefinition<T, N>
230where
231    T: Inspect + Debug + 'static,
232{
233    fn default() -> Self {
234        Self {
235            phantom: PhantomData::default(),
236        }
237    }
238}
239
240impl<T, const N: usize> PropertyEditorDefinition for ArrayPropertyEditorDefinition<T, N>
241where
242    T: Inspect + Debug + 'static,
243{
244    fn value_type_id(&self) -> TypeId {
245        TypeId::of::<[T; N]>()
246    }
247
248    fn create_instance(
249        &self,
250        ctx: PropertyEditorBuildContext,
251    ) -> Result<PropertyEditorInstance, InspectorError> {
252        let value = ctx.property_info.cast_value::<[T; N]>()?;
253
254        Ok(PropertyEditorInstance::Simple {
255            editor: ArrayEditorBuilder::new(
256                WidgetBuilder::new().with_margin(Thickness::uniform(1.0)),
257            )
258            .with_collection(value.iter())
259            .with_environment(ctx.environment.clone())
260            .with_layer_index(ctx.layer_index + 1)
261            .with_definition_container(ctx.definition_container.clone())
262            .build(ctx.build_context, ctx.sync_flag),
263        })
264    }
265
266    fn create_message(
267        &self,
268        ctx: PropertyEditorMessageContext,
269    ) -> Result<Option<UiMessage>, InspectorError> {
270        let PropertyEditorMessageContext {
271            instance,
272            ui,
273            property_info,
274            ..
275        } = ctx;
276
277        let instance_ref = if let Some(instance) = ui.node(instance).cast::<ArrayEditor>() {
278            instance
279        } else {
280            return Err(InspectorError::Custom(
281                "Property editor is not ArrayEditor!".to_string(),
282            ));
283        };
284
285        let value = property_info.cast_value::<[T; N]>()?;
286
287        let mut error_group = Vec::new();
288
289        // Just sync inspector of every item.
290        for (item, obj) in instance_ref.items.clone().iter().zip(value.iter()) {
291            let layer_index = ctx.layer_index;
292            let ctx = ui
293                .node(item.inspector)
294                .cast::<Inspector>()
295                .expect("Must be Inspector!")
296                .context()
297                .clone();
298            if let Err(e) = ctx.sync(obj, ui, layer_index + 1) {
299                error_group.extend(e.into_iter())
300            }
301        }
302
303        if error_group.is_empty() {
304            Ok(None)
305        } else {
306            Err(InspectorError::Group(error_group))
307        }
308    }
309
310    fn translate_message(
311        &self,
312        name: &str,
313        owner_type_id: TypeId,
314        message: &UiMessage,
315    ) -> Option<PropertyChanged> {
316        if message.direction() == MessageDirection::FromWidget {
317            if let Some(collection_changed) = message.data::<CollectionChanged>() {
318                return Some(PropertyChanged {
319                    name: name.to_string(),
320                    owner_type_id,
321                    value: FieldKind::Collection(Box::new(collection_changed.clone())),
322                });
323            }
324        }
325        None
326    }
327}