cranpose_ui/widgets/
text.rs1#![allow(non_snake_case)]
9
10use crate::composable;
11use crate::layout::policies::EmptyMeasurePolicy;
12use crate::modifier::Modifier;
13use crate::text_modifier_node::TextModifierElement;
14use crate::widgets::Layout;
15use cranpose_core::{MutableState, NodeId, State};
16use cranpose_foundation::modifier_element;
17use std::rc::Rc;
18
19#[derive(Clone)]
20pub struct DynamicTextSource(Rc<dyn Fn() -> Rc<str>>);
21
22impl DynamicTextSource {
23 pub fn new<F>(resolver: F) -> Self
24 where
25 F: Fn() -> Rc<str> + 'static,
26 {
27 Self(Rc::new(resolver))
28 }
29
30 fn resolve(&self) -> Rc<str> {
31 (self.0)()
32 }
33}
34
35impl PartialEq for DynamicTextSource {
36 fn eq(&self, other: &Self) -> bool {
37 Rc::ptr_eq(&self.0, &other.0)
38 }
39}
40
41impl Eq for DynamicTextSource {}
42
43#[derive(Clone, PartialEq, Eq)]
44enum TextSource {
45 Static(Rc<str>),
46 Dynamic(DynamicTextSource),
47}
48
49impl TextSource {
50 fn resolve(&self) -> Rc<str> {
51 match self {
52 TextSource::Static(text) => text.clone(),
53 TextSource::Dynamic(dynamic) => dynamic.resolve(),
54 }
55 }
56}
57
58trait IntoTextSource {
59 fn into_text_source(self) -> TextSource;
60}
61
62impl IntoTextSource for String {
63 fn into_text_source(self) -> TextSource {
64 TextSource::Static(Rc::from(self))
65 }
66}
67
68impl IntoTextSource for &str {
69 fn into_text_source(self) -> TextSource {
70 TextSource::Static(Rc::from(self))
71 }
72}
73
74impl<T> IntoTextSource for State<T>
75where
76 T: ToString + Clone + 'static,
77{
78 fn into_text_source(self) -> TextSource {
79 let state = self;
80 TextSource::Dynamic(DynamicTextSource::new(move || {
81 Rc::from(state.value().to_string())
82 }))
83 }
84}
85
86impl<T> IntoTextSource for MutableState<T>
87where
88 T: ToString + Clone + 'static,
89{
90 fn into_text_source(self) -> TextSource {
91 let state = self;
92 TextSource::Dynamic(DynamicTextSource::new(move || {
93 Rc::from(state.value().to_string())
94 }))
95 }
96}
97
98impl<F> IntoTextSource for F
99where
100 F: Fn() -> String + 'static,
101{
102 fn into_text_source(self) -> TextSource {
103 TextSource::Dynamic(DynamicTextSource::new(move || Rc::from(self())))
104 }
105}
106
107impl IntoTextSource for DynamicTextSource {
108 fn into_text_source(self) -> TextSource {
109 TextSource::Dynamic(self)
110 }
111}
112
113#[composable]
129pub fn Text<S>(value: S, modifier: Modifier) -> NodeId
130where
131 S: IntoTextSource + Clone + PartialEq + 'static,
132{
133 let current = value.into_text_source().resolve();
134
135 let text_element = modifier_element(TextModifierElement::new(current));
138 let final_modifier = Modifier::from_parts(vec![text_element]);
139 let combined_modifier = modifier.then(final_modifier);
140
141 Layout(
144 combined_modifier,
145 EmptyMeasurePolicy,
146 || {}, )
148}