use bevy::prelude::*;
use super::apply_js_ops;
use super::stats::OpApplyStats;
use crate::bridge::JsBridge;
use crate::plugin::Fonts;
use crate::protocol::{NodeId, Op, Outbound, Props};
use crate::ui_map::AtlasLayoutCache;
pub(crate) fn op_app() -> (App, crossbeam_channel::Sender<Vec<Op>>) {
let (app, ops_tx, _root) = ordering_app();
(app, ops_tx)
}
pub(crate) fn ordering_app() -> (App, crossbeam_channel::Sender<Vec<Op>>, Entity) {
let mut app = App::new();
app.add_plugins((MinimalPlugins, AssetPlugin::default()));
app.init_asset::<Image>();
app.init_asset::<TextureAtlasLayout>();
app.init_resource::<Fonts>();
app.init_resource::<OpApplyStats>();
app.init_resource::<AtlasLayoutCache>();
let (ops_tx, ops_rx) = crossbeam_channel::unbounded::<Vec<Op>>();
let (out_tx, out_rx) = tokio::sync::mpsc::unbounded_channel::<Outbound>();
std::mem::forget(out_rx); let root = app.world_mut().spawn_empty().id();
app.insert_resource(JsBridge::new(ops_rx, out_tx, root));
app.add_systems(Update, apply_js_ops);
(app, ops_tx, root)
}
pub(crate) fn create_node(id: NodeId) -> Op {
Op::Create {
id,
kind: "node".into(),
props: Props::default(),
text: None,
}
}
pub(crate) fn update_delta(id: NodeId, props: Props, unset: &[&str], style_unset: &[&str]) -> Op {
Op::Update {
id,
props,
unset: unset.iter().map(|s| s.to_string()).collect(),
style_unset: style_unset.iter().map(|s| s.to_string()).collect(),
}
}
pub(crate) fn text_props(rotate: f32) -> Props {
serde_json::from_value(serde_json::json!({
"style": {
"transform": { "rotate": format!("{rotate}rad") },
"transition": { "transform": { "duration": 0.3 } },
}
}))
.expect("valid text props")
}
pub(crate) fn ent(app: &App, id: NodeId) -> Entity {
app.world().resource::<JsBridge>().nodes[&id]
}
pub(crate) fn children_of(app: &App, parent: Entity) -> Vec<Entity> {
app.world()
.entity(parent)
.get::<Children>()
.map(|c| c.iter().collect())
.unwrap_or_default()
}