rg3d_ui/inspector/editors/
collection.rs

1use crate::{
2    button::{ButtonBuilder, ButtonMessage},
3    core::{inspect::Inspect, pool::Handle},
4    define_constructor,
5    inspector::{
6        editors::{
7            PropertyEditorBuildContext, PropertyEditorDefinition,
8            PropertyEditorDefinitionContainer, PropertyEditorInstance,
9            PropertyEditorMessageContext,
10        },
11        make_expander_container, CollectionChanged, FieldKind, Inspector, InspectorBuilder,
12        InspectorContext, InspectorEnvironment, InspectorError, InspectorMessage, PropertyChanged,
13    },
14    message::{MessageDirection, UiMessage},
15    stack_panel::StackPanelBuilder,
16    widget::{Widget, WidgetBuilder, WidgetMessage},
17    BuildContext, Control, HorizontalAlignment, Thickness, UiNode, UserInterface,
18    VerticalAlignment,
19};
20use std::{
21    any::{Any, TypeId},
22    fmt::Debug,
23    marker::PhantomData,
24    ops::{Deref, DerefMut},
25    rc::Rc,
26};
27
28#[derive(Clone, Debug, PartialEq)]
29pub struct Item {
30    inspector: Handle<UiNode>,
31    remove: Handle<UiNode>,
32}
33
34#[derive(Clone, Debug)]
35pub struct CollectionEditor {
36    widget: Widget,
37    add: Handle<UiNode>,
38    items: Vec<Item>,
39    panel: Handle<UiNode>,
40    layer_index: usize,
41}
42
43crate::define_widget_deref!(CollectionEditor);
44
45#[derive(Debug, PartialEq, Clone)]
46pub enum CollectionEditorMessage {
47    Items(Vec<Item>),
48}
49
50impl CollectionEditorMessage {
51    define_constructor!(CollectionEditorMessage:Items => fn items(Vec<Item>), layout: false);
52}
53
54impl Control for CollectionEditor {
55    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
56        if type_id == TypeId::of::<Self>() {
57            Some(self)
58        } else {
59            None
60        }
61    }
62
63    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
64        self.widget.handle_routed_message(ui, message);
65
66        if let Some(InspectorMessage::PropertyChanged(p)) = message.data::<InspectorMessage>() {
67            if let Some(index) = self
68                .items
69                .iter()
70                .position(|i| i.inspector == message.destination())
71            {
72                ui.send_message(CollectionChanged::item_changed(
73                    self.handle,
74                    MessageDirection::FromWidget,
75                    index,
76                    p.clone(),
77                ))
78            }
79        } else if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
80            if let Some(index) = self
81                .items
82                .iter()
83                .position(|i| i.remove == message.destination())
84            {
85                ui.send_message(CollectionChanged::remove(
86                    self.handle,
87                    MessageDirection::FromWidget,
88                    index,
89                ));
90            }
91        } else if let Some(msg) = message.data::<CollectionEditorMessage>() {
92            if message.destination == self.handle {
93                match msg {
94                    CollectionEditorMessage::Items(items) => {
95                        let views = create_item_views(items, &mut ui.build_ctx(), self.layer_index);
96
97                        for old_item in ui.node(self.panel).children() {
98                            ui.send_message(WidgetMessage::remove(
99                                *old_item,
100                                MessageDirection::ToWidget,
101                            ));
102                        }
103
104                        for view in views {
105                            ui.send_message(WidgetMessage::link(
106                                view,
107                                MessageDirection::ToWidget,
108                                self.panel,
109                            ));
110                        }
111
112                        self.items = items.clone();
113                    }
114                }
115            }
116        }
117    }
118
119    fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
120        if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
121            if message.destination() == self.add {
122                ui.send_message(CollectionChanged::add(
123                    self.handle,
124                    MessageDirection::FromWidget,
125                ))
126            }
127        }
128    }
129}
130
131pub struct CollectionEditorBuilder<'a, T, I>
132where
133    T: Inspect + 'static,
134    I: IntoIterator<Item = &'a T>,
135{
136    widget_builder: WidgetBuilder,
137    collection: Option<I>,
138    environment: Option<Rc<dyn InspectorEnvironment>>,
139    definition_container: Option<Rc<PropertyEditorDefinitionContainer>>,
140    add: Handle<UiNode>,
141    layer_index: usize,
142}
143
144fn create_item_views(
145    items: &[Item],
146    ctx: &mut BuildContext,
147    layer_index: usize,
148) -> Vec<Handle<UiNode>> {
149    items
150        .iter()
151        .enumerate()
152        .map(|(n, item)| {
153            make_expander_container(
154                layer_index,
155                &format!("Item {}", n),
156                item.remove,
157                item.inspector,
158                ctx,
159            )
160        })
161        .collect::<Vec<_>>()
162}
163
164fn create_items<'a, T, I>(
165    iter: I,
166    environment: Option<Rc<dyn InspectorEnvironment>>,
167    definition_container: Rc<PropertyEditorDefinitionContainer>,
168    ctx: &mut BuildContext,
169    sync_flag: u64,
170    layer_index: usize,
171) -> Vec<Item>
172where
173    T: Inspect + 'static,
174    I: IntoIterator<Item = &'a T>,
175{
176    iter.into_iter()
177        .map(|entry| {
178            let inspector_context = InspectorContext::from_object(
179                entry,
180                ctx,
181                definition_container.clone(),
182                environment.clone(),
183                sync_flag,
184                layer_index,
185            );
186
187            let inspector = InspectorBuilder::new(WidgetBuilder::new())
188                .with_context(inspector_context)
189                .build(ctx);
190
191            let remove = ButtonBuilder::new(
192                WidgetBuilder::new()
193                    .with_margin(Thickness::uniform(1.0))
194                    .with_vertical_alignment(VerticalAlignment::Center)
195                    .with_horizontal_alignment(HorizontalAlignment::Right)
196                    .on_column(1)
197                    .with_width(16.0)
198                    .with_height(16.0),
199            )
200            .with_text("-")
201            .build(ctx);
202
203            Item { inspector, remove }
204        })
205        .collect::<Vec<_>>()
206}
207
208impl<'a, T, I> CollectionEditorBuilder<'a, T, I>
209where
210    T: Inspect + 'static,
211    I: IntoIterator<Item = &'a T>,
212{
213    pub fn new(widget_builder: WidgetBuilder) -> Self {
214        Self {
215            widget_builder,
216            collection: None,
217            environment: None,
218            definition_container: None,
219            add: Default::default(),
220            layer_index: 0,
221        }
222    }
223
224    pub fn with_collection(mut self, collection: I) -> Self {
225        self.collection = Some(collection);
226        self
227    }
228
229    pub fn with_environment(mut self, environment: Option<Rc<dyn InspectorEnvironment>>) -> Self {
230        self.environment = environment;
231        self
232    }
233
234    pub fn with_add(mut self, add: Handle<UiNode>) -> Self {
235        self.add = add;
236        self
237    }
238
239    pub fn with_definition_container(
240        mut self,
241        definition_container: Rc<PropertyEditorDefinitionContainer>,
242    ) -> Self {
243        self.definition_container = Some(definition_container);
244        self
245    }
246
247    pub fn with_layer_index(mut self, layer_index: usize) -> Self {
248        self.layer_index = layer_index;
249        self
250    }
251
252    pub fn build(self, ctx: &mut BuildContext, sync_flag: u64) -> Handle<UiNode> {
253        let definition_container = self
254            .definition_container
255            .unwrap_or_else(|| Rc::new(PropertyEditorDefinitionContainer::new()));
256
257        let environment = self.environment;
258        let items = self
259            .collection
260            .map(|collection| {
261                create_items(
262                    collection,
263                    environment,
264                    definition_container,
265                    ctx,
266                    sync_flag,
267                    self.layer_index + 1,
268                )
269            })
270            .unwrap_or_default();
271
272        let panel = StackPanelBuilder::new(WidgetBuilder::new().with_children(create_item_views(
273            &items,
274            ctx,
275            self.layer_index,
276        )))
277        .build(ctx);
278
279        let ce = CollectionEditor {
280            widget: self
281                .widget_builder
282                .with_preview_messages(true)
283                .with_child(panel)
284                .build(),
285            add: self.add,
286            items,
287            panel,
288            layer_index: self.layer_index,
289        };
290
291        ctx.add_node(UiNode::new(ce))
292    }
293}
294
295#[derive(Debug)]
296pub struct VecCollectionPropertyEditorDefinition<T>
297where
298    T: Inspect + Debug + 'static,
299{
300    phantom: PhantomData<T>,
301}
302
303impl<T> VecCollectionPropertyEditorDefinition<T>
304where
305    T: Inspect + Debug + 'static,
306{
307    pub fn new() -> Self {
308        Self::default()
309    }
310}
311
312impl<T> Default for VecCollectionPropertyEditorDefinition<T>
313where
314    T: Inspect + Debug + 'static,
315{
316    fn default() -> Self {
317        Self {
318            phantom: PhantomData::default(),
319        }
320    }
321}
322
323impl<T> PropertyEditorDefinition for VecCollectionPropertyEditorDefinition<T>
324where
325    T: Inspect + Debug + 'static,
326{
327    fn value_type_id(&self) -> TypeId {
328        TypeId::of::<Vec<T>>()
329    }
330
331    fn create_instance(
332        &self,
333        ctx: PropertyEditorBuildContext,
334    ) -> Result<PropertyEditorInstance, InspectorError> {
335        let value = ctx.property_info.cast_value::<Vec<T>>()?;
336
337        let add = ButtonBuilder::new(
338            WidgetBuilder::new()
339                .with_horizontal_alignment(HorizontalAlignment::Right)
340                .with_width(16.0)
341                .with_height(16.0)
342                .on_column(1)
343                .with_margin(Thickness::uniform(1.0)),
344        )
345        .with_text("+")
346        .build(ctx.build_context);
347
348        let editor;
349        let container = make_expander_container(
350            ctx.layer_index,
351            ctx.property_info.display_name,
352            add,
353            {
354                editor = CollectionEditorBuilder::new(
355                    WidgetBuilder::new().with_margin(Thickness::uniform(1.0)),
356                )
357                .with_add(add)
358                .with_collection(value.iter())
359                .with_environment(ctx.environment.clone())
360                .with_layer_index(ctx.layer_index + 1)
361                .with_definition_container(ctx.definition_container.clone())
362                .build(ctx.build_context, ctx.sync_flag);
363                editor
364            },
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            property_info,
380            definition_container,
381            layer_index,
382        } = ctx;
383
384        let instance_ref = if let Some(instance) = ui.node(instance).cast::<CollectionEditor>() {
385            instance
386        } else {
387            return Err(InspectorError::Custom(
388                "Property editor is not CollectionEditor!".to_string(),
389            ));
390        };
391
392        let value = property_info.cast_value::<Vec<T>>()?;
393
394        if value.len() != instance_ref.items.len() {
395            // Re-create items.
396            let items = create_items(
397                value.iter(),
398                None,
399                definition_container,
400                &mut ui.build_ctx(),
401                sync_flag,
402                layer_index + 1,
403            );
404
405            Ok(Some(CollectionEditorMessage::items(
406                instance,
407                MessageDirection::ToWidget,
408                items,
409            )))
410        } else {
411            let mut error_group = Vec::new();
412
413            // Just sync inspector of every item.
414            for (item, obj) in instance_ref.items.clone().iter().zip(value.iter()) {
415                let layer_index = ctx.layer_index;
416                let ctx = ui
417                    .node(item.inspector)
418                    .cast::<Inspector>()
419                    .expect("Must be Inspector!")
420                    .context()
421                    .clone();
422                if let Err(e) = ctx.sync(obj, ui, layer_index + 1) {
423                    error_group.extend(e.into_iter())
424                }
425            }
426
427            if error_group.is_empty() {
428                Ok(None)
429            } else {
430                Err(InspectorError::Group(error_group))
431            }
432        }
433    }
434
435    fn translate_message(
436        &self,
437        name: &str,
438        owner_type_id: TypeId,
439        message: &UiMessage,
440    ) -> Option<PropertyChanged> {
441        if message.direction() == MessageDirection::FromWidget {
442            if let Some(collection_changed) = message.data::<CollectionChanged>() {
443                return Some(PropertyChanged {
444                    name: name.to_string(),
445                    owner_type_id,
446                    value: FieldKind::Collection(Box::new(collection_changed.clone())),
447                });
448            }
449        }
450        None
451    }
452}