use bevy::input::ButtonState;
use bevy::input::keyboard::{KeyCode, KeyboardInput};
use bevy::input_focus::FocusedInput;
use bevy::picking::events::{PointerClick, PointerPress};
use bevy::prelude::*;
use bevy::ui::InteractionDisabled;
use bevy::ui_widgets::{Activate, ValueChange};
use crate::model::{choice_next, choice_step};
use crate::select::Select;
use crate::skin::Role;
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct ChoiceValue(pub usize);
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct ChoiceCount(pub usize);
#[derive(Component, Clone, Copy, Debug)]
pub(crate) struct StepBy(pub(crate) i32);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Move {
Step(i32),
First,
Last,
Next,
}
#[derive(EntityEvent, Clone, Copy, Debug)]
pub(crate) struct MoveChoice {
#[event_target]
pub(crate) entity: Entity,
pub(crate) to: Move,
}
pub(crate) fn choice_on_move(
event: On<MoveChoice>,
choices: Query<(&ChoiceValue, &ChoiceCount), Without<InteractionDisabled>>,
mut commands: Commands,
) {
let Ok((value, count)) = choices.get(event.entity) else {
return;
};
let len = count.0;
let last = len.saturating_sub(1);
let selected = value.0.min(last);
let to = match event.to {
Move::Step(delta) => choice_step(selected, len, delta),
Move::First => 0,
Move::Last => last,
Move::Next => choice_next(selected, len),
};
if to != value.0 {
commands.trigger(ValueChange {
source: event.entity,
value: to,
is_final: true,
});
}
}
pub(crate) fn choice_self_update(
change: On<ValueChange<usize>>,
mut choices: Query<&mut ChoiceValue>,
) {
if let Ok(mut value) = choices.get_mut(change.source) {
value.set_if_neq(ChoiceValue(change.value));
}
}
pub(crate) fn choice_on_activate(
event: On<Activate>,
selects: Query<&Select>,
mut commands: Commands,
) {
if selects.get(event.entity).is_ok_and(Select::filters) {
return;
}
commands.trigger(MoveChoice {
entity: event.entity,
to: Move::Next,
});
}
pub(crate) fn choice_on_key_input(
mut input: On<FocusedInput<KeyboardInput>>,
choices: Query<(), (With<ChoiceCount>, Without<InteractionDisabled>)>,
mut commands: Commands,
) {
if !choices.contains(input.focused_entity) || input.input.state != ButtonState::Pressed {
return;
}
let to = match input.input.key_code {
KeyCode::ArrowLeft => Move::Step(-1),
KeyCode::ArrowRight => Move::Step(1),
KeyCode::Home => Move::First,
KeyCode::End => Move::Last,
_ => return,
};
input.propagate(false);
commands.trigger(MoveChoice {
entity: input.focused_entity,
to,
});
}
pub(crate) fn arrow_on_press(mut press: On<PointerPress>) {
press.propagate(false);
}
pub(crate) fn arrow_on_click(
mut click: On<PointerClick>,
arrows: Query<&StepBy>,
parents: Query<&ChildOf>,
choices: Query<(), (With<ChoiceCount>, Without<InteractionDisabled>)>,
mut commands: Commands,
) {
let Ok(step) = arrows.get(click.entity) else {
return;
};
click.propagate(false);
if let Some(choice) = parents
.iter_ancestors(click.entity)
.find(|&ancestor| choices.contains(ancestor))
{
commands.trigger(MoveChoice {
entity: choice,
to: Move::Step(step.0),
});
}
}
pub fn update_choices(
mut choices: Query<(Entity, &mut ChoiceValue, &ChoiceCount), Changed<ChoiceValue>>,
children: Query<&Children>,
mut parts: Query<(&Role, &mut Visibility)>,
) {
for (choice, mut value, count) in &mut choices {
let last = count.0.saturating_sub(1);
if value.0 > last {
value.0 = last;
}
let options: Vec<Entity> = children
.iter_descendants(choice)
.filter(|&part| {
parts
.get(part)
.is_ok_and(|(role, _)| *role == Role::ChoiceOption)
})
.collect();
for (index, option) in options.into_iter().enumerate() {
let Ok((_, mut visibility)) = parts.get_mut(option) else {
continue;
};
let shown = if index == value.0 {
Visibility::Inherited
} else {
Visibility::Hidden
};
visibility.set_if_neq(shown);
}
}
}