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