use std::collections::HashMap;
use bevy::prelude::*;
use crate::render::{NoesisRenderState, NoesisSet};
#[derive(Component, Clone, Default, Debug)]
pub struct NoesisText {
pub set: HashMap<String, String>,
pub watch: Vec<String>,
}
impl NoesisText {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(mut self, name: impl Into<String>, text: impl Into<String>) -> Self {
self.set.insert(name.into(), text.into());
self
}
#[must_use]
pub fn watching(mut self, names: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.watch.extend(names.into_iter().map(Into::into));
self
}
pub fn write(&mut self, name: impl Into<String>, text: impl Into<String>) {
self.set.insert(name.into(), text.into());
}
pub fn observe(&mut self, name: impl Into<String>) {
let name = name.into();
if !self.watch.contains(&name) {
self.watch.push(name);
}
}
}
#[derive(Message, Debug, Clone)]
pub struct NoesisTextChanged {
pub view: Entity,
pub name: String,
pub text: String,
}
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn sync_text_bridge(
views: Query<(Entity, Ref<NoesisText>)>,
state: Option<NonSendMut<NoesisRenderState>>,
mut changed: MessageWriter<NoesisTextChanged>,
) {
let Some(mut state) = state else {
return;
};
for (entity, text) in &views {
if text.is_changed() || state.scene_rebuilt_this_frame(entity) {
state.apply_text_writes_for(entity, &text.set);
}
for (name, value) in state.poll_text_reads_for(entity, &text.watch) {
changed.write(NoesisTextChanged {
view: entity,
name,
text: value,
});
}
}
}
pub struct NoesisTextPlugin;
impl Plugin for NoesisTextPlugin {
fn build(&self, app: &mut App) {
app.add_message::<NoesisTextChanged>()
.add_systems(PostUpdate, sync_text_bridge.in_set(NoesisSet::Apply));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_collects_set_and_watch() {
let t = NoesisText::new()
.with("Title", "Hello")
.with("Sub", "World")
.watching(["Status", "Clock"]);
assert_eq!(t.set.get("Title").map(String::as_str), Some("Hello"));
assert_eq!(t.set.get("Sub").map(String::as_str), Some("World"));
assert_eq!(t.watch, vec!["Status".to_string(), "Clock".to_string()]);
}
}