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
1018        egui::Grid::new(id).show(ui, |ui| {
1019            let mut i = 0;
1020            map.retain(&mut |key, value| {
1021                let ui_id = id.with(i);
1022                i += 1;
1023
1024                self.ui_for_reflect_readonly_with_options(key, ui, ui_id, &());
1025                changed |= self.ui_for_reflect_with_options(value, ui, ui_id, &());
1026                let delete = remove_button(ui).on_hover_text("Remove element").clicked();
1027                ui.end_row();
1028                !delete
1029            });
1030
1031            self.map_add_element_ui(map, ui, id, &mut changed);
1032        });
1033
1034        changed
1035    }
1036
1037    fn map_add_element_ui(
1038        &mut self,
1039        map: &mut (dyn Map + 'static),
1040        ui: &mut egui::Ui,
1041        id: egui::Id,
1042        changed: &mut bool,
1043    ) -> Option<()> {
1044        let map_draft_id = id.with("map_draft");
1045        let draft_clone = ui.data_mut(|data| {
1046            data.get_temp_mut_or_default::<Option<MapDraftElement>>(map_draft_id)
1047                .to_owned()
1048        });
1049
1050        let map_info = map.get_represented_map_info()?;
1051
1052        let key_default = self.get_reflect_default(map_info.key_ty().id())?;
1053        let value_default = self.get_reflect_default(map_info.value_ty().id())?;
1054
1055        ui.separator();
1056        ui.end_row();
1057        ui.label("New element");
1058        match draft_clone {
1059            None => {
1060                // If no draft element exists, show a button to create one.
1061                if add_button(ui).clicked() {
1062                    // Insert a temporary 'draft' key-value pair into UI state.
1063                    let key = key_default.default().into_partial_reflect();
1064                    let value = value_default.default().into_partial_reflect();
1065                    ui.data_mut(|data| {
1066                        data.insert_temp(map_draft_id, Some(MapDraftElement { key, value }))
1067                    });
1068                }
1069                ui.end_row();
1070            }
1071            Some(MapDraftElement { mut key, mut value }) => {
1072                ui.end_row();
1073                // Show controls for editing our draft element.
1074                let key_changed = self.ui_for_reflect_with_options(key.as_mut(), ui, id, &());
1075                let value_changed = self.ui_for_reflect_with_options(value.as_mut(), ui, id, &());
1076
1077                // If the clone changed, update the data in UI state.
1078                if key_changed || value_changed {
1079                    let next_draft = MapDraftElement { key, value };
1080                    ui.data_mut(|data| data.insert_temp(map_draft_id, Some(next_draft)));
1081                }
1082
1083                // Show controls to insert the draft into the map, or remove it.
1084                if ui.button("Insert").clicked() {
1085                    let draft = ui
1086                        .data_mut(|data| data.get_temp::<Option<MapDraftElement>>(map_draft_id))
1087                        .flatten();
1088                    if let Some(draft) = draft {
1089                        map.insert_boxed(draft.key, draft.value);
1090                        ui.data_mut(|data| data.remove_by_type::<Option<MapDraftElement>>());
1091                    }
1092                    *changed = true;
1093                }
1094
1095                if ui.button("Cancel").clicked() {
1096                    ui.data_mut(|data| data.remove_by_type::<Option<MapDraftElement>>());
1097                    *changed = true;
1098                }
1099                ui.end_row();
1100            }
1101        }
1102
1103        Some(())
1104    }
1105
1106    fn ui_for_reflect_map_readonly(
1107        &mut self,
1108        map: &dyn Map,
1109        ui: &mut egui::Ui,
1110        id: egui::Id,
1111        _options: &dyn Any,
1112    ) {
1113        egui::Grid::new(id).show(ui, |ui| {
1114            for (i, (key, value)) in map.iter().enumerate() {
1115                let ui_id = id.with(i);
1116                self.ui_for_reflect_readonly_with_options(key, ui, ui_id, &());
1117                self.ui_for_reflect_readonly_with_options(value, ui, ui_id, &());
1118                ui.end_row();
1119            }
1120        });
1121    }
1122
1123    /// Mutate one or more lists based on a [`SetOp`], generated by some user interaction.
1124    fn respond_to_sets_op<'a>(
1125        &mut self,
1126        sets: impl Iterator<Item = &'a mut dyn Set>,
1127        op: SetOp,
1128    ) -> bool {
1129        let mut changed = false;
1130
1131        for set in sets {
1132            changed |= self.respond_to_set_op(set, &op);
1133        }
1134        changed
1135    }
1136    fn respond_to_set_op<'a>(&mut self, set: &'a mut dyn Set, op: &SetOp) -> bool {
1137        use SetOp::*;
1138        match &op {
1139            AddElement(new_value) => {
1140                set.insert_boxed(new_value.to_dynamic());
1141            }
1142            RemoveElement(val) => {
1143                set.remove(&**val);
1144            }
1145        }
1146        true
1147    }
1148
1149    fn ui_for_set(
1150        &mut self,
1151        set: &mut dyn Set,
1152        ui: &mut egui::Ui,
1153        id: egui::Id,
1154        options: &dyn Any,
1155    ) -> bool {
1156        use SetOp::*;
1157        let mut changed = false;
1158
1159        ui.vertical(|ui| {
1160            let mut op = None;
1161
1162            let len = set.len();
1163            if len == 0 {
1164                ui_for_empty_set(ui);
1165            }
1166
1167            for (i, val) in set.iter().enumerate() {
1168                egui::Grid::new((id, i)).show(ui, |ui| {
1169                    ui.horizontal_top(|ui| {
1170                        self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options);
1171                    });
1172                    ui.horizontal_top(|ui| {
1173                        if remove_button(ui).on_hover_text("Remove element").clicked() {
1174                            let copy = val.to_dynamic();
1175                            op = Some(RemoveElement(copy));
1176                        }
1177                    });
1178                    ui.end_row();
1179                });
1180
1181                if i != len - 1 {
1182                    ui.separator();
1183                }
1184            }
1185            let Some(TypeInfo::Set(set_info)) = set.get_represented_type_info() else {
1186                return;
1187            };
1188            let value_type = set_info.value_ty();
1189            let new_op = self.set_add_element_ui(value_type, ui, id, options, &mut changed);
1190            if new_op.is_some() {
1191                op = new_op;
1192            }
1193
1194            ui.end_row();
1195
1196            let error_id = id.with("error");
1197
1198            // Respond to control interaction
1199            if let Some(op) = op {
1200                changed |= self.respond_to_set_op(set, &op);
1201            }
1202
1203            let error = ui.data_mut(|data| *data.get_temp_mut_or_default::<bool>(error_id));
1204            if error {
1205                errors::no_default_value(ui, set_info.type_path());
1206            }
1207            if ui.input(|input| input.pointer.any_down()) {
1208                ui.data_mut(|data| data.insert_temp::<bool>(error_id, false));
1209            }
1210        });
1211
1212        changed
1213    }
1214
1215    #[must_use]
1216    fn set_add_element_ui(
1217        &mut self,
1218        value_type: bevy_reflect::Type,
1219        ui: &mut egui::Ui,
1220        id: egui::Id,
1221        options: &dyn Any,
1222        changed: &mut bool,
1223    ) -> Option<SetOp> {
1224        let mut op = None;
1225
1226        let item_default = self.get_reflect_default(value_type.id())?.clone();
1227
1228        ui.vertical(|ui| {
1229            ui.label("New element");
1230            let set_draft_id = id.with("set_draft");
1231            let draft_clone = ui.data_mut(|data| {
1232                data.get_temp_mut_or_default::<Option<SetDraftElement>>(set_draft_id)
1233                    .to_owned()
1234            });
1235            ui.end_row();
1236            match draft_clone {
1237                None => {
1238                    // If no draft element exists, show a button to create one.
1239                    if add_button(ui).clicked() {
1240                        // Insert a temporary 'draft' value into UI state, once inserted, we cannot modify it.
1241                        let draft = SetDraftElement(item_default.default().into_partial_reflect());
1242                        ui.data_mut(|data| data.insert_temp(set_draft_id, Some(draft)));
1243                    }
1244
1245                    ui.end_row();
1246                }
1247                Some(SetDraftElement(mut v)) => {
1248                    ui.end_row();
1249                    // Show controls for editing our draft element.
1250                    // FIXME: is the id passed here correct?
1251                    let value_changed =
1252                        self.ui_for_reflect_with_options(v.as_mut(), ui, id, options);
1253
1254                    // If the clone changed, update the data in UI state.
1255                    if value_changed {
1256                        let next_draft = SetDraftElement(v);
1257                        ui.data_mut(|data| data.insert_temp(set_draft_id, Some(next_draft)));
1258                    }
1259
1260                    // Show controls to insert the draft into the set, or remove it.
1261                    if ui.button("Insert").clicked() {
1262                        let draft = ui
1263                            .data_mut(|data| data.get_temp::<Option<SetDraftElement>>(set_draft_id))
1264                            .flatten();
1265                        if let Some(draft) = draft {
1266                            op = Some(SetOp::AddElement(draft.0));
1267                            ui.data_mut(|data| data.remove_by_type::<Option<SetDraftElement>>());
1268                        }
1269                        *changed = true;
1270                    }
1271
1272                    if ui.button("Cancel").clicked() {
1273                        ui.data_mut(|data| data.remove_by_type::<Option<SetDraftElement>>());
1274                        *changed = true;
1275                    }
1276                    ui.end_row();
1277                }
1278            }
1279        });
1280
1281        op
1282    }
1283
1284    fn ui_for_set_readonly(
1285        &mut self,
1286        set: &dyn Set,
1287        ui: &mut egui::Ui,
1288        id: egui::Id,
1289        options: &dyn Any,
1290    ) {
1291        let len = set.len();
1292        ui.vertical(|ui| {
1293            for (i, val) in set.iter().enumerate() {
1294                ui.horizontal_top(|ui| {
1295                    self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options)
1296                });
1297
1298                if i != len - 1 {
1299                    ui.separator();
1300                }
1301            }
1302        });
1303    }
1304
1305    fn ui_for_set_many(
1306        &mut self,
1307        info: &SetInfo,
1308        ui: &mut egui::Ui,
1309        id: egui::Id,
1310        options: &dyn Any,
1311        values: &mut [&mut dyn PartialReflect],
1312        projector: impl ProjectorReflect,
1313    ) -> bool {
1314        use SetOp::*;
1315        let mut changed = false;
1316
1317        let same_len =
1318            iter_all_eq(
1319                values
1320                    .iter_mut()
1321                    .map(|value| match projector(*value).reflect_mut() {
1322                        ReflectMut::List(l) => l.len(),
1323                        _ => unreachable!(),
1324                    }),
1325            );
1326
1327        let Some(len) = same_len else {
1328            ui.label("lists have different sizes, cannot multiedit");
1329            return changed;
1330        };
1331
1332        ui.vertical(|ui| {
1333            let mut op = None;
1334
1335            if len == 0 {
1336                ui_for_empty_set(ui)
1337            }
1338
1339            let set0 = match projector(values[0]).reflect_mut() {
1340                ReflectMut::Set(set) => set,
1341                _ => unreachable!(),
1342            };
1343            let Some(TypeInfo::Set(set_info)) = set0.get_represented_type_info() else {
1344                return;
1345            };
1346            let value_type = set_info.value_ty();
1347            let reflected_values: Vec<Box<dyn PartialReflect>> =
1348                set0.iter().map(|v| v.to_dynamic()).collect();
1349
1350            for (i, value_to_check) in reflected_values.iter().enumerate() {
1351                let value_type_id = (**value_to_check).type_id();
1352                egui::Grid::new((value_type_id, i)).show(ui, |ui| {
1353                    // Do all sets contain this value ?
1354                    if len == 1
1355                        || values[1..].iter_mut().all(|set_to_compare| {
1356                            let set_to_compare = match projector(*set_to_compare).reflect_mut() {
1357                                ReflectMut::Set(set) => set,
1358                                _ => unreachable!(),
1359                            };
1360                            set_to_compare.iter().any(|value| {
1361                                value.reflect_partial_eq(value_to_check.borrow()) == Some(true)
1362                            })
1363                        })
1364                    {
1365                        // All sets contain this value: Show value
1366                        ui.horizontal_top(|ui| {
1367                            self.ui_for_reflect_readonly_with_options(
1368                                value_to_check.borrow(),
1369                                ui,
1370                                // FIXME: is the id passed here correct?
1371                                id.with(i),
1372                                options,
1373                            );
1374                        });
1375                        ui.horizontal_top(|ui| {
1376                            if remove_button(ui).on_hover_text("Remove element").clicked() {
1377                                let copy = value_to_check.to_dynamic();
1378                                op = Some(RemoveElement(copy));
1379                            }
1380                        });
1381                    } else {
1382                        ui.label("Different values");
1383                    }
1384
1385                    ui.end_row();
1386                });
1387                if i != len - 1 {
1388                    ui.separator();
1389                }
1390            }
1391            let op = self.set_add_element_ui(value_type, ui, id, options, &mut changed);
1392
1393            ui.end_row();
1394
1395            let error_id = id.with("error");
1396            let error = ui.data_mut(|data| *data.get_temp_mut_or_default::<bool>(error_id));
1397            if error {
1398                errors::no_default_value(ui, info.type_path());
1399            }
1400            if ui.input(|input| input.pointer.any_down()) {
1401                ui.data_mut(|data| data.insert_temp::<bool>(error_id, false));
1402            }
1403            if let Some(op) = op {
1404                let sets = values
1405                    .iter_mut()
1406                    .map(|l| match projector(*l).reflect_mut() {
1407                        ReflectMut::Set(list) => list,
1408                        _ => unreachable!(),
1409                    });
1410                changed |= self.respond_to_sets_op(sets, op);
1411            }
1412        });
1413
1414        changed
1415    }
1416
1417    fn ui_for_array(
1418        &mut self,
1419        array: &mut dyn Array,
1420        ui: &mut egui::Ui,
1421        id: egui::Id,
1422        options: &dyn Any,
1423    ) -> bool {
1424        let mut changed = false;
1425
1426        ui.vertical(|ui| {
1427            let len = array.len();
1428            for i in 0..len {
1429                let val = array.get_mut(i).unwrap();
1430                ui.horizontal_top(|ui| {
1431                    changed |= self.ui_for_reflect_with_options(val, ui, id.with(i), options);
1432                });
1433
1434                if i != len - 1 {
1435                    ui.separator();
1436                }
1437            }
1438        });
1439
1440        changed
1441    }
1442
1443    fn ui_for_array_readonly(
1444        &mut self,
1445        array: &dyn Array,
1446        ui: &mut egui::Ui,
1447        id: egui::Id,
1448        options: &dyn Any,
1449    ) {
1450        ui.vertical(|ui| {
1451            let len = array.len();
1452            for i in 0..len {
1453                let val = array.get(i).unwrap();
1454                ui.horizontal_top(|ui| {
1455                    self.ui_for_reflect_readonly_with_options(val, ui, id.with(i), options);
1456                });
1457
1458                if i != len - 1 {
1459                    ui.separator();
1460                }
1461            }
1462        });
1463    }
1464
1465    fn ui_for_enum(
1466        &mut self,
1467        value: &mut dyn Enum,
1468        ui: &mut egui::Ui,
1469        id: egui::Id,
1470        options: &dyn Any,
1471    ) -> bool {
1472        let Some(type_info) = value.get_represented_type_info() else {
1473            ui.label("Unrepresentable");
1474            return false;
1475        };
1476        let type_info = match type_info {
1477            TypeInfo::Enum(info) => info,
1478            _ => unreachable!("invalid reflect impl: type info mismatch"),
1479        };
1480
1481        let mut changed = false;
1482
1483        ui.vertical(|ui| {
1484            let changed_variant =
1485                self.ui_for_enum_variant_select(id, ui, value.variant_index(), type_info);
1486            if let Some((_new_variant, dynamic_enum)) = changed_variant {
1487                changed = true;
1488                value.apply(&dynamic_enum);
1489            }
1490            let variant_index = value.variant_index();
1491
1492            let always_show_label = matches!(value.variant_type(), VariantType::Struct);
1493            changed |=
1494                maybe_grid_label_if(value.field_len(), ui, id, always_show_label, |ui, label| {
1495                    (0..value.field_len())
1496                        .map(|i| {
1497                            if label {
1498                                #[cfg(feature = "documentation")]
1499                                let field_docs = type_info.variant_at(variant_index).and_then(
1500                                    |info| match info {
1501                                        VariantInfo::Struct(info) => info.field_at(i)?.docs(),
1502                                        _ => None,
1503                                    },
1504                                );
1505
1506                                let _response = if let Some(name) = value.name_at(i) {
1507                                    ui.label(name)
1508                                } else {
1509                                    ui.label(i.to_string())
1510                                };
1511                                #[cfg(feature = "documentation")]
1512                                show_docs(_response, field_docs);
1513                            }
1514                            let field_value = value
1515                                .field_at_mut(i)
1516                                .expect("invalid reflect impl: field len");
1517                            let changed = self.ui_for_reflect_with_options(
1518                                field_value,
1519                                ui,
1520                                id.with(i),
1521                                inspector_options_enum_variant_field(options, variant_index, i),
1522                            );
1523                            ui.end_row();
1524                            changed
1525                        })
1526                        .fold(false, or)
1527                });
1528        });
1529
1530        changed
1531    }
1532
1533    fn ui_for_enum_many(
1534        &mut self,
1535        info: &EnumInfo,
1536        ui: &mut egui::Ui,
1537        id: egui::Id,
1538        options: &dyn Any,
1539        values: &mut [&mut dyn PartialReflect],
1540        projector: &dyn ProjectorReflect,
1541    ) -> bool {
1542        let mut changed = false;
1543
1544        let same_variant =
1545            iter_all_eq(
1546                values
1547                    .iter_mut()
1548                    .map(|value| match projector(*value).reflect_mut() {
1549                        ReflectMut::Enum(info) => info.variant_index(),
1550                        _ => unreachable!(),
1551                    }),
1552            );
1553
1554        if let Some(variant_index) = same_variant {
1555            let mut variant = info.variant_at(variant_index).unwrap();
1556
1557            ui.vertical(|ui| {
1558                let variant_changed = self.ui_for_enum_variant_select(id, ui, variant_index, info);
1559                if let Some((new_variant_idx, dynamic_enum)) = variant_changed {
1560                    changed = true;
1561                    variant = info.variant_at(new_variant_idx).unwrap();
1562
1563                    for value in values.iter_mut() {
1564                        let value = projector(*value);
1565                        value.apply(&dynamic_enum);
1566                    }
1567                }
1568
1569                let field_len = match variant {
1570                    VariantInfo::Struct(info) => info.field_len(),
1571                    VariantInfo::Tuple(info) => info.field_len(),
1572                    VariantInfo::Unit(_) => 0,
1573                };
1574
1575                let always_show_label = matches!(variant, VariantInfo::Struct(_));
1576                changed |=
1577                    maybe_grid_label_if(field_len, ui, id, always_show_label, |ui, label| {
1578                        let handle = |(field_index, field_name, field_type_id, field_type_name)| {
1579                            if label {
1580                                ui.label(field_name);
1581                            }
1582
1583                            let mut variants_across: Vec<&mut dyn PartialReflect> = values
1584                                .iter_mut()
1585                                .map(|value| match projector(*value).reflect_mut() {
1586                                    ReflectMut::Enum(value) => {
1587                                        value.field_at_mut(field_index).unwrap()
1588                                    }
1589                                    _ => unreachable!(),
1590                                })
1591                                .collect();
1592
1593                            self.ui_for_reflect_many_with_options(
1594                                field_type_id,
1595                                field_type_name,
1596                                ui,
1597                                id.with(field_index),
1598                                inspector_options_enum_variant_field(
1599                                    options,
1600                                    variant_index,
1601                                    field_index,
1602                                ),
1603                                variants_across.as_mut_slice(),
1604                                &|a| a,
1605                            );
1606
1607                            ui.end_row();
1608
1609                            false
1610                        };
1611
1612                        match variant {
1613                            VariantInfo::Struct(info) => info
1614                                .iter()
1615                                .enumerate()
1616                                .map(|(i, field)| {
1617                                    (
1618                                        i,
1619                                        Cow::Borrowed(field.name()),
1620                                        field.type_id(),
1621                                        field.type_path(),
1622                                    )
1623                                })
1624                                .map(handle)
1625                                .fold(false, or),
1626                            VariantInfo::Tuple(info) => info
1627                                .iter()
1628                                .enumerate()
1629                                .map(|(i, field)| {
1630                                    (
1631                                        i,
1632                                        Cow::Owned(i.to_string()),
1633                                        field.type_id(),
1634                                        field.type_path(),
1635                                    )
1636                                })
1637                                .map(handle)
1638                                .fold(false, or),
1639                            VariantInfo::Unit(_) => false,
1640                        }
1641                    });
1642            });
1643        } else {
1644            ui.label("enums have different selected variants, cannot multiedit");
1645        }
1646
1647        changed
1648    }
1649
1650    fn ui_for_enum_variant_select(
1651        &mut self,
1652        id: egui::Id,
1653        ui: &mut egui::Ui,
1654        active_variant_idx: usize,
1655        info: &bevy_reflect::EnumInfo,
1656    ) -> Option<(usize, DynamicEnum)> {
1657        let mut changed_variant = None;
1658
1659        ui.horizontal_top(|ui| {
1660            egui::ComboBox::new(id.with("select"), "")
1661                .selected_text(info.variant_names()[active_variant_idx])
1662                .show_ui(ui, |ui| {
1663                    for (i, variant) in info.iter().enumerate() {
1664                        let variant_name = variant.name();
1665                        let is_active_variant = i == active_variant_idx;
1666
1667                        let variant_is_constructable =
1668                            variant_constructable(self.type_registry, variant);
1669
1670                        ui.add_enabled_ui(variant_is_constructable.is_ok(), |ui| {
1671                            let mut variant_label_response =
1672                                ui.selectable_label(is_active_variant, variant_name);
1673
1674                            if let Err(fields) = variant_is_constructable {
1675                                variant_label_response = variant_label_response
1676                                    .on_disabled_hover_ui(|ui| {
1677                                        errors::unconstructable_variant(
1678                                            ui,
1679                                            info.type_path(),
1680                                            variant_name,
1681                                            &fields,
1682                                        );
1683                                    });
1684                            }
1685
1686                            /*let res = variant_label_response.on_hover_ui(|ui| {
1687                                if !unconstructable_variants.is_empty() {
1688                                    errors::unconstructable_variants(
1689                                        ui,
1690                                        info.type_name(),
1691                                        &unconstructable_variants,
1692                                    );
1693                                }
1694                            });*/
1695
1696                            if variant_label_response.clicked()
1697                                && let Ok(dynamic_enum) =
1698                                    self.construct_default_variant(variant, ui)
1699                            {
1700                                changed_variant = Some((i, dynamic_enum));
1701                            };
1702                        });
1703                    }
1704
1705                    false
1706                });
1707        });
1708
1709        changed_variant
1710    }
1711
1712    fn ui_for_enum_readonly(
1713        &mut self,
1714        value: &dyn Enum,
1715        ui: &mut egui::Ui,
1716        id: egui::Id,
1717        options: &dyn Any,
1718    ) {
1719        ui.vertical(|ui| {
1720            let active_variant = value.variant_name();
1721            ui.add_enabled_ui(false, |ui| {
1722                egui::ComboBox::new(id, "")
1723                    .selected_text(active_variant)
1724                    .show_ui(ui, |_| {})
1725            });
1726
1727            let always_show_label = matches!(value.variant_type(), VariantType::Struct);
1728            maybe_grid_readonly_label_if(
1729                value.field_len(),
1730                ui,
1731                id,
1732                always_show_label,
1733                |ui, label| {
1734                    for i in 0..value.field_len() {
1735                        if label {
1736                            if let Some(name) = value.name_at(i) {
1737                                ui.label(name);
1738                            } else {
1739                                ui.label(i.to_string());
1740                            }
1741                        }
1742                        let field_value =
1743                            value.field_at(i).expect("invalid reflect impl: field len");
1744                        self.ui_for_reflect_readonly_with_options(
1745                            field_value,
1746                            ui,
1747                            id.with(i),
1748                            inspector_options_enum_variant_field(options, value.variant_index(), i),
1749                        );
1750                        ui.end_row();
1751                    }
1752                },
1753            );
1754        });
1755    }
1756}
1757
1758impl<'a, 'c> InspectorUi<'a, 'c> {
1759    pub fn reborrow<'s>(&'s mut self) -> InspectorUi<'s, 'c> {
1760        InspectorUi {
1761            type_registry: self.type_registry,
1762            context: self.context,
1763            short_circuit: self.short_circuit,
1764            short_circuit_readonly: self.short_circuit_readonly,
1765            short_circuit_many: self.short_circuit_many,
1766        }
1767    }
1768
1769    fn get_reflect_default(&self, type_id: TypeId) -> Option<&ReflectDefault> {
1770        self.type_registry.get_type_data::<ReflectDefault>(type_id)
1771    }
1772
1773    fn get_default_value_for(&mut self, type_id: TypeId) -> Option<Box<dyn Reflect>> {
1774        if let Some(reflect_default) = self.type_registry.get_type_data::<ReflectDefault>(type_id) {
1775            return Some(reflect_default.default());
1776        }
1777
1778        None
1779    }
1780
1781    fn construct_default_variant(
1782        &mut self,
1783        variant: &VariantInfo,
1784        ui: &mut egui::Ui,
1785    ) -> Result<DynamicEnum, ()> {
1786        let dynamic_variant = match variant {
1787            VariantInfo::Struct(struct_info) => {
1788                let mut dynamic_struct = DynamicStruct::default();
1789                for field in struct_info.iter() {
1790                    let field_default_value = match self.get_default_value_for(field.type_id()) {
1791                        Some(value) => value,
1792                        None => {
1793                            errors::no_default_value(ui, field.type_path());
1794                            return Err(());
1795                        }
1796                    };
1797                    dynamic_struct.insert_boxed(field.name(), field_default_value.to_dynamic());
1798                }
1799                DynamicVariant::Struct(dynamic_struct)
1800            }
1801            VariantInfo::Tuple(tuple_info) => {
1802                let mut dynamic_tuple = DynamicTuple::default();
1803                for field in tuple_info.iter() {
1804                    let field_default_value = match self.get_default_value_for(field.type_id()) {
1805                        Some(value) => value,
1806                        None => {
1807                            errors::no_default_value(ui, field.type_path());
1808                            return Err(());
1809                        }
1810                    };
1811                    dynamic_tuple.insert_boxed(field_default_value.to_dynamic());
1812                }
1813                DynamicVariant::Tuple(dynamic_tuple)
1814            }
1815            VariantInfo::Unit(_) => DynamicVariant::Unit,
1816        };
1817        let dynamic_enum = DynamicEnum::new(variant.name(), dynamic_variant);
1818        Ok(dynamic_enum)
1819    }
1820}
1821
1822#[must_use]
1823fn maybe_grid(
1824    i: usize,
1825    ui: &mut egui::Ui,
1826    id: egui::Id,
1827    mut f: impl FnMut(&mut egui::Ui, bool) -> bool,
1828) -> bool {
1829    match i {
1830        0 => false,
1831        1 => f(ui, false),
1832        _ => Grid::new(id).show(ui, |ui| f(ui, true)).inner,
1833    }
1834}
1835#[must_use]
1836fn maybe_grid_label_if(
1837    i: usize,
1838    ui: &mut egui::Ui,
1839    id: egui::Id,
1840    always_show_label: bool,
1841    mut f: impl FnMut(&mut egui::Ui, bool) -> bool,
1842) -> bool {
1843    match i {
1844        0 => false,
1845        1 if !always_show_label => f(ui, false),
1846        _ => Grid::new(id).show(ui, |ui| f(ui, true)).inner,
1847    }
1848}
1849
1850fn maybe_grid_readonly(
1851    i: usize,
1852    ui: &mut egui::Ui,
1853    id: egui::Id,
1854    mut f: impl FnMut(&mut egui::Ui, bool),
1855) {
1856    match i {
1857        0 => {}
1858        1 => f(ui, false),
1859        _ => {
1860            Grid::new(id).show(ui, |ui| f(ui, true));
1861        }
1862    }
1863}
1864fn maybe_grid_readonly_label_if(
1865    i: usize,
1866    ui: &mut egui::Ui,
1867    id: egui::Id,
1868    always_show_label: bool,
1869    mut f: impl FnMut(&mut egui::Ui, bool),
1870) {
1871    match i {
1872        0 => {}
1873        1 if !always_show_label => f(ui, false),
1874        _ => {
1875            Grid::new(id).show(ui, |ui| f(ui, true));
1876        }
1877    }
1878}
1879
1880fn variant_constructable<'a>(
1881    type_registry: &TypeRegistry,
1882    variant: &'a VariantInfo,
1883) -> Result<(), Vec<&'a str>> {
1884    let type_id_is_constructable = |type_id: TypeId| {
1885        type_registry
1886            .get_type_data::<ReflectDefault>(type_id)
1887            .is_some()
1888    };
1889
1890    let unconstructable_fields: Vec<&'a str> = match variant {
1891        VariantInfo::Struct(variant) => variant
1892            .iter()
1893            .filter_map(|field| {
1894                (!type_id_is_constructable(field.type_id())).then_some(field.type_path())
1895            })
1896            .collect(),
1897        VariantInfo::Tuple(variant) => variant
1898            .iter()
1899            .filter_map(|field| {
1900                (!type_id_is_constructable(field.type_id())).then_some(field.type_path())
1901            })
1902            .collect(),
1903        VariantInfo::Unit(_) => return Ok(()),
1904    };
1905
1906    if unconstructable_fields.is_empty() {
1907        Ok(())
1908    } else {
1909        Err(unconstructable_fields)
1910    }
1911}
1912
1913fn inspector_options_struct_field(options: &dyn Any, field: usize) -> &dyn Any {
1914    options
1915        .downcast_ref::<InspectorOptions>()
1916        .and_then(|options| options.get(Target::Field(field)))
1917        .unwrap_or(&())
1918}
1919
1920fn inspector_options_enum_variant_field<'a>(
1921    options: &'a dyn Any,
1922    variant_index: usize,
1923    field_index: usize,
1924) -> &'a dyn Any {
1925    options
1926        .downcast_ref::<InspectorOptions>()
1927        .and_then(|options| {
1928            options.get(Target::VariantField {
1929                variant_index,
1930                field_index,
1931            })
1932        })
1933        .unwrap_or(&())
1934}
1935
1936fn or(a: bool, b: bool) -> bool {
1937    a || b
1938}
1939
1940fn get_type_data<'a>(
1941    type_registry: &'a TypeRegistry,
1942    type_id: &dyn DynamicTyped,
1943) -> Result<&'a InspectorEguiImpl, TypeDataError> {
1944    let registration = type_registry
1945        .get(type_id.reflect_type_info().type_id())
1946        .ok_or(TypeDataError::NotRegistered)?;
1947    let data = registration
1948        .data::<InspectorEguiImpl>()
1949        .ok_or(TypeDataError::NoTypeData)?;
1950    Ok(data)
1951}