use bevy::platform::collections::{HashMap, HashSet};
use bevy::prelude::*;
use bevy::text::{LetterSpacing, LineHeight};
use crossbeam_channel::Receiver;
use crate::protocol::{NodeId, Op, Outbound, Style};
pub type ResolvedTextStyle = (TextColor, TextFont, LineHeight, LetterSpacing);
pub type OpReceiver = Receiver<Vec<Op>>;
#[cfg(not(target_arch = "wasm32"))]
pub type OutboundSender = tokio::sync::mpsc::UnboundedSender<Outbound>;
#[cfg(target_arch = "wasm32")]
pub type OutboundSender = crossbeam_channel::Sender<Outbound>;
#[derive(Component, Debug, Clone, Copy)]
pub struct RNode(pub NodeId);
#[derive(Component, Debug, Clone, Default)]
pub struct StyleVariants {
pub base: Option<Style>,
pub hover: Option<Style>,
pub press: Option<Style>,
pub focus: Option<Style>,
}
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct FocusState(pub bool);
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct PointerHandlers {
pub down: bool,
pub moved: bool,
pub up: bool,
pub enter: bool,
pub leave: bool,
}
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct HoverState(pub bool);
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct ScrollListener;
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct WheelListener;
#[derive(Component, Debug, Clone, Copy)]
pub struct ScrollStep(pub f32);
#[derive(Component, Debug, Clone, Copy, Default)]
pub struct CanvasSizeTracker(pub (u32, u32));
#[derive(Resource, Clone)]
pub struct OutboundResource(pub OutboundSender);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpanKind {
RawInherited,
InlineStyled,
}
#[derive(Resource)]
pub struct JsBridge {
pub ops_rx: OpReceiver,
pub outbound_tx: OutboundSender,
pub nodes: HashMap<NodeId, Entity>,
pub props_cache: HashMap<NodeId, Box<crate::protocol::Props>>,
pub text_styles: HashMap<NodeId, ResolvedTextStyle>,
pub spans: HashMap<NodeId, SpanKind>,
pub editable_inputs: HashSet<NodeId>,
pub surfaces: HashSet<NodeId>,
pub editable_values: HashMap<NodeId, String>,
pub editable_selections: HashMap<NodeId, (usize, usize)>,
pub editable_select_handlers: HashSet<NodeId>,
pub editable_focus_handlers: HashSet<NodeId>,
pub editable_pending_selection: HashMap<NodeId, (usize, usize)>,
pub scroll_positions: HashMap<NodeId, Vec2>,
pub siblings: HashMap<NodeId, SiblingLinks>,
pub child_list: HashMap<NodeId, ChildList>,
pub parent_of: HashMap<NodeId, NodeId>,
pub surface_parent: HashMap<NodeId, NodeId>,
pub child_surfaces: HashMap<NodeId, Vec<NodeId>>,
}
#[derive(Clone, Copy, Default)]
pub struct SiblingLinks {
prev: Option<NodeId>,
next: Option<NodeId>,
}
#[derive(Clone, Copy)]
pub struct ChildList {
head: NodeId,
tail: NodeId,
}
impl JsBridge {
pub fn new(ops_rx: OpReceiver, outbound_tx: OutboundSender, root: Entity) -> Self {
let mut nodes = HashMap::new();
nodes.insert(crate::protocol::ROOT_ID, root);
Self {
ops_rx,
outbound_tx,
nodes,
props_cache: HashMap::new(),
text_styles: HashMap::new(),
spans: HashMap::new(),
editable_inputs: HashSet::new(),
surfaces: HashSet::new(),
editable_values: HashMap::new(),
editable_selections: HashMap::new(),
editable_select_handlers: HashSet::new(),
editable_focus_handlers: HashSet::new(),
editable_pending_selection: HashMap::new(),
scroll_positions: HashMap::new(),
siblings: HashMap::new(),
child_list: HashMap::new(),
parent_of: HashMap::new(),
surface_parent: HashMap::new(),
child_surfaces: HashMap::new(),
}
}
pub fn attach_surface(&mut self, surface: NodeId, parent: NodeId) {
self.detach_surface(surface);
self.surface_parent.insert(surface, parent);
self.child_surfaces.entry(parent).or_default().push(surface);
}
pub fn detach_surface(&mut self, surface: NodeId) {
if let Some(parent) = self.surface_parent.remove(&surface)
&& let Some(list) = self.child_surfaces.get_mut(&parent)
{
list.retain(|&id| id != surface);
}
}
pub fn surfaces_under(&mut self, node: NodeId) -> Vec<NodeId> {
let mut out = Vec::new();
self.collect_surfaces(node, &mut out);
out
}
fn collect_surfaces(&mut self, node: NodeId, out: &mut Vec<NodeId>) {
if let Some(surfaces) = self.child_surfaces.remove(&node) {
for surface in surfaces {
self.surface_parent.remove(&surface);
out.push(surface);
self.collect_surfaces(surface, out);
}
}
let kids: Vec<NodeId> = self.children_of(node).collect();
for kid in kids {
self.collect_surfaces(kid, out);
}
}
pub fn detach(&mut self, child: NodeId) {
let Some(parent) = self.parent_of.remove(&child) else {
return;
};
let Some(links) = self.siblings.remove(&child) else {
return;
};
if let Some(prev) = links.prev
&& let Some(l) = self.siblings.get_mut(&prev)
{
l.next = links.next;
}
if let Some(next) = links.next
&& let Some(l) = self.siblings.get_mut(&next)
{
l.prev = links.prev;
}
if let Some(list) = self.child_list.get_mut(&parent) {
match (links.prev, links.next) {
(None, None) => {
self.child_list.remove(&parent);
}
(None, Some(next)) => list.head = next,
(Some(prev), None) => list.tail = prev,
(Some(_), Some(_)) => {}
}
}
}
pub fn append_child(&mut self, parent: NodeId, child: NodeId) {
self.detach(child);
self.parent_of.insert(child, parent);
match self.child_list.get_mut(&parent) {
Some(list) => {
let old_tail = list.tail;
if let Some(l) = self.siblings.get_mut(&old_tail) {
l.next = Some(child);
}
self.siblings.insert(
child,
SiblingLinks {
prev: Some(old_tail),
next: None,
},
);
list.tail = child;
}
None => {
self.siblings.insert(child, SiblingLinks::default());
self.child_list.insert(
parent,
ChildList {
head: child,
tail: child,
},
);
}
}
}
pub fn insert_before(&mut self, parent: NodeId, child: NodeId, before: NodeId) {
self.detach(child);
if self.parent_of.get(&before) != Some(&parent) {
self.append_child(parent, child);
return;
}
self.parent_of.insert(child, parent);
let before_links = self
.siblings
.get_mut(&before)
.expect("attached child has sibling links");
let prev = before_links.prev;
before_links.prev = Some(child);
self.siblings.insert(
child,
SiblingLinks {
prev,
next: Some(before),
},
);
match prev {
Some(p) => {
if let Some(l) = self.siblings.get_mut(&p) {
l.next = Some(child);
}
}
None => {
if let Some(list) = self.child_list.get_mut(&parent) {
list.head = child;
}
}
}
}
pub fn children_of(&self, parent: NodeId) -> impl Iterator<Item = NodeId> + '_ {
let mut cursor = self.child_list.get(&parent).map(|l| l.head);
std::iter::from_fn(move || {
let id = cursor?;
cursor = self.siblings.get(&id).and_then(|l| l.next);
Some(id)
})
}
fn forget_node_data(&mut self, id: NodeId) {
self.nodes.remove(&id);
self.props_cache.remove(&id);
self.text_styles.remove(&id);
self.spans.remove(&id);
self.editable_inputs.remove(&id);
self.surfaces.remove(&id);
self.editable_values.remove(&id);
self.editable_selections.remove(&id);
self.editable_select_handlers.remove(&id);
self.editable_focus_handlers.remove(&id);
self.editable_pending_selection.remove(&id);
self.scroll_positions.remove(&id);
}
pub fn forget_subtree(&mut self, child: NodeId) {
self.forget_node_data(child);
let grandkids: Vec<NodeId> = self.children_of(child).collect();
self.child_list.remove(&child);
for grandkid in grandkids {
self.parent_of.remove(&grandkid);
self.siblings.remove(&grandkid);
self.forget_subtree(grandkid);
}
}
}