Skip to main content

ferroui_controls/documents/
inline.rs

1use super::{TextElement, TextElementImpl};
2use crate::Control;
3use ferroui_base::media::text_formatting::{GenericTextRunProperties, TextRun, TextRunProperties};
4use ferroui_base::media::{BaselineAlignment, IBrush, TextDecorationCollection, Typeface};
5use ferroui_base::{
6    ferro_class, ferro_property, AttachedProperty, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
7    FerroPropertyChangedEventArgs, Size, StyledElementImpl, StyledProperty, StyledPropertyOptions,
8};
9use std::rc::Rc;
10
11/// `Inline` is the base class for inline text elements.
12///
13/// This class is abstract.
14#[repr(C)]
15pub struct Inline {
16    base: TextElement,
17}
18
19ferro_class! {
20    Inline: TextElement, virtuals InlineImpl: TextElementImpl {
21        /// Appends the text runs of the inline to `text_runs`.
22        #[doc(hidden)]
23        fn build_text_run(this, text_runs: &mut Vec<Rc<dyn TextRun>>);
24        /// Measures the controls embedded in the inline for a block of
25        /// `block_size`. Returns true when the size of one of them changed.
26        #[doc(hidden)]
27        fn measure_embedded_controls(this, block_size: Size) -> bool;
28        /// Appends the text of the inline to `string_builder`.
29        #[doc(hidden)]
30        fn append_text(this, string_builder: &mut String);
31    }
32}
33
34ferroui_base::ferro_impl_classes!(Inline: StyledElementImpl);
35ferroui_base::ferro_impl_classes!(Inline: TextElementImpl);
36
37impl FerroObjectImpl for Inline {
38    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
39        Self::parent_on_property_changed(this, change);
40
41        match change.property().name() {
42            "TextDecorations" | "BaselineAlignment" => {
43                if let Some(host) = this.inline_host() {
44                    host.invalidate();
45                }
46            }
47            _ => {}
48        }
49    }
50}
51
52impl InlineImpl for Inline {
53    fn build_text_run(_this: &Self, _text_runs: &mut Vec<Rc<dyn TextRun>>) {
54        panic!("Inline is abstract.")
55    }
56
57    fn measure_embedded_controls(_this: &Self, _block_size: Size) -> bool {
58        false
59    }
60
61    fn append_text(_this: &Self, _string_builder: &mut String) {
62        panic!("Inline is abstract.")
63    }
64}
65
66ferroui_base::ferro_properties! { impl Inline {
67    ferro_property!(
68        /// `AttachedProperty` for the `TextDecorations` property.
69        pub fn text_decorations_property() -> AttachedProperty<Option<TextDecorationCollection>> {
70            FerroProperty::register_attached_with::<Inline, Inline, _>(
71                "TextDecorations",
72                StyledPropertyOptions::new(None).inherits(true),
73            )
74        }
75    );
76
77    ferro_property!(
78        /// `StyledProperty` for the `BaselineAlignment` property.
79        pub fn baseline_alignment_property() -> StyledProperty<BaselineAlignment> {
80            FerroProperty::register::<Inline, _>("BaselineAlignment", BaselineAlignment::Baseline)
81        }
82    );
83} }
84
85impl Inline {
86    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
87    pub fn construct() -> Self {
88        Self { base: TextElement::construct() }
89    }
90
91    /// The `TextDecorations` property specifies decorations that are added
92    /// to the text of an element.
93    pub fn text_decorations(&self) -> Option<TextDecorationCollection> {
94        self.get_value(Self::text_decorations_property())
95    }
96
97    pub fn set_text_decorations(&self, value: Option<TextDecorationCollection>) {
98        self.set_value(Self::text_decorations_property(), value)
99    }
100
101    /// Describes how the baseline for a text-based element is positioned on
102    /// the vertical axis, relative to the established baseline for text.
103    pub fn baseline_alignment(&self) -> BaselineAlignment {
104        self.get_value(Self::baseline_alignment_property())
105    }
106
107    pub fn set_baseline_alignment(&self, value: BaselineAlignment) {
108        self.set_value(Self::baseline_alignment_property(), value)
109    }
110
111    /// Gets the value of the attached `TextDecorations` property on a
112    /// control.
113    pub fn get_text_decorations(control: &Control) -> Option<TextDecorationCollection> {
114        control.get_value(Self::text_decorations_property())
115    }
116
117    /// Sets the value of the attached `TextDecorations` property on a
118    /// control.
119    pub fn set_text_decorations_on(control: &Control, value: Option<TextDecorationCollection>) {
120        control.set_value(Self::text_decorations_property(), value)
121    }
122
123    /// Creates the run properties of the text of the inline.
124    pub fn create_text_run_properties(&self) -> Rc<dyn TextRunProperties> {
125        let parent_or_self_background = self.background().or_else(|| self.find_parent_background());
126
127        let typeface =
128            Typeface::with_style(self.font_family(), self.font_style(), self.font_weight(), self.font_stretch());
129
130        Rc::new(GenericTextRunProperties::with_all(
131            typeface,
132            self.font_size(),
133            self.text_decorations(),
134            self.foreground(),
135            parent_or_self_background,
136            self.baseline_alignment(),
137            None,
138            self.font_features(),
139        ))
140    }
141
142    fn find_parent_background(&self) -> Option<Rc<dyn IBrush>> {
143        let mut parent = self.parent();
144
145        while let Some(inline) = parent.and_then(|parent| parent.cast::<Inline>()) {
146            if let Some(background) = inline.background() {
147                return Some(background);
148            }
149
150            parent = inline.parent();
151        }
152
153        None
154    }
155}