use bevy::ecs::lifecycle::HookContext;
use bevy::ecs::world::DeferredWorld;
use bevy::prelude::*;
use crate::skin::Role;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
#[reflect(Debug, Default, PartialEq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FieldProblemKind {
Warning,
#[default]
Error,
}
#[derive(Component, Clone, Debug, PartialEq, Reflect)]
#[reflect(Component, Debug, PartialEq, Clone)]
#[component(on_insert = queue_message_sync, on_remove = queue_message_sync)]
#[require(ProblemRoom)]
pub struct FieldProblem {
pub text: String,
pub kind: FieldProblemKind,
}
impl FieldProblem {
pub fn new(kind: FieldProblemKind, text: impl Into<String>) -> Self {
Self {
text: text.into(),
kind,
}
}
pub fn error(text: impl Into<String>) -> Self {
Self::new(FieldProblemKind::Error, text)
}
pub fn warning(text: impl Into<String>) -> Self {
Self::new(FieldProblemKind::Warning, text)
}
}
#[derive(Component, Clone, Copy, Debug, Default, PartialEq)]
pub struct ProblemRoom(pub f32);
fn queue_message_sync(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
world
.commands()
.queue(move |world: &mut World| sync_message(world, entity));
}
fn sync_message(world: &mut World, entity: Entity) {
let Ok(row) = world.get_entity(entity) else {
return;
};
let problem = row.get::<FieldProblem>().map(|p| p.text.clone());
let message = row
.get::<Children>()
.into_iter()
.flat_map(|children| children.iter())
.find(|child| world.get::<Role>(*child) == Some(&Role::FieldMessage));
match (problem, message) {
(Some(text), None) => {
world.spawn((
Node {
position_type: PositionType::Absolute,
left: Val::Px(0.0),
right: Val::Px(0.0),
top: Val::Percent(100.0),
..default()
},
Role::FieldMessage,
Pickable::IGNORE,
ChildOf(entity),
children![
(Node::default(), Role::FieldMark, Pickable::IGNORE),
(
Node {
flex_grow: 1.0,
flex_basis: Val::Px(0.0),
min_width: Val::Px(0.0),
..default()
},
Text::new(text),
Role::Label,
Pickable::IGNORE
),
],
));
}
(Some(text), Some(message)) => {
let line = world
.get::<Children>(message)
.into_iter()
.flat_map(|children| children.iter())
.find(|child| world.get::<Text>(*child).is_some());
if let Some(mut line) = line.and_then(|line| world.get_mut::<Text>(line))
&& line.0 != text
{
line.0 = text;
}
}
(None, Some(message)) => {
world.despawn(message);
world.entity_mut(entity).remove::<ProblemRoom>();
}
(None, None) => {}
}
}
pub fn update_field_problems(
mut rows: Query<(&Children, &mut ProblemRoom), With<FieldProblem>>,
parts: Query<(&Role, &ComputedNode)>,
) {
for (children, mut room) in &mut rows {
let height = children
.iter()
.filter_map(|child| parts.get(child).ok())
.find(|(role, _)| **role == Role::FieldMessage)
.map_or(0.0, |(_, node)| node.size().y * node.inverse_scale_factor());
if (room.0 - height).abs() > 0.25 {
room.0 = height;
}
}
}