1use crate::platform::{
2 INativeControlHostControlTopLevelAttachment, INativeControlHostImpl, IPlatformHandle,
3};
4use crate::presentation_source::PresentationSource;
5use crate::{Control, ControlImpl};
6use ferroui_base::input::InputElementImpl;
7use ferroui_base::interactivity::InteractiveImpl;
8use ferroui_base::layout::{LayoutHelper, LayoutableImpl};
9use ferroui_base::platform::IOptionalFeatureProvider;
10use ferroui_base::reactive::{Disposable, IDisposable};
11use ferroui_base::threading::{Dispatcher, DispatcherPriority};
12use ferroui_base::utilities::HandlerList;
13use ferroui_base::{
14 ferro_class, ferro_class_info, ferro_impl_classes, instantiate, FerroObjectImpl, FerroPropertyChangedEventArgs,
15 Point, Rect, Ref, StyledElementImpl, Visual, VisualImpl, VisualTreeAttachmentEventArgs,
16};
17use std::cell::{Cell, RefCell};
18use std::rc::Rc;
19
20#[repr(C)]
24pub struct NativeControlHost {
25 base: Control,
26 current_root: RefCell<Option<Rc<PresentationSource>>>,
27 current_host: RefCell<Option<Rc<dyn INativeControlHostImpl>>>,
28 attachment: RefCell<Option<Rc<dyn INativeControlHostControlTopLevelAttachment>>>,
29 native_control_handle: RefCell<Option<Rc<dyn IPlatformHandle>>>,
30 queued_for_destruction: Cell<bool>,
31 queued_for_move_resize: Cell<bool>,
32 property_changed_subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
33 render_transform_subscriptions: RefCell<Vec<Rc<dyn IDisposable>>>,
37 native_control_handle_changed: HandlerList<dyn Fn()>,
38}
39
40ferro_class! {
41 NativeControlHost: Control, virtuals NativeControlHostImpl: ControlImpl {
42 fn create_native_control_core(this, parent: Rc<dyn IPlatformHandle>) -> Rc<dyn IPlatformHandle>;
46 fn destroy_native_control_core(this, control: Rc<dyn IPlatformHandle>);
49 }
50}
51ferro_class_info!(NativeControlHost { new: NativeControlHost::new });
52
53ferro_impl_classes!(
54 NativeControlHost: FerroObjectImpl,
55 StyledElementImpl,
56 LayoutableImpl,
57 InteractiveImpl,
58 InputElementImpl
59);
60
61impl VisualImpl for NativeControlHost {
62 fn on_attached_to_visual_tree(this: &Self, e: &VisualTreeAttachmentEventArgs) {
64 *this.current_root.borrow_mut() = PresentationSource::from_root_visual(e.root_visual());
65 let mut visual = Some(this.to_ref().upcast::<Visual>());
66 while let Some(current) = visual {
67 let weak = this.to_ref().downgrade();
68 let subscription = current.property_changed(move |e| {
69 if let Some(this) = weak.upgrade() {
70 this.property_changed_handler(e);
71 }
72 });
73 this.property_changed_subscriptions.borrow_mut().push(subscription);
74
75 visual = current.get_visual_parent();
76 }
77 this.subscribe_render_transforms();
78
79 this.update_host();
80 }
81
82 fn on_detached_from_visual_tree(this: &Self, _e: &VisualTreeAttachmentEventArgs) {
84 *this.current_root.borrow_mut() = None;
85 let subscriptions = std::mem::take(&mut *this.property_changed_subscriptions.borrow_mut());
86 for subscription in subscriptions {
87 subscription.dispose();
88 }
89 this.unsubscribe_render_transforms();
90 this.update_host();
91 }
92}
93
94impl NativeControlHostImpl for NativeControlHost {
95 fn create_native_control_core(this: &Self, parent: Rc<dyn IPlatformHandle>) -> Rc<dyn IPlatformHandle> {
96 let Some(current_host) = this.current_host.borrow().clone() else {
97 panic!("Operation is not valid due to the current state of the object.");
98 };
99 current_host.create_default_child(parent)
100 }
101
102 fn destroy_native_control_core(_this: &Self, control: Rc<dyn IPlatformHandle>) {
103 if let Some(native_control_host_destroyable_control_handle) =
104 control.as_native_control_host_destroyable_control_handle()
105 {
106 native_control_host_destroyable_control_handle.destroy();
107 }
108 }
109}
110
111impl ControlImpl for NativeControlHost {
112 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
113 crate::automation::peers::NativeControlHostPeer::new(this).upcast()
114 }
115}
116
117fn same_host(a: Option<&Rc<dyn INativeControlHostImpl>>, b: Option<&Rc<dyn INativeControlHostImpl>>) -> bool {
118 match (a, b) {
119 (Some(a), Some(b)) => std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b)),
120 (None, None) => true,
121 _ => false,
122 }
123}
124
125fn same_handle(a: Option<&Rc<dyn IPlatformHandle>>, b: Option<&Rc<dyn IPlatformHandle>>) -> bool {
126 match (a, b) {
127 (Some(a), Some(b)) => std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b)),
128 (None, None) => true,
129 _ => false,
130 }
131}
132
133impl NativeControlHost {
134 fn static_constructor() {
135 Visual::flow_direction_property()
136 .changed()
137 .add_class_handler::<NativeControlHost>(Self::on_flow_direction_changed);
138 }
139
140 pub fn construct() -> Self {
142 Self {
143 base: Control::construct(),
144 current_root: RefCell::new(None),
145 current_host: RefCell::new(None),
146 attachment: RefCell::new(None),
147 native_control_handle: RefCell::new(None),
148 queued_for_destruction: Cell::new(false),
149 queued_for_move_resize: Cell::new(false),
150 property_changed_subscriptions: RefCell::new(Vec::new()),
151 render_transform_subscriptions: RefCell::new(Vec::new()),
152 native_control_handle_changed: HandlerList::new(),
153 }
154 }
155
156 pub fn new() -> Ref<Self> {
158 instantiate(Self::construct())
159 }
160
161 pub fn native_control_handle(&self) -> Option<Rc<dyn IPlatformHandle>> {
164 self.native_control_handle.borrow().clone()
165 }
166
167 fn set_native_control_handle(&self, value: Option<Rc<dyn IPlatformHandle>>) {
168 let changed = !same_handle(self.native_control_handle.borrow().as_ref(), value.as_ref());
169 if changed {
170 let old = self.native_control_handle.replace(value);
171 drop(old);
172 let handlers = self.native_control_handle_changed.snapshot();
173 for (_, handler) in handlers.iter() {
174 handler();
175 }
176 }
177 }
178
179 pub fn native_control_handle_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
182 let token = self.native_control_handle_changed.add(Rc::new(handler));
183 let weak = self.to_ref().downgrade();
184 Disposable::create(move || {
185 if let Some(this) = weak.upgrade() {
186 this.native_control_handle_changed.remove(token);
187 }
188 })
189 }
190
191 fn on_flow_direction_changed(native_control_host: &NativeControlHost, _e: &FerroPropertyChangedEventArgs<'_>) {
192 native_control_host.try_update_native_control_position();
193 }
194
195 fn property_changed_handler(&self, e: &FerroPropertyChangedEventArgs<'_>) {
196 if !e.is_effective_value_change() {
197 return;
198 }
199 if e.property() == Visual::bounds_property().as_property()
200 || e.property() == Visual::is_visible_property().as_property()
201 {
202 self.enqueue_for_move_resize();
203 } else if e.property() == Visual::render_transform_property().as_property() {
204 self.subscribe_render_transforms();
207 self.enqueue_for_move_resize();
208 } else if e.property() == Visual::render_transform_origin_property().as_property() {
209 self.enqueue_for_move_resize();
210 }
211 }
212
213 fn subscribe_render_transforms(&self) {
227 self.unsubscribe_render_transforms();
228 let mut subscriptions = Vec::new();
229 let mut visual = Some(self.to_ref().upcast::<Visual>());
230 while let Some(current) = visual {
231 let render_transform = current.render_transform();
232 if let Some(mutable_transform) =
233 render_transform.as_ref().and_then(|render_transform| render_transform.as_mutable_transform())
234 {
235 let weak = self.to_ref().downgrade();
236 subscriptions.push(mutable_transform.changed(Rc::new(move || {
237 if let Some(this) = weak.upgrade() {
238 this.enqueue_for_move_resize();
239 }
240 })));
241 }
242
243 visual = current.get_visual_parent();
244 }
245 *self.render_transform_subscriptions.borrow_mut() = subscriptions;
246 }
247
248 fn unsubscribe_render_transforms(&self) {
249 let subscriptions = std::mem::take(&mut *self.render_transform_subscriptions.borrow_mut());
250 for subscription in subscriptions {
251 subscription.dispose();
252 }
253 }
254
255 fn update_host(&self) {
256 self.queued_for_move_resize.set(false);
257 let current_host = self
258 .current_root
259 .borrow()
260 .as_ref()
261 .and_then(|current_root| current_root.platform_impl())
262 .and_then(|platform_impl| {
263 let provider: &dyn IOptionalFeatureProvider = &*platform_impl;
264 provider.try_get::<dyn INativeControlHostImpl>()
265 });
266 *self.current_host.borrow_mut() = current_host.clone();
267
268 if let Some(current_host) = ¤t_host {
269 let attachment = self.attachment.borrow().clone();
271 if let Some(attachment) = attachment {
272 if !same_host(attachment.attached_to().as_ref(), Some(current_host)) {
273 if attachment.is_compatible_with(&**current_host) {
274 attachment.set_attached_to(Some(current_host.clone()));
275 } else {
276 attachment.dispose();
277 *self.attachment.borrow_mut() = None;
278 }
279 }
280 }
281
282 let has_attachment = self.attachment.borrow().is_some();
287 let native_control_handle = self.native_control_handle();
288 if let (false, Some(native_control_handle)) = (has_attachment, native_control_handle) {
289 if current_host.is_compatible_with(&*native_control_handle) {
290 let attachment = current_host.create_new_attachment(native_control_handle);
291 *self.attachment.borrow_mut() = Some(attachment);
292 } else {
293 self.destroy_native_control();
294 }
295 }
296
297 if self.native_control_handle().is_none() {
299 let weak = self.to_ref().downgrade();
300 let attachment = current_host.create_new_attachment_with(Rc::new(move |parent| {
301 let this = weak.upgrade().expect("the native control host is alive while its control is created");
302 let handle = this.create_native_control_core(parent);
303 this.set_native_control_handle(Some(handle.clone()));
304 handle
305 }));
306 *self.attachment.borrow_mut() = Some(attachment);
307 }
308 } else {
309 let attachment = self.attachment.borrow().clone();
311 if let Some(attachment) = attachment {
312 attachment.set_attached_to(None);
313 }
314
315 if self.native_control_handle().is_some() && !self.queued_for_destruction.get() {
317 self.queued_for_destruction.set(true);
318 let this = self.to_ref();
319 Dispatcher::ui_thread().post_local(move || this.check_destruction(), DispatcherPriority::BACKGROUND);
320 }
321 }
322
323 let attached_to = self.attachment.borrow().as_ref().and_then(|attachment| attachment.attached_to());
324 if !same_host(attached_to.as_ref(), current_host.as_ref()) {
325 return;
326 }
327
328 self.try_update_native_control_position();
329 }
330
331 fn get_absolute_bounds(&self) -> Option<Rect> {
332 let current_root = self.current_root.borrow().clone();
333 debug_assert!(current_root.is_some());
334
335 let bounds = self.bounds();
336 let root_visual = current_root.and_then(|current_root| current_root.root_visual());
338 let transform_to_visual = root_visual.and_then(|root_visual| self.transform_to_visual(&root_visual))?;
339 let mut transformed_rect =
340 Rect::from_position_size(Point::default(), bounds.size()).transform_to_aabb(transform_to_visual);
341 if self.use_layout_rounding() {
344 let scale = LayoutHelper::get_layout_scale(self);
345 let left = LayoutHelper::round_layout_value(transformed_rect.x, scale);
346 let top = LayoutHelper::round_layout_value(transformed_rect.y, scale);
347 let right = LayoutHelper::round_layout_value(transformed_rect.right(), scale);
348 let bottom = LayoutHelper::round_layout_value(transformed_rect.bottom(), scale);
349 transformed_rect = Rect::from_points(Point::new(left, top), Point::new(right, bottom));
350 }
351
352 Some(transformed_rect)
353 }
354
355 fn enqueue_for_move_resize(&self) {
356 if self.queued_for_move_resize.get() {
357 return;
358 }
359 self.queued_for_move_resize.set(true);
360 let this = self.to_ref();
361 Dispatcher::ui_thread().post_local(move || this.update_host(), DispatcherPriority::AFTER_RENDER);
362 }
363
364 pub fn try_update_native_control_position(&self) -> bool {
368 if self.current_host.borrow().is_none() {
369 return false;
370 }
371
372 let bounds = self.get_absolute_bounds();
373 let attachment = self.attachment.borrow().clone();
374
375 match bounds {
376 Some(bounds) if self.is_effectively_visible() => {
377 if bounds.width == 0.0 && bounds.height == 0.0 {
378 return false;
379 }
380 if let Some(attachment) = attachment {
381 attachment.show_in_bounds(bounds);
382 }
383 }
384 _ => {
385 if let Some(attachment) = attachment {
386 attachment.hide_with_size(self.bounds().size());
387 }
388 }
389 }
390 true
391 }
392
393 fn check_destruction(&self) {
394 self.queued_for_destruction.set(false);
395 if self.current_root.borrow().is_none() {
396 self.destroy_native_control();
397 }
398 }
399
400 fn destroy_native_control(&self) {
401 if let Some(native_control_handle) = self.native_control_handle() {
402 let attachment = self.attachment.borrow_mut().take();
403 if let Some(attachment) = attachment {
404 attachment.dispose();
405 }
406
407 self.destroy_native_control_core(native_control_handle);
408 self.set_native_control_handle(None);
409 }
410 }
411}