use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::rc::Rc;
use cranpose_core::{
Composer, NodeError, NodeId, Phase, SlotId, SlotTable, SlotsHost, SubcomposeState,
};
use crate::modifier::{
collect_modifier_slices_into, Modifier, ModifierChainHandle, ModifierNodeSlices, Point,
ResolvedModifiers, Size,
};
use crate::widgets::nodes::{
allocate_virtual_node_id, is_virtual_node, register_layout_node, LayoutNode, LayoutState,
};
use cranpose_foundation::{InvalidationKind, ModifierInvalidation, NodeCapabilities};
pub use cranpose_ui_layout::{Constraints, MeasureResult, Placement};
#[derive(Clone, Copy, Debug)]
pub struct SubcomposeChild {
node_id: NodeId,
measured_size: Option<Size>,
}
impl SubcomposeChild {
pub fn new(node_id: NodeId) -> Self {
Self {
node_id,
measured_size: None,
}
}
pub fn with_size(node_id: NodeId, size: Size) -> Self {
Self {
node_id,
measured_size: Some(size),
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn size(&self) -> Size {
self.measured_size.unwrap_or(Size {
width: 0.0,
height: 0.0,
})
}
pub fn width(&self) -> f32 {
self.size().width
}
pub fn height(&self) -> f32 {
self.size().height
}
pub fn set_size(&mut self, size: Size) {
self.measured_size = Some(size);
}
}
impl PartialEq for SubcomposeChild {
fn eq(&self, other: &Self) -> bool {
self.node_id == other.node_id
}
}
pub type SubcomposePlaceable = cranpose_ui_layout::Placeable;
pub trait SubcomposeLayoutScope {
fn constraints(&self) -> Constraints;
fn layout<I>(&mut self, width: f32, height: f32, placements: I) -> MeasureResult
where
I: IntoIterator<Item = Placement>,
{
MeasureResult::new(Size { width, height }, placements.into_iter().collect())
}
}
pub trait SubcomposeMeasureScope: SubcomposeLayoutScope {
fn subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<SubcomposeChild>
where
Content: FnMut() + 'static;
fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable;
fn node_has_no_parent(&self, node_id: NodeId) -> bool;
}
pub struct SubcomposeMeasureScopeImpl<'a> {
composer: Composer,
state: &'a mut SubcomposeState,
constraints: Constraints,
measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
error: &'a RefCell<Option<NodeError>>,
parent_handle: SubcomposeLayoutNodeHandle,
root_id: NodeId,
}
impl<'a> SubcomposeMeasureScopeImpl<'a> {
pub fn new(
composer: Composer,
state: &'a mut SubcomposeState,
constraints: Constraints,
measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
error: &'a RefCell<Option<NodeError>>,
parent_handle: SubcomposeLayoutNodeHandle,
root_id: NodeId,
) -> Self {
Self {
composer,
state,
constraints,
measurer,
error,
parent_handle,
root_id,
}
}
fn record_error(&self, err: NodeError) {
let mut slot = self.error.borrow_mut();
if slot.is_none() {
eprintln!("[SubcomposeLayout] Error suppressed: {:?}", err);
*slot = Some(err);
}
}
fn perform_subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<NodeId>
where
Content: FnMut() + 'static,
{
let content_holder = self.state.callback_holder(slot_id);
content_holder.update(content);
let mut inner = self.parent_handle.inner.borrow_mut();
let (virtual_node_id, is_reused) =
if let Some(node_id) = self.state.take_node_from_reusables(slot_id) {
(node_id, true)
} else {
let id = allocate_virtual_node_id();
let node = LayoutNode::new_virtual();
if let Err(e) = self
.composer
.register_virtual_node(id, Box::new(node.clone()))
{
eprintln!(
"[Subcompose] Failed to register virtual node {}: {:?}",
id, e
);
}
register_layout_node(id, &node);
inner.virtual_nodes.insert(id, Rc::new(node));
inner.children.push(id);
(id, false)
};
self.composer.record_subcompose_child(virtual_node_id);
if let Some(v_node) = inner.virtual_nodes.get(&virtual_node_id) {
v_node.set_parent(self.root_id);
}
drop(inner);
let _ = self
.composer
.with_node_mut::<LayoutNode, _>(virtual_node_id, |node| {
node.set_parent(self.root_id);
});
if is_reused {
self.composer.clear_node_children(virtual_node_id);
}
let slot_host = self.state.get_or_create_slots(slot_id);
let holder_for_slot = content_holder.clone();
let scopes = self
.composer
.subcompose_slot(&slot_host, Some(virtual_node_id), move |_| {
compose_subcompose_slot_content(holder_for_slot.clone());
})
.map(|(_, scopes)| scopes)
.unwrap_or_default();
self.state
.register_active(slot_id, &[virtual_node_id], &scopes);
self.composer.get_node_children(virtual_node_id).to_vec()
}
}
impl<'a> SubcomposeLayoutScope for SubcomposeMeasureScopeImpl<'a> {
fn constraints(&self) -> Constraints {
self.constraints
}
}
impl<'a> SubcomposeMeasureScope for SubcomposeMeasureScopeImpl<'a> {
fn subcompose<Content>(&mut self, slot_id: SlotId, content: Content) -> Vec<SubcomposeChild>
where
Content: FnMut() + 'static,
{
let nodes = self.perform_subcompose(slot_id, content);
nodes.into_iter().map(SubcomposeChild::new).collect()
}
fn measure(&mut self, child: SubcomposeChild, constraints: Constraints) -> SubcomposePlaceable {
if self.error.borrow().is_some() {
return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
}
if let Err(err) = self.composer.apply_pending_commands() {
self.record_error(err);
return SubcomposePlaceable::value(0.0, 0.0, child.node_id);
}
let size = (self.measurer)(child.node_id, constraints);
SubcomposePlaceable::value(size.width, size.height, child.node_id)
}
fn node_has_no_parent(&self, node_id: NodeId) -> bool {
self.composer.node_has_no_parent(node_id)
}
}
impl<'a> SubcomposeMeasureScopeImpl<'a> {
pub fn subcompose_with_size<Content, F>(
&mut self,
slot_id: SlotId,
content: Content,
estimate_size: F,
) -> Vec<SubcomposeChild>
where
Content: FnMut() + 'static,
F: Fn(usize) -> Size,
{
let nodes = self.perform_subcompose(slot_id, content);
nodes
.into_iter()
.enumerate()
.map(|(i, node_id)| SubcomposeChild::with_size(node_id, estimate_size(i)))
.collect()
}
pub fn active_slots_count(&self) -> usize {
self.state.active_slots_count()
}
pub fn reusable_slots_count(&self) -> usize {
self.state.reusable_slots_count()
}
pub fn register_content_type(&mut self, slot_id: SlotId, content_type: u64) {
self.state.register_content_type(slot_id, content_type);
}
pub fn update_content_type(&mut self, slot_id: SlotId, content_type: Option<u64>) {
self.state.update_content_type(slot_id, content_type);
}
pub fn was_last_slot_reused(&self) -> Option<bool> {
self.state.was_last_slot_reused()
}
}
fn compose_subcompose_slot_content(holder: cranpose_core::CallbackHolder) {
cranpose_core::with_current_composer(|composer| {
let holder_for_recompose = holder.clone();
composer.set_recranpose_callback(move |_composer| {
compose_subcompose_slot_content(holder_for_recompose.clone());
});
});
let invoke = holder.clone_rc();
invoke();
}
pub type MeasurePolicy =
dyn for<'scope> Fn(&mut SubcomposeMeasureScopeImpl<'scope>, Constraints) -> MeasureResult;
pub struct SubcomposeLayoutNode {
inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
parent: Cell<Option<NodeId>>,
id: Cell<Option<NodeId>>,
needs_measure: Cell<bool>,
needs_layout: Cell<bool>,
needs_semantics: Cell<bool>,
needs_redraw: Cell<bool>,
needs_pointer_pass: Cell<bool>,
needs_focus_sync: Cell<bool>,
virtual_children_count: Cell<usize>,
layout_state: RefCell<LayoutState>,
modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
modifier_slices_dirty: Cell<bool>,
}
impl SubcomposeLayoutNode {
pub fn new(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
let inner = Rc::new(RefCell::new(SubcomposeLayoutNodeInner::new(measure_policy)));
let node = Self {
inner,
parent: Cell::new(None),
id: Cell::new(None),
needs_measure: Cell::new(true),
needs_layout: Cell::new(true),
needs_semantics: Cell::new(true),
needs_redraw: Cell::new(true),
needs_pointer_pass: Cell::new(false),
needs_focus_sync: Cell::new(false),
virtual_children_count: Cell::new(0),
layout_state: RefCell::new(LayoutState::default()),
modifier_slices_snapshot: RefCell::new(Rc::default()),
modifier_slices_dirty: Cell::new(true),
};
let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
node.update_modifier_slices_cache();
node
}
pub fn with_content_type_policy(modifier: Modifier, measure_policy: Rc<MeasurePolicy>) -> Self {
let mut inner_data = SubcomposeLayoutNodeInner::new(measure_policy);
inner_data
.state
.set_policy(Box::new(cranpose_core::ContentTypeReusePolicy::new()));
let inner = Rc::new(RefCell::new(inner_data));
let node = Self {
inner,
parent: Cell::new(None),
id: Cell::new(None),
needs_measure: Cell::new(true),
needs_layout: Cell::new(true),
needs_semantics: Cell::new(true),
needs_redraw: Cell::new(true),
needs_pointer_pass: Cell::new(false),
needs_focus_sync: Cell::new(false),
virtual_children_count: Cell::new(0),
layout_state: RefCell::new(LayoutState::default()),
modifier_slices_snapshot: RefCell::new(Rc::default()),
modifier_slices_dirty: Cell::new(true),
};
let (invalidations, _) = node.inner.borrow_mut().set_modifier_collect(modifier);
node.dispatch_modifier_invalidations(&invalidations, NodeCapabilities::empty());
node.update_modifier_slices_cache();
node
}
pub fn handle(&self) -> SubcomposeLayoutNodeHandle {
SubcomposeLayoutNodeHandle {
inner: Rc::clone(&self.inner),
}
}
#[doc(hidden)]
pub fn debug_scope_ids_by_slot(&self) -> Vec<(u64, Vec<usize>)> {
self.inner.borrow().state.debug_scope_ids_by_slot()
}
#[doc(hidden)]
pub fn debug_slot_table_for_slot(
&self,
slot_id: cranpose_core::SlotId,
) -> Option<Vec<cranpose_core::SlotDebugEntry>> {
self.inner.borrow().state.debug_slot_table_for_slot(slot_id)
}
#[doc(hidden)]
pub fn debug_slot_table_groups_for_slot(
&self,
slot_id: cranpose_core::SlotId,
) -> Option<Vec<cranpose_core::subcompose::DebugSlotGroup>> {
self.inner
.borrow()
.state
.debug_slot_table_groups_for_slot(slot_id)
}
pub fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
let mut inner = self.inner.borrow_mut();
if Rc::ptr_eq(&inner.measure_policy, &policy) {
return;
}
inner.set_measure_policy(policy);
drop(inner);
self.invalidate_subcomposition();
}
pub fn set_modifier(&mut self, modifier: Modifier) {
let prev_caps = self.modifier_capabilities();
let (invalidations, modifier_changed) = {
let mut inner = self.inner.borrow_mut();
inner.set_modifier_collect(modifier)
};
self.dispatch_modifier_invalidations(&invalidations, prev_caps);
self.update_modifier_slices_cache();
if modifier_changed {
self.request_semantics_update();
}
}
fn update_modifier_slices_cache(&self) {
let inner = self.inner.borrow();
let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
collect_modifier_slices_into(inner.modifier_chain.chain(), Rc::make_mut(&mut snapshot));
self.modifier_slices_dirty.set(false);
}
pub fn set_debug_modifiers(&mut self, enabled: bool) {
self.inner.borrow_mut().set_debug_modifiers(enabled);
}
pub fn modifier(&self) -> Modifier {
self.handle().modifier()
}
pub fn resolved_modifiers(&self) -> ResolvedModifiers {
self.inner.borrow().resolved_modifiers
}
pub fn layout_state(&self) -> LayoutState {
self.layout_state.borrow().clone()
}
pub fn set_position(&self, position: Point) {
let mut state = self.layout_state.borrow_mut();
state.position = position;
state.is_placed = true;
}
pub fn set_measured_size(&self, size: Size) {
let mut state = self.layout_state.borrow_mut();
state.size = size;
}
pub fn clear_placed(&self) {
self.layout_state.borrow_mut().is_placed = false;
}
pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
if self.modifier_slices_dirty.get() {
self.update_modifier_slices_cache();
}
self.modifier_slices_snapshot.borrow().clone()
}
pub fn state(&self) -> Ref<'_, SubcomposeState> {
Ref::map(self.inner.borrow(), |inner| &inner.state)
}
pub fn state_mut(&self) -> RefMut<'_, SubcomposeState> {
RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.state)
}
pub fn invalidate_subcomposition(&self) {
self.inner.borrow().state.invalidate_scopes();
self.mark_needs_measure();
if let Some(id) = self.id.get() {
cranpose_core::bubble_measure_dirty_in_composer(id);
}
}
pub fn request_measure_recompose(&self) {
self.mark_needs_measure();
if let Some(id) = self.id.get() {
cranpose_core::bubble_measure_dirty_in_composer(id);
}
}
pub fn active_children(&self) -> Vec<NodeId> {
current_subcompose_children(&self.inner.borrow())
}
pub fn mark_needs_measure(&self) {
self.needs_measure.set(true);
self.needs_layout.set(true);
}
pub fn mark_needs_layout_flag(&self) {
self.needs_layout.set(true);
}
pub fn mark_needs_redraw(&self) {
self.needs_redraw.set(true);
if let Some(id) = self.id.get() {
crate::schedule_draw_repass(id);
}
crate::request_render_invalidation();
}
pub fn needs_measure(&self) -> bool {
self.needs_measure.get()
}
pub(crate) fn clear_needs_measure(&self) {
self.needs_measure.set(false);
}
pub(crate) fn clear_needs_layout(&self) {
self.needs_layout.set(false);
}
pub fn mark_needs_semantics(&self) {
self.needs_semantics.set(true);
}
pub fn needs_semantics_flag(&self) -> bool {
self.needs_semantics.get()
}
pub(crate) fn clear_needs_semantics(&self) {
self.needs_semantics.set(false);
}
#[cfg(test)]
pub(crate) fn clear_needs_semantics_for_tests(&self) {
self.clear_needs_semantics();
}
pub fn needs_redraw(&self) -> bool {
self.needs_redraw.get()
}
pub fn clear_needs_redraw(&self) {
self.needs_redraw.set(false);
}
pub fn mark_needs_pointer_pass(&self) {
self.needs_pointer_pass.set(true);
}
pub fn needs_pointer_pass(&self) -> bool {
self.needs_pointer_pass.get()
}
pub fn clear_needs_pointer_pass(&self) {
self.needs_pointer_pass.set(false);
}
pub fn mark_needs_focus_sync(&self) {
self.needs_focus_sync.set(true);
}
pub fn needs_focus_sync(&self) -> bool {
self.needs_focus_sync.get()
}
pub fn clear_needs_focus_sync(&self) {
self.needs_focus_sync.set(false);
}
fn request_semantics_update(&self) {
let already_dirty = self.needs_semantics.replace(true);
if already_dirty {
return;
}
if let Some(id) = self.id.get() {
cranpose_core::queue_semantics_invalidation(id);
}
}
pub fn modifier_capabilities(&self) -> NodeCapabilities {
self.inner.borrow().modifier_capabilities
}
pub fn has_layout_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::LAYOUT)
}
pub fn has_draw_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::DRAW)
}
pub fn has_pointer_input_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::POINTER_INPUT)
}
pub fn has_semantics_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::SEMANTICS)
}
pub fn has_focus_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::FOCUS)
}
fn dispatch_modifier_invalidations(
&self,
invalidations: &[ModifierInvalidation],
prev_caps: NodeCapabilities,
) {
let curr_caps = self.modifier_capabilities();
for invalidation in invalidations {
self.modifier_slices_dirty.set(true);
match invalidation.kind() {
InvalidationKind::Layout => {
if curr_caps.contains(NodeCapabilities::LAYOUT)
|| prev_caps.contains(NodeCapabilities::LAYOUT)
{
self.mark_needs_measure();
}
}
InvalidationKind::Draw => {
if curr_caps.contains(NodeCapabilities::DRAW)
|| prev_caps.contains(NodeCapabilities::DRAW)
{
self.mark_needs_redraw();
}
}
InvalidationKind::PointerInput => {
if curr_caps.contains(NodeCapabilities::POINTER_INPUT)
|| prev_caps.contains(NodeCapabilities::POINTER_INPUT)
{
self.mark_needs_pointer_pass();
crate::request_pointer_invalidation();
if let Some(id) = self.id.get() {
crate::schedule_pointer_repass(id);
}
}
}
InvalidationKind::Semantics => {
self.request_semantics_update();
}
InvalidationKind::Focus => {
if curr_caps.contains(NodeCapabilities::FOCUS)
|| prev_caps.contains(NodeCapabilities::FOCUS)
{
self.mark_needs_focus_sync();
crate::request_focus_invalidation();
if let Some(id) = self.id.get() {
crate::schedule_focus_invalidation(id);
}
}
}
}
}
}
}
impl cranpose_core::Node for SubcomposeLayoutNode {
fn mount(&mut self) {
let mut inner = self.inner.borrow_mut();
let (chain, mut context) = inner.modifier_chain.chain_and_context_mut();
chain.repair_chain();
chain.attach_nodes(&mut *context);
}
fn unmount(&mut self) {
self.inner
.borrow_mut()
.modifier_chain
.chain_mut()
.detach_nodes();
}
fn insert_child(&mut self, child: NodeId) {
let mut inner = self.inner.borrow_mut();
if inner.children.contains(&child) {
return;
}
if is_virtual_node(child) {
let count = self.virtual_children_count.get();
self.virtual_children_count.set(count + 1);
}
inner.children.push(child);
}
fn remove_child(&mut self, child: NodeId) {
let mut inner = self.inner.borrow_mut();
let before = inner.children.len();
inner.children.retain(|&id| id != child);
if inner.children.len() < before && is_virtual_node(child) {
let count = self.virtual_children_count.get();
if count > 0 {
self.virtual_children_count.set(count - 1);
}
}
}
fn move_child(&mut self, from: usize, to: usize) {
let mut inner = self.inner.borrow_mut();
if from == to || from >= inner.children.len() {
return;
}
let child = inner.children.remove(from);
let target = to.min(inner.children.len());
inner.children.insert(target, child);
}
fn update_children(&mut self, children: &[NodeId]) {
let mut inner = self.inner.borrow_mut();
inner.children.clear();
inner.children.extend_from_slice(children);
}
fn children(&self) -> Vec<NodeId> {
current_subcompose_children(&self.inner.borrow())
}
fn set_node_id(&mut self, id: NodeId) {
self.id.set(Some(id));
self.inner.borrow_mut().modifier_chain.set_node_id(Some(id));
}
fn on_attached_to_parent(&mut self, parent: NodeId) {
self.parent.set(Some(parent));
}
fn on_removed_from_parent(&mut self) {
self.parent.set(None);
}
fn parent(&self) -> Option<NodeId> {
self.parent.get()
}
fn mark_needs_layout(&self) {
self.needs_layout.set(true);
}
fn needs_layout(&self) -> bool {
self.needs_layout.get()
}
fn mark_needs_measure(&self) {
self.needs_measure.set(true);
self.needs_layout.set(true); }
fn needs_measure(&self) -> bool {
self.needs_measure.get()
}
fn mark_needs_semantics(&self) {
self.needs_semantics.set(true);
}
fn needs_semantics(&self) -> bool {
self.needs_semantics.get()
}
fn set_parent_for_bubbling(&mut self, parent: NodeId) {
self.parent.set(Some(parent));
}
}
#[derive(Clone)]
pub struct SubcomposeLayoutNodeHandle {
inner: Rc<RefCell<SubcomposeLayoutNodeInner>>,
}
impl SubcomposeLayoutNodeHandle {
pub fn modifier(&self) -> Modifier {
self.inner.borrow().modifier.clone()
}
pub fn layout_properties(&self) -> crate::modifier::LayoutProperties {
self.resolved_modifiers().layout_properties()
}
pub fn resolved_modifiers(&self) -> ResolvedModifiers {
self.inner.borrow().resolved_modifiers
}
pub fn total_offset(&self) -> Point {
self.resolved_modifiers().offset()
}
pub fn modifier_capabilities(&self) -> NodeCapabilities {
self.inner.borrow().modifier_capabilities
}
pub fn has_layout_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::LAYOUT)
}
pub fn has_draw_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::DRAW)
}
pub fn has_pointer_input_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::POINTER_INPUT)
}
pub fn has_semantics_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::SEMANTICS)
}
pub fn has_focus_modifier_nodes(&self) -> bool {
self.modifier_capabilities()
.contains(NodeCapabilities::FOCUS)
}
pub fn set_debug_modifiers(&self, enabled: bool) {
self.inner.borrow_mut().set_debug_modifiers(enabled);
}
pub fn measure<'a>(
&self,
composer: &Composer,
node_id: NodeId,
constraints: Constraints,
measurer: Box<dyn FnMut(NodeId, Constraints) -> Size + 'a>,
error: &'a RefCell<Option<NodeError>>,
) -> Result<MeasureResult, NodeError> {
let (policy, mut state, slots_host) = {
let mut inner = self.inner.borrow_mut();
let policy = Rc::clone(&inner.measure_policy);
let state = std::mem::take(&mut inner.state);
let slots_host = Rc::clone(&inner.slots);
(policy, state, slots_host)
};
state.begin_pass();
let previous = composer.phase();
if !matches!(previous, Phase::Measure | Phase::Layout) {
composer.enter_phase(Phase::Measure);
}
let constraints_copy = constraints;
let (result, _) =
composer.subcompose_slot(&slots_host, Some(node_id), |inner_composer| {
let mut scope = SubcomposeMeasureScopeImpl::new(
inner_composer.clone(),
&mut state,
constraints_copy,
measurer,
error,
self.clone(), node_id, );
(policy)(&mut scope, constraints_copy)
})?;
state.finish_pass();
if previous != composer.phase() {
composer.enter_phase(previous);
}
{
let mut inner = self.inner.borrow_mut();
inner.state = state;
inner.last_placements = result.placements.iter().map(|p| p.node_id).collect();
}
Ok(result)
}
pub fn set_active_children<I>(&self, children: I)
where
I: IntoIterator<Item = NodeId>,
{
let mut inner = self.inner.borrow_mut();
inner.children.clear();
inner.children.extend(children);
}
}
fn current_subcompose_children(inner: &SubcomposeLayoutNodeInner) -> Vec<NodeId> {
if !inner.last_placements.is_empty() {
inner.last_placements.clone()
} else {
inner.children.clone()
}
}
struct SubcomposeLayoutNodeInner {
modifier: Modifier,
modifier_chain: ModifierChainHandle,
resolved_modifiers: ResolvedModifiers,
modifier_capabilities: NodeCapabilities,
state: SubcomposeState,
measure_policy: Rc<MeasurePolicy>,
children: Vec<NodeId>,
slots: Rc<SlotsHost>,
debug_modifiers: bool,
virtual_nodes: HashMap<NodeId, Rc<LayoutNode>>,
last_placements: Vec<NodeId>,
}
impl SubcomposeLayoutNodeInner {
fn new(measure_policy: Rc<MeasurePolicy>) -> Self {
Self {
modifier: Modifier::empty(),
modifier_chain: ModifierChainHandle::new(),
resolved_modifiers: ResolvedModifiers::default(),
modifier_capabilities: NodeCapabilities::default(),
state: SubcomposeState::default(),
measure_policy,
children: Vec::new(),
slots: Rc::new(SlotsHost::new(SlotTable::default())),
debug_modifiers: false,
virtual_nodes: HashMap::new(),
last_placements: Vec::new(),
}
}
fn set_measure_policy(&mut self, policy: Rc<MeasurePolicy>) {
self.measure_policy = policy;
if let Err(err) = self.slots.reset() {
log::error!(
"failed to reset root measurement slots after measure policy update: {err}"
);
}
}
fn set_modifier_collect(&mut self, modifier: Modifier) -> (Vec<ModifierInvalidation>, bool) {
let modifier_changed = !self.modifier.structural_eq(&modifier);
self.modifier = modifier;
self.modifier_chain.set_debug_logging(self.debug_modifiers);
let modifier_local_invalidations = self.modifier_chain.update(&self.modifier);
self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
self.modifier_capabilities = self.modifier_chain.capabilities();
let mut invalidations = self.modifier_chain.take_invalidations();
invalidations.extend(modifier_local_invalidations);
(invalidations, modifier_changed)
}
fn set_debug_modifiers(&mut self, enabled: bool) {
self.debug_modifiers = enabled;
self.modifier_chain.set_debug_logging(enabled);
}
}
#[cfg(test)]
#[path = "tests/subcompose_layout_tests.rs"]
mod tests;