1use crate::utils::debug_display::{append_optional_boxed_value, build_base_debug_display};
2use super::as_content_presenter_host;
3use crate::documents::TextElement;
4use crate::metadata::PseudoClassesAttribute;
5use crate::templates::{FuncDataTemplate, IDataTemplate};
6use crate::{Border, ContentControl, Control, ControlImpl, Decorator, TextBlock};
7use ferroui_base::collections::FerroList;
8use ferroui_base::input::InputElementImpl;
9use ferroui_base::interactivity::InteractiveImpl;
10use ferroui_base::layout::{HorizontalAlignment, LayoutHelper, Layoutable, LayoutableImpl, VerticalAlignment};
11use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
12use crate::utils::BorderRenderHelper;
13use ferroui_base::media::{
14 BackgroundSizing, BoxShadows, DrawingContext, FontFamily, FontStretch, FontStyle, FontWeight, IBrush,
15 TextAlignment, TextTrimming, TextWrapping,
16};
17use ferroui_base::utilities::MathUtilities;
18use ferroui_base::{
19 ferro_class, ferro_impl_classes, ferro_property, instantiate, BoxedValue, CornerRadius, DirectProperty,
20 FerroObject, FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Point, Rect, Ref,
21 Size, StyledElement, StyledElementImpl, StyledElementImplExt, StyledProperty, Thickness, Vector, Visual,
22 VisualImpl, WeakRef,
23};
24use std::cell::{Cell, RefCell};
25use std::rc::Rc;
26
27#[repr(C)]
29pub struct ContentPresenter {
30 base: Control,
31 child: RefCell<Option<Ref<Control>>>,
32 created_child: Cell<bool>,
33 recycling_data_template: RefCell<Option<Rc<dyn IDataTemplate>>>,
34 override_data_context: RefCell<Option<Option<BoxedValue>>>,
37 layout_thickness: Cell<Option<Thickness>>,
38 scale: Cell<f64>,
39 host: RefCell<Option<WeakRef<FerroObject>>>,
40 border_renderer: RefCell<BorderRenderHelper>,
41}
42
43ferro_class!(ContentPresenter: Control);
44ferroui_base::ferro_class_info!(ContentPresenter { new: ContentPresenter::new });
45ferro_impl_classes!(ContentPresenter: InteractiveImpl, InputElementImpl, ControlImpl);
46
47impl VisualImpl for ContentPresenter {
48 fn render(this: &Self, context: &mut DrawingContext) {
49 this.border_renderer.borrow_mut().render(
50 context,
51 this.bounds().size(),
52 this.layout_thickness(),
53 this.corner_radius(),
54 this.background_sizing(),
55 this.background().as_ref(),
56 this.border_brush().as_ref(),
57 &this.box_shadow(),
58 );
59 }
60}
61
62ferroui_base::ferro_overrides! { impl FerroObjectImpl for ContentPresenter {
63 fn constructed(this: &Self) {
64 Self::parent_constructed(this);
65 this.update_pseudo_classes();
66 }
67
68 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
69 Self::parent_on_property_changed(this, change);
70 match change.property().name() {
71 "Content" | "ContentTemplate" => this.content_changed(change),
72 "TemplatedParent" => this.templated_parent_changed(change),
73 "UseLayoutRounding" | "BorderThickness" => this.layout_thickness.set(None),
74 _ => {}
75 }
76 }
77} }
78
79impl StyledElementImpl for ContentPresenter {
80 fn on_attached_to_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
81 Self::parent_on_attached_to_logical_tree(this, e);
82 *this.recycling_data_template.borrow_mut() = None;
83 this.created_child.set(false);
84 this.invalidate_measure();
85 }
86}
87
88impl LayoutableImpl for ContentPresenter {
89 fn apply_template(this: &Self) {
90 if !this.created_child.get() && this.is_attached_to_logical_tree() {
91 this.update_child();
92 }
93 }
94
95 fn measure_override(this: &Self, available_size: Size) -> Size {
96 let child = this.child();
97 LayoutHelper::measure_child_with_border(
98 child.as_deref().map(|c| -> &Layoutable { c }),
99 available_size,
100 this.padding(),
101 this.border_thickness(),
102 )
103 }
104
105 fn arrange_override(this: &Self, final_size: Size) -> Size {
106 this.arrange_override_impl(final_size, Vector::default())
107 }
108}
109
110impl ContentPresenter {
111 pub const PSEUDO_CLASSES: PseudoClassesAttribute = PseudoClassesAttribute::new(&[":empty"]);
113}
114
115ferroui_base::ferro_properties! { impl ContentPresenter {
116 ferro_property!(
117 pub fn background_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
119 Border::background_property().add_owner::<ContentPresenter>()
120 }
121 );
122
123 ferro_property!(
124 pub fn background_sizing_property() -> StyledProperty<BackgroundSizing> {
126 Border::background_sizing_property().add_owner::<ContentPresenter>()
127 }
128 );
129
130 ferro_property!(
131 pub fn border_brush_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
133 Border::border_brush_property().add_owner::<ContentPresenter>()
134 }
135 );
136
137 ferro_property!(
138 pub fn border_thickness_property() -> StyledProperty<Thickness> {
140 Border::border_thickness_property().add_owner::<ContentPresenter>()
141 }
142 );
143
144 ferro_property!(
145 pub fn corner_radius_property() -> StyledProperty<CornerRadius> {
147 Border::corner_radius_property().add_owner::<ContentPresenter>()
148 }
149 );
150
151 ferro_property!(
152 pub fn box_shadow_property() -> StyledProperty<BoxShadows> {
154 Border::box_shadow_property().add_owner::<ContentPresenter>()
155 }
156 );
157
158 ferro_property!(
159 pub fn foreground_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
161 TextElement::foreground_property().add_owner::<ContentPresenter>()
162 }
163 );
164
165 ferro_property!(
166 pub fn font_family_property() -> StyledProperty<FontFamily> {
168 TextElement::font_family_property().add_owner::<ContentPresenter>()
169 }
170 );
171
172 ferro_property!(
173 pub fn font_size_property() -> StyledProperty<f64> {
175 TextElement::font_size_property().add_owner::<ContentPresenter>()
176 }
177 );
178
179 ferro_property!(
180 pub fn font_style_property() -> StyledProperty<FontStyle> {
182 TextElement::font_style_property().add_owner::<ContentPresenter>()
183 }
184 );
185
186 ferro_property!(
187 pub fn font_weight_property() -> StyledProperty<FontWeight> {
189 TextElement::font_weight_property().add_owner::<ContentPresenter>()
190 }
191 );
192
193 ferro_property!(
194 pub fn font_stretch_property() -> StyledProperty<FontStretch> {
196 TextElement::font_stretch_property().add_owner::<ContentPresenter>()
197 }
198 );
199
200 ferro_property!(
201 pub fn text_alignment_property() -> StyledProperty<TextAlignment> {
203 TextBlock::text_alignment_property().add_owner::<ContentPresenter>()
204 }
205 );
206
207 ferro_property!(
208 pub fn text_wrapping_property() -> StyledProperty<TextWrapping> {
210 TextBlock::text_wrapping_property().add_owner::<ContentPresenter>()
211 }
212 );
213
214 ferro_property!(
215 pub fn text_trimming_property() -> StyledProperty<Rc<dyn TextTrimming>> {
217 TextBlock::text_trimming_property().add_owner::<ContentPresenter>()
218 }
219 );
220
221 ferro_property!(
222 pub fn line_height_property() -> StyledProperty<f64> {
224 TextBlock::line_height_property().add_owner::<ContentPresenter>()
225 }
226 );
227
228 ferro_property!(
229 pub fn letter_spacing_property() -> StyledProperty<f64> {
231 TextElement::letter_spacing_property().add_owner::<ContentPresenter>()
232 }
233 );
234
235 ferro_property!(
236 pub fn max_lines_property() -> StyledProperty<i32> {
238 TextBlock::max_lines_property().add_owner::<ContentPresenter>()
239 }
240 );
241
242 ferro_property!(
243 pub fn child_property() -> DirectProperty<ContentPresenter, Option<Ref<Control>>> {
245 FerroProperty::register_direct::<ContentPresenter, _>("Child", |o| o.child(), None, None)
246 }
247 );
248
249 ferro_property!(
250 pub fn content_property() -> StyledProperty<Option<BoxedValue>> {
252 ContentControl::content_property().add_owner::<ContentPresenter>()
253 }
254 );
255
256 ferro_property!(
257 pub fn content_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
259 ContentControl::content_template_property().add_owner::<ContentPresenter>()
260 }
261 );
262
263 ferro_property!(
264 pub fn horizontal_content_alignment_property() -> StyledProperty<HorizontalAlignment> {
266 ContentControl::horizontal_content_alignment_property().add_owner::<ContentPresenter>()
267 }
268 );
269
270 ferro_property!(
271 pub fn vertical_content_alignment_property() -> StyledProperty<VerticalAlignment> {
273 ContentControl::vertical_content_alignment_property().add_owner::<ContentPresenter>()
274 }
275 );
276
277 ferro_property!(
278 pub fn padding_property() -> StyledProperty<Thickness> {
280 Decorator::padding_property().add_owner::<ContentPresenter>()
281 }
282 );
283
284 ferro_property!(
285 pub fn recognizes_access_key_property() -> StyledProperty<bool> {
287 FerroProperty::register::<ContentPresenter, _>("RecognizesAccessKey", false)
288 }
289 );
290} }
291
292impl ContentPresenter {
293 fn static_constructor() {
294 Visual::affects_render::<ContentPresenter>(&[
295 Self::background_property().as_property(),
296 Self::background_sizing_property().as_property(),
297 Self::border_brush_property().as_property(),
298 Self::border_thickness_property().as_property(),
299 Self::box_shadow_property().as_property(),
300 Self::corner_radius_property().as_property(),
301 ]);
302 Layoutable::affects_arrange::<ContentPresenter>(&[
303 Self::horizontal_content_alignment_property().as_property(),
304 Self::vertical_content_alignment_property().as_property(),
305 ]);
306 Layoutable::affects_measure::<ContentPresenter>(&[
307 Self::border_thickness_property().as_property(),
308 Self::padding_property().as_property(),
309 ]);
310 }
311
312 pub fn construct() -> Self {
314 Self {
315 base: Control::construct(),
316 child: RefCell::new(None),
317 created_child: Cell::new(false),
318 recycling_data_template: RefCell::new(None),
319 override_data_context: RefCell::new(None),
320 layout_thickness: Cell::new(None),
321 scale: Cell::new(0.0),
322 host: RefCell::new(None),
323 border_renderer: RefCell::new(BorderRenderHelper::new()),
324 }
325 }
326
327 pub fn new() -> Ref<Self> {
328 instantiate(Self::construct())
329 }
330
331 pub fn background(&self) -> Option<Rc<dyn IBrush>> {
333 self.get_value(Self::background_property())
334 }
335
336 pub fn set_background(&self, value: Option<Rc<dyn IBrush>>) {
337 self.set_value(Self::background_property(), value)
338 }
339
340 pub fn background_sizing(&self) -> BackgroundSizing {
342 self.get_value(Self::background_sizing_property())
343 }
344
345 pub fn set_background_sizing(&self, value: BackgroundSizing) {
346 self.set_value(Self::background_sizing_property(), value)
347 }
348
349 pub fn border_brush(&self) -> Option<Rc<dyn IBrush>> {
351 self.get_value(Self::border_brush_property())
352 }
353
354 pub fn set_border_brush(&self, value: Option<Rc<dyn IBrush>>) {
355 self.set_value(Self::border_brush_property(), value)
356 }
357
358 pub fn border_thickness(&self) -> Thickness {
360 self.get_value(Self::border_thickness_property())
361 }
362
363 pub fn set_border_thickness(&self, value: Thickness) {
364 self.set_value(Self::border_thickness_property(), value)
365 }
366
367 pub fn corner_radius(&self) -> CornerRadius {
369 self.get_value(Self::corner_radius_property())
370 }
371
372 pub fn set_corner_radius(&self, value: CornerRadius) {
373 self.set_value(Self::corner_radius_property(), value)
374 }
375
376 pub fn box_shadow(&self) -> BoxShadows {
378 self.get_value(Self::box_shadow_property())
379 }
380
381 pub fn set_box_shadow(&self, value: BoxShadows) {
382 self.set_value(Self::box_shadow_property(), value)
383 }
384
385 pub fn foreground(&self) -> Option<Rc<dyn IBrush>> {
387 self.get_value(Self::foreground_property())
388 }
389
390 pub fn set_foreground(&self, value: Option<Rc<dyn IBrush>>) {
391 self.set_value(Self::foreground_property(), value)
392 }
393
394 pub fn font_family(&self) -> FontFamily {
396 self.get_value(Self::font_family_property())
397 }
398
399 pub fn set_font_family(&self, value: FontFamily) {
400 self.set_value(Self::font_family_property(), value)
401 }
402
403 pub fn font_size(&self) -> f64 {
405 self.get_value(Self::font_size_property())
406 }
407
408 pub fn set_font_size(&self, value: f64) {
409 self.set_value(Self::font_size_property(), value)
410 }
411
412 pub fn font_style(&self) -> FontStyle {
414 self.get_value(Self::font_style_property())
415 }
416
417 pub fn set_font_style(&self, value: FontStyle) {
418 self.set_value(Self::font_style_property(), value)
419 }
420
421 pub fn font_weight(&self) -> FontWeight {
423 self.get_value(Self::font_weight_property())
424 }
425
426 pub fn set_font_weight(&self, value: FontWeight) {
427 self.set_value(Self::font_weight_property(), value)
428 }
429
430 pub fn font_stretch(&self) -> FontStretch {
432 self.get_value(Self::font_stretch_property())
433 }
434
435 pub fn set_font_stretch(&self, value: FontStretch) {
436 self.set_value(Self::font_stretch_property(), value)
437 }
438
439 pub fn text_alignment(&self) -> TextAlignment {
441 self.get_value(Self::text_alignment_property())
442 }
443
444 pub fn set_text_alignment(&self, value: TextAlignment) {
445 self.set_value(Self::text_alignment_property(), value)
446 }
447
448 pub fn text_wrapping(&self) -> TextWrapping {
450 self.get_value(Self::text_wrapping_property())
451 }
452
453 pub fn set_text_wrapping(&self, value: TextWrapping) {
454 self.set_value(Self::text_wrapping_property(), value)
455 }
456
457 pub fn text_trimming(&self) -> Rc<dyn TextTrimming> {
459 self.get_value(Self::text_trimming_property())
460 }
461
462 pub fn set_text_trimming(&self, value: Rc<dyn TextTrimming>) {
463 self.set_value(Self::text_trimming_property(), value)
464 }
465
466 pub fn line_height(&self) -> f64 {
468 self.get_value(Self::line_height_property())
469 }
470
471 pub fn set_line_height(&self, value: f64) {
472 self.set_value(Self::line_height_property(), value)
473 }
474
475 pub fn letter_spacing(&self) -> f64 {
477 self.get_value(Self::letter_spacing_property())
478 }
479
480 pub fn set_letter_spacing(&self, value: f64) {
481 self.set_value(Self::letter_spacing_property(), value)
482 }
483
484 pub fn max_lines(&self) -> i32 {
486 self.get_value(Self::max_lines_property())
487 }
488
489 pub fn set_max_lines(&self, value: i32) {
490 self.set_value(Self::max_lines_property(), value)
491 }
492
493 pub fn child(&self) -> Option<Ref<Control>> {
495 self.child.borrow().clone()
496 }
497
498 fn set_child(&self, value: Option<Ref<Control>>) {
499 self.set_and_raise(Self::child_property(), &self.child, value);
500 }
501
502 pub fn content(&self) -> Option<BoxedValue> {
504 self.get_value(Self::content_property())
505 }
506
507 pub fn set_content(&self, value: Option<BoxedValue>) {
508 self.set_value(Self::content_property(), value)
509 }
510
511 pub fn content_template(&self) -> Option<Rc<dyn IDataTemplate>> {
513 self.get_value(Self::content_template_property())
514 }
515
516 pub fn set_content_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
517 self.set_value(Self::content_template_property(), value)
518 }
519
520 pub fn horizontal_content_alignment(&self) -> HorizontalAlignment {
523 self.get_value(Self::horizontal_content_alignment_property())
524 }
525
526 pub fn set_horizontal_content_alignment(&self, value: HorizontalAlignment) {
527 self.set_value(Self::horizontal_content_alignment_property(), value)
528 }
529
530 pub fn vertical_content_alignment(&self) -> VerticalAlignment {
533 self.get_value(Self::vertical_content_alignment_property())
534 }
535
536 pub fn set_vertical_content_alignment(&self, value: VerticalAlignment) {
537 self.set_value(Self::vertical_content_alignment_property(), value)
538 }
539
540 pub fn padding(&self) -> Thickness {
542 self.get_value(Self::padding_property())
543 }
544
545 pub fn set_padding(&self, value: Thickness) {
546 self.set_value(Self::padding_property(), value)
547 }
548
549 pub fn recognizes_access_key(&self) -> bool {
551 self.get_value(Self::recognizes_access_key_property())
552 }
553
554 pub fn set_recognizes_access_key(&self, value: bool) {
555 self.set_value(Self::recognizes_access_key_property(), value)
556 }
557
558 pub fn host(&self) -> Option<Ref<FerroObject>> {
560 self.host.borrow().as_ref().and_then(WeakRef::upgrade)
561 }
562
563 pub fn set_content_with_data_context(&self, content: Option<BoxedValue>, data_context: Option<BoxedValue>) {
567 *self.override_data_context.borrow_mut() = Some(data_context.clone());
568
569 struct Finally<'a>(&'a ContentPresenter, Option<BoxedValue>);
573
574 impl Drop for Finally<'_> {
575 fn drop(&mut self) {
576 let is_set = self.0.override_data_context.borrow().is_some();
577 if is_set {
578 *self.0.override_data_context.borrow_mut() = None;
579 self.0.set_data_context(self.1.take());
580 }
581 }
582 }
583
584 let _finally = Finally(self, data_context);
585 self.set_current_value(Self::content_property(), content);
586 }
587
588 pub fn update_child(&self) {
597 let content = self.content();
598 self.update_child_with(content);
599 }
600
601 fn update_child_with(&self, content: Option<BoxedValue>) {
602 let content_template = self.content_template();
603 let old_child = self.child();
604 let new_child = self.create_child(content.as_ref(), old_child.as_ref(), content_template.as_ref());
605
606 if new_child != old_child {
608 ferroui_base::perf_count!(ContentPresenterChildrenReplaced);
609 if let Some(old_child) = &old_child {
610 self.visual_children().remove(&old_child.clone().upcast());
611 self.with_effective_logical_children(|logical_children| {
612 logical_children.remove(&old_child.clone().upcast());
613 });
614 old_child.set_inheritance_parent(old_child.parent().map(Ref::upcast::<FerroObject>));
615 }
616 }
617
618 let override_data_context = self.override_data_context.borrow_mut().take();
621
622 let content_is_control = content.as_ref().is_some_and(|content| Control::from_boxed(content).is_some());
626 if let Some(data_context) = override_data_context {
627 self.set_data_context(data_context);
628 } else if content_template.is_some() || !content_is_control {
629 self.set_data_context(content);
630 } else {
631 self.clear_value(StyledElement::data_context_property());
632 }
633
634 match new_child {
636 None => self.set_child(None),
637 Some(new_child) => {
638 if Some(&new_child) != old_child.as_ref() {
639 new_child.set_inheritance_parent(self.to_ref().upcast::<FerroObject>());
640 self.set_child(Some(new_child.clone()));
641
642 self.with_effective_logical_children(|logical_children| {
643 let logical: Ref<StyledElement> = new_child.clone().upcast();
644 if !logical_children.contains(&logical) {
645 logical_children.add(logical);
646 }
647 });
648
649 self.visual_children().add(new_child.upcast());
650 }
651 }
652 }
653
654 self.created_child.set(true);
655 }
656
657 fn with_effective_logical_children(&self, f: impl FnOnce(&FerroList<Ref<StyledElement>>)) {
660 let host = self.host();
661 match host.as_ref().and_then(|host| as_content_presenter_host(host)) {
662 Some(host) => f(host.logical_children()),
663 None => f(self.logical_children()),
664 }
665 }
666
667 pub fn layout_thickness(&self) -> Thickness {
670 self.verify_scale();
671
672 match self.layout_thickness.get() {
673 Some(thickness) => thickness,
674 None => {
675 let mut border_thickness = self.border_thickness();
676
677 if self.use_layout_rounding() {
678 border_thickness = LayoutHelper::round_layout_thickness(border_thickness, self.scale.get());
679 }
680
681 self.layout_thickness.set(Some(border_thickness));
682 border_thickness
683 }
684 }
685 }
686
687 fn verify_scale(&self) {
688 let current_scale = LayoutHelper::get_layout_scale(self);
689 if MathUtilities::are_close(current_scale, self.scale.get()) {
690 return;
691 }
692
693 self.scale.set(current_scale);
694 self.layout_thickness.set(None);
695 }
696
697 fn create_child(
698 &self,
699 content: Option<&BoxedValue>,
700 old_child: Option<&Ref<Control>>,
701 template: Option<&Rc<dyn IDataTemplate>>,
702 ) -> Option<Ref<Control>> {
703 let mut new_child = content.and_then(Control::from_boxed);
704
705 if (new_child.is_none() && (content.is_some() || template.is_some()))
710 || (new_child.is_some() && template.is_some())
711 {
712 let data_template: Rc<dyn IDataTemplate> = match self.find_data_template(content, template) {
713 Some(data_template) => data_template,
714 None if self.recognizes_access_key() => FuncDataTemplate::access(),
715 None => FuncDataTemplate::default_template(),
716 };
717
718 if let Some(rdt) = data_template.as_recycling_data_template() {
719 let is_same = self.recycling_data_template.borrow().as_ref().is_some_and(|current| {
720 std::ptr::addr_eq(Rc::as_ptr(current), Rc::as_ptr(&data_template))
721 });
722 let to_recycle = if is_same { old_child.cloned() } else { None };
723 new_child = rdt.build_with_existing(content, to_recycle);
724 *self.recycling_data_template.borrow_mut() = Some(data_template.clone());
725 } else {
726 new_child = data_template.build(&content.cloned());
727 *self.recycling_data_template.borrow_mut() = None;
728 }
729 } else {
730 *self.recycling_data_template.borrow_mut() = None;
731 }
732
733 new_child
734 }
735
736 pub fn arrange_override_impl(&self, final_size: Size, offset: Vector) -> Size {
739 let Some(child) = self.child() else { return final_size };
740
741 let use_layout_rounding = self.use_layout_rounding();
742 let scale = LayoutHelper::get_layout_scale(self);
743 let mut padding = self.padding();
744 let mut border_thickness = self.border_thickness();
745
746 if use_layout_rounding {
747 padding = LayoutHelper::round_layout_thickness(padding, scale);
748 border_thickness = LayoutHelper::round_layout_thickness(border_thickness, scale);
749 }
750
751 padding = padding + border_thickness;
752 let horizontal_content_alignment = self.horizontal_content_alignment();
753 let vertical_content_alignment = self.vertical_content_alignment();
754 let mut available_size = final_size;
755 let mut size_for_child = available_size;
756 let mut origin_x = offset.x;
757 let mut origin_y = offset.y;
758 let desired_size = self.desired_size();
759
760 if horizontal_content_alignment != HorizontalAlignment::Stretch {
761 size_for_child = size_for_child.with_width(size_for_child.width.min(desired_size.width));
762 }
763
764 if vertical_content_alignment != VerticalAlignment::Stretch {
765 size_for_child = size_for_child.with_height(size_for_child.height.min(desired_size.height));
766 }
767
768 if use_layout_rounding {
769 size_for_child = LayoutHelper::round_layout_size_up(size_for_child, scale);
770 available_size = LayoutHelper::round_layout_size_up(available_size, scale);
771 }
772
773 match horizontal_content_alignment {
774 HorizontalAlignment::Center => origin_x += (available_size.width - size_for_child.width) / 2.0,
775 HorizontalAlignment::Right => origin_x += available_size.width - size_for_child.width,
776 _ => {}
777 }
778
779 match vertical_content_alignment {
780 VerticalAlignment::Center => origin_y += (available_size.height - size_for_child.height) / 2.0,
781 VerticalAlignment::Bottom => origin_y += available_size.height - size_for_child.height,
782 _ => {}
783 }
784
785 let mut origin = Point::new(origin_x, origin_y);
786
787 if use_layout_rounding {
788 origin = LayoutHelper::round_layout_point(origin, scale);
789 }
790
791 let bounds_for_child = Rect::from_position_size(origin, size_for_child).deflate_thickness(padding);
792
793 child.arrange(bounds_for_child);
794
795 final_size
796 }
797
798 fn content_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
800 self.created_child.set(false);
801
802 if self.is_attached_to_logical_tree() {
803 if e.property().name() == "Content" {
804 self.update_child_with(e.get_new_value::<Option<BoxedValue>>());
805 } else {
806 self.update_child();
807 }
808 } else if let Some(child) = self.child() {
809 self.visual_children().remove(&child.clone().upcast());
810 self.with_effective_logical_children(|logical_children| {
811 logical_children.remove(&child.clone().upcast());
812 });
813 child.set_inheritance_parent(child.parent().map(Ref::upcast::<FerroObject>));
814 self.set_child(None);
815 *self.recycling_data_template.borrow_mut() = None;
816 }
817
818 self.update_pseudo_classes();
819 self.invalidate_measure();
820 }
821
822 fn update_pseudo_classes(&self) {
823 self.pseudo_classes().set(":empty", self.content().is_none());
824 }
825
826 fn templated_parent_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
827 let new_value = e.get_new_value::<Option<Ref<FerroObject>>>();
828 let registered = new_value.as_ref().is_some_and(|parent| {
829 as_content_presenter_host(parent).is_some_and(|host| host.register_content_presenter(self))
830 });
831 *self.host.borrow_mut() = if registered { new_value.as_ref().map(Ref::downgrade) } else { None };
832 }
833}
834
835impl ContentPresenter {
836 pub(crate) fn build_debug_display(&self, builder: &mut String, include_content: bool) {
839 build_base_debug_display(self, builder);
840
841 let host: Option<BoxedValue> = self.host().map(|host| Rc::new(host) as BoxedValue);
842 append_optional_boxed_value(builder, "Host", host.as_ref(), false);
843
844 if include_content {
845 append_optional_boxed_value(builder, "Content", self.content().as_ref(), true);
846 }
847 }
848}