1#[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
94pub 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
109pub 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
130pub 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>;
142pub 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<()>;
154pub 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 pub type_registry: &'a TypeRegistry,
173 pub context: &'a mut Context<'c>,
175
176 pub short_circuit: ShortCircuitFn,
179 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 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 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 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 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 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 if let Some(element) = list.get(i) {
811 let clone = element.to_dynamic();
812 list.insert(prev_idx, clone);
813 }
814 list.remove(i + 1);
816 changed = true;
817 }
818 }
819 MoveElementDown(i) => {
820 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 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 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 add_button(ui).clicked() {
1062 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 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 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 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 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 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 add_button(ui).clicked() {
1240 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 let value_changed =
1252 self.ui_for_reflect_with_options(v.as_mut(), ui, id, options);
1253
1254 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 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 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 ui.horizontal_top(|ui| {
1367 self.ui_for_reflect_readonly_with_options(
1368 value_to_check.borrow(),
1369 ui,
1370 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 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}