ferroui_controls/primitives/
templated_control.rs1use super::TemplateAppliedEventArgs;
2use crate::documents::TextElement;
3use crate::templates::IControlTemplate;
4use crate::{Border, Control, ControlImpl, Decorator};
5use ferroui_base::controls::ResourcesChangedEventArgs;
6use ferroui_base::input::InputElementImpl;
7use ferroui_base::interactivity::{Interactive, InteractiveImpl, RoutedEvent, RoutedEventHandlerToken, RoutingStrategies};
8use ferroui_base::layout::LayoutableImpl;
9use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
10use ferroui_base::media::{
11 BackgroundSizing, FontFamily, FontFeatureCollection, FontStretch, FontStyle, FontWeight, IBrush,
12};
13use ferroui_base::{
14 ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, instantiate, AttachedProperty,
15 CornerRadius, FerroObject, FerroObjectImpl, FerroProperty, FerroPropertyChangedEventArgs,
16 Nullable, Ref, StyledElement, StyledElementImpl, StyledElementImplExt, StyledProperty, Thickness, Visual,
17 VisualImpl,
18};
19use std::cell::RefCell;
20use std::rc::Rc;
21
22#[repr(C)]
24pub struct TemplatedControl {
25 base: Control,
26 applied_template: RefCell<Option<Rc<dyn IControlTemplate>>>,
27}
28
29ferro_class! {
30 TemplatedControl: Control, virtuals TemplatedControlImpl: ControlImpl {
31 fn on_apply_template(this, e: &TemplateAppliedEventArgs);
36 fn on_template_changed(this, e: &FerroPropertyChangedEventArgs<'_>);
38 }
39}
40ferroui_base::ferro_class_info!(TemplatedControl { new: TemplatedControl::new });
41
42ferro_impl_classes!(TemplatedControl: VisualImpl, InteractiveImpl, InputElementImpl);
43
44ferroui_base::ferro_impl_classes!(TemplatedControl: FerroObjectImpl);
45
46impl StyledElementImpl for TemplatedControl {
47 fn notify_child_resources_changed(this: &Self, e: ResourcesChangedEventArgs) {
48 if let Some(children) = this.visual_children_snapshot() {
49 for child in children.iter() {
50 child.notify_resources_changed(e, true);
51 }
52 }
53
54 Self::parent_notify_child_resources_changed(this, e);
55 }
56
57 fn on_attached_to_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
58 if let Some(child) = this.visual_children_snapshot().and_then(|children| children.first().cloned()) {
59 child.notify_attached_to_logical_tree(e);
60 }
61
62 Self::parent_on_attached_to_logical_tree(this, e);
63 }
64
65 fn on_detached_from_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
66 if let Some(child) = this.visual_children_snapshot().and_then(|children| children.first().cloned()) {
67 child.notify_detached_from_logical_tree(e);
68 }
69
70 Self::parent_on_detached_from_logical_tree(this, e);
71 }
72
73 fn on_control_theme_changed(this: &Self) {
74 Self::parent_on_control_theme_changed(this);
75
76 if let Some(children) = this.visual_children_snapshot() {
77 let this_ref: Ref<FerroObject> = this.to_ref().upcast();
78 for child in children.iter() {
79 if child.templated_parent().as_ref() == Some(&this_ref) {
80 child.on_templated_parent_control_theme_changed();
81 }
82 }
83 }
84 }
85}
86
87impl LayoutableImpl for TemplatedControl {
88 fn apply_template(this: &Self) {
89 let template = this.template();
90
91 let applied_template = this.applied_template.borrow().clone();
99 if applied_template != template && (template.is_some() || this.is_attached_to_logical_tree()) {
100 if this.visual_children_count() > 0 {
101 for child in this.get_template_descendants() {
102 child.set_templated_parent(None);
103 child.set_parent(None);
104 }
105
106 this.visual_children().clear();
107 }
108
109 if let Some(template) = &template {
110 let this_ref = this.to_ref();
111 if let Some(template_result) = template.build(&this_ref) {
112 let (child, name_scope) = template_result.deconstruct();
113 Self::apply_templated_parent(&child, Some(&this_ref.clone().upcast()));
114 child.set_parent(this_ref);
115 this.visual_children().add(child.upcast());
116
117 let e = TemplateAppliedEventArgs::new(name_scope);
118 this.on_apply_template(&e);
119 this.raise_event(&e);
120 }
121 }
122
123 *this.applied_template.borrow_mut() = template;
124 }
125 }
126}
127
128impl ControlImpl for TemplatedControl {
129 fn get_template_focus_target(this: &Self) -> Option<Ref<Control>> {
130 for child in this.get_template_descendants() {
131 if let Some(control) = child.cast::<Control>() {
132 if Self::get_is_template_focus_target(&control) {
133 return Some(control);
134 }
135 }
136 }
137
138 Some(this.to_ref().upcast())
139 }
140}
141
142impl TemplatedControlImpl for TemplatedControl {
143 fn on_apply_template(_this: &Self, _e: &TemplateAppliedEventArgs) {}
144
145 fn on_template_changed(this: &Self, _e: &FerroPropertyChangedEventArgs<'_>) {
146 this.invalidate_measure();
147 }
148}
149
150ferroui_base::ferro_properties! { impl TemplatedControl {
151 ferro_property!(
152 pub fn background_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
154 Border::background_property().add_owner::<TemplatedControl>()
155 }
156 );
157
158 ferro_property!(
159 pub fn background_sizing_property() -> StyledProperty<BackgroundSizing> {
161 Border::background_sizing_property().add_owner::<TemplatedControl>()
162 }
163 );
164
165 ferro_property!(
166 pub fn border_brush_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
168 Border::border_brush_property().add_owner::<TemplatedControl>()
169 }
170 );
171
172 ferro_property!(
173 pub fn border_thickness_property() -> StyledProperty<Thickness> {
175 Border::border_thickness_property().add_owner::<TemplatedControl>()
176 }
177 );
178
179 ferro_property!(
180 pub fn corner_radius_property() -> StyledProperty<CornerRadius> {
182 Border::corner_radius_property().add_owner::<TemplatedControl>()
183 }
184 );
185
186 ferro_property!(
187 pub fn font_family_property() -> StyledProperty<FontFamily> {
189 TextElement::font_family_property().add_owner::<TemplatedControl>()
190 }
191 );
192
193 ferro_property!(
194 pub fn font_features_property() -> StyledProperty<Option<FontFeatureCollection>> {
196 TextElement::font_features_property().add_owner::<TemplatedControl>()
197 }
198 );
199
200 ferro_property!(
201 pub fn font_size_property() -> StyledProperty<f64> {
203 TextElement::font_size_property().add_owner::<TemplatedControl>()
204 }
205 );
206
207 ferro_property!(
208 pub fn font_style_property() -> StyledProperty<FontStyle> {
210 TextElement::font_style_property().add_owner::<TemplatedControl>()
211 }
212 );
213
214 ferro_property!(
215 pub fn font_weight_property() -> StyledProperty<FontWeight> {
217 TextElement::font_weight_property().add_owner::<TemplatedControl>()
218 }
219 );
220
221 ferro_property!(
222 pub fn font_stretch_property() -> StyledProperty<FontStretch> {
224 TextElement::font_stretch_property().add_owner::<TemplatedControl>()
225 }
226 );
227
228 ferro_property!(
229 pub fn foreground_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
231 TextElement::foreground_property().add_owner::<TemplatedControl>()
232 }
233 );
234
235 ferro_property!(
236 pub fn letter_spacing_property() -> StyledProperty<f64> {
238 TextElement::letter_spacing_property().add_owner::<TemplatedControl>()
239 }
240 );
241
242 ferro_property!(
243 pub fn padding_property() -> StyledProperty<Thickness> {
245 Decorator::padding_property().add_owner::<TemplatedControl>()
246 }
247 );
248
249 ferro_property!(
250 pub fn template_property() -> StyledProperty<Option<Rc<dyn IControlTemplate>>> {
252 FerroProperty::register::<TemplatedControl, _>("Template", None)
253 }
254 );
255
256 ferro_property!(
257 pub fn is_template_focus_target_property() -> AttachedProperty<bool> {
259 FerroProperty::register_attached::<TemplatedControl, Control, _>("IsTemplateFocusTarget", false)
260 }
261 );
262} }
263
264impl TemplatedControl {
265 ferro_routed_event!(
266 pub fn template_applied_event() -> RoutedEvent<TemplateAppliedEventArgs> {
268 RoutedEvent::register::<TemplatedControl, _>("TemplateApplied", RoutingStrategies::DIRECT)
269 }
270 );
271
272 fn static_constructor() {
273 Visual::clip_to_bounds_property().override_default_value::<TemplatedControl>(true);
274 Self::template_property().changed().add_class_handler::<TemplatedControl>(|x, e| x.on_template_changed(e));
275 }
276
277 pub fn construct() -> Self {
279 Self { base: Control::construct(), applied_template: RefCell::new(None) }
280 }
281
282 pub fn new() -> Ref<Self> {
283 instantiate(Self::construct())
284 }
285
286 pub fn template_applied(
288 &self,
289 handler: impl Fn(&Interactive, &TemplateAppliedEventArgs) + 'static,
290 ) -> RoutedEventHandlerToken {
291 self.add_handler(Self::template_applied_event(), handler)
292 }
293
294 pub fn background(&self) -> Option<Rc<dyn IBrush>> {
296 self.get_value(Self::background_property())
297 }
298
299 pub fn set_background(&self, value: Option<Rc<dyn IBrush>>) {
300 self.set_value(Self::background_property(), value)
301 }
302
303 pub fn background_sizing(&self) -> BackgroundSizing {
306 self.get_value(Self::background_sizing_property())
307 }
308
309 pub fn set_background_sizing(&self, value: BackgroundSizing) {
310 self.set_value(Self::background_sizing_property(), value)
311 }
312
313 pub fn border_brush(&self) -> Option<Rc<dyn IBrush>> {
315 self.get_value(Self::border_brush_property())
316 }
317
318 pub fn set_border_brush(&self, value: Option<Rc<dyn IBrush>>) {
319 self.set_value(Self::border_brush_property(), value)
320 }
321
322 pub fn border_thickness(&self) -> Thickness {
324 self.get_value(Self::border_thickness_property())
325 }
326
327 pub fn set_border_thickness(&self, value: Thickness) {
328 self.set_value(Self::border_thickness_property(), value)
329 }
330
331 pub fn corner_radius(&self) -> CornerRadius {
333 self.get_value(Self::corner_radius_property())
334 }
335
336 pub fn set_corner_radius(&self, value: CornerRadius) {
337 self.set_value(Self::corner_radius_property(), value)
338 }
339
340 pub fn font_family(&self) -> FontFamily {
342 self.get_value(Self::font_family_property())
343 }
344
345 pub fn set_font_family(&self, value: FontFamily) {
346 self.set_value(Self::font_family_property(), value)
347 }
348
349 pub fn font_features(&self) -> Option<FontFeatureCollection> {
351 self.get_value(Self::font_features_property())
352 }
353
354 pub fn set_font_features(&self, value: Option<FontFeatureCollection>) {
355 self.set_value(Self::font_features_property(), value)
356 }
357
358 pub fn font_size(&self) -> f64 {
360 self.get_value(Self::font_size_property())
361 }
362
363 pub fn set_font_size(&self, value: f64) {
364 self.set_value(Self::font_size_property(), value)
365 }
366
367 pub fn font_style(&self) -> FontStyle {
369 self.get_value(Self::font_style_property())
370 }
371
372 pub fn set_font_style(&self, value: FontStyle) {
373 self.set_value(Self::font_style_property(), value)
374 }
375
376 pub fn font_weight(&self) -> FontWeight {
378 self.get_value(Self::font_weight_property())
379 }
380
381 pub fn set_font_weight(&self, value: FontWeight) {
382 self.set_value(Self::font_weight_property(), value)
383 }
384
385 pub fn font_stretch(&self) -> FontStretch {
387 self.get_value(Self::font_stretch_property())
388 }
389
390 pub fn set_font_stretch(&self, value: FontStretch) {
391 self.set_value(Self::font_stretch_property(), value)
392 }
393
394 pub fn foreground(&self) -> Option<Rc<dyn IBrush>> {
396 self.get_value(Self::foreground_property())
397 }
398
399 pub fn set_foreground(&self, value: Option<Rc<dyn IBrush>>) {
400 self.set_value(Self::foreground_property(), value)
401 }
402
403 pub fn letter_spacing(&self) -> f64 {
405 self.get_value(Self::letter_spacing_property())
406 }
407
408 pub fn set_letter_spacing(&self, value: f64) {
409 self.set_value(Self::letter_spacing_property(), value)
410 }
411
412 pub fn padding(&self) -> Thickness {
414 self.get_value(Self::padding_property())
415 }
416
417 pub fn set_padding(&self, value: Thickness) {
418 self.set_value(Self::padding_property(), value)
419 }
420
421 pub fn template(&self) -> Option<Rc<dyn IControlTemplate>> {
423 self.get_value(Self::template_property())
424 }
425
426 pub fn set_template(&self, value: Option<Rc<dyn IControlTemplate>>) {
427 self.set_value(Self::template_property(), value)
428 }
429
430 pub fn get_is_template_focus_target(control: &Control) -> bool {
433 control.get_value(Self::is_template_focus_target_property())
434 }
435
436 pub fn set_is_template_focus_target(control: &Control, value: bool) {
444 control.set_value(Self::is_template_focus_target_property(), value)
445 }
446
447 pub fn apply_templated_parent(control: &StyledElement, templated_parent: Option<&Ref<FerroObject>>) {
450 control.set_templated_parent(Nullable(templated_parent.cloned()));
451
452 let children = control.logical_children().snapshot();
453
454 for child in children.iter() {
455 if child.templated_parent().is_none() {
456 Self::apply_templated_parent(child, templated_parent);
457 }
458 }
459 }
460}