use std::fmt::Debug;
pub use std::fmt::Write; use std::ops::DerefMut;
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use crate::prelude::{FontMap, FontRequest, LocalizedText, TextLocalizer};
#[derive(SystemParam)]
pub struct TextEditor<'w, 's>
{
localized: Query<'w, 's, &'static mut LocalizedText>,
writer: TextUiWriter<'w, 's>,
localizer: Res<'w, TextLocalizer>,
fonts: Res<'w, FontMap>,
}
impl<'w, 's> TextEditor<'w, 's>
{
pub fn root(&mut self, root_entity: impl Into<Entity>) -> Option<(&mut String, &mut TextFont, &mut Color)>
{
let root_entity: Entity = root_entity.into();
self.span(root_entity, 0).map(|(_, t, f, c)| (t, f, c))
}
pub fn span(
&mut self,
root_entity: impl Into<Entity>,
span: usize,
) -> Option<(Entity, &mut String, &mut TextFont, &mut Color)>
{
let root_entity: Entity = root_entity.into();
self.writer
.get(root_entity, span)
.map(|(e, _, t, f, c)| (e, t.into_inner(), f.into_inner(), c.into_inner().deref_mut()))
}
pub fn write<E: Debug>(
&mut self,
root_entity: impl Into<Entity>,
writer: impl FnOnce(&mut String) -> Result<(), E>,
) -> bool
{
self.write_span(root_entity, 0, writer)
}
pub fn write_span<E: Debug>(
&mut self,
root_entity: impl Into<Entity>,
span: usize,
writer: impl FnOnce(&mut String) -> Result<(), E>,
) -> bool
{
let root_entity: Entity = root_entity.into();
let Some((_, _, mut text, mut text_font, _)) = self.writer.get(root_entity, span) else {
tracing::warn!("failed writing to text span {span} of text block {root_entity:?}, entity not found");
return false;
};
if let Ok(mut localized) = self.localized.get_mut(root_entity) {
localized.set_localization_for_span("", span);
let localization_span = localized.localization_for_span_mut(span).unwrap();
let result = match (writer)(&mut localization_span.template) {
Ok(()) => true,
Err(err) => {
tracing::warn!("failed writing to localized text span {span} of text block {root_entity:?}, \
write callback error {err:?}");
false
}
};
result && localized.localize_span(&self.localizer, &self.fonts, &mut text, &mut text_font.font, span)
} else {
text.clear();
match (writer)(&mut *text) {
Ok(()) => true,
Err(err) => {
tracing::warn!("failed writing to text span {span} of text block {root_entity:?}, \
write callback error {err:?}");
false
}
}
}
}
pub fn set_font(&mut self, entity: impl Into<Entity>, font: impl Into<FontRequest>) -> bool
{
self.set_span_font(entity, 0, font)
}
pub fn set_span_font(
&mut self,
root_entity: impl Into<Entity>,
span: usize,
font: impl Into<FontRequest>,
) -> bool
{
let root_entity: Entity = root_entity.into();
let font = font.into();
let Some((_, _, _, mut text_font, _)) = self.writer.get(root_entity, span) else {
tracing::warn!("failed setting font {font:?} on text span {span} of text block {root_entity:?}, \
root entity not found");
return false;
};
let font = self.fonts.get(&font);
if font == Handle::default() {
tracing::warn!("failed setting font {font:?} on text span {span} of text block {root_entity:?}, font \
not found in FontMap");
return false;
}
text_font.font = font.clone();
if let Ok(mut localized) = self.localized.get_mut(root_entity) {
if localized.localization_for_span_mut(span).is_none() {
localized.set_localization_for_span("", span);
}
let loc_span = localized.localization_for_span_mut(span).unwrap();
loc_span.set_font_backup(font.clone());
if loc_span.lang().is_none() {
return true;
}
loc_span.update_font(&self.fonts, &mut text_font.font);
}
true
}
pub fn set_font_size(&mut self, entity: impl Into<Entity>, size: f32)
{
self.set_span_font_size(entity, 0, size);
}
pub fn set_span_font_size(&mut self, root_entity: impl Into<Entity>, span: usize, size: f32)
{
let root_entity: Entity = root_entity.into();
let Some((_, _, _, mut text_font, _)) = self.writer.get(root_entity, span) else {
tracing::warn!("failed setting font size {size:?} on text span {span} of text block {root_entity:?}, \
root entity not found");
return;
};
text_font.font_size = size;
}
pub fn set_font_color(&mut self, root_entity: impl Into<Entity>, color: Color)
{
self.set_span_font_color(root_entity, 0, color);
}
pub fn set_span_font_color(&mut self, root_entity: impl Into<Entity>, span: usize, color: Color)
{
let root_entity: Entity = root_entity.into();
let Some((_, _, _, _, mut text_color)) = self.writer.get(root_entity, span) else {
tracing::warn!("failed setting font color {color:?} on text span {span} of text block {root_entity:?}, \
root entity not found");
return;
};
*text_color = TextColor(color);
}
}
#[macro_export]
macro_rules! write_text {
($editor: ident, $entity: expr, $($arg:tt)*) => {{
$editor.write($entity, |text| write!(text, $($arg)*))
}};
}
#[macro_export]
macro_rules! write_text_span {
($editor: ident, $entity: expr, $span: expr, $($arg:tt)*) => {{
$editor.write_span($entity, $span, |text| write!(text, $($arg)*))
}};
}