use crate::events::{ActivatePage, DeactivatePage, DespawnPage, SpawnPage};
use crate::page::Page;
use bevy::asset::{Assets, Handle};
use bevy::prelude::{Commands, On, ResMut, Resource, World};
use rustc_hash::FxHashMap;
use smol_str::{SmolStr, ToSmolStr};
pub(crate) fn spawn_page(world: &mut World) {
let handles = world
.resource_mut::<PageManager>()
.spawn_handles
.drain()
.collect::<Vec<_>>();
for (name, handle) in handles {
if let Some(page_asset) = world.resource::<Assets<Page>>().get(&handle) {
let mut page = page_asset.clone();
let was_active = world
.resource::<PageManager>()
.pages
.get(&name)
.is_some_and(|p| p.is_active());
if let Some(old_page) = world.resource_mut::<PageManager>().pages.remove(&name)
&& let Some(entity) = old_page.entity()
{
world.commands().entity(entity).despawn();
}
page.spawn(world);
if was_active {
page.set_active(&mut world.commands(), true);
}
world
.resource_mut::<PageManager>()
.pages
.insert(name.clone(), page);
world.trigger(SpawnPage { name });
} else {
world
.resource_mut::<PageManager>()
.spawn_handles
.insert(name, handle);
}
}
}
pub(crate) fn de_activate_pages(mut commands: Commands, mut manager: ResMut<PageManager>) {
let requests: Vec<(SmolStr, bool)> = manager
.active_requests
.iter()
.map(|(k, v)| (k.clone(), *v))
.collect();
for (name, active) in requests {
if let Some(page) = manager.pages.get_mut(&name) {
page.set_active(&mut commands, active);
manager.active_requests.remove(&name);
if active {
commands.trigger(ActivatePage { name });
} else {
commands.trigger(DeactivatePage { name });
}
}
}
}
pub(crate) fn despawn_page(
event: On<DespawnPage>,
mut commands: Commands,
mut manager: ResMut<PageManager>,
) {
if let Some(page) = manager.pages.remove(&event.name)
&& let Some(entity) = page.entity()
{
commands.entity(entity).despawn();
}
}
#[derive(Resource)]
pub struct PageManager {
spawn_handles: FxHashMap<SmolStr, Handle<Page>>,
pages: FxHashMap<SmolStr, Page>,
active_requests: FxHashMap<SmolStr, bool>,
#[cfg(feature = "hot-reload")]
handle_registry: FxHashMap<Handle<Page>, SmolStr>,
}
impl PageManager {
#[inline(always)]
pub fn new() -> Self {
Self {
spawn_handles: FxHashMap::with_capacity_and_hasher(1, Default::default()),
pages: FxHashMap::with_capacity_and_hasher(1, Default::default()),
active_requests: FxHashMap::with_capacity_and_hasher(1, Default::default()),
#[cfg(feature = "hot-reload")]
handle_registry: FxHashMap::with_capacity_and_hasher(1, Default::default()),
}
}
pub fn any_active(&self) -> bool {
self.pages.values().any(|p| p.is_active())
}
pub fn has_active_requests(&self) -> bool {
!self.active_requests.is_empty()
}
pub fn get(&self, name: impl ToSmolStr) -> Option<&Page> {
self.pages.get(&name.to_smolstr())
}
#[inline(always)]
pub fn spawn(&mut self, name: impl ToSmolStr, handle: Handle<Page>) {
let name = name.to_smolstr();
#[cfg(feature = "hot-reload")]
self.handle_registry.insert(handle.clone(), name.clone());
self.spawn_handles.insert(name, handle);
}
#[inline(always)]
pub fn set_active(&mut self, name: impl ToSmolStr, active: bool) {
self.active_requests.insert(name.to_smolstr(), active);
}
#[inline(always)]
pub fn despawn(&mut self, commands: &mut Commands, name: impl ToSmolStr) {
let name = name.to_smolstr();
self.spawn_handles.remove(&name);
#[cfg(feature = "hot-reload")]
{
if let Some(handle) = self
.handle_registry
.iter()
.find(|(_, handle_name)| **handle_name == name)
.map(|(h, _)| h.clone())
{
self.handle_registry.remove(&handle);
}
}
commands.trigger(DespawnPage { name });
}
#[cfg(feature = "hot-reload")]
pub fn reload(&mut self, handle: Handle<Page>) {
if let Some(name) = self.handle_registry.get(&handle).cloned() {
self.spawn_handles.insert(name, handle);
}
}
}
impl Default for PageManager {
fn default() -> Self {
Self::new()
}
}