use re_ui::UiExt as _;
use re_viewer_context::{AppContext, MaybeMutRef};
use crate::response_utils::response_with_changes_of_inner;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantAvailable {
Yes,
No {
reason_markdown: String,
},
}
pub trait VariantAvailableProvider<EnumT: re_types_core::reflection::Enum> {
fn is_variant_enabled(ctx: &AppContext<'_>, variant: EnumT) -> VariantAvailable;
}
struct AllEnumVariantAvailable<EnumT: re_types_core::reflection::Enum> {
_phantom: std::marker::PhantomData<EnumT>,
}
impl<EnumT: re_types_core::reflection::Enum> VariantAvailableProvider<EnumT>
for AllEnumVariantAvailable<EnumT>
{
fn is_variant_enabled(_ctx: &AppContext<'_>, _variant: EnumT) -> VariantAvailable {
VariantAvailable::Yes
}
}
pub fn edit_view_enum<EnumT: re_types_core::reflection::Enum + re_types_core::Component>(
ctx: &AppContext<'_>,
ui: &mut egui::Ui,
current_value: &mut MaybeMutRef<'_, EnumT>,
) -> egui::Response {
edit_view_enum_with_variant_available::<EnumT, AllEnumVariantAvailable<EnumT>>(
ctx,
ui,
current_value,
)
}
pub fn edit_view_enum_with_variant_available<
EnumT: re_types_core::reflection::Enum + re_types_core::Component,
VariantAvailT: VariantAvailableProvider<EnumT>,
>(
ctx: &AppContext<'_>,
ui: &mut egui::Ui,
current_value: &mut MaybeMutRef<'_, EnumT>,
) -> egui::Response {
let id_salt = EnumT::name().full_name();
edit_view_enum_impl::<_, VariantAvailT>(ctx, ui, id_salt, current_value)
}
fn edit_view_enum_impl<
EnumT: re_types_core::reflection::Enum,
VariantAvailT: VariantAvailableProvider<EnumT>,
>(
ctx: &AppContext<'_>,
ui: &mut egui::Ui,
id_salt: &str,
current_value: &mut MaybeMutRef<'_, EnumT>,
) -> egui::Response {
if let Some(current_value) = current_value.as_mut() {
let prev_selected_value = *current_value;
let variants = EnumT::variants();
if variants.is_empty() {
ui.label("<no variants>")
} else if variants.len() <= 2 {
let mut response = ui
.selectable_toggle(|ui| {
for variant in variants.iter().copied() {
enum_variant_ui(
ui,
current_value,
variant,
VariantAvailT::is_variant_enabled(ctx, variant),
);
}
})
.response;
if prev_selected_value != *current_value {
response.mark_changed();
}
response
} else {
let mut combobox_response = egui::ComboBox::from_id_salt(id_salt)
.selected_text(format!("{current_value}"))
.height(250.0)
.show_ui(ui, |ui| {
ui.set_min_width(60.0);
let mut iter = variants.iter().copied();
let Some(first) = iter.next() else {
return ui.label("<no variants>");
};
let mut response = crate::encoding_uis::enum_ui::enum_variant_ui(
ui,
current_value,
first,
VariantAvailT::is_variant_enabled(ctx, first),
);
for variant in iter {
response |= enum_variant_ui(
ui,
current_value,
variant,
VariantAvailT::is_variant_enabled(ctx, variant),
);
}
response
});
combobox_response.response = combobox_response.response.on_hover_ui(|ui| {
ui.markdown_ui(prev_selected_value.docstring_md());
});
combobox_response.response.widget_info(|| {
egui::WidgetInfo::labeled(
egui::WidgetType::ComboBox,
ui.is_enabled(),
current_value.to_string(),
)
});
response_with_changes_of_inner(combobox_response)
}
} else {
ui.add(egui::Label::new(current_value.to_string()).truncate())
}
}
pub fn enum_variant_ui<EnumT: re_types_core::reflection::Enum>(
ui: &mut egui::Ui,
current_value: &mut EnumT,
variant: EnumT,
variant_available: VariantAvailable,
) -> egui::Response {
match variant_available {
VariantAvailable::Yes => ui
.selectable_value(current_value, variant, variant.to_string())
.on_hover_ui(|ui| {
ui.markdown_ui(variant.docstring_md());
}),
VariantAvailable::No {
reason_markdown: reason,
} => {
ui.add_enabled_ui(false, |ui| {
ui.selectable_value(current_value, variant, variant.to_string())
.on_disabled_hover_ui(|ui| {
ui.markdown_ui(&format!("{}\n\n{}", variant.docstring_md(), reason));
})
})
.inner
}
}
}