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