1use pax_message::{AnyCreatePatch, TextPatch};
2use pax_runtime::api::{Layer, Property, RenderContext};
3use pax_runtime::{
4 BaseInstance, ExpandedNode, ExpandedNodeIdentifier, InstanceFlags, InstanceNode,
5 InstantiationArgs, RuntimeContext,
6};
7use pax_runtime_api::{borrow, borrow_mut, use_RefCell};
8use pax_std::primitives::Text;
9use_RefCell!();
10use std::collections::HashMap;
11use std::rc::Rc;
12#[cfg(feature = "designtime")]
13use {
14 kurbo::{RoundedRect, Shape},
15 pax_runtime::DEBUG_TEXT_GREEN_BACKGROUND,
16 piet::Color,
17};
18
19use crate::patch_if_needed;
20
21pub struct TextInstance {
22 base: BaseInstance,
23 native_message_props: RefCell<HashMap<ExpandedNodeIdentifier, Property<()>>>,
28}
29
30impl InstanceNode for TextInstance {
31 fn instantiate(args: InstantiationArgs) -> Rc<Self>
32 where
33 Self: Sized,
34 {
35 Rc::new(Self {
36 base: BaseInstance::new(
37 args,
38 InstanceFlags {
39 invisible_to_slot: false,
40 invisible_to_raycasting: false,
41 layer: Layer::Native,
42 is_component: false,
43 },
44 ),
45 native_message_props: Default::default(),
46 })
47 }
48
49 fn update(self: Rc<Self>, expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {
50 borrow!(self.native_message_props)
52 .get(&expanded_node.id)
53 .unwrap()
54 .get();
55 }
56
57 fn render(
58 &self,
59 _expanded_node: &ExpandedNode,
60 _context: &Rc<RuntimeContext>,
61 _rc: &mut dyn RenderContext,
62 ) {
63 #[cfg(feature = "designtime")]
66 if DEBUG_TEXT_GREEN_BACKGROUND {
67 let computed_props = borrow!(expanded_node.layout_properties);
68 let tab = &computed_props.as_ref().unwrap().computed_tab;
69 let layer_id = format!("{}", borrow!(expanded_node.occlusion_id));
70 let width: f64 = tab.bounds.0;
71 let height: f64 = tab.bounds.1;
72 let rect = RoundedRect::new(0.0, 0.0, width, height, 0.0);
73 let bez_path = rect.to_path(0.1);
74 let transformed_bez_path = Into::<kurbo::Affine>::into(tab.transform) * bez_path;
75 rc.fill(
76 &layer_id,
77 transformed_bez_path,
78 &piet::PaintBrush::Color(Color::rgba8(0, 255, 0, 100)),
79 );
80 }
81 }
82
83 fn handle_mount(
84 self: Rc<Self>,
85 expanded_node: &Rc<ExpandedNode>,
86 context: &Rc<RuntimeContext>,
87 ) {
88 let id = expanded_node.id.to_u32();
90 context.enqueue_native_message(pax_message::NativeMessage::TextCreate(AnyCreatePatch {
91 id,
92 parent_frame: expanded_node.parent_frame.get().map(|v| v.to_u32()),
93 occlusion_layer_id: 0,
94 }));
95
96 let weak_self_ref = Rc::downgrade(&expanded_node);
98 let context = Rc::clone(context);
99 let last_patch = Rc::new(RefCell::new(TextPatch {
100 id,
101 ..Default::default()
102 }));
103
104 let deps: Vec<_> = borrow!(expanded_node.properties_scope)
105 .values()
106 .cloned()
107 .chain([expanded_node.transform_and_bounds.untyped()])
108 .collect();
109
110 borrow_mut!(self.native_message_props).insert(
111 expanded_node.id,
112 Property::computed(
113 move || {
114 let Some(expanded_node) = weak_self_ref.upgrade() else {
115 unreachable!()
116 };
117 let mut old_state = borrow_mut!(last_patch);
118
119 let mut patch = TextPatch {
120 id,
121 ..Default::default()
122 };
123 expanded_node.with_properties_unwrapped(|properties: &mut Text| {
124 let computed_tab = expanded_node.transform_and_bounds.get();
125 let (width, height) = computed_tab.bounds;
126 let cp = expanded_node.get_common_properties();
127 let cp = borrow!(cp);
128 let (width, height) = (
132 cp.width.get().is_some().then_some(width).unwrap_or(-1.0),
133 cp.height.get().is_some().then_some(height).unwrap_or(-1.0),
134 );
135
136 let updates = [
137 patch_if_needed(
139 &mut old_state.content,
140 &mut patch.content,
141 properties.text.get(),
142 ),
143 patch_if_needed(
145 &mut old_state.style,
146 &mut patch.style,
147 (&properties.style.get()).into(),
148 ),
149 patch_if_needed(
150 &mut old_state.style_link,
151 &mut patch.style_link,
152 (&properties.style_link.get()).into(),
153 ),
154 patch_if_needed(
155 &mut old_state.editable,
156 &mut patch.editable,
157 properties.editable.get(),
158 ),
159 patch_if_needed(&mut old_state.size_x, &mut patch.size_x, width),
161 patch_if_needed(&mut old_state.size_y, &mut patch.size_y, height),
162 patch_if_needed(
163 &mut old_state.transform,
164 &mut patch.transform,
165 computed_tab.transform.coeffs().to_vec(),
166 ),
167 ];
168 if updates.into_iter().any(|v| v == true) {
169 context.enqueue_native_message(pax_message::NativeMessage::TextUpdate(
170 patch,
171 ));
172 }
173 });
174 ()
175 },
176 &deps,
177 ),
178 );
179 }
180
181 fn handle_unmount(&self, expanded_node: &Rc<ExpandedNode>, context: &Rc<RuntimeContext>) {
182 let id = expanded_node.id.to_u32();
183 context.enqueue_native_message(pax_message::NativeMessage::TextDelete(id));
184 borrow_mut!(self.native_message_props).remove(&expanded_node.id);
186 }
187
188 fn resolve_debug(
189 &self,
190 f: &mut std::fmt::Formatter,
191 expanded_node: Option<&ExpandedNode>,
192 ) -> std::fmt::Result {
193 match expanded_node {
194 Some(expanded_node) => expanded_node.with_properties_unwrapped(|r: &mut Text| {
195 f.debug_struct("Text").field("text", &r.text.get()).finish()
196 }),
197 None => f.debug_struct("Text").finish_non_exhaustive(),
198 }
199 }
200
201 fn base(&self) -> &BaseInstance {
202 &self.base
203 }
204
205 fn handle_text_change(&self, expanded_node: &Rc<ExpandedNode>, text: String) {
206 expanded_node.with_properties_unwrapped(|properties: &mut Text| properties.text.set(text));
207 }
208}