use super::*;
use waterui_graphics::{resolve_scene_proposal, scene_stretch_axis};
impl RenderNode {
pub(super) fn priority(&self) -> i32 {
match self {
RenderNode::Wrapper(node) => match &node.effect {
WrapperEffect::LayoutPriority(priority) => priority.get(),
_ => node.child.priority(),
},
RenderNode::Opacity(node) => node.child.priority(),
RenderNode::Scale(node) => node.child.priority(),
RenderNode::Rotation(node) => node.child.priority(),
RenderNode::Offset(node) => node.child.priority(),
RenderNode::Retain(node) => node.child.priority(),
RenderNode::Env(node) => node.child.priority(),
RenderNode::Dynamic(node) => node.child.priority(),
RenderNode::AppliedFilter(node) => node.child.priority(),
RenderNode::Widget(node) => node.behavior.priority(),
_ => 0,
}
}
pub(super) fn stretch(&self) -> StretchAxis {
match self {
RenderNode::Color(_) => StretchAxis::Both,
RenderNode::Text(_) => StretchAxis::None,
RenderNode::Container(container) => {
let child_axes: Vec<StretchAxis> =
container.children.iter().map(RenderNode::stretch).collect();
container.layout.stretch_axis(&child_axes)
}
RenderNode::Opacity(node) => node.child.stretch(),
RenderNode::Scale(node) => node.child.stretch(),
RenderNode::Rotation(node) => node.child.stretch(),
RenderNode::Offset(node) => node.child.stretch(),
RenderNode::Retain(node) => node.child.stretch(),
RenderNode::Env(node) => node.child.stretch(),
RenderNode::Dynamic(node) => node.child.stretch(),
RenderNode::SceneView(node) => {
scene_stretch_axis(node.content.borrow().intrinsic_size())
}
RenderNode::GpuSurface(_) => StretchAxis::Both,
RenderNode::ViewEffect(_) => StretchAxis::None,
RenderNode::AppliedFilter(node) => node.child.stretch(),
RenderNode::Scroll(_) => StretchAxis::Both,
RenderNode::LazyStack(_) => StretchAxis::None,
RenderNode::Collection(node) => {
node.layout.stretch_axis(&[])
}
RenderNode::Wrapper(node) => node.child.stretch(),
RenderNode::Widget(node) => node.stretch,
}
}
pub(in crate::renderer) fn measure(
&self,
state: &mut HydroState,
env: &Environment,
theme: &Rc<dyn crate::engine::WidgetTheme>,
proposal: ProposalSize,
) -> ViewDimensions {
match self {
RenderNode::Color(_) => ViewDimensions::new(Size::new(
proposal.width.unwrap_or(0.0),
proposal.height.unwrap_or(0.0),
)),
RenderNode::Text(text) => HydrolysisRenderer::measure_text_dimensions(
state,
text.content.get(),
text.alignment.get(),
env,
proposal.width,
text.line_limit,
),
RenderNode::Container(container) => {
let cell = RefCell::new(state);
let subs: Vec<NodeSubView> = container
.children
.iter()
.map(|child| NodeSubView::new(child, &cell, env, theme))
.collect();
let refs: Vec<&dyn SubView> = subs.iter().map(|sub| sub as &dyn SubView).collect();
ViewDimensions::new(container.layout.size_that_fits(proposal, &refs))
}
RenderNode::Opacity(node) => node.child.measure(state, env, theme, proposal),
RenderNode::Scale(node) => node.child.measure(state, env, theme, proposal),
RenderNode::Rotation(node) => node.child.measure(state, env, theme, proposal),
RenderNode::Offset(node) => node.child.measure(state, env, theme, proposal),
RenderNode::Retain(node) => node.child.measure(state, env, theme, proposal),
RenderNode::Env(node) => node.child.measure(state, &node.env, theme, proposal),
RenderNode::Dynamic(node) => node.child.measure(state, env, theme, proposal),
RenderNode::SceneView(node) => {
let resolved =
resolve_scene_proposal(node.content.borrow().intrinsic_size(), proposal);
ViewDimensions::new(Size::new(
resolved.width.unwrap_or(0.0),
resolved.height.unwrap_or(0.0),
))
}
RenderNode::GpuSurface(_) => ViewDimensions::new(Size::new(
proposal.width.unwrap_or(0.0),
proposal.height.unwrap_or(0.0),
)),
RenderNode::ViewEffect(node) => node
.child
.borrow()
.measure(state, &node.env, theme, proposal),
RenderNode::AppliedFilter(node) => {
node.child.measure(state, &node.env, theme, proposal)
}
RenderNode::Scroll(_) => ViewDimensions::new(Size::new(
proposal.width.unwrap_or(0.0),
proposal.height.unwrap_or(0.0),
)),
RenderNode::LazyStack(node) => node.measure(state, theme, proposal),
RenderNode::Collection(node) => node.measure(state, theme, proposal),
RenderNode::Wrapper(node) => node.child.measure(state, &node.env, theme, proposal),
RenderNode::Widget(node) => node.behavior.measure(state, proposal, &node.env, theme),
}
}
pub(in crate::renderer) fn prepare_for_measure(&mut self, renderer: &mut HydrolysisRenderer) {
match self {
RenderNode::Widget(node) => node.behavior.prepare(renderer, &node.env),
RenderNode::Opacity(node) => node.child.prepare_for_measure(renderer),
RenderNode::Scale(node) => node.child.prepare_for_measure(renderer),
RenderNode::Rotation(node) => node.child.prepare_for_measure(renderer),
RenderNode::Offset(node) => node.child.prepare_for_measure(renderer),
RenderNode::Retain(node) => node.child.prepare_for_measure(renderer),
RenderNode::Dynamic(node) => node.child.prepare_for_measure(renderer),
RenderNode::Env(node) => node.child.prepare_for_measure(renderer),
RenderNode::Wrapper(node) => node.child.prepare_for_measure(renderer),
RenderNode::AppliedFilter(node) => node.child.prepare_for_measure(renderer),
RenderNode::ViewEffect(node) => {
node.child.borrow_mut().prepare_for_measure(renderer);
}
RenderNode::Container(node) => {
for child in &mut node.children {
child.prepare_for_measure(renderer);
}
}
RenderNode::Scroll(node) => node.child.prepare_for_measure(renderer),
RenderNode::Collection(node) => {
for entry in &mut node.entries {
entry.node.prepare_for_measure(renderer);
}
}
RenderNode::LazyStack(node) => {
node.item_cache.borrow_mut().prepare_for_measure(renderer)
}
RenderNode::Color(_)
| RenderNode::Text(_)
| RenderNode::SceneView(_)
| RenderNode::GpuSurface(_) => {}
}
}
pub(crate) fn layout(
&mut self,
renderer: &mut HydrolysisRenderer,
env: &Environment,
proposal: ProposalSize,
size: Size,
) {
let theme = renderer.theme();
match self {
RenderNode::Container(container) => {
let placements = {
let cell = RefCell::new(&mut renderer.state);
let subs: Vec<NodeSubView> = container
.children
.iter()
.map(|child| NodeSubView::new(child, &cell, env, &theme))
.collect();
let refs: Vec<&dyn SubView> =
subs.iter().map(|sub| sub as &dyn SubView).collect();
container
.layout
.place(Rect::from_size(size), proposal, &refs)
};
for (child, placement) in container.children.iter_mut().zip(&placements) {
child.layout(renderer, env, placement.proposal, *placement.frame.size());
}
container.placed = placements
.into_iter()
.map(|placement| placement.frame)
.collect();
}
RenderNode::Opacity(node) => node.child.layout(renderer, env, proposal, size),
RenderNode::Scale(node) => node.child.layout(renderer, env, proposal, size),
RenderNode::Rotation(node) => node.child.layout(renderer, env, proposal, size),
RenderNode::Offset(node) => node.child.layout(renderer, env, proposal, size),
RenderNode::Retain(node) => node.child.layout(renderer, env, proposal, size),
RenderNode::Env(node) => {
let node_env = node.env.clone();
node.child.layout(renderer, &node_env, proposal, size);
}
RenderNode::Wrapper(node) => {
let node_env = node.env.clone();
node.child.layout(renderer, &node_env, proposal, size);
}
RenderNode::Dynamic(node) => node.child.layout(renderer, env, proposal, size),
RenderNode::Scroll(node) => {
let child_proposal = match node.axis {
ScrollAxis::Horizontal => ProposalSize::new(None, Some(size.height)),
ScrollAxis::Vertical => ProposalSize::new(Some(size.width), None),
ScrollAxis::All => ProposalSize::UNSPECIFIED,
_ => panic!("hydrolysis render tree: unsupported scroll axis"),
};
let intrinsic = node
.child
.measure(&mut renderer.state, env, &theme, child_proposal)
.size;
let content_size = match node.axis {
ScrollAxis::Horizontal => {
Size::new(intrinsic.width.max(size.width), size.height)
}
ScrollAxis::Vertical => {
Size::new(size.width, intrinsic.height.max(size.height))
}
ScrollAxis::All => Size::new(
intrinsic.width.max(size.width),
intrinsic.height.max(size.height),
),
_ => panic!("hydrolysis render tree: unsupported scroll axis"),
};
node.child
.layout(renderer, env, child_proposal, content_size);
let handle = if let Some(handle) = node.handle.borrow_mut().as_mut() {
handle.rebind(
node.axis,
f64::from(size.width),
f64::from(size.height),
f64::from(content_size.width),
f64::from(content_size.height),
)
} else {
ScrollHandle::new(
node.axis,
f64::from(size.width),
f64::from(size.height),
f64::from(content_size.width),
f64::from(content_size.height),
)
};
if let Some(controller) = &node.controller {
let generation = renderer.read_signal(&controller.generation());
if generation != node.applied_scroll_generation.get() {
let target = renderer.read_signal(&controller.target());
let _ = handle.scroll_to(f64::from(target.x), f64::from(target.y));
node.applied_scroll_generation.set(generation);
}
}
*node.handle.borrow_mut() = Some(handle);
node.content_size = content_size;
node.viewport = size;
}
RenderNode::Collection(node) => node.layout(renderer, proposal, size),
RenderNode::ViewEffect(node) => {
let node_env = node.env.clone();
node.child
.borrow_mut()
.layout(renderer, &node_env, proposal, size);
}
RenderNode::AppliedFilter(node) => {
let node_env = node.env.clone();
node.child.layout(renderer, &node_env, proposal, size);
}
RenderNode::Color(_)
| RenderNode::Text(_)
| RenderNode::SceneView(_)
| RenderNode::GpuSurface(_)
| RenderNode::LazyStack(_)
| RenderNode::Widget(_) => {}
}
}
}