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 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 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}