use std::{
cell::Cell,
hash::{Hash, Hasher},
rc::Rc,
};
use cranpose_core::NodeId;
use cranpose_foundation::{
DelegatableNode, InvalidationKind, ModifierNode, ModifierNodeContext, ModifierNodeElement,
NodeCapabilities, NodeState, PointerEvent, PointerInputNode, SemanticsConfiguration,
SemanticsNode,
text::{TextFieldLineLimits, TextFieldState},
};
use cranpose_ui_graphics::Point;
use crate::{
focus_dispatch::FocusTargetHandle,
text::TextStyle,
text_field_modifier_node::{
TextFieldFocusBridge, TextFieldModifierNode, TextFieldRefs, merge_text_field_semantics,
},
};
#[derive(Clone)]
pub(crate) struct TextFieldDecoratorElement {
state: TextFieldState,
refs: TextFieldRefs,
style: TextStyle,
line_limits: TextFieldLineLimits,
modal_depth: usize,
}
impl TextFieldDecoratorElement {
pub(crate) fn new(
state: TextFieldState,
refs: TextFieldRefs,
style: TextStyle,
line_limits: TextFieldLineLimits,
modal_depth: usize,
) -> Self {
Self {
state,
refs,
style,
line_limits,
modal_depth,
}
}
fn pointer_handler(&self) -> Rc<dyn Fn(PointerEvent)> {
let field = TextFieldModifierNode::create_handler(
self.state,
self.refs.clone(),
self.line_limits,
self.style.clone(),
self.modal_depth,
);
let field_origin: Rc<Cell<Point>> = Rc::clone(&self.refs.node_origin);
Rc::new(move |event: PointerEvent| {
let origin = field_origin.get();
field(event.copy_with_local_position(Point {
x: event.global_position.x - origin.x,
y: event.global_position.y - origin.y,
}));
})
}
}
impl std::fmt::Debug for TextFieldDecoratorElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TextFieldDecoratorElement")
.field("text", &self.state.text())
.field("line_limits", &self.line_limits)
.finish()
}
}
impl Hash for TextFieldDecoratorElement {
fn hash<H: Hasher>(&self, state: &mut H) {
self.state.id().hash(state);
self.refs.key().hash(state);
self.style.render_hash().hash(state);
self.line_limits.hash(state);
self.modal_depth.hash(state);
}
}
impl PartialEq for TextFieldDecoratorElement {
fn eq(&self, other: &Self) -> bool {
self.state == other.state
&& self.refs == other.refs
&& self.style == other.style
&& self.line_limits == other.line_limits
&& self.modal_depth == other.modal_depth
}
}
impl Eq for TextFieldDecoratorElement {}
impl ModifierNodeElement for TextFieldDecoratorElement {
type Node = TextFieldDecoratorNode;
fn create(&self) -> Self::Node {
TextFieldDecoratorNode {
handler: self.pointer_handler(),
field: self.clone(),
focus_target: None,
node_state: NodeState::new(),
}
}
fn update(&self, node: &mut Self::Node) {
if node.field != *self {
node.handler = self.pointer_handler();
node.field = self.clone();
}
}
fn key(&self) -> Option<u64> {
Some(self.refs.key())
}
fn capabilities(&self) -> NodeCapabilities {
NodeCapabilities::SEMANTICS | NodeCapabilities::POINTER_INPUT
}
}
pub(crate) struct TextFieldDecoratorNode {
field: TextFieldDecoratorElement,
handler: Rc<dyn Fn(PointerEvent)>,
focus_target: Option<(NodeId, Rc<dyn FocusTargetHandle>)>,
node_state: NodeState,
}
impl DelegatableNode for TextFieldDecoratorNode {
fn node_state(&self) -> &NodeState {
&self.node_state
}
}
impl ModifierNode for TextFieldDecoratorNode {
fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
context.invalidate(InvalidationKind::Semantics);
let Some(node_id) = context.node_id() else {
return;
};
let field = &self.field;
field.refs.focus_node.set(Some(node_id));
let bridge = TextFieldFocusBridge::handle(
field.state,
field.refs.clone(),
field.style.clone(),
field.line_limits,
);
crate::focus_dispatch::register_focus_target(node_id, Rc::clone(&bridge));
self.focus_target = Some((node_id, bridge));
}
fn on_detach(&mut self) {
if let Some((node_id, bridge)) = self.focus_target.take() {
crate::focus_dispatch::unregister_focus_target(node_id, &bridge);
}
}
fn as_semantics_node(&self) -> Option<&dyn SemanticsNode> {
Some(self)
}
fn as_semantics_node_mut(&mut self) -> Option<&mut dyn SemanticsNode> {
Some(self)
}
fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
Some(self)
}
fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
Some(self)
}
}
impl SemanticsNode for TextFieldDecoratorNode {
fn merge_semantics(&self, config: &mut SemanticsConfiguration) {
merge_text_field_semantics(self.field.state, self.field.line_limits, config);
}
}
impl PointerInputNode for TextFieldDecoratorNode {
fn on_pointer_event(
&mut self,
_context: &mut dyn ModifierNodeContext,
_event: &PointerEvent,
) -> bool {
false
}
fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
Some(Rc::clone(&self.handler))
}
}
#[cfg(test)]
#[path = "tests/text_field_decorator_node_tests.rs"]
mod tests;