use crate::{construct::*, prelude::*};
use bevy::prelude::*;
use std::borrow::Cow;
#[derive(Component, Clone, Reflect)]
pub struct Toggle {
pub message: Cow<'static, str>,
pub options: [Cow<'static, str>; 2],
pub index: usize,
}
impl OptionPrompt for Toggle {
fn name(&self, index: usize) -> &str {
match index {
0 => &self.options[0],
1 => &self.options[1],
_ => panic!("No such option for Confirm."),
}
}
fn state(&self) -> usize {
self.index
}
}
impl Toggle {
pub fn new<T: Into<Cow<'static, str>>>(
message: impl Into<Cow<'static, str>>,
options: [T; 2],
) -> Self {
let mut iter = options.into_iter();
Toggle {
message: message.into(),
options: [iter.next().unwrap().into(), iter.next().unwrap().into()],
index: 0,
}
}
}
pub(crate) fn plugin(app: &mut App) {
app.add_systems(Update, toggle_controller.in_set(AskySet::Controller));
}
impl Construct for Toggle {
type Props = Toggle;
fn construct(
context: &mut ConstructContext,
props: Self::Props,
) -> Result<Self, ConstructError> {
let mut commands = context.world.commands();
commands
.entity(context.id)
.insert(Prompt(props.message.clone()))
.insert(Focusable::default());
context.world.flush();
Ok(props)
}
}
fn toggle_controller(
mut query: Query<(Entity, &mut Toggle)>,
input: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
mut focus: FocusParam,
) {
for (id, mut toggle) in query.iter_mut() {
if !focus.is_focused(id) {
continue;
}
if input.any_just_pressed([
KeyCode::KeyH,
KeyCode::ArrowLeft,
KeyCode::KeyL,
KeyCode::ArrowRight,
KeyCode::Enter,
KeyCode::Escape,
]) {
if input.any_just_pressed([KeyCode::KeyH, KeyCode::ArrowLeft]) {
toggle.index = 0;
}
if input.any_just_pressed([KeyCode::KeyL, KeyCode::ArrowRight]) {
toggle.index = 1;
}
if input.just_pressed(KeyCode::Enter) {
commands.trigger(Submit::new(id, Ok(toggle.index)));
focus.block_and_move(id);
}
if input.just_pressed(KeyCode::Escape) {
commands.trigger(Submit::<bool>::new(id, Err(Error::Cancel)));
focus.move_focus_from(id);
commands.entity(id).try_insert(Feedback::error("canceled"));
}
}
}
}
#[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_toggle_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((
Toggle::new("Choose:", ["Option A", "Option B"]),
Focusable::default(),
Prompt(Cow::Borrowed("Choose:")),
))
.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 toggle = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle.index, 0);
simulate_key_press(&mut app, KeyCode::KeyL);
let toggle = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle.index, 1);
simulate_key_press(&mut app, KeyCode::KeyH);
let toggle = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle.index, 0);
simulate_key_press(&mut app, KeyCode::ArrowRight);
let toggle = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle.index, 1);
simulate_key_press(&mut app, KeyCode::ArrowLeft);
let toggle = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle.index, 0);
simulate_key_press(&mut app, KeyCode::KeyL);
let toggle_before = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle_before.index, 1);
simulate_key_press(&mut app, KeyCode::Enter);
let toggle_after = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle_after.index, 1);
simulate_key_press(&mut app, KeyCode::Escape);
let toggle_after_escape = app.world().get::<Toggle>(entity).unwrap();
assert_eq!(toggle_after_escape.index, 1);
}
}