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