use crate::{Part, construct::*, prelude::*};
use bevy::prelude::*;
use std::borrow::Cow;
#[derive(Component, Reflect)]
pub struct Checkbox {
pub checked: bool,
}
pub(crate) fn plugin(app: &mut App) {
app.add_systems(
PreUpdate,
(checkbox_controller, checkbox_group_controller).in_set(AskySet::Controller),
);
}
impl Part for Checkbox {
type Group = CheckboxGroup;
}
impl Construct for Checkbox {
type Props = Cow<'static, str>;
fn construct(
context: &mut ConstructContext,
props: Self::Props,
) -> Result<Self, ConstructError> {
let mut commands = context.world.commands();
commands
.entity(context.id)
.insert(Focusable::default())
.insert(Prompt(props.clone()));
context.world.flush();
Ok(Checkbox { checked: false })
}
}
fn checkbox_controller(
focus: Focus,
mut query: Query<(Entity, &mut Checkbox)>,
input: Res<ButtonInput<KeyCode>>,
) {
use KeyCode::*;
if input.any_just_pressed([Space, KeyY, KeyN]) {
for (id, mut checkbox) in query.iter_mut() {
if !focus.is_focused(id) {
continue;
}
if input.just_pressed(Space) {
checkbox.checked = !checkbox.checked;
}
if input.any_just_pressed([KeyY]) {
checkbox.checked = true;
}
if input.any_just_pressed([KeyN]) {
checkbox.checked = false;
}
}
}
}
#[derive(Component, Reflect, Default)]
pub struct CheckboxGroup;
unsafe impl Submitter for CheckboxGroup {
type Out = Vec<bool>;
}
impl Construct for CheckboxGroup {
type Props = Cow<'static, str>;
fn construct(
context: &mut ConstructContext,
props: Self::Props,
) -> Result<Self, ConstructError> {
let mut commands = context.world.commands();
commands
.entity(context.id)
.column()
.with_children(|parent| {
parent.spawn(Text::new(props));
});
context.world.flush();
Ok(CheckboxGroup)
}
}
fn checkbox_group_controller(
mut query: Query<(Entity, &Children), With<CheckboxGroup>>,
checkboxes: Query<(Entity, &Checkbox)>,
input: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
focus: FocusParam,
) {
if !input.any_just_pressed([KeyCode::Escape, KeyCode::Enter]) {
return;
}
for (id, children) in query.iter_mut() {
if children.iter().any(|id| focus.is_focused(id)) {
if input.just_pressed(KeyCode::Enter) {
let result: Vec<bool> = checkboxes
.iter_many(children)
.map(|(_, checkbox)| checkbox.checked)
.collect();
commands.trigger(Submit::new(id, Ok(result)));
}
if input.just_pressed(KeyCode::Escape) {
commands.trigger(Submit::<Vec<bool>>::new(id, Err(Error::Cancel)));
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use bevy::ecs::system::RunSystemOnce;
use bevy::input::keyboard::KeyCode;
#[derive(Resource)]
struct KeyToPress(KeyCode);
fn press_key_system(key: Res<KeyToPress>, mut input: ResMut<ButtonInput<KeyCode>>) {
input.press(key.0);
}
fn clear_keys_system(mut input: ResMut<ButtonInput<KeyCode>>) {
*input = ButtonInput::default();
}
#[test]
fn test_checkbox_key_presses() {
let mut app = App::new();
app.add_plugins(MinimalPlugins)
.add_plugins(AskyPlugin)
.add_message::<bevy::input::keyboard::KeyboardInput>()
.init_resource::<ButtonInput<KeyCode>>()
.init_resource::<bevy::input_focus::InputFocus>();
let entity = app
.world_mut()
.spawn((
Checkbox { checked: false },
Focusable::default(),
Prompt(Cow::Borrowed("Test checkbox")),
))
.id();
app.update();
fn simulate_key_press(app: &mut App, key: KeyCode) {
app.world_mut().insert_resource(KeyToPress(key));
app.world_mut().run_system_once(press_key_system).unwrap();
app.update();
app.world_mut().run_system_once(clear_keys_system).unwrap();
app.update();
}
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(!checkbox.checked);
simulate_key_press(&mut app, KeyCode::Space);
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(checkbox.checked);
simulate_key_press(&mut app, KeyCode::Space);
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(!checkbox.checked);
simulate_key_press(&mut app, KeyCode::KeyY);
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(checkbox.checked);
simulate_key_press(&mut app, KeyCode::KeyN);
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(!checkbox.checked);
simulate_key_press(&mut app, KeyCode::Space);
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(checkbox.checked);
simulate_key_press(&mut app, KeyCode::Space);
let checkbox = app.world().get::<Checkbox>(entity).unwrap();
assert!(!checkbox.checked);
}
}