ferroui_diagnostics/diagnostics/controls/
control_highlight_adorner.rs1use ferroui_base::input::{InputElementImpl, InputElement};
4use ferroui_base::interactivity::InteractiveImpl;
5use ferroui_base::layout::{Layoutable, LayoutableImpl};
6use ferroui_base::media::{Color, Colors, DrawingContext, IBrush, IPen, Pen, SolidColorBrush};
7use ferroui_base::reactive::{Disposable, IDisposable};
8use ferroui_base::{
9 ferro_class, ferro_class_info, ferro_impl_classes, instantiate, FerroObjectImpl, Ref, StyledElementImpl,
10 Thickness, Visual, VisualImpl, VisualImplExt,
11};
12use ferroui_controls::primitives::{AdornerLayer, TemplatedControl};
13use ferroui_controls::{Border, Control, ControlImpl, Panel};
14use std::rc::Rc;
15
16ferroui_base::ferro_thread_local! {
17 static LAYOUT_HIGHLIGHT_ADORNER: Ref<Panel> = ControlHighlightAdorner::create_layout_highlight_adorner();
21}
22
23#[repr(C)]
26pub struct ControlHighlightAdorner {
27 base: Control,
28 pen: Rc<dyn IPen>,
29}
30
31ferro_class!(ControlHighlightAdorner: Control);
32ferro_impl_classes!(
33 ControlHighlightAdorner: FerroObjectImpl,
34 StyledElementImpl,
35 LayoutableImpl,
36 InteractiveImpl,
37 InputElementImpl,
38 ControlImpl
39);
40ferro_class_info!(ControlHighlightAdorner {});
41
42impl VisualImpl for ControlHighlightAdorner {
43 fn render(this: &Self, context: &mut DrawingContext) {
44 Self::parent_render(this, context);
45 context.draw_rectangle_outline(&this.pen, this.bounds().deflate(2.0), 0.0);
46 }
47}
48
49impl ControlHighlightAdorner {
50 fn create_layout_highlight_adorner() -> Ref<Panel> {
52 let panel = Panel::new();
53 panel.set_clip_to_bounds(false);
54
55 let padding_frame = Border::new();
57 padding_frame.set_border_brush(Some(SolidColorBrush::with_color_and_opacity(Colors::GREEN, 0.5).into()));
58 panel.children().add(padding_frame);
59
60 let content_frame = Border::new();
62 content_frame.set_background(Some(
63 SolidColorBrush::with_color_and_opacity(Color::from_rgb(160, 197, 232), 0.5).into(),
64 ));
65 panel.children().add(content_frame);
66
67 let margin_frame = Border::new();
69 margin_frame.set_border_brush(Some(SolidColorBrush::with_color_and_opacity(Colors::YELLOW, 0.5).into()));
70 panel.children().add(margin_frame);
71
72 AdornerLayer::set_is_clip_enabled(&panel, false);
73 panel
74 }
75
76 fn construct(pen: Rc<dyn IPen>) -> Self {
77 Self { base: Control::construct(), pen }
78 }
79
80 fn new(pen: Rc<dyn IPen>) -> Ref<Self> {
82 let this = instantiate(Self::construct(pen));
83 this.set_value(Visual::clip_property(), None);
84 this
85 }
86
87 pub fn add(owner: &InputElement, highlight_brush: Rc<dyn IBrush>) -> Option<Rc<dyn IDisposable>> {
90 let layer = AdornerLayer::get_adorner_layer(owner)?;
91
92 let pen: Rc<dyn IPen> = Rc::new(Pen::with_brush(Some(highlight_brush), 2.0).to_immutable());
93 let adorner = ControlHighlightAdorner::new(pen);
94 AdornerLayer::set_adorned_element(&adorner, owner.to_ref());
95 layer.children().add(adorner.clone());
96
97 Some(Disposable::create_with_state((layer, adorner), |(layer, adorner)| {
98 layer.children().remove(adorner);
99 }))
100 }
101
102 pub fn add_visual(visual: &Visual, visualize_margin_padding: bool) -> Option<Rc<dyn IDisposable>> {
107 let layer = AdornerLayer::get_adorner_layer(visual)?;
108 let layout_highlight_adorner = LAYOUT_HIGHLIGHT_ADORNER.with(Ref::clone);
109
110 if layer.children().contains(&layout_highlight_adorner.clone().upcast()) {
111 return None;
112 }
113 layer.children().add(layout_highlight_adorner.clone());
114 AdornerLayer::set_adorned_element(&layout_highlight_adorner, visual.to_ref());
115 let children = layout_highlight_adorner.children();
116 let border = |index: usize| children.get(index).cast::<Border>().expect("the frames of the layout are borders");
117 let padding_border = border(0);
118 let content_border = border(1);
119 let margin_border = border(2);
120 if visualize_margin_padding {
121 padding_border.set_border_thickness(visual.get_value(TemplatedControl::padding_property()));
122 content_border.set_margin(visual.get_value(TemplatedControl::padding_property()));
123 margin_border.set_border_thickness(visual.get_value(Layoutable::margin_property()));
124 margin_border.set_margin(Self::invert_thickness(visual.get_value(Layoutable::margin_property())));
125 } else {
126 padding_border.set_border_thickness(Thickness::default());
127 content_border.set_margin(Thickness::default());
128 margin_border.set_border_thickness(Thickness::default());
129 margin_border.set_margin(Thickness::default());
130 }
131
132 Some(Disposable::create_with_state((layer, layout_highlight_adorner), |(layer, adorner)| {
133 layer.children().remove(adorner);
134 }))
135 }
136
137 fn invert_thickness(input: Thickness) -> Thickness {
138 Thickness::new(-input.left, -input.top, -input.right, -input.bottom)
139 }
140}