bevy_inspector_egui/reflect_inspector/
mod.rs

1//! General-purpose machinery for displaying [`Reflect`] types using [`egui`]
2//!
3//! # Examples
4//! **Basic usage**
5//! ```rust
6//! use bevy_reflect::{Reflect, TypeRegistry};
7//! use bevy_inspector_egui::reflect_inspector::{ui_for_value, InspectorUi, Context};
8//!
9//! #[derive(Reflect)]
10//! struct Data {
11//!     value: f32,
12//! }
13//!
14//! fn ui(data: &mut Data, ui: &mut egui::Ui, type_registry: &TypeRegistry) {
15//!     let mut cx = Context::default(); // empty context, with no access to the bevy world
16//!     let mut env = InspectorUi::new_no_short_circuit(type_registry, &mut cx); // no short circuiting, couldn't display `Handle<StandardMaterial>`
17//!
18//!     let _changed = env.ui_for_reflect(data, ui);
19//!
20//!     // alternatively, if you are using an empty `Context`:
21//!     let _changed = ui_for_value(data, ui, type_registry);
22//! }
23//! ```
24//!
25//!
26//! **Bevy specific usage**
27//! ```rust
28//! use bevy_reflect::{Reflect, TypeRegistry};
29//! use bevy_inspector_egui::reflect_inspector::{InspectorUi, Context};
30//!
31//! use bevy_ecs::prelude::*;
32//! use bevy_ecs::world::CommandQueue;
33//! use bevy_asset::Handle;
34//! use bevy_pbr::StandardMaterial;
35//!
36//! #[derive(Reflect)]
37//! struct Data {
38//!     material: Handle<StandardMaterial>,
39//! }
40//!
41//! fn ui(mut data: Mut<Data>, ui: &mut egui::Ui, world: &mut World, type_registry: &TypeRegistry) {
42//!     let mut queue = CommandQueue::default();
43//!     let mut cx = Context {
44//!         world: Some(world.into()),
45//!         queue: Some(&mut queue),
46//!     };
47//!     let mut env = InspectorUi::for_bevy(type_registry, &mut cx);
48//!
49//!     // alternatively
50//!     // use crate::bevy_inspector::short_circuit;
51//!     // let mut env = InspectorUi::new(type_registry, &mut cx, Some(short_circuit::short_circuit), Some(short_circuit::short_circuit_readonly));
52//!
53//!     let changed = env.ui_for_reflect(data.bypass_change_detection(), ui);
54//!     if changed {
55//!         data.set_changed();
56//!     }
57//!
58//!     queue.apply(world);
59//! }
60//! ```
61
62#[cfg(feature = "documentation")]
63use crate::egui_utils::show_docs;
64
65use crate::inspector_egui_impls::{InspectorEguiImpl, iter_all_eq};
66use crate::inspector_options::{InspectorOptions, ReflectInspectorOptions, Target};
67use crate::reflect_inspector::errors::TypeDataError;
68use crate::restricted_world_view::RestrictedWorldView;
69use crate::{
70    egui_utils::{add_button, down_button, remove_button, up_button},
71    utils::pretty_type_name_str,
72};
73use bevy_ecs::world::CommandQueue;
74use bevy_reflect::{
75    Array, DynamicEnum, DynamicTuple, DynamicTyped, DynamicVariant, Enum, EnumInfo, List, ListInfo,
76    Map, Reflect, ReflectMut, ReflectRef, Struct, StructInfo, Tuple, TupleInfo, TupleStruct,
77    TupleStructInfo, TypeInfo, TypeRegistry, VariantInfo, VariantType,
78};
79use bevy_reflect::{DynamicStruct, std_traits::ReflectDefault};
80use bevy_reflect::{PartialReflect, Set, SetInfo};
81use egui::{Grid, WidgetText};
82use std::borrow::Cow;
83use std::{
84    any::{Any, TypeId},
85    borrow::Borrow,
86};
87
88pub(crate) mod errors;
89
90pub trait ProjectorReflect: Fn(&mut dyn PartialReflect) -> &mut dyn PartialReflect {}
91
92impl<T> ProjectorReflect for T where T: Fn(&mut dyn PartialReflect) -> &mut dyn PartialReflect {}
93
94/// Display the value without any [`Context`] or short circuiting behaviour.
95///
96/// This means that for example bevy's `Handle<StandardMaterial>` values cannot be displayed,
97/// as they would need to have access to the `World`.
98///
99/// Use [`InspectorUi::new`] instead to provide context or use one of the methods in [`bevy_inspector`](crate::bevy_inspector).
100pub fn ui_for_value(
101    value: &mut dyn PartialReflect,
102    ui: &mut egui::Ui,
103    type_registry: &TypeRegistry,
104) -> bool {
105    InspectorUi::new_no_short_circuit(type_registry, &mut Context::default())
106        .ui_for_reflect(value, ui)
107}
108
109/// Display the readonly value without any [`Context`] or short circuiting behaviour.
110///
111/// This means that for example bevy's `Handle<StandardMaterial>` values cannot be displayed,
112/// as they would need to have access to the `World`.
113///
114/// Use [`InspectorUi::new`] instead to provide context or use one of the methods in [`bevy_inspector`](crate::bevy_inspector).
115pub fn ui_for_value_readonly(
116    value: &dyn PartialReflect,
117    ui: &mut egui::Ui,
118    type_registry: &TypeRegistry,
119) {
120    InspectorUi::new_no_short_circuit(type_registry, &mut Context::default())
121        .ui_for_reflect_readonly(value, ui);
122}
123
124#[derive(Default)]
125pub struct Context<'a> {
126    pub world: Option<RestrictedWorldView<'a>>,
127    pub queue: Option<&'a mut CommandQueue>,
128}
129
130/// Function which will be executed for every field recursively, which can be used to skip regular traversal.
131///
132/// This can be used to recognize `Handle<T>` types and display them as their actual value instead.
133/// Returning `None` means that no short circuiting is required, and `Some(changed)` means that the value was short-circuited
134/// and changed if the boolean is true.
135pub type ShortCircuitFn = fn(
136    &mut InspectorUi<'_, '_>,
137    value: &mut dyn PartialReflect,
138    ui: &mut egui::Ui,
139    id: egui::Id,
140    options: &dyn Any,
141) -> Option<bool>;
142/// Function which will be executed for every field recursively, which can be used to skip regular traversal, `_readonly` variant
143///
144/// This can be used to recognize `Handle<T>` types and display them as their actual value instead.
145/// Returning `None` means that no short circuiting is required, and `Some(changed)` means that the value was short-circuited
146/// and changed if the boolean is true.
147pub type ShortCircuitFnReadonly = fn(
148    &mut InspectorUi<'_, '_>,
149    value: &dyn PartialReflect,
150    ui: &mut egui::Ui,
151    id: egui::Id,
152    options: &dyn Any,
153) -> Option<()>;
154/// Function which will be executed for every field recursively, which can be used to skip regular traversal, `_many` variant
155///
156/// This can be used to recognize `Handle<T>` types and display them as their actual value instead.
157/// Returning `None` means that no short circuiting is required, and `Some(changed)` means that the value was short-circuited
158/// and changed if the boolean is true.
159pub type ShortCircuitFnMany = fn(
160    &mut InspectorUi<'_, '_>,
161    type_id: TypeId,
162    type_name: &str,
163    ui: &mut egui::Ui,
164    id: egui::Id,
165    options: &dyn Any,
166    values: &mut [&mut dyn PartialReflect],
167    projector: &dyn ProjectorReflect,
168) -> Option<bool>;
169
170pub struct InspectorUi<'a, 'c> {
171    /// Reference to the [`TypeRegistry`]
172    pub type_registry: &'a TypeRegistry,
173    /// [`Context`] with additional data that can be used to display values
174    pub context: &'a mut Context<'c>,
175
176    /// Function which will be executed for every field recursively, which can be used to skip regular traversal.
177    /// This can be used to recognize `Handle<T>` types and display them as their actual value instead.
178    pub short_circuit: ShortCircuitFn,
179    /// Same as [`short_circuit`](InspectorUi::short_circuit), but for read only usage.
180    pub short_circuit_readonly: ShortCircuitFnReadonly,
181    pub short_circuit_many: ShortCircuitFnMany,
182}
183
184impl<'a, 'c> InspectorUi<'a, 'c> {
185    pub fn new(
186        type_registry: &'a TypeRegistry,
187        context: &'a mut Context<'c>,
188        short_circuit: Option<ShortCircuitFn>,
189        short_circuit_readonly: Option<ShortCircuitFnReadonly>,
190        short_circuit_many: Option<ShortCircuitFnMany>,
191    ) -> Self {
192        Self {
193            type_registry,
194            context,
195            short_circuit: short_circuit.unwrap_or(|_, _, _, _, _| None),
196            short_circuit_readonly: short_circuit_readonly.unwrap_or(|_, _, _, _, _| None),
197            short_circuit_many: short_circuit_many.unwrap_or(|_, _, _, _, _, _, _, _| None),
198        }
199    }
200
201    pub fn new_no_short_circuit(
202        type_registry: &'a TypeRegistry,
203        context: &'a mut Context<'c>,
204    ) -> Self {
205        InspectorUi::new(type_registry, context, None, None, None)
206    }
207}
208
209impl InspectorUi<'_, '_> {
210    /// Draws the inspector UI for the given value.
211    pub fn ui_for_reflect(&mut self, value: &mut dyn PartialReflect, ui: &mut egui::Ui) -> bool {
212        self.ui_for_reflect_with_options(value, ui, egui::Id::NULL, &())
213    }
214
215    /// Draws the inspector UI for the given value in a read-only way.
216    pub fn ui_for_reflect_readonly(&mut self, value: &dyn PartialReflect, ui: &mut egui::Ui) {
217        self.ui_for_reflect_readonly_with_options(value, ui, egui::Id::NULL, &());
218    }
219
220    /// Draws the inspector UI for the given value with some options.
221    ///
222    /// The options can be [`struct@InspectorOptions`] for structs or enums with nested options for their fields,
223    /// or other structs like [`NumberOptions`](crate::inspector_options::std_options::NumberOptions) which are interpreted
224    /// by leaf types like `f32` or `Vec3`,
225    pub fn ui_for_reflect_with_options(
226        &mut self,
227        value: &mut dyn PartialReflect,
228        ui: &mut egui::Ui,
229        id: egui::Id,
230        options: &dyn Any,
231    ) -> bool {
232        let mut options = options;
233        if options.is::<()>()
234            && let Some(data) = value.try_as_reflect().and_then(|val| {
235                self.type_registry
236                    .get_type_data::<ReflectInspectorOptions>(val.type_id())
237            })
238        {
239            options = &data.0;
240        }
241        let reason = match value.try_as_reflect_mut() {
242            Some(value) => match get_type_data(self.type_registry, value) {
243                Ok(ui_impl) => {
244                    return ui_impl.execute(value.as_any_mut(), ui, options, id, self.reborrow());
245                }
246                Err(e) => e,
247            },
248            None => TypeDataError::NotFullyReflected,
249        };
250
251        if let Some(changed) = (self.short_circuit)(self, value, ui, id, options) {
252            return changed;
253        }
254
255        match value.reflect_mut() {
256            ReflectMut::Struct(value) => self.ui_for_struct(value, ui, id, options),
257            ReflectMut::TupleStruct(value) => self.ui_for_tuple_struct(value, ui, id, options),
258            ReflectMut::Tuple(value) => self.ui_for_tuple(value, ui, id, options),
259            ReflectMut::List(value) => self.ui_for_list(value, ui, id, options),
260            ReflectMut::Array(value) => self.ui_for_array(value, ui, id, options),
261            ReflectMut::Map(value) => self.ui_for_reflect_map(value, ui, id, options),
262            ReflectMut::Enum(value) => self.ui_for_enum(value, ui, id, options),
263            ReflectMut::Opaque(value) => {
264                errors::reflect_value_no_impl(ui, reason, value.reflect_short_type_path());
265                false
266            }
267            ReflectMut::Set(value) => self.ui_for_set(value, ui, id, options),
268            #[allow(unreachable_patterns)]
269            _ => {
270                ui.label("unsupported");
271                false
272            }
273        }
274    }
275
276    /// Draws the inspector UI for the given value with some options in a read-only way.
277    ///
278    /// The options can be [`struct@InspectorOptions`] for structs or enums with nested options for their fields,
279    /// or other structs like [`NumberOptions`](crate::inspector_options::std_options::NumberOptions) which are interpreted
280    /// by leaf types like `f32` or `Vec3`,
281    pub fn ui_for_reflect_readonly_with_options(
282        &mut self,
283        value: &dyn PartialReflect,
284        ui: &mut egui::Ui,
285        id: egui::Id,
286        options: &dyn Any,
287    ) {
288        let mut options = options;
289        if options.is::<()>()
290            && let Some(value_reflect) = value.try_as_reflect()
291            && let Some(data) = self
292                .type_registry
293                .get_type_data::<ReflectInspectorOptions>(value_reflect.type_id())
294        {
295            options = &data.0;
296        }
297
298        let reason = match value.try_as_reflect() {
299            Some(value) => match get_type_data(self.type_registry, value) {
300                Ok(ui_impl) => {
301                    return ui_impl.execute_readonly(
302                        value.as_any(),
303                        ui,
304                        options,
305                        id,
306                        self.reborrow(),
307                    );
308                }
309                Err(e) => e,
310            },
311            None => TypeDataError::NotFullyReflected,
312        };
313
314        if let Some(()) = (self.short_circuit_readonly)(self, value, ui, id, options) {
315            return;
316        }
317
318        match value.reflect_ref() {
319            ReflectRef::Struct(value) => self.ui_for_struct_readonly(value, ui, id, options),
320            ReflectRef::TupleStruct(value) => {
321                self.ui_for_tuple_struct_readonly(value, ui, id, options)
322            }
323            ReflectRef::Tuple(value) => self.ui_for_tuple_readonly(value, ui, id, options),
324            ReflectRef::List(value) => self.ui_for_list_readonly(value, ui, id, options),
325            ReflectRef::Array(value) => self.ui_for_array_readonly(value, ui, id, options),
326            ReflectRef::Map(value) => self.ui_for_reflect_map_readonly(value, ui, id, options),
327            ReflectRef::Enum(value) => self.ui_for_enum_readonly(value, ui, id, options),
328            ReflectRef::Opaque(value) => {
329                errors::reflect_value_no_impl(ui, reason, value.reflect_short_type_path())
330            }
331            ReflectRef::Set(value) => self.ui_for_set_readonly(value, ui, id, options),
332            #[allow(unreachable_patterns)]
333            _ => {
334                ui.label("unsupported");
335            }
336        }
337    }
338
339    pub fn ui_for_reflect_many(
340        &mut self,
341        type_id: TypeId,
342        name: &str,
343        ui: &mut egui::Ui,
344        id: egui::Id,
345        values: &mut [&mut dyn PartialReflect],
346        projector: &dyn ProjectorReflect,
347    ) -> bool {
348        self.ui_for_reflect_many_with_options(type_id, name, ui, id, &(), values, projector)
349    }
350
351    pub fn ui_for_reflect_many_with_options(
352        &mut self,
353        type_id: TypeId,
354        name: &str,
355        ui: &mut egui::Ui,
356        id: egui::Id,
357        options: &dyn Any,
358        values: &mut [&mut dyn PartialReflect],
359        projector: &dyn ProjectorReflect,
360    ) -> bool {
361        let Some(registration) = self.type_registry.get(type_id) else {
362            errors::not_in_type_registry(ui, name);
363            return false;
364        };
365        let info = registration.type_info();
366
367        let mut options = options;
368        if options.is::<()>()
369            && let Some(data) = self
370                .type_registry
371                .get_type_data::<ReflectInspectorOptions>(type_id)
372        {
373            options = &data.0;
374        }
375
376        let reason = match registration.data::<InspectorEguiImpl>() {
377            Some(ui_impl) => {
378                return ui_impl.execute_many(ui, options, id, self.reborrow(), values, projector);
379            }
380            None => TypeDataError::NoTypeData,
381        };
382
383        if let Some(s) = self
384            .type_registry
385            .get_type_data::<InspectorEguiImpl>(type_id)
386        {
387            return s.execute_many(ui, options, id, self.reborrow(), values, projector);
388        }
389
390        if let Some(changed) =
391            (self.short_circuit_many)(self, type_id, name, ui, id, options, values, projector)
392        {
393            return changed;
394        }
395
396        match info {
397            TypeInfo::Struct(info) => {
398                self.ui_for_struct_many(info, ui, id, options, values, projector)
399            }
400            TypeInfo::TupleStruct(info) => {
401                self.ui_for_tuple_struct_many(info, ui, id, options, values, projector)
402            }
403            TypeInfo::Tuple(info) => {
404                self.ui_for_tuple_many(info, ui, id, options, values, projector)
405            }
406            TypeInfo::List(info) => self.ui_for_list_many(info, ui, id, options, values, projector),
407            TypeInfo::Array(info) => {
408                errors::no_multiedit(ui, &pretty_type_name_str(info.type_path()));
409                false
410            }
411            TypeInfo::Map(info) => {
412                errors::no_multiedit(ui, &pretty_type_name_str(info.type_path()));
413                false
414            }
415            TypeInfo::Enum(info) => self.ui_for_enum_many(info, ui, id, options, values, projector),
416            TypeInfo::Opaque(info) => {
417                errors::reflect_value_no_impl(ui, reason, info.type_path());
418                false
419            }
420            TypeInfo::Set(info) => self.ui_for_set_many(info, ui, id, options, values, projector),
421        }
422    }
423}
424
425enum ListOp {
426    AddElement(usize),
427    RemoveElement(usize),
428    MoveElementUp(usize),
429    MoveElementDown(usize),
430}
431
432enum SetOp {
433    RemoveElement(Box<dyn PartialReflect>),
434    AddElement(Box<dyn PartialReflect>),
435}
436
437fn ui_for_empty_collection(ui: &mut egui::Ui, label: impl Into<WidgetText>) -> bool {
438    let mut add = false;
439    ui.vertical_centered(|ui| {
440        ui.label(label);
441        if add_button(ui).on_hover_text("Add element").clicked() {
442            add = true;
443        }
444    });
445    add
446}
447
448fn ui_for_empty_list(ui: &mut egui::Ui) -> bool {
449    ui_for_empty_collection(ui, "(Empty List)")
450}
451
452fn ui_for_list_controls(ui: &mut egui::Ui, index: usize, len: usize) -> Option<ListOp> {
453    use ListOp::*;
454    let mut op = None;
455    ui.horizontal_top(|ui| {
456        if add_button(ui).on_hover_text("Add element").clicked() {
457            op = Some(AddElement(index));
458        }
459        if remove_button(ui).on_hover_text("Remove element").clicked() {
460            op = Some(RemoveElement(index));
461        }
462        let up_enabled = index > 0;
463        ui.add_enabled_ui(up_enabled, |ui| {
464            if up_button(ui).on_hover_text("Move element up").clicked() {
465                op = Some(MoveElementUp(index));
466            }
467        });
468        let down_enabled = len.checked_sub(1).map(|l| index < l).unwrap_or(false);
469        ui.add_enabled_ui(down_enabled, |ui| {
470            if down_button(ui).on_hover_text("Move element down").clicked() {
471                op = Some(MoveElementDown(index));
472            }
473        });
474    });
475    op
476}
477
478fn ui_for_empty_set(ui: &mut egui::Ui) {
479    ui.vertical_centered(|ui| ui.label("(Empty Set)"));
480}
481
482struct MapDraftElement {
483    key: Box<dyn PartialReflect>,
484    value: Box<dyn PartialReflect>,
485}
486impl Clone for MapDraftElement {
487    fn clone(&self) -> Self {
488        Self {
489            key: self.key.to_dynamic(),
490            value: self.value.to_dynamic(),
491        }
492    }
493}
494
495struct SetDraftElement(Box<dyn PartialReflect>);
496
497impl Clone for SetDraftElement {
498    fn clone(&self) -> Self {
499        Self(self.0.to_dynamic())
500    }
501}
502
503impl InspectorUi<'_, '_> {
504    fn ui_for_struct(
505        &mut self,
506        value: &mut dyn Struct,
507        ui: &mut egui::Ui,
508        id: egui::Id,
509        options: &dyn Any,
510    ) -> bool {
511        let Some(TypeInfo::Struct(type_info)) = value.get_represented_type_info() else {
512            return false;
513        };
514
515        let mut changed = false;
516        Grid::new(id).show(ui, |ui| {
517            for i in 0..value.field_len() {
518                let field_info = type_info.field_at(i).unwrap();
519
520                let _response = ui.label(field_info.name());
521                #[cfg(feature = "documentation")]
522                show_docs(_response, field_info.docs());
523
524                let field = value.field_at_mut(i).unwrap();
525                changed |= self.ui_for_reflect_with_options(
526                    field,
527                    ui,
528                    id.with(i),
529                    inspector_options_struct_field(options, i),
530                );
531                ui.end_row();
532            }
533        });
534        changed
535    }
536
537    fn ui_for_struct_readonly(
538        &mut self,
539        value: &dyn Struct,
540        ui: &mut egui::Ui,
541        id: egui::Id,
542        options: &dyn Any,
543    ) {
544        let Some(TypeInfo::Struct(type_info)) = value.get_represented_type_info() else {
545            return;
546        };
547
548        Grid::new(id).show(ui, |ui| {
549            for i in 0..value.field_len() {
550                let field_info = type_info.field_at(i).unwrap();
551
552                let _response = ui.label(field_info.name());
553                #[cfg(feature = "documentation")]
554                show_docs(_response, field_info.docs());
555
556                let field = value.field_at(i).unwrap();
557                self.ui_for_reflect_readonly_with_options(
558                    field,
559                    ui,
560                    id.with(i),
561                    inspector_options_struct_field(options, i),
562                );
563                ui.end_row();
564            }
565        });
566    }
567
568    fn ui_for_struct_many(
569        &mut self,
570        info: &StructInfo,
571        ui: &mut egui::Ui,
572        id: egui::Id,
573        options: &dyn Any,
574        values: &mut [&mut dyn PartialReflect],
575        projector: impl ProjectorReflect,
576    ) -> bool {
577        let mut changed = false;
578        Grid::new(id).show(ui, |ui| {
579            for (i, field) in info.iter().enumerate() {
580                let _response = ui.label(field.name());
581                #[cfg(feature = "documentation")]
582                show_docs(_response, field.docs());
583
584                changed |= self.ui_for_reflect_many_with_options(
585                    field.type_id(),
586                    field.type_path(),
587                    ui,
588                    id.with(i),
589                    inspector_options_struct_field(options, i),
590                    values,
591                    &|a| match projector(a).reflect_mut() {
592                        ReflectMut::Struct(strukt) => strukt.field_at_mut(i).unwrap(),
593                        _ => unreachable!(),
594                    },
595                );
596                ui.end_row();
597            }
598        });
599        changed
600    }
601
602    fn ui_for_tuple_struct(
603        &mut self,
604        value: &mut dyn TupleStruct,
605        ui: &mut egui::Ui,
606        id: egui::Id,
607        options: &dyn Any,
608    ) -> bool {
609        maybe_grid(value.field_len(), ui, id, |ui, label| {
610            (0..value.field_len())
611                .map(|i| {
612                    if label {
613                        ui.label(i.to_string());
614                    }
615                    let field = value.field_mut(i).unwrap();
616                    let changed = self.ui_for_reflect_with_options(
617                        field,
618                        ui,
619                        id.with(i),
620                        inspector_options_struct_field(options, i),
621                    );
622                    ui.end_row();
623                    changed
624                })
625                .fold(false, or)
626        })
627    }
628
629    fn ui_for_tuple_struct_readonly(
630        &mut self,
631        value: &dyn TupleStruct,
632        ui: &mut egui::Ui,
633        id: egui::Id,
634        options: &dyn Any,
635    ) {
636        maybe_grid_readonly(value.field_len(), ui, id, |ui, label| {
637            for i in 0..value.field_len() {
638                if label {
639                    ui.label(i.to_string());
640                }
641                let field = value.field(i).unwrap();
642                self.ui_for_reflect_readonly_with_options(
643                    field,
644                    ui,
645                    id.with(i),
646                    inspector_options_struct_field(options, i),
647                );
648                ui.end_row();
649            }
650        })
651    }
652
653    fn ui_for_tuple_struct_many(
654        &mut self,
655        info: &TupleStructInfo,
656        ui: &mut egui::Ui,
657        id: egui::Id,
658        options: &dyn Any,
659        values: &mut [&mut dyn PartialReflect],
660        projector: impl ProjectorReflect,
661    ) -> bool {
662        maybe_grid(info.field_len(), ui, id, |ui, label| {
663            info.iter()
664                .enumerate()
665                .map(|(i, field)| {
666                    if label {
667                        ui.label(i.to_string());
668                    }
669                    let changed = self.ui_for_reflect_many_with_options(
670                        field.type_id(),
671                        field.type_path(),
672                        ui,
673                        id.with(i),
674                        inspector_options_struct_field(options, i),
675                        values,
676                        &|a| match projector(a).reflect_mut() {
677                            ReflectMut::TupleStruct(strukt) => strukt.field_mut(i).unwrap(),
678                            _ => unreachable!(),
679                        },
680                    );
681                    ui.end_row();
682                    changed
683                })
684                .fold(false, or)
685        })
686    }
687
688    fn ui_for_tuple(
689        &mut self,
690        value: &mut dyn Tuple,
691        ui: &mut egui::Ui,
692        id: egui::Id,
693        options: &dyn Any,
694    ) -> bool {
695        maybe_grid(value.field_len(), ui, id, |ui, label| {
696            (0..value.field_len())
697                .map(|i| {
698                    if label {
699                        ui.label(i.to_string());
700                    }
701                    let field = value.field_mut(i).unwrap();
702                    let changed = self.ui_for_reflect_with_options(
703                        field,
704                        ui,
705                        id.with(i),
706                        inspector_options_struct_field(options, i),
707                    );
708                    ui.end_row();
709                    changed
710                })
711                .fold(false, or)
712        })
713    }
714
715    fn ui_for_tuple_readonly(
716        &mut self,
717        value: &dyn Tuple,
718        ui: &mut egui::Ui,
719        id: egui::Id,
720        options: &dyn Any,
721    ) {
722        maybe_grid_readonly(value.field_len(), ui, id, |ui, label| {
723            for i in 0..value.field_len() {
724                if label {
725                    ui.label(i.to_string());
726                }
727                let field = value.field(i).unwrap();
728                self.ui_for_reflect_readonly_with_options(
729                    field,
730                    ui,
731                    id.with(i),
732                    inspector_options_struct_field(options, i),
733                );
734                ui.end_row();
735            }
736        });
737    }
738
739    fn ui_for_tuple_many(
740        &mut self,
741        info: &TupleInfo,
742        ui: &mut egui::Ui,
743        id: egui::Id,
744        options: &dyn Any,
745        values: &mut [&mut dyn PartialReflect],
746        projector: impl ProjectorReflect,
747    ) -> bool {
748        maybe_grid(info.field_len(), ui, id, |ui, label| {
749            info.iter()
750                .enumerate()
751                .map(|(i, field)| {
752                    if label {
753                        ui.label(i.to_string());
754                    }
755                    let changed = self.ui_for_reflect_many_with_options(
756                        field.type_id(),
757                        field.type_path(),
758                        ui,
759                        id.with(i),
760                        inspector_options_struct_field(options, i),
761                        values,
762                        &|a| match projector(a).reflect_mut() {
763                            ReflectMut::Tuple(strukt) => strukt.field_mut(i).unwrap(),
764                            _ => unreachable!(),
765                        },
766                    );
767                    ui.end_row();
768                    changed
769                })
770                .fold(false, or)
771        })
772    }
773
774    /// Mutate one or more lists based on a [`ListOp`], generated by some user interaction.
775    fn respond_to_list_op<'a>(
776        &mut self,
777        ui: &mut egui::Ui,
778        id: egui::Id,
779        lists: impl Iterator<Item = &'a mut dyn List>,
780        op: ListOp,
781    ) -> bool {
782        use ListOp::*;
783        let mut changed = false;
784        let error_id = id.with("error");
785
786        for list in lists {
787            let Some(TypeInfo::List(info)) = list.get_represented_type_info() else {
788                continue;
789            };
790            match op {
791                AddElement(i) => {
792                    let default = self
793                        .get_default_value_for(info.item_ty().id())
794                        .map(|def| def.into_partial_reflect())
795                        .or_else(|| list.get(i).map(|v| v.to_dynamic()));
796                    if let Some(new_value) = default {
797                        list.insert(i, new_value);
798                    } else {
799                        ui.data_mut(|data| data.insert_temp::<bool>(error_id, true));
800                    }
801                    changed = true;
802                }
803                RemoveElement(i) => {
804                    list.remove(i);
805                    changed = true;
806                }
807                MoveElementUp(i) => {
808                    if let Some(prev_idx) = i.checked_sub(1) {
809                        // Clone this element and insert it at its index - 1.
810                        if let Some(element) = list.get(i) {
811                            let clone = element.to_dynamic();
812                            list.insert(prev_idx, clone);
813                        }
814                        // Remove the original, now at its index + 1.
815                        list.remove(i + 1);
816                        changed = true;
817                    }
818                }
819                MoveElementDown(i) => {
820                    // Clone the next element and insert it at this index.
821                    if let Some(next_element) = list.get(i + 1) {
822                        let next_clone = next_element.to_dynamic();
823                        list.insert(i, next_clone);
824                    }
825                    // Remove the original, now at i + 2.
826                    list.remove(i + 2);
827                    changed = true;
828                }
829            }
830        }
831        changed
832    }
833
834    fn ui_for_list(
835        &mut self,
836        list: &mut dyn List,
837        ui: &mut egui::Ui,
838        id: egui::Id,
839        options: &dyn Any,
840    ) -> bool {
841        use ListOp::*;
842        let mut changed = false;
843
844        ui.vertical(|ui| {
845            let mut op = None;
846            let len = list.len();
847            if len == 0 && ui_for_empty_list(ui) {
848                op = Some(AddElement(0))
849            }
850            for i in 0..len {
851                egui::Grid::new((id, i)).show(ui, |ui| {
852                    ui.label(i.to_string());
853                    let val = list.get_mut(i).unwrap();
854                    ui.horizontal_top(|ui| {
855                        changed |= self.ui_for_reflect_with_options(val, ui, id.with(i), options);
856                    });
857                    ui.end_row();
858
859                    let item_op = ui_for_list_controls(ui, i, len);
860                    if item_op.is_some() {
861                        op = item_op;
862                    }
863                });
864
865                if i != len - 1 {
866                    ui.separator();
867                }
868            }
869
870            let Some(TypeInfo::List(info)) = list.get_represented_type_info() else {
871                return;
872            };
873            let error_id = id.with("error");
874
875            // Respond to control interaction
876            if let Some(op) = op {
877                let lists = std::iter::once(list);
878                changed |= self.respond_to_list_op(ui, id, lists, op);
879            }
880
881            let error = ui.data_mut(|data| *data.get_temp_mut_or_default::<bool>(error_id));
882            if error {
883                errors::no_default_value(ui, info.type_path());
884            }
885            if ui.input(|input| input.pointer.any_down()) {
886                ui.data_mut(|data| data.insert_temp::<bool>(error_id, false));
887            }
888        });
889
890        changed
891    }
892
893    fn ui_for_list_readonly(
894        &mut self,
895        list: &dyn List,
896        ui: &mut egui::Ui,
897        id: egui::Id,
898        options: &dyn Any,
899    ) {
900        ui.vertical(|ui| {
901            let len = list.len();
902            for i in 0..len {
903                let val = list.get(i).unwrap();
904                ui.horizontal_top(|ui| {
905                    self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options)
906                });
907
908                if i != len - 1 {
909                    ui.separator();
910                }
911            }
912        });
913    }
914
915    fn ui_for_list_many(
916        &mut self,
917        info: &ListInfo,
918        ui: &mut egui::Ui,
919        id: egui::Id,
920        options: &dyn Any,
921        values: &mut [&mut dyn PartialReflect],
922        projector: impl ProjectorReflect,
923    ) -> bool {
924        use ListOp::*;
925        let mut changed = false;
926
927        let same_len =
928            iter_all_eq(
929                values
930                    .iter_mut()
931                    .map(|value| match projector(*value).reflect_mut() {
932                        ReflectMut::List(l) => l.len(),
933                        _ => unreachable!(),
934                    }),
935            );
936
937        let Some(len) = same_len else {
938            ui.label("lists have different sizes, cannot multiedit");
939            return changed;
940        };
941
942        ui.vertical(|ui| {
943            let mut op = None;
944
945            if len == 0 && ui_for_empty_list(ui) {
946                op = Some(AddElement(0));
947            }
948
949            for i in 0..len {
950                let mut items_at_i: Vec<&mut dyn PartialReflect> = values
951                    .iter_mut()
952                    .map(|value| match projector(*value).reflect_mut() {
953                        ReflectMut::List(list) => list.get_mut(i).unwrap(),
954                        _ => unreachable!(),
955                    })
956                    .collect();
957
958                egui::Grid::new((id, i)).show(ui, |ui| {
959                    ui.label(i.to_string());
960                    ui.horizontal_top(|ui| {
961                        changed |= self.ui_for_reflect_many_with_options(
962                            info.item_ty().id(),
963                            info.type_path(),
964                            ui,
965                            id.with(i),
966                            options,
967                            items_at_i.as_mut_slice(),
968                            &|a| a,
969                        );
970                    });
971                    ui.end_row();
972                    let item_op = ui_for_list_controls(ui, i, len);
973                    if item_op.is_some() {
974                        op = item_op;
975                    }
976                });
977
978                if i != len - 1 {
979                    ui.separator();
980                }
981            }
982
983            let error_id = id.with("error");
984            let error = ui.data_mut(|data| *data.get_temp_mut_or_default::<bool>(error_id));
985            if error {
986                errors::no_default_value(ui, info.type_path());
987            }
988            if ui.input(|input| input.pointer.any_down()) {
989                ui.data_mut(|data| data.insert_temp::<bool>(error_id, false));
990            }
991            if let Some(op) = op {
992                let lists = values
993                    .iter_mut()
994                    .map(|l| match projector(*l).reflect_mut() {
995                        ReflectMut::List(list) => list,
996                        _ => unreachable!(),
997                    });
998                changed |= self.respond_to_list_op(ui, id, lists, op);
999            }
1000        });
1001
1002        changed
1003    }
1004
1005    fn ui_for_reflect_map(
1006        &mut self,
1007        map: &mut dyn Map,
1008        ui: &mut egui::Ui,
1009        id: egui::Id,
1010        _options: &dyn Any,
1011    ) -> bool {
1012        let mut changed = false;
1013        if map.is_empty() {
1014            ui.label("(Empty Map)");
1015            ui.end_row();
1016        }
1017        let mut to_delete: Option<Box<dyn PartialReflect>> = None;
1018
1019        egui::Grid::new(id).show(ui, |ui| {
1020            for (i, (key, value)) in map.iter().enumerate() {
1021                let ui_id = id.with(i);
1022                self.ui_for_reflect_readonly_with_options(key, ui, ui_id, &());
1023                changed |=
1024                    self.ui_for_reflect_with_options(value.to_dynamic().as_mut(), ui, ui_id, &());
1025                if remove_button(ui).on_hover_text("Remove element").clicked() {
1026                    to_delete = Some(key.to_dynamic());
1027                }
1028                ui.end_row();
1029            }
1030
1031            self.map_add_element_ui(map, ui, id, &mut changed);
1032        });
1033
1034        if let Some(key) = to_delete {
1035            map.remove(key.as_ref());
1036        }
1037
1038        changed
1039    }
1040
1041    fn map_add_element_ui(
1042        &mut self,
1043        map: &mut (dyn Map + 'static),
1044        ui: &mut egui::Ui,
1045        id: egui::Id,
1046        changed: &mut bool,
1047    ) -> Option<()> {
1048        let map_draft_id = id.with("map_draft");
1049        let draft_clone = ui.data_mut(|data| {
1050            data.get_temp_mut_or_default::<Option<MapDraftElement>>(map_draft_id)
1051                .to_owned()
1052        });
1053
1054        let map_info = map.get_represented_map_info()?;
1055
1056        let key_default = self.get_reflect_default(map_info.key_ty().id())?;
1057        let value_default = self.get_reflect_default(map_info.value_ty().id())?;
1058
1059        ui.separator();
1060        ui.end_row();
1061        ui.label("New element");
1062        match draft_clone {
1063            None => {
1064                // If no draft element exists, show a button to create one.
1065                if add_button(ui).clicked() {
1066                    // Insert a temporary 'draft' key-value pair into UI state.
1067                    let key = key_default.default().into_partial_reflect();
1068                    let value = value_default.default().into_partial_reflect();
1069                    ui.data_mut(|data| {
1070                        data.insert_temp(map_draft_id, MapDraftElement { key, value })
1071                    });
1072                }
1073                ui.end_row();
1074            }
1075            Some(MapDraftElement { mut key, mut value }) => {
1076                ui.end_row();
1077                // Show controls for editing our draft element.
1078                let key_changed = self.ui_for_reflect_with_options(key.as_mut(), ui, id, &());
1079                let value_changed = self.ui_for_reflect_with_options(value.as_mut(), ui, id, &());
1080
1081                // If the clone changed, update the data in UI state.
1082                if key_changed || value_changed {
1083                    let next_draft = MapDraftElement { key, value };
1084                    ui.data_mut(|data| data.insert_temp(map_draft_id, Some(next_draft)));
1085                }
1086
1087                // Show controls to insert the draft into the map, or remove it.
1088                if ui.button("Insert").clicked() {
1089                    let draft = ui
1090                        .data_mut(|data| data.get_temp::<Option<MapDraftElement>>(map_draft_id))
1091                        .flatten();
1092                    if let Some(draft) = draft {
1093                        map.insert_boxed(draft.key, draft.value);
1094                        ui.data_mut(|data| data.remove_by_type::<Option<MapDraftElement>>());
1095                    }
1096                    *changed = true;
1097                }
1098
1099                if ui.button("Cancel").clicked() {
1100                    ui.data_mut(|data| data.remove_by_type::<Option<MapDraftElement>>());
1101                    *changed = true;
1102                }
1103                ui.end_row();
1104            }
1105        }
1106
1107        Some(())
1108    }
1109
1110    fn ui_for_reflect_map_readonly(
1111        &mut self,
1112        map: &dyn Map,
1113        ui: &mut egui::Ui,
1114        id: egui::Id,
1115        _options: &dyn Any,
1116    ) {
1117        egui::Grid::new(id).show(ui, |ui| {
1118            for (i, (key, value)) in map.iter().enumerate() {
1119                let ui_id = id.with(i);
1120                self.ui_for_reflect_readonly_with_options(key, ui, ui_id, &());
1121                self.ui_for_reflect_readonly_with_options(value, ui, ui_id, &());
1122                ui.end_row();
1123            }
1124        });
1125    }
1126
1127    /// Mutate one or more lists based on a [`SetOp`], generated by some user interaction.
1128    fn respond_to_sets_op<'a>(
1129        &mut self,
1130        sets: impl Iterator<Item = &'a mut dyn Set>,
1131        op: SetOp,
1132    ) -> bool {
1133        let mut changed = false;
1134
1135        for set in sets {
1136            changed |= self.respond_to_set_op(set, &op);
1137        }
1138        changed
1139    }
1140    fn respond_to_set_op<'a>(&mut self, set: &'a mut dyn Set, op: &SetOp) -> bool {
1141        use SetOp::*;
1142        match &op {
1143            AddElement(new_value) => {
1144                set.insert_boxed(new_value.to_dynamic());
1145            }
1146            RemoveElement(val) => {
1147                set.remove(&**val);
1148            }
1149        }
1150        true
1151    }
1152
1153    fn ui_for_set(
1154        &mut self,
1155        set: &mut dyn Set,
1156        ui: &mut egui::Ui,
1157        id: egui::Id,
1158        options: &dyn Any,
1159    ) -> bool {
1160        use SetOp::*;
1161        let mut changed = false;
1162
1163        ui.vertical(|ui| {
1164            let mut op = None;
1165
1166            let len = set.len();
1167            if len == 0 {
1168                ui_for_empty_set(ui);
1169            }
1170
1171            for (i, val) in set.iter().enumerate() {
1172                egui::Grid::new((id, i)).show(ui, |ui| {
1173                    ui.horizontal_top(|ui| {
1174                        self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options);
1175                    });
1176                    ui.horizontal_top(|ui| {
1177                        if remove_button(ui).on_hover_text("Remove element").clicked() {
1178                            let copy = val.to_dynamic();
1179                            op = Some(RemoveElement(copy));
1180                        }
1181                    });
1182                    ui.end_row();
1183                });
1184
1185                if i != len - 1 {
1186                    ui.separator();
1187                }
1188            }
1189            let Some(TypeInfo::Set(set_info)) = set.get_represented_type_info() else {
1190                return;
1191            };
1192            let value_type = set_info.value_ty();
1193            let new_op = self.set_add_element_ui(value_type, ui, id, options, &mut changed);
1194            if new_op.is_some() {
1195                op = new_op;
1196            }
1197
1198            ui.end_row();
1199
1200            let error_id = id.with("error");
1201
1202            // Respond to control interaction
1203            if let Some(op) = op {
1204                changed |= self.respond_to_set_op(set, &op);
1205            }
1206
1207            let error = ui.data_mut(|data| *data.get_temp_mut_or_default::<bool>(error_id));
1208            if error {
1209                errors::no_default_value(ui, set_info.type_path());
1210            }
1211            if ui.input(|input| input.pointer.any_down()) {
1212                ui.data_mut(|data| data.insert_temp::<bool>(error_id, false));
1213            }
1214        });
1215
1216        changed
1217    }
1218
1219    #[must_use]
1220    fn set_add_element_ui(
1221        &mut self,
1222        value_type: bevy_reflect::Type,
1223        ui: &mut egui::Ui,
1224        id: egui::Id,
1225        options: &dyn Any,
1226        changed: &mut bool,
1227    ) -> Option<SetOp> {
1228        let mut op = None;
1229
1230        let item_default = self.get_reflect_default(value_type.id())?.clone();
1231
1232        ui.vertical(|ui| {
1233            ui.label("New element");
1234            let set_draft_id = id.with("set_draft");
1235            let draft_clone = ui.data_mut(|data| {
1236                data.get_temp_mut_or_default::<Option<SetDraftElement>>(set_draft_id)
1237                    .to_owned()
1238            });
1239            ui.end_row();
1240            match draft_clone {
1241                None => {
1242                    // If no draft element exists, show a button to create one.
1243                    if add_button(ui).clicked() {
1244                        // Insert a temporary 'draft' value into UI state, once inserted, we cannot modify it.
1245                        let draft = SetDraftElement(item_default.default().into_partial_reflect());
1246                        ui.data_mut(|data| data.insert_temp(set_draft_id, Some(draft)));
1247                    }
1248
1249                    ui.end_row();
1250                }
1251                Some(SetDraftElement(mut v)) => {
1252                    ui.end_row();
1253                    // Show controls for editing our draft element.
1254                    // FIXME: is the id passed here correct?
1255                    let value_changed =
1256                        self.ui_for_reflect_with_options(v.as_mut(), ui, id, options);
1257
1258                    // If the clone changed, update the data in UI state.
1259                    if value_changed {
1260                        let next_draft = SetDraftElement(v);
1261                        ui.data_mut(|data| data.insert_temp(set_draft_id, Some(next_draft)));
1262                    }
1263
1264                    // Show controls to insert the draft into the set, or remove it.
1265                    if ui.button("Insert").clicked() {
1266                        let draft = ui
1267                            .data_mut(|data| data.get_temp::<Option<SetDraftElement>>(set_draft_id))
1268                            .flatten();
1269                        if let Some(draft) = draft {
1270                            op = Some(SetOp::AddElement(draft.0));
1271                            ui.data_mut(|data| data.remove_by_type::<Option<SetDraftElement>>());
1272                        }
1273                        *changed = true;
1274                    }
1275
1276                    if ui.button("Cancel").clicked() {
1277                        ui.data_mut(|data| data.remove_by_type::<Option<SetDraftElement>>());
1278                        *changed = true;
1279                    }
1280                    ui.end_row();
1281                }
1282            }
1283        });
1284
1285        op
1286    }
1287
1288    fn ui_for_set_readonly(
1289        &mut self,
1290        set: &dyn Set,
1291        ui: &mut egui::Ui,
1292        id: egui::Id,
1293        options: &dyn Any,
1294    ) {
1295        let len = set.len();
1296        ui.vertical(|ui| {
1297            for (i, val) in set.iter().enumerate() {
1298                ui.horizontal_top(|ui| {
1299                    self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options)
1300                });
1301
1302                if i != len - 1 {
1303                    ui.separator();
1304                }
1305            }
1306        });
1307    }
1308
1309    fn ui_for_set_many(
1310        &mut self,
1311        info: &SetInfo,
1312        ui: &mut egui::Ui,
1313        id: egui::Id,
1314        options: &dyn Any,
1315        values: &mut [&mut dyn PartialReflect],
1316        projector: impl ProjectorReflect,
1317    ) -> bool {
1318        use SetOp::*;
1319        let mut changed = false;
1320
1321        let same_len =
1322            iter_all_eq(
1323                values
1324                    .iter_mut()
1325                    .map(|value| match projector(*value).reflect_mut() {
1326                        ReflectMut::List(l) => l.len(),
1327                        _ => unreachable!(),
1328                    }),
1329            );
1330
1331        let Some(len) = same_len else {
1332            ui.label("lists have different sizes, cannot multiedit");
1333            return changed;
1334        };
1335
1336        ui.vertical(|ui| {
1337            let mut op = None;
1338
1339            if len == 0 {
1340                ui_for_empty_set(ui)
1341            }
1342
1343            let set0 = match projector(values[0]).reflect_mut() {
1344                ReflectMut::Set(set) => set,
1345                _ => unreachable!(),
1346            };
1347            let Some(TypeInfo::Set(set_info)) = set0.get_represented_type_info() else {
1348                return;
1349            };
1350            let value_type = set_info.value_ty();
1351            let reflected_values: Vec<Box<dyn PartialReflect>> =
1352                set0.iter().map(|v| v.to_dynamic()).collect();
1353
1354            for (i, value_to_check) in reflected_values.iter().enumerate() {
1355                let value_type_id = (**value_to_check).type_id();
1356                egui::Grid::new((value_type_id, i)).show(ui, |ui| {
1357                    // Do all sets contain this value ?
1358                    if len == 1
1359                        || values[1..].iter_mut().all(|set_to_compare| {
1360                            let set_to_compare = match projector(*set_to_compare).reflect_mut() {
1361                                ReflectMut::Set(set) => set,
1362                                _ => unreachable!(),
1363                            };
1364                            set_to_compare.iter().any(|value| {
1365                                value.reflect_partial_eq(value_to_check.borrow()) == Some(true)
1366                            })
1367                        })
1368                    {
1369                        // All sets contain this value: Show value
1370                        ui.horizontal_top(|ui| {
1371                            self.ui_for_reflect_readonly_with_options(
1372                                value_to_check.borrow(),
1373                                ui,
1374                                // FIXME: is the id passed here correct?
1375                                id.with(i),
1376                                options,
1377                            );
1378                        });
1379                        ui.horizontal_top(|ui| {
1380                            if remove_button(ui).on_hover_text("Remove element").clicked() {
1381                                let copy = value_to_check.to_dynamic();
1382                                op = Some(RemoveElement(copy));
1383                            }
1384                        });
1385                    } else {
1386                        ui.label("Different values");
1387                    }
1388
1389                    ui.end_row();
1390                });
1391                if i != len - 1 {
1392                    ui.separator();
1393                }
1394            }
1395            let op = self.set_add_element_ui(value_type, ui, id, options, &mut changed);
1396
1397            ui.end_row();
1398
1399            let error_id = id.with("error");
1400            let error = ui.data_mut(|data| *data.get_temp_mut_or_default::<bool>(error_id));
1401            if error {
1402                errors::no_default_value(ui, info.type_path());
1403            }
1404            if ui.input(|input| input.pointer.any_down()) {
1405                ui.data_mut(|data| data.insert_temp::<bool>(error_id, false));
1406            }
1407            if let Some(op) = op {
1408                let sets = values
1409                    .iter_mut()
1410                    .map(|l| match projector(*l).reflect_mut() {
1411                        ReflectMut::Set(list) => list,
1412                        _ => unreachable!(),
1413                    });
1414                changed |= self.respond_to_sets_op(sets, op);
1415            }
1416        });
1417
1418        changed
1419    }
1420
1421    fn ui_for_array(
1422        &mut self,
1423        array: &mut dyn Array,
1424        ui: &mut egui::Ui,
1425        id: egui::Id,
1426        options: &dyn Any,
1427    ) -> bool {
1428        let mut changed = false;
1429
1430        ui.vertical(|ui| {
1431            let len = array.len();
1432            for i in 0..len {
1433                let val = array.get_mut(i).unwrap();
1434                ui.horizontal_top(|ui| {
1435                    changed |= self.ui_for_reflect_with_options(val, ui, id.with(i), options);
1436                });
1437
1438                if i != len - 1 {
1439                    ui.separator();
1440                }
1441            }
1442        });
1443
1444        changed
1445    }
1446
1447    fn ui_for_array_readonly(
1448        &mut self,
1449        array: &dyn Array,
1450        ui: &mut egui::Ui,
1451        id: egui::Id,
1452        options: &dyn Any,
1453    ) {
1454        ui.vertical(|ui| {
1455            let len = array.len();
1456            for i in 0..len {
1457                let val = array.get(i).unwrap();
1458                ui.horizontal_top(|ui| {
1459                    self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options);
1460                });
1461
1462                if i != len - 1 {
1463                    ui.separator();
1464                }
1465            }
1466        });
1467    }
1468
1469    fn ui_for_enum(
1470        &mut self,
1471        value: &mut dyn Enum,
1472        ui: &mut egui::Ui,
1473        id: egui::Id,
1474        options: &dyn Any,
1475    ) -> bool {
1476        let Some(type_info) = value.get_represented_type_info() else {
1477            ui.label("Unrepresentable");
1478            return false;
1479        };
1480        let type_info = match type_info {
1481            TypeInfo::Enum(info) => info,
1482            _ => unreachable!("invalid reflect impl: type info mismatch"),
1483        };
1484
1485        let mut changed = false;
1486
1487        ui.vertical(|ui| {
1488            let changed_variant =
1489                self.ui_for_enum_variant_select(id, ui, value.variant_index(), type_info);
1490            if let Some((_new_variant, dynamic_enum)) = changed_variant {
1491                changed = true;
1492                value.apply(&dynamic_enum);
1493            }
1494            let variant_index = value.variant_index();
1495
1496            let always_show_label = matches!(value.variant_type(), VariantType::Struct);
1497            changed |=
1498                maybe_grid_label_if(value.field_len(), ui, id, always_show_label, |ui, label| {
1499                    (0..value.field_len())
1500                        .map(|i| {
1501                            if label {
1502                                #[cfg(feature = "documentation")]
1503                                let field_docs = type_info.variant_at(variant_index).and_then(
1504                                    |info| match info {
1505                                        VariantInfo::Struct(info) => info.field_at(i)?.docs(),
1506                                        _ => None,
1507                                    },
1508                                );
1509
1510                                let _response = if let Some(name) = value.name_at(i) {
1511                                    ui.label(name)
1512                                } else {
1513                                    ui.label(i.to_string())
1514                                };
1515                                #[cfg(feature = "documentation")]
1516                                show_docs(_response, field_docs);
1517                            }
1518                            let field_value = value
1519                                .field_at_mut(i)
1520                                .expect("invalid reflect impl: field len");
1521                            let changed = self.ui_for_reflect_with_options(
1522                                field_value,
1523                                ui,
1524                                id.with(i),
1525                                inspector_options_enum_variant_field(options, variant_index, i),
1526                            );
1527                            ui.end_row();
1528                            changed
1529                        })
1530                        .fold(false, or)
1531                });
1532        });
1533
1534        changed
1535    }
1536
1537    fn ui_for_enum_many(
1538        &mut self,
1539        info: &EnumInfo,
1540        ui: &mut egui::Ui,
1541        id: egui::Id,
1542        options: &dyn Any,
1543        values: &mut [&mut dyn PartialReflect],
1544        projector: &dyn ProjectorReflect,
1545    ) -> bool {
1546        let mut changed = false;
1547
1548        let same_variant =
1549            iter_all_eq(
1550                values
1551                    .iter_mut()
1552                    .map(|value| match projector(*value).reflect_mut() {
1553                        ReflectMut::Enum(info) => info.variant_index(),
1554                        _ => unreachable!(),
1555                    }),
1556            );
1557
1558        if let Some(variant_index) = same_variant {
1559            let mut variant = info.variant_at(variant_index).unwrap();
1560
1561            ui.vertical(|ui| {
1562                let variant_changed = self.ui_for_enum_variant_select(id, ui, variant_index, info);
1563                if let Some((new_variant_idx, dynamic_enum)) = variant_changed {
1564                    changed = true;
1565                    variant = info.variant_at(new_variant_idx).unwrap();
1566
1567                    for value in values.iter_mut() {
1568                        let value = projector(*value);
1569                        value.apply(&dynamic_enum);
1570                    }
1571                }
1572
1573                let field_len = match variant {
1574                    VariantInfo::Struct(info) => info.field_len(),
1575                    VariantInfo::Tuple(info) => info.field_len(),
1576                    VariantInfo::Unit(_) => 0,
1577                };
1578
1579                let always_show_label = matches!(variant, VariantInfo::Struct(_));
1580                changed |=
1581                    maybe_grid_label_if(field_len, ui, id, always_show_label, |ui, label| {
1582                        let handle = |(field_index, field_name, field_type_id, field_type_name)| {
1583                            if label {
1584                                ui.label(field_name);
1585                            }
1586
1587                            let mut variants_across: Vec<&mut dyn PartialReflect> = values
1588                                .iter_mut()
1589                                .map(|value| match projector(*value).reflect_mut() {
1590                                    ReflectMut::Enum(value) => {
1591                                        value.field_at_mut(field_index).unwrap()
1592                                    }
1593                                    _ => unreachable!(),
1594                                })
1595                                .collect();
1596
1597                            self.ui_for_reflect_many_with_options(
1598                                field_type_id,
1599                                field_type_name,
1600                                ui,
1601                                id.with(field_index),
1602                                inspector_options_enum_variant_field(
1603                                    options,
1604                                    variant_index,
1605                                    field_index,
1606                                ),
1607                                variants_across.as_mut_slice(),
1608                                &|a| a,
1609                            );
1610
1611                            ui.end_row();
1612
1613                            false
1614                        };
1615
1616                        match variant {
1617                            VariantInfo::Struct(info) => info
1618                                .iter()
1619                                .enumerate()
1620                                .map(|(i, field)| {
1621                                    (
1622                                        i,
1623                                        Cow::Borrowed(field.name()),
1624                                        field.type_id(),
1625                                        field.type_path(),
1626                                    )
1627                                })
1628                                .map(handle)
1629                                .fold(false, or),
1630                            VariantInfo::Tuple(info) => info
1631                                .iter()
1632                                .enumerate()
1633                                .map(|(i, field)| {
1634                                    (
1635                                        i,
1636                                        Cow::Owned(i.to_string()),
1637                                        field.type_id(),
1638                                        field.type_path(),
1639                                    )
1640                                })
1641                                .map(handle)
1642                                .fold(false, or),
1643                            VariantInfo::Unit(_) => false,
1644                        }
1645                    });
1646            });
1647        } else {
1648            ui.label("enums have different selected variants, cannot multiedit");
1649        }
1650
1651        changed
1652    }
1653
1654    fn ui_for_enum_variant_select(
1655        &mut self,
1656        id: egui::Id,
1657        ui: &mut egui::Ui,
1658        active_variant_idx: usize,
1659        info: &bevy_reflect::EnumInfo,
1660    ) -> Option<(usize, DynamicEnum)> {
1661        let mut changed_variant = None;
1662
1663        ui.horizontal_top(|ui| {
1664            egui::ComboBox::new(id.with("select"), "")
1665                .selected_text(info.variant_names()[active_variant_idx])
1666                .show_ui(ui, |ui| {
1667                    for (i, variant) in info.iter().enumerate() {
1668                        let variant_name = variant.name();
1669                        let is_active_variant = i == active_variant_idx;
1670
1671                        let variant_is_constructable =
1672                            variant_constructable(self.type_registry, variant);
1673
1674                        ui.add_enabled_ui(variant_is_constructable.is_ok(), |ui| {
1675                            let mut variant_label_response =
1676                                ui.selectable_label(is_active_variant, variant_name);
1677
1678                            if let Err(fields) = variant_is_constructable {
1679                                variant_label_response = variant_label_response
1680                                    .on_disabled_hover_ui(|ui| {
1681                                        errors::unconstructable_variant(
1682                                            ui,
1683                                            info.type_path(),
1684                                            variant_name,
1685                                            &fields,
1686                                        );
1687                                    });
1688                            }
1689
1690                            /*let res = variant_label_response.on_hover_ui(|ui| {
1691                                if !unconstructable_variants.is_empty() {
1692                                    errors::unconstructable_variants(
1693                                        ui,
1694                                        info.type_name(),
1695                                        &unconstructable_variants,
1696                                    );
1697                                }
1698                            });*/
1699
1700                            if variant_label_response.clicked()
1701                                && let Ok(dynamic_enum) =
1702                                    self.construct_default_variant(variant, ui)
1703                            {
1704                                changed_variant = Some((i, dynamic_enum));
1705                            };
1706                        });
1707                    }
1708
1709                    false
1710                });
1711        });
1712
1713        changed_variant
1714    }
1715
1716    fn ui_for_enum_readonly(
1717        &mut self,
1718        value: &dyn Enum,
1719        ui: &mut egui::Ui,
1720        id: egui::Id,
1721        options: &dyn Any,
1722    ) {
1723        ui.vertical(|ui| {
1724            let active_variant = value.variant_name();
1725            ui.add_enabled_ui(false, |ui| {
1726                egui::ComboBox::new(id, "")
1727                    .selected_text(active_variant)
1728                    .show_ui(ui, |_| {})
1729            });
1730
1731            let always_show_label = matches!(value.variant_type(), VariantType::Struct);
1732            maybe_grid_readonly_label_if(
1733                value.field_len(),
1734                ui,
1735                id,
1736                always_show_label,
1737                |ui, label| {
1738                    for i in 0..value.field_len() {
1739                        if label {
1740                            if let Some(name) = value.name_at(i) {
1741                                ui.label(name);
1742                            } else {
1743                                ui.label(i.to_string());
1744                            }
1745                        }
1746                        let field_value =
1747                            value.field_at(i).expect("invalid reflect impl: field len");
1748                        self.ui_for_reflect_readonly_with_options(
1749                            field_value,
1750                            ui,
1751                            id.with(i),
1752                            inspector_options_enum_variant_field(options, value.variant_index(), i),
1753                        );
1754                        ui.end_row();
1755                    }
1756                },
1757            );
1758        });
1759    }
1760}
1761
1762impl<'a, 'c> InspectorUi<'a, 'c> {
1763    pub fn reborrow<'s>(&'s mut self) -> InspectorUi<'s, 'c> {
1764        InspectorUi {
1765            type_registry: self.type_registry,
1766            context: self.context,
1767            short_circuit: self.short_circuit,
1768            short_circuit_readonly: self.short_circuit_readonly,
1769            short_circuit_many: self.short_circuit_many,
1770        }
1771    }
1772
1773    fn get_reflect_default(&self, type_id: TypeId) -> Option<&ReflectDefault> {
1774        self.type_registry.get_type_data::<ReflectDefault>(type_id)
1775    }
1776
1777    fn get_default_value_for(&mut self, type_id: TypeId) -> Option<Box<dyn Reflect>> {
1778        if let Some(reflect_default) = self.type_registry.get_type_data::<ReflectDefault>(type_id) {
1779            return Some(reflect_default.default());
1780        }
1781
1782        None
1783    }
1784
1785    fn construct_default_variant(
1786        &mut self,
1787        variant: &VariantInfo,
1788        ui: &mut egui::Ui,
1789    ) -> Result<DynamicEnum, ()> {
1790        let dynamic_variant = match variant {
1791            VariantInfo::Struct(struct_info) => {
1792                let mut dynamic_struct = DynamicStruct::default();
1793                for field in struct_info.iter() {
1794                    let field_default_value = match self.get_default_value_for(field.type_id()) {
1795                        Some(value) => value,
1796                        None => {
1797                            errors::no_default_value(ui, field.type_path());
1798                            return Err(());
1799                        }
1800                    };
1801                    dynamic_struct.insert_boxed(field.name(), field_default_value.to_dynamic());
1802                }
1803                DynamicVariant::Struct(dynamic_struct)
1804            }
1805            VariantInfo::Tuple(tuple_info) => {
1806                let mut dynamic_tuple = DynamicTuple::default();
1807                for field in tuple_info.iter() {
1808                    let field_default_value = match self.get_default_value_for(field.type_id()) {
1809                        Some(value) => value,
1810                        None => {
1811                            errors::no_default_value(ui, field.type_path());
1812                            return Err(());
1813                        }
1814                    };
1815                    dynamic_tuple.insert_boxed(field_default_value.to_dynamic());
1816                }
1817                DynamicVariant::Tuple(dynamic_tuple)
1818            }
1819            VariantInfo::Unit(_) => DynamicVariant::Unit,
1820        };
1821        let dynamic_enum = DynamicEnum::new(variant.name(), dynamic_variant);
1822        Ok(dynamic_enum)
1823    }
1824}
1825
1826#[must_use]
1827fn maybe_grid(
1828    i: usize,
1829    ui: &mut egui::Ui,
1830    id: egui::Id,
1831    mut f: impl FnMut(&mut egui::Ui, bool) -> bool,
1832) -> bool {
1833    match i {
1834        0 => false,
1835        1 => f(ui, false),
1836        _ => Grid::new(id).show(ui, |ui| f(ui, true)).inner,
1837    }
1838}
1839#[must_use]
1840fn maybe_grid_label_if(
1841    i: usize,
1842    ui: &mut egui::Ui,
1843    id: egui::Id,
1844    always_show_label: bool,
1845    mut f: impl FnMut(&mut egui::Ui, bool) -> bool,
1846) -> bool {
1847    match i {
1848        0 => false,
1849        1 if !always_show_label => f(ui, false),
1850        _ => Grid::new(id).show(ui, |ui| f(ui, true)).inner,
1851    }
1852}
1853
1854fn maybe_grid_readonly(
1855    i: usize,
1856    ui: &mut egui::Ui,
1857    id: egui::Id,
1858    mut f: impl FnMut(&mut egui::Ui, bool),
1859) {
1860    match i {
1861        0 => {}
1862        1 => f(ui, false),
1863        _ => {
1864            Grid::new(id).show(ui, |ui| f(ui, true));
1865        }
1866    }
1867}
1868fn maybe_grid_readonly_label_if(
1869    i: usize,
1870    ui: &mut egui::Ui,
1871    id: egui::Id,
1872    always_show_label: bool,
1873    mut f: impl FnMut(&mut egui::Ui, bool),
1874) {
1875    match i {
1876        0 => {}
1877        1 if !always_show_label => f(ui, false),
1878        _ => {
1879            Grid::new(id).show(ui, |ui| f(ui, true));
1880        }
1881    }
1882}
1883
1884fn variant_constructable<'a>(
1885    type_registry: &TypeRegistry,
1886    variant: &'a VariantInfo,
1887) -> Result<(), Vec<&'a str>> {
1888    let type_id_is_constructable = |type_id: TypeId| {
1889        type_registry
1890            .get_type_data::<ReflectDefault>(type_id)
1891            .is_some()
1892    };
1893
1894    let unconstructable_fields: Vec<&'a str> = match variant {
1895        VariantInfo::Struct(variant) => variant
1896            .iter()
1897            .filter_map(|field| {
1898                (!type_id_is_constructable(field.type_id())).then_some(field.type_path())
1899            })
1900            .collect(),
1901        VariantInfo::Tuple(variant) => variant
1902            .iter()
1903            .filter_map(|field| {
1904                (!type_id_is_constructable(field.type_id())).then_some(field.type_path())
1905            })
1906            .collect(),
1907        VariantInfo::Unit(_) => return Ok(()),
1908    };
1909
1910    if unconstructable_fields.is_empty() {
1911        Ok(())
1912    } else {
1913        Err(unconstructable_fields)
1914    }
1915}
1916
1917fn inspector_options_struct_field(options: &dyn Any, field: usize) -> &dyn Any {
1918    options
1919        .downcast_ref::<InspectorOptions>()
1920        .and_then(|options| options.get(Target::Field(field)))
1921        .unwrap_or(&())
1922}
1923
1924fn inspector_options_enum_variant_field<'a>(
1925    options: &'a dyn Any,
1926    variant_index: usize,
1927    field_index: usize,
1928) -> &'a dyn Any {
1929    options
1930        .downcast_ref::<InspectorOptions>()
1931        .and_then(|options| {
1932            options.get(Target::VariantField {
1933                variant_index,
1934                field_index,
1935            })
1936        })
1937        .unwrap_or(&())
1938}
1939
1940fn or(a: bool, b: bool) -> bool {
1941    a || b
1942}
1943
1944fn get_type_data<'a>(
1945    type_registry: &'a TypeRegistry,
1946    type_id: &dyn DynamicTyped,
1947) -> Result<&'a InspectorEguiImpl, TypeDataError> {
1948    let registration = type_registry
1949        .get(type_id.reflect_type_info().type_id())
1950        .ok_or(TypeDataError::NotRegistered)?;
1951    let data = registration
1952        .data::<InspectorEguiImpl>()
1953        .ok_or(TypeDataError::NoTypeData)?;
1954    Ok(data)
1955}