ferroui_diagnostics/diagnostics/views/
layout_explorer_view.rs1use crate::diagnostics::controls::ThicknessEditor;
5use ferroui_base::input::InputElementImpl;
6use ferroui_base::interactivity::InteractiveImpl;
7use ferroui_base::layout::LayoutableImpl;
8use ferroui_base::{
9 ferro_class, ferro_class_info, ferro_impl_classes, instantiate, FerroObjectImpl, FerroPropertyChangedEventArgs,
10 Point, Rect, Ref, StyledElementImpl, Visual, VisualImpl,
11};
12use ferroui_controls::primitives::TemplatedControlImpl;
13use ferroui_controls::shapes::Rectangle;
14use ferroui_controls::{Border, Canvas, ContentControlImpl, ControlImpl, Grid, UserControl};
15
16#[repr(C)]
20pub struct LayoutExplorerView {
21 base: UserControl,
22}
23
24ferro_class!(LayoutExplorerView: UserControl);
25ferro_impl_classes!(
26 LayoutExplorerView: FerroObjectImpl,
27 StyledElementImpl,
28 VisualImpl,
29 LayoutableImpl,
30 InteractiveImpl,
31 InputElementImpl,
32 ControlImpl,
33 TemplatedControlImpl,
34 ContentControlImpl
35);
36ferro_class_info!(LayoutExplorerView { new: LayoutExplorerView::new });
37
38struct LayoutParts {
40 border_area: Ref<ThicknessEditor>,
41 padding_area: Ref<ThicknessEditor>,
42 horizontal_size_begin: Ref<Rectangle>,
43 horizontal_size_end: Ref<Rectangle>,
44 vertical_size_begin: Ref<Rectangle>,
45 vertical_size_end: Ref<Rectangle>,
46 layout_root: Ref<Grid>,
47 horizontal_size: Ref<Border>,
48 vertical_size: Ref<Border>,
49 content_area: Ref<Border>,
50}
51
52impl LayoutExplorerView {
53 pub const DOCUMENT_URI: &'static str = "ferres://FerroUI.Diagnostics/diagnostics/views/LayoutExplorerView.xaml";
55
56 pub fn construct() -> Self {
57 Self { base: UserControl::construct() }
58 }
59
60 pub fn new() -> Ref<Self> {
64 let this = instantiate(Self::construct());
65 Self::initialize_component(&this);
66
67 let content_area = this.get_control::<Border>("ContentArea");
68
69 let mut visual: Option<Ref<Visual>> = Some(content_area.upcast());
70 while let Some(current) = visual {
71 if current.ptr_eq(&this) {
72 break;
73 }
74 let weak = this.downgrade();
75 let _ = current.property_changed(move |e: &FerroPropertyChangedEventArgs<'_>| {
78 if e.property() == Visual::bounds_property().as_property() {
79 if let Some(this) = weak.upgrade() {
80 this.update_size_guidelines();
81 }
82 }
83 });
84 visual = current.visual_parent();
85 }
86
87 this
88 }
89
90 fn initialize_component(this: &Ref<Self>) {
91 crate::register_types();
92 if let Err(error) = crate::compiled_diagnostics_views_layout_explorer_view::populate(None, this) {
93 panic!("{}: {error}", Self::DOCUMENT_URI);
94 }
95 }
96
97 fn parts(&self) -> LayoutParts {
98 LayoutParts {
99 border_area: self.get_control::<ThicknessEditor>("BorderArea"),
100 padding_area: self.get_control::<ThicknessEditor>("PaddingArea"),
101 horizontal_size_begin: self.get_control::<Rectangle>("HorizontalSizeBegin"),
102 horizontal_size_end: self.get_control::<Rectangle>("HorizontalSizeEnd"),
103 vertical_size_begin: self.get_control::<Rectangle>("VerticalSizeBegin"),
104 vertical_size_end: self.get_control::<Rectangle>("VerticalSizeEnd"),
105 horizontal_size: self.get_control::<Border>("HorizontalSize"),
106 vertical_size: self.get_control::<Border>("VerticalSize"),
107 content_area: self.get_control::<Border>("ContentArea"),
108 layout_root: self.get_control::<Grid>("LayoutRoot"),
109 }
110 }
111
112 fn update_size_guidelines(&self) {
113 let parts = self.parts();
114
115 if parts.border_area.is_present() {
116 Self::update_guidelines(&parts, &parts.border_area);
117 } else if parts.padding_area.is_present() {
118 Self::update_guidelines(&parts, &parts.padding_area);
119 } else {
120 Self::update_guidelines(&parts, &parts.content_area);
121 }
122 }
123
124 fn get_pseudo_transformed_bounds(v: &Visual) -> Rect {
127 Rect::from_size(v.bounds().size())
128 }
129
130 fn translate_to_root(parts: &LayoutParts, point: Point, from: &Visual) -> Point {
131 from.translate_point(point, &parts.layout_root).unwrap_or_default()
132 }
133
134 fn set_position(rect: &Rectangle, start: Point) {
135 Canvas::set_left(rect, start.x);
136 Canvas::set_top(rect, start.y);
137 }
138
139 fn update_guidelines(parts: &LayoutParts, area: &Visual) {
140 let bounds = Self::get_pseudo_transformed_bounds(area);
141
142 {
144 let size_area = Self::translate_to_root(
145 parts,
146 Self::get_pseudo_transformed_bounds(&parts.horizontal_size).bottom_left(),
147 &parts.horizontal_size,
148 );
149
150 let start = Self::translate_to_root(parts, bounds.bottom_left(), area);
151
152 Self::set_position(&parts.horizontal_size_begin, start);
153
154 let end = Self::translate_to_root(parts, bounds.bottom_right(), area);
155
156 Self::set_position(&parts.horizontal_size_end, end.with_x(end.x - 1.0));
157
158 let height = size_area.y - start.y + 2.0;
159
160 parts.horizontal_size_begin.set_height(height);
161 parts.horizontal_size_end.set_height(height);
162 }
163
164 {
166 let size_area = Self::translate_to_root(
167 parts,
168 Self::get_pseudo_transformed_bounds(&parts.vertical_size).top_right(),
169 &parts.vertical_size,
170 );
171
172 let start = Self::translate_to_root(parts, bounds.top_right(), area);
173
174 Self::set_position(&parts.vertical_size_begin, start);
175
176 let end = Self::translate_to_root(parts, bounds.bottom_right(), area);
177
178 Self::set_position(&parts.vertical_size_end, end.with_y(end.y - 1.0));
179
180 let width = size_area.x - start.x + 2.0;
181
182 parts.vertical_size_begin.set_width(width);
183 parts.vertical_size_end.set_width(width);
184 }
185 }
186}