ferroui_base/styling/target_property_path.rs
1//! WinUI's `TargetPropertyPath` (`Microsoft.UI.Xaml.TargetPropertyPath`,
2//! `dxaml/xcp/core/core/elements/TargetPropertyPath.cpp` of microsoft-ui-xaml commit
3//! `7e4b0c2096be402f51cc8f7d203ebf67cff183d0`; MIT, Copyright (c) Microsoft Corporation, see
4//! NOTICE.md): the object and the property a [`Setter`](super::Setter) of a visual state sets.
5//!
6//! An addition (docs/porting/winui-calculator-gaps.md, section 5.2): upstream setters have no
7//! target.
8
9use crate::{FerroObject, FerroProperty, Ref, WeakRef};
10use std::cell::{Cell, RefCell};
11use std::fmt;
12use std::rc::Rc;
13
14/// The target of a [`Setter`](super::Setter) of a visual state: an object and a property of it.
15///
16/// Markup writes it as the text of `Setter.Target`: `Element.Property` or
17/// `Element.(Owner.Property)` for an attached property. The compiler keeps the name of the element
18/// ([`target_name`](Self::target_name)) and resolves the property against the type of the
19/// element; the property is the [`property`](super::Setter::property) of the setter. The element
20/// is found by its name in the name scope of the template or the document when the state is
21/// applied, as WinUI's `CTargetPropertyPath` does (`m_targetName`, `ResolveTargetForVisualStateSetter`),
22/// so a setter may name an element declared after it. An explicit [`target`](Self::target)
23/// object, set in code or markup, is used instead of the name (WinUI's manual target).
24///
25/// The target is held weakly: a setter of a visual state usually targets an element of the tree
26/// that holds the visual states, and so the setter would keep that tree alive.
27#[derive(Default)]
28pub struct TargetPropertyPath {
29 path: RefCell<String>,
30 target_name: RefCell<String>,
31 target: RefCell<Option<WeakRef<FerroObject>>>,
32 property: Cell<Option<&'static FerroProperty>>,
33}
34
35/// Compares by identity (reference equality), as the reference type this mirrors.
36impl PartialEq for TargetPropertyPath {
37 fn eq(&self, other: &Self) -> bool {
38 std::ptr::eq(self, other)
39 }
40}
41
42impl TargetPropertyPath {
43 /// Creates a path without a target or a property.
44 pub fn empty() -> Rc<Self> {
45 Rc::new(Self::default())
46 }
47
48 /// Creates the path of `property` of `target` (WinUI's constructor that takes a property,
49 /// with the target set).
50 pub fn new(target: &FerroObject, property: &'static FerroProperty) -> Rc<Self> {
51 let path = Self::default();
52 *path.target.borrow_mut() = Some(target.to_weak());
53 path.property.set(Some(property));
54 *path.path.borrow_mut() = property.name().to_string();
55 Rc::new(path)
56 }
57
58 /// The target object, if it is set and alive.
59 pub fn target(&self) -> Option<Ref<FerroObject>> {
60 self.target.borrow().as_ref().and_then(WeakRef::upgrade)
61 }
62
63 pub fn set_target(&self, value: Option<Ref<FerroObject>>) {
64 *self.target.borrow_mut() = value.map(|target| target.downgrade());
65 }
66
67 /// The name of the target element, resolved in a name scope when the setter applies; empty
68 /// when the target is an explicit object or not set.
69 pub fn target_name(&self) -> String {
70 self.target_name.borrow().clone()
71 }
72
73 pub fn set_target_name(&self, value: impl Into<String>) {
74 *self.target_name.borrow_mut() = value.into();
75 }
76
77 /// The text of the path of the property (`Property` or `(Owner.Property)`), as markup wrote
78 /// it.
79 pub fn path(&self) -> String {
80 self.path.borrow().clone()
81 }
82
83 pub fn set_path(&self, value: impl Into<String>) {
84 *self.path.borrow_mut() = value.into();
85 }
86
87 /// The property of the target, when the path is resolved. A setter of a visual state keeps
88 /// the resolved property in its own `Property`.
89 pub fn property(&self) -> Option<&'static FerroProperty> {
90 self.property.get()
91 }
92
93 pub fn set_property(&self, value: Option<&'static FerroProperty>) {
94 self.property.set(value);
95 }
96}
97
98impl fmt::Debug for TargetPropertyPath {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 let target = self.target().map(|target| target.get_type().name().to_string());
101 f.debug_struct("TargetPropertyPath")
102 .field("target", &target)
103 .field("target_name", &*self.target_name.borrow())
104 .field("path", &*self.path.borrow())
105 .finish()
106 }
107}