use bevy_color::Color;
use bevy_ecs::{
component::Mutable,
prelude::*,
system::{Query, SystemParam},
};
use crate::{TextColor, TextFont, TextSpan};
pub trait TextSpanAccess: Component<Mutability = Mutable> {
fn read_span(&self) -> &str;
fn write_span(&mut self) -> &mut String;
}
pub trait TextRoot: TextSpanAccess + From<String> {}
pub trait TextSpanComponent: TextSpanAccess + From<String> {}
#[derive(Resource, Default)]
pub struct TextIterScratch {
stack: Vec<(&'static Children, usize)>,
}
impl TextIterScratch {
fn take<'a>(&mut self) -> Vec<(&'a Children, usize)> {
core::mem::take(&mut self.stack)
.into_iter()
.map(|_| -> (&Children, usize) { unreachable!() })
.collect()
}
fn recover(&mut self, mut stack: Vec<(&Children, usize)>) {
stack.clear();
self.stack = stack
.into_iter()
.map(|_| -> (&'static Children, usize) { unreachable!() })
.collect();
}
}
#[derive(SystemParam)]
pub struct TextReader<'w, 's, R: TextRoot> {
scratch: Local<'s, TextIterScratch>,
roots: Query<
'w,
's,
(
&'static R,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
spans: Query<
'w,
's,
(
&'static TextSpan,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
}
impl<'w, 's, R: TextRoot> TextReader<'w, 's, R> {
pub fn iter(&mut self, root_entity: Entity) -> TextSpanIter<'_, R> {
let stack = self.scratch.take();
TextSpanIter {
scratch: &mut self.scratch,
root_entity: Some(root_entity),
stack,
roots: &self.roots,
spans: &self.spans,
}
}
pub fn get(
&mut self,
root_entity: Entity,
index: usize,
) -> Option<(Entity, usize, &str, &TextFont, Color)> {
self.iter(root_entity).nth(index)
}
pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option<&str> {
self.get(root_entity, index).map(|(_, _, text, _, _)| text)
}
pub fn get_font(&mut self, root_entity: Entity, index: usize) -> Option<&TextFont> {
self.get(root_entity, index).map(|(_, _, _, font, _)| font)
}
pub fn get_color(&mut self, root_entity: Entity, index: usize) -> Option<Color> {
self.get(root_entity, index)
.map(|(_, _, _, _, color)| color)
}
pub fn text(&mut self, root_entity: Entity, index: usize) -> &str {
self.get_text(root_entity, index).unwrap()
}
pub fn font(&mut self, root_entity: Entity, index: usize) -> &TextFont {
self.get_font(root_entity, index).unwrap()
}
pub fn color(&mut self, root_entity: Entity, index: usize) -> Color {
self.get_color(root_entity, index).unwrap()
}
}
pub struct TextSpanIter<'a, R: TextRoot> {
scratch: &'a mut TextIterScratch,
root_entity: Option<Entity>,
stack: Vec<(&'a Children, usize)>,
roots: &'a Query<
'a,
'a,
(
&'static R,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
spans: &'a Query<
'a,
'a,
(
&'static TextSpan,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
}
impl<'a, R: TextRoot> Iterator for TextSpanIter<'a, R> {
type Item = (Entity, usize, &'a str, &'a TextFont, Color);
fn next(&mut self) -> Option<Self::Item> {
if let Some(root_entity) = self.root_entity.take() {
if let Ok((text, text_font, color, maybe_children)) = self.roots.get(root_entity) {
if let Some(children) = maybe_children {
self.stack.push((children, 0));
}
return Some((root_entity, 0, text.read_span(), text_font, color.0));
}
return None;
}
loop {
let (children, idx) = self.stack.last_mut()?;
loop {
let Some(child) = children.get(*idx) else {
break;
};
*idx += 1;
let entity = *child;
let Ok((span, text_font, color, maybe_children)) = self.spans.get(entity) else {
continue;
};
let depth = self.stack.len();
if let Some(children) = maybe_children {
self.stack.push((children, 0));
}
return Some((entity, depth, span.read_span(), text_font, color.0));
}
self.stack.pop();
}
}
}
impl<'a, R: TextRoot> Drop for TextSpanIter<'a, R> {
fn drop(&mut self) {
let stack = core::mem::take(&mut self.stack);
self.scratch.recover(stack);
}
}
#[derive(SystemParam)]
pub struct TextWriter<'w, 's, R: TextRoot> {
scratch: ResMut<'w, TextIterScratch>,
roots: Query<
'w,
's,
(
&'static mut R,
&'static mut TextFont,
&'static mut TextColor,
),
Without<TextSpan>,
>,
spans: Query<
'w,
's,
(
&'static mut TextSpan,
&'static mut TextFont,
&'static mut TextColor,
),
Without<R>,
>,
children: Query<'w, 's, &'static Children>,
}
impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> {
pub fn get(
&mut self,
root_entity: Entity,
index: usize,
) -> Option<(
Entity,
usize,
Mut<'_, String>,
Mut<'_, TextFont>,
Mut<'_, TextColor>,
)> {
if index == 0 {
let (text, font, color) = self.roots.get_mut(root_entity).ok()?;
return Some((
root_entity,
0,
text.map_unchanged(|t| t.write_span()),
font,
color,
));
}
let mut stack: Vec<(&Children, usize)> = self.scratch.take();
if let Ok(children) = self.children.get(root_entity) {
stack.push((children, 0));
}
let mut count = 1;
let (depth, entity) = 'l: loop {
let Some((children, idx)) = stack.last_mut() else {
self.scratch.recover(stack);
return None;
};
loop {
let Some(child) = children.get(*idx) else {
stack.pop();
break;
};
*idx += 1;
if !self.spans.contains(*child) {
continue;
};
count += 1;
if count - 1 == index {
let depth = stack.len();
self.scratch.recover(stack);
break 'l (depth, *child);
}
if let Ok(children) = self.children.get(*child) {
stack.push((children, 0));
break;
}
}
};
let (text, font, color) = self.spans.get_mut(entity).unwrap();
Some((
entity,
depth,
text.map_unchanged(|t| t.write_span()),
font,
color,
))
}
pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option<Mut<'_, String>> {
self.get(root_entity, index).map(|(_, _, text, ..)| text)
}
pub fn get_font(&mut self, root_entity: Entity, index: usize) -> Option<Mut<'_, TextFont>> {
self.get(root_entity, index).map(|(_, _, _, font, _)| font)
}
pub fn get_color(&mut self, root_entity: Entity, index: usize) -> Option<Mut<'_, TextColor>> {
self.get(root_entity, index)
.map(|(_, _, _, _, color)| color)
}
pub fn text(&mut self, root_entity: Entity, index: usize) -> Mut<'_, String> {
self.get_text(root_entity, index).unwrap()
}
pub fn font(&mut self, root_entity: Entity, index: usize) -> Mut<'_, TextFont> {
self.get_font(root_entity, index).unwrap()
}
pub fn color(&mut self, root_entity: Entity, index: usize) -> Mut<'_, TextColor> {
self.get_color(root_entity, index).unwrap()
}
pub fn for_each(
&mut self,
root_entity: Entity,
mut callback: impl FnMut(Entity, usize, Mut<String>, Mut<TextFont>, Mut<TextColor>),
) {
self.for_each_until(root_entity, |a, b, c, d, e| {
(callback)(a, b, c, d, e);
true
});
}
pub fn for_each_text(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut<String>)) {
self.for_each(root_entity, |_, _, text, _, _| {
(callback)(text);
});
}
pub fn for_each_font(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut<TextFont>)) {
self.for_each(root_entity, |_, _, _, font, _| {
(callback)(font);
});
}
pub fn for_each_color(
&mut self,
root_entity: Entity,
mut callback: impl FnMut(Mut<TextColor>),
) {
self.for_each(root_entity, |_, _, _, _, color| {
(callback)(color);
});
}
pub fn for_each_until(
&mut self,
root_entity: Entity,
mut callback: impl FnMut(Entity, usize, Mut<String>, Mut<TextFont>, Mut<TextColor>) -> bool,
) {
let Ok((text, font, color)) = self.roots.get_mut(root_entity) else {
return;
};
if !(callback)(
root_entity,
0,
text.map_unchanged(|t| t.write_span()),
font,
color,
) {
return;
}
let mut stack: Vec<(&Children, usize)> = self.scratch.take();
if let Ok(children) = self.children.get(root_entity) {
stack.push((children, 0));
}
loop {
let depth = stack.len();
let Some((children, idx)) = stack.last_mut() else {
self.scratch.recover(stack);
return;
};
loop {
let Some(child) = children.get(*idx) else {
stack.pop();
break;
};
*idx += 1;
let entity = *child;
let Ok((text, font, color)) = self.spans.get_mut(entity) else {
continue;
};
if !(callback)(
entity,
depth,
text.map_unchanged(|t| t.write_span()),
font,
color,
) {
self.scratch.recover(stack);
return;
}
if let Ok(children) = self.children.get(entity) {
stack.push((children, 0));
break;
}
}
}
}
}