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() -> String>);
21
22impl DynamicTextSource {
23 pub fn new<F>(resolver: F) -> Self
24 where
25 F: Fn() -> String + 'static,
26 {
27 Self(Rc::new(resolver))
28 }
29
30 fn resolve(&self) -> String {
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(String),
46 Dynamic(DynamicTextSource),
47}
48
49impl TextSource {
50 fn resolve(&self) -> String {
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(self)
65 }
66}
67
68impl IntoTextSource for &str {
69 fn into_text_source(self) -> TextSource {
70 TextSource::Static(self.to_string())
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 || state.value().to_string()))
81 }
82}
83
84impl<T> IntoTextSource for MutableState<T>
85where
86 T: ToString + Clone + 'static,
87{
88 fn into_text_source(self) -> TextSource {
89 let state = self;
90 TextSource::Dynamic(DynamicTextSource::new(move || state.value().to_string()))
91 }
92}
93
94impl<F> IntoTextSource for F
95where
96 F: Fn() -> String + 'static,
97{
98 fn into_text_source(self) -> TextSource {
99 TextSource::Dynamic(DynamicTextSource::new(self))
100 }
101}
102
103impl IntoTextSource for DynamicTextSource {
104 fn into_text_source(self) -> TextSource {
105 TextSource::Dynamic(self)
106 }
107}
108
109#[composable]
125pub fn Text<S>(value: S, modifier: Modifier) -> NodeId
126where
127 S: IntoTextSource + Clone + PartialEq + 'static,
128{
129 let current = value.into_text_source().resolve();
130
131 let text_element = modifier_element(TextModifierElement::new(current.clone()));
134 let final_modifier = Modifier::from_parts(vec![text_element]);
135 let combined_modifier = modifier.then(final_modifier);
136
137 Layout(
140 combined_modifier,
141 EmptyMeasurePolicy,
142 || {}, )
144}