use std::{cell::RefCell, fmt, rc::Rc};
use cranpose_core::{MemoryApplier, NodeError, NodeId};
use cranpose_foundation::SemanticsConfiguration;
use cranpose_ui_graphics::{ProjectiveTransform, layer_transform::layer_transform_to_window};
use super::{SemanticsRole, SemanticsText};
use crate::{
modifier::{Modifier, ModifierNodeSlices, Point, ResolvedModifiers, Size},
subcompose_layout::SubcomposeLayoutNode,
widgets::nodes::{LayoutNode, LayoutState},
};
#[derive(Debug, Clone)]
pub struct LayoutNodeData {
#[cfg(feature = "inspection")]
pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
pub modifier: Modifier,
pub resolved_modifiers: ResolvedModifiers,
pub modifier_slices: Rc<ModifierNodeSlices>,
pub semantics: Option<Rc<SemanticsConfiguration>>,
pub kind: LayoutNodeKind,
pub z_index: f32,
}
impl LayoutNodeData {
pub fn new(
modifier: Modifier,
resolved_modifiers: ResolvedModifiers,
modifier_slices: Rc<ModifierNodeSlices>,
semantics: Option<Rc<SemanticsConfiguration>>,
kind: LayoutNodeKind,
) -> Self {
Self {
#[cfg(feature = "inspection")]
source_trace: Rc::default(),
modifier,
resolved_modifiers,
modifier_slices,
semantics,
kind,
z_index: 0.0,
}
}
pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
self.semantics.as_deref()
}
pub fn resolved_modifiers(&self) -> ResolvedModifiers {
self.resolved_modifiers
}
pub fn modifier_slices(&self) -> &ModifierNodeSlices {
&self.modifier_slices
}
}
#[derive(Clone)]
pub enum LayoutNodeKind {
Layout,
Subcompose,
Spacer,
Button {
on_click: Rc<RefCell<dyn FnMut()>>,
},
Unknown,
}
impl fmt::Debug for LayoutNodeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Layout => f.write_str("Layout"),
Self::Subcompose => f.write_str("Subcompose"),
Self::Spacer => f.write_str("Spacer"),
Self::Button { .. } => f.write_str("Button"),
Self::Unknown => f.write_str("Unknown"),
}
}
}
pub(super) fn snapshot_node_data(
applier: &mut MemoryApplier,
node_id: NodeId,
top_left: Point,
size: Size,
parent_transform: ProjectiveTransform,
) -> Result<(LayoutNodeData, ProjectiveTransform), NodeError> {
let info = runtime_metadata_for(applier, node_id)?;
let kind = layout_kind_from_metadata(node_id, &info);
let RuntimeNodeMetadata {
modifier,
resolved_modifiers,
modifier_slices,
semantics,
z_index,
..
} = info;
let window_transform = modifier_slices
.graphics_layer()
.map_or(parent_transform, |layer| {
layer_transform_to_window(
parent_transform,
top_left,
modifier_slices.layer_bounds(size),
&layer,
)
});
modifier_slices.publish_window_geometry(top_left, window_transform, size);
let data = LayoutNodeData {
#[cfg(feature = "inspection")]
source_trace: applier
.with_node::<LayoutNode, _>(node_id, |node| Rc::clone(&node.source_trace))
.unwrap_or_default(),
modifier,
resolved_modifiers,
modifier_slices,
semantics,
kind,
z_index,
};
Ok((data, window_transform))
}
#[derive(Clone)]
struct RuntimeNodeMetadata {
modifier: Modifier,
resolved_modifiers: ResolvedModifiers,
modifier_slices: Rc<ModifierNodeSlices>,
semantics: Option<Rc<SemanticsConfiguration>>,
role: SemanticsRole,
z_index: f32,
button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
}
impl Default for RuntimeNodeMetadata {
fn default() -> Self {
Self {
modifier: Modifier::empty(),
resolved_modifiers: ResolvedModifiers::default(),
modifier_slices: Rc::default(),
semantics: None,
role: SemanticsRole::Unknown,
button_handler: None,
z_index: 0.0,
}
}
}
pub(super) fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
modifier_slices
.annotated_text()
.map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
value: SemanticsText(Rc::clone(&text)),
})
}
fn runtime_metadata_for(
applier: &mut MemoryApplier,
node_id: NodeId,
) -> Result<RuntimeNodeMetadata, NodeError> {
if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
let modifier = layout.modifier.clone();
let resolved_modifiers = layout.resolved_modifiers();
let modifier_slices = layout.modifier_slices_snapshot();
let role = role_from_modifier_slices(&modifier_slices);
let z_index = modifier_slices.z_index() + layout.with_layout_state(LayoutState::z_index);
RuntimeNodeMetadata {
modifier,
resolved_modifiers,
modifier_slices,
semantics: layout.semantics_configuration().map(Rc::new),
role,
button_handler: None,
z_index,
}
}) {
return Ok(meta);
}
if let Ok((modifier, resolved_modifiers, modifier_slices, semantics, z_index)) =
applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
let modifier_slices = node.modifier_slices_snapshot();
let placed = node.layout_parts().layout_state().borrow().z_index();
let z_index = modifier_slices.z_index() + placed;
(
node.modifier(),
node.resolved_modifiers(),
modifier_slices,
node.semantics_configuration().map(Rc::new),
z_index,
)
})
{
return Ok(RuntimeNodeMetadata {
modifier,
resolved_modifiers,
modifier_slices,
semantics,
role: SemanticsRole::Subcompose,
button_handler: None,
z_index,
});
}
Ok(RuntimeNodeMetadata::default())
}
fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
match &info.role {
SemanticsRole::Layout => LayoutNodeKind::Layout,
SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
SemanticsRole::Spacer => LayoutNodeKind::Spacer,
SemanticsRole::Button => {
let handler = info
.button_handler
.as_ref()
.cloned()
.unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
LayoutNodeKind::Button { on_click: handler }
}
SemanticsRole::Unknown => LayoutNodeKind::Unknown,
}
}