1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
use std::ops::{Deref, DerefMut};
use bevy::ecs::query::{QueryData, QueryFilter, QueryIter, QuerySingleError, WorldQuery};
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use bevy_egui::egui;
/// Marks which entities are currently being edited in the level editor.
#[derive(Component)]
pub struct YoleckEditMarker;
/// Wrapper for writing queries in edit systems.
///
/// To future-proof for the multi-entity editing feature, use this instead of
/// regular queries with `With<YoleckEditMarker>`.
///
/// The methods of `YoleckEdit` that have the same name as methods of a regular Bevy `Query`
/// delegate to them, but if there are edited entities that do not fit the query they will act as
/// if they found no match.
#[derive(SystemParam)]
pub struct YoleckEdit<'w, 's, Q: 'static + QueryData, F: 'static + QueryFilter = ()> {
query: Query<'w, 's, Q, (With<YoleckEditMarker>, F)>,
verification_query: Query<'w, 's, (), With<YoleckEditMarker>>,
}
impl<'w, 's, Q: 'static + QueryData, F: 'static + QueryFilter> YoleckEdit<'w, 's, Q, F> {
pub fn get_single(
&self,
) -> Result<<<Q as QueryData>::ReadOnly as WorldQuery>::Item<'_>, QuerySingleError> {
let single = self.query.get_single()?;
// This will return an error if multiple entities are selected (but only one fits F and Q)
self.verification_query.get_single()?;
Ok(single)
}
pub fn get_single_mut(&mut self) -> Result<<Q as WorldQuery>::Item<'_>, QuerySingleError> {
let single = self.query.get_single_mut()?;
// This will return an error if multiple entities are selected (but only one fits F and Q)
self.verification_query.get_single()?;
Ok(single)
}
pub fn is_empty(&self) -> bool {
self.query.is_empty()
}
/// Check if some non-matching entities are selected for editing.
///
/// Use this, together with [`is_empty`](Self::is_empty) for systems that can edit multiple
/// entities but want to not show their UI when some irrelevant entities are selected as well.
pub fn has_nonmatching(&self) -> bool {
// Note - cannot use len for query.iter() because then `F` would be limited to archetype
// filters only.
self.query.iter().count() != self.verification_query.iter().len()
}
/// Iterate over all the matching entities, _even_ if some selected entities do not match.
///
/// If both matching and non-matching entities are selected, this will iterate over the
/// matching entities only. If it is not desired to iterate at all in such cases,
/// check [`has_nonmatching`](Self::has_nonmatching) must be checked manually.
pub fn iter_matching(
&mut self,
) -> QueryIter<<Q as QueryData>::ReadOnly, (bevy::prelude::With<YoleckEditMarker>, F)> {
self.query.iter()
}
/// Iterate mutably over all the matching entities, _even_ if some selected entities do not match.
///
/// If both matching and non-matching entities are selected, this will iterate over the
/// matching entities only. If it is not desired to iterate at all in such cases,
/// check [`has_nonmatching`](Self::has_nonmatching) must be checked manually.
pub fn iter_matching_mut(&mut self) -> QueryIter<Q, (With<YoleckEditMarker>, F)> {
self.query.iter_mut()
}
}
/// An handle for the egui UI frame used in editing systems.
#[derive(Resource)]
pub struct YoleckUi(pub egui::Ui);
impl Deref for YoleckUi {
type Target = egui::Ui;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for YoleckUi {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}