Skip to main content

kozan_core/dom/
text.rs

1//! `Text` — a leaf node containing text content.
2//!
3//! Like Chrome's `Text` class: inherits from `CharacterData → Node`.
4//! Text nodes are NOT elements — they have no tag name, no attributes,
5//! and cannot have children.
6//!
7//! `text.append(child)` is a **compile error** because `Text`
8//! only implements `Node`, not `ContainerNode`.
9
10use kozan_macros::Node;
11
12use crate::Handle;
13
14/// A text node. Leaf only — cannot have children.
15///
16/// Implements `Node` but NOT `ContainerNode` or `Element`.
17/// Access text content via `content()` / `set_content()`.
18#[derive(Copy, Clone, Node)]
19pub struct Text(Handle);
20
21/// Text content storage in `DataStorage`.
22#[derive(Default, Clone)]
23pub struct TextData {
24    pub content: String,
25}
26
27impl Text {
28    /// Wrap a Handle into a Text node. Called by `Document::create_text`.
29    pub(crate) fn from_raw(handle: Handle) -> Self {
30        Self(handle)
31    }
32
33    /// Get the text content.
34    #[inline]
35    #[must_use]
36    pub fn content(&self) -> String {
37        self.0
38            .read_data::<TextData, _>(|d| d.content.clone())
39            .unwrap_or_default()
40    }
41
42    /// Set the text content. No-op if value is unchanged.
43    ///
44    /// Chrome: `CharacterData::setData()` — compares old vs new, skips if same.
45    /// When text changes, marks parent for restyle + sets `needs_layout`.
46    pub fn set_content(&self, value: impl Into<String>) {
47        let value = value.into();
48        let changed = self
49            .0
50            .write_data::<TextData, _>(|d| {
51                if d.content == value {
52                    return false; // Same value — no-op.
53                }
54                d.content = value;
55                true
56            })
57            .unwrap_or(false);
58
59        if changed {
60            // Text content changed → parent needs relayout (text affects sizing).
61            // Text nodes have no ElementData, so mark the parent element.
62            self.0.mark_parent_needs_layout();
63        }
64    }
65}