rg3d_ui/inspector/editors/
array.rs1use 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 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}