pecs_core 0.4.0

Asynchronous operations for Bevy Engine
Documentation
use bevy::prelude::*;

use crate::{AsynOps, Promise, PromiseCommandsExtension, PromiseId, PromiseLikeBase};

pub mod asyn {
    use super::AsynButton;
    use bevy::prelude::Entity;

    pub fn button(entity: Entity) -> AsynButton {
        AsynButton(entity)
    }
}

pub struct PromiseUiPlugin;
impl Plugin for PromiseUiPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, resolve_buttons);
    }
}

pub struct StatefulAsynUi<S>(S);
impl<S: 'static> StatefulAsynUi<S> {
    pub fn button(self, entity: Entity) -> StatefulAsynButton<S> {
        StatefulAsynButton(self.0, entity)
    }
}

#[derive(Component)]
pub struct AsynButtonIteraction {
    promise: PromiseId,
    interaction: Interaction,
    entity: Entity,
}

pub struct AsynButton(Entity);

impl AsynButton {
    pub fn pressed(&self) -> Promise<(), ()> {
        let entity = self.0;
        Promise::register(
            move |world, id| {
                world.spawn(AsynButtonIteraction {
                    entity,
                    promise: id,
                    interaction: Interaction::Pressed,
                });
            },
            move |world, id| {
                if let Some(despawn) = world
                    .query::<(Entity, &AsynButtonIteraction)>()
                    .iter(world)
                    .filter(|(_, b)| b.promise == id)
                    .map(|(e, _)| e)
                    .next()
                {
                    world.despawn(despawn);
                }
            },
        )
    }
}

pub struct StatefulAsynButton<S>(S, Entity);
impl<S: 'static> StatefulAsynButton<S> {
    pub fn pressed(self) -> Promise<S, ()> {
        AsynButton(self.1).pressed().with(self.0)
    }
}

pub trait UiOpsExtension<S> {
    fn ui(self) -> StatefulAsynUi<S>;
}
impl<S: 'static> UiOpsExtension<S> for AsynOps<S> {
    fn ui(self) -> StatefulAsynUi<S> {
        StatefulAsynUi(self.0)
    }
}

fn resolve_buttons(
    mut commands: Commands,
    buttons: Query<(Entity, &AsynButtonIteraction)>,
    interactions: Query<(Entity, &Interaction), (Changed<Interaction>, With<Button>)>,
) {
    for (btn, interaction) in interactions.iter() {
        if let Some((entity, btn)) = buttons
            .iter()
            .filter(|(_, b)| b.entity == btn && interaction == &b.interaction)
            .next()
        {
            commands.entity(entity).despawn();
            commands.promise(btn.promise).resolve(())
        }
    }
}