use bevy_animation::AnimationPlayer;
use bevy_app::{App, Plugin};
use bevy_audio::{AudioPlayer, AudioSink};
use bevy_camera::{Camera, Camera2d};
#[cfg(feature = "render")]
use bevy_core_pipeline::Skybox;
use bevy_ecs::{
component::ComponentId, entity::Entity, name::Name, observer::Observer, resource::Resource,
system::SystemIdMarker, world::World,
};
use bevy_input::gamepad::Gamepad;
use bevy_light::{
AmbientLight, Atmosphere, DirectionalLight, FogVolume, IrradianceVolume, LightProbe,
PointLight, SpotLight, SunDisk,
};
use bevy_mesh::{Mesh2d, Mesh3d};
#[cfg(feature = "render")]
use bevy_pbr::{wireframe::Wireframe, DistanceFog, Lightmap};
use bevy_picking::pointer::PointerId;
use bevy_platform::collections::HashMap;
use bevy_sprite::{Sprite, Text2d};
use bevy_text::TextSpan;
use bevy_ui::{
widget::{ImageNode, Text, ViewportNode},
Node,
};
use bevy_ui_widgets::Button;
use bevy_window::{Monitor, Window};
use core::{
any::TypeId,
ops::{Deref, DerefMut},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct LabelDefinitionPriority(pub i8);
impl LabelDefinitionPriority {
pub const USER: Self = Self(0);
pub const LIBRARY: Self = Self(-10);
pub const FALLBACK: Self = Self(-20);
}
#[derive(Clone, Copy, Debug)]
pub struct ComponentLabelData<'a> {
pub component_id: ComponentId,
pub short_name: &'a str,
pub label_definition_priority: Option<LabelDefinitionPriority>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct EntityLabel {
pub label: Name,
pub origin: LabelOrigin,
}
impl Deref for EntityLabel {
type Target = Name;
fn deref(&self) -> &Self::Target {
&self.label
}
}
impl DerefMut for EntityLabel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.label
}
}
impl EntityLabel {
pub fn custom(label: &str) -> Self {
Self {
label: Name::new(label.to_owned()),
origin: LabelOrigin::Custom,
}
}
pub fn resolved(label: &str) -> Self {
Self {
label: Name::new(label.to_owned()),
origin: LabelOrigin::Resolved,
}
}
pub fn fallback(label: &str) -> Self {
Self {
label: Name::new(label.to_owned()),
origin: LabelOrigin::Fallback,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum LabelOrigin {
Custom,
Resolved,
Fallback,
}
pub fn resolve_label(
world: &World,
entity: Entity,
components: &[ComponentLabelData],
) -> Option<EntityLabel> {
if let Some(custom_label) = world.get::<Name>(entity).cloned() {
return Some(EntityLabel::custom(custom_label.as_str()));
}
let mut label_resolution_priorities: Vec<(&str, LabelDefinitionPriority)> = components
.iter()
.filter_map(|c| c.label_definition_priority.map(|p| (c.short_name, p)))
.collect();
if label_resolution_priorities.is_empty() {
return None;
}
label_resolution_priorities.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(b.0)));
let highest_priority = label_resolution_priorities[0].1;
label_resolution_priorities.retain(|&(_, priority)| priority == highest_priority);
let resolved_label = label_resolution_priorities
.into_iter()
.map(|(label, _)| label)
.collect::<Vec<&str>>()
.join(" | ");
Some(EntityLabel::resolved(&resolved_label))
}
#[derive(Debug, Resource, Default)]
pub struct LabelResolutionRegistry {
label_defining_types: HashMap<TypeId, LabelDefinitionPriority>,
}
impl LabelResolutionRegistry {
pub const fn new() -> Self {
Self {
label_defining_types: HashMap::new(),
}
}
pub fn register_label_defining_type<T: 'static>(&mut self, priority: LabelDefinitionPriority) {
let type_id = TypeId::of::<T>();
self.label_defining_types.insert(type_id, priority);
}
pub fn get_priority<T: 'static>(&self) -> Option<LabelDefinitionPriority> {
let type_id = TypeId::of::<T>();
self.get_priority_by_type_id(type_id)
}
pub fn get_priority_by_type_id(&self, type_id: TypeId) -> Option<LabelDefinitionPriority> {
self.label_defining_types.get(&type_id).copied()
}
pub fn unregister_label_defining_type<T: 'static>(&mut self) {
let type_id = TypeId::of::<T>();
self.unregister_label_defining_type_by_type_id(type_id);
}
pub fn unregister_label_defining_type_by_type_id(&mut self, type_id: TypeId) {
self.label_defining_types.remove(&type_id);
}
}
pub struct LabelResolutionPlugin;
impl Plugin for LabelResolutionPlugin {
fn build(&self, app: &mut App) {
let mut label_resolution_registry = LabelResolutionRegistry::new();
label_resolution_registry
.register_label_defining_type::<Window>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Monitor>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Gamepad>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<PointerId>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Node>(LabelDefinitionPriority::FALLBACK);
label_resolution_registry
.register_label_defining_type::<Button>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Text>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<TextSpan>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Text2d>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<ImageNode>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<ViewportNode>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Camera>(LabelDefinitionPriority::FALLBACK);
label_resolution_registry
.register_label_defining_type::<Camera2d>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Camera2d>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<DirectionalLight>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<PointLight>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<SpotLight>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<AmbientLight>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<LightProbe>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<IrradianceVolume>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<SunDisk>(LabelDefinitionPriority::LIBRARY);
#[cfg(feature = "render")]
label_resolution_registry
.register_label_defining_type::<Lightmap>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Sprite>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Mesh2d>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Mesh3d>(LabelDefinitionPriority::LIBRARY);
#[cfg(feature = "render")]
label_resolution_registry
.register_label_defining_type::<Wireframe>(LabelDefinitionPriority::LIBRARY);
#[cfg(feature = "render")]
label_resolution_registry
.register_label_defining_type::<Skybox>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<FogVolume>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Atmosphere>(LabelDefinitionPriority::LIBRARY);
#[cfg(feature = "render")]
label_resolution_registry
.register_label_defining_type::<DistanceFog>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<AnimationPlayer>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<AudioPlayer>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<AudioSink>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<Observer>(LabelDefinitionPriority::LIBRARY);
label_resolution_registry
.register_label_defining_type::<SystemIdMarker>(LabelDefinitionPriority::LIBRARY);
app.insert_resource(label_resolution_registry);
}
}