bevy_state_stack 0.2.1

An improved state stack for bevy.
Documentation
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;

use bevy_state_stack::*;

#[derive(Debug, Clone, Eq, PartialEq, Hash, StageLabel)]
enum AppState {
	Menu,
	Map,
	Encounter,
}

#[derive(Component)]
struct Menu;

#[derive(Debug, PartialEq, Eq)]
enum MenuAction {
	Pause,
	Resume,
	NewGame,
}

#[derive(Default)]
struct Test {
	exit_count: u8,
	enter_count: u8,
}

#[test]
fn test() {
	let mut app = App::new();

	app.init_resource::<Test>();

	app.add_event::<MenuAction>();
	app.add_state_stack(AppState::Menu);

	app.add_system_on_enter(AppState::Menu, enter_counter);
	app.add_system_on_enter(AppState::Map, enter_counter);

	app.add_system_on_exit(AppState::Menu, exit_counter);
	app.add_system_on_exit(AppState::Map, exit_counter);

	app.add_system_on_enter(AppState::Menu, setup_menu);
	app.add_system_on_exit(AppState::Menu, despawn::<Menu>);

	app.add_system_on_enter(AppState::Map, setup_map);

	app.add_system_on_update(AppState::Map, pause)
		.add_system_on_update(AppState::Encounter, pause)
		.add_system_set_on_update(
			AppState::Menu,
			SystemSet::new().with_system(resume).with_system(start_game),
		);

	app.update();

	let test = app.world.get_resource::<Test>().unwrap();
	assert_eq!(test.enter_count, 1);
	assert_eq!(test.exit_count, 0);

	app.world.send_event(MenuAction::NewGame);

	// add SetState resource
	app.update();
	// Update State stack
	app.update();

	let top_state = app.world.get_resource::<TopState<AppState>>().unwrap();
	assert_eq!(top_state.0, AppState::Map);

	let test = app.world.get_resource::<Test>().unwrap();
	assert_eq!(test.enter_count, 2);
	assert_eq!(test.exit_count, 1);

	app.world.send_event(MenuAction::Pause);

	app.update();
	app.update();

	let test = app.world.get_resource::<Test>().unwrap();
	assert_eq!(test.enter_count, 3);
	assert_eq!(test.exit_count, 1);

	app.world.send_event(MenuAction::Resume);

	app.update();
	app.update();

	let top_state = app.world.get_resource::<TopState<AppState>>().unwrap();
	assert_eq!(top_state.0, AppState::Map);

	let test = app.world.get_resource::<Test>().unwrap();
	assert_eq!(test.enter_count, 3);
	assert_eq!(test.exit_count, 2);
}

fn enter_counter(mut c: ResMut<Test>, state: Res<TopState<AppState>>) {
	println!("entering: {:?}", state);
	c.enter_count += 1;
}

fn exit_counter(mut c: ResMut<Test>, state: Res<TopState<AppState>>) {
	println!("leaving: {:?}", state);
	c.exit_count += 1;
}

fn despawn<T: Component>(mut c: Commands, q: Query<Entity, With<T>>) {
	for e in q.iter() {
		c.entity(e).despawn()
	}
}

fn setup_menu(mut _c: Commands) {
	// Setup menu
}

fn setup_map(mut _c: Commands) {
	// Setup map
}

fn start_game(mut c: Commands, mut ev: EventReader<MenuAction>) {
	println!("New game check");
	for e in ev.iter() {
		if *e == MenuAction::NewGame {
			println!("New game event");
			c.insert_resource(Stack::Set(AppState::Map))
		}
	}
}

fn pause(mut c: Commands, mut ev: EventReader<MenuAction>) {
	for e in ev.iter() {
		if *e == MenuAction::Pause {
			c.insert_resource(Stack::Push(AppState::Menu))
		}
	}
}

fn resume(mut c: Commands, mut ev: EventReader<MenuAction>) {
	for e in ev.iter() {
		if *e == MenuAction::Resume {
			c.insert_resource(Stack::<AppState>::Pop);
		}
	}
}