Skip to main content

ferroui_base/media/
dash_style.rs

1use crate::animation::Animatable;
2use crate::media::immutable::ImmutableDashStyle;
3use crate::media::MediaCollection;
4use crate::reactive::{Disposable, IDisposable};
5use crate::utilities::HandlerList;
6use crate::{
7    ferro_class, ferro_property, instantiate, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
8    FerroPropertyChangedEventArgs, Ref, StyledProperty,
9};
10use std::cell::RefCell;
11use std::rc::Rc;
12
13/// Represents the sequence of dashes and gaps that will be applied by a
14/// [`Pen`](crate::media::Pen).
15#[repr(C)]
16pub struct DashStyle {
17    base: Animatable,
18    invalidated: HandlerList<dyn Fn()>,
19    dashes_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
20}
21
22ferro_class!(DashStyle: Animatable);
23crate::ferro_class_info!(DashStyle { new: DashStyle::new, interfaces: [std::rc::Rc<dyn crate::media::IDashStyle>] });
24
25impl FerroObjectImpl for DashStyle {
26    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
27        Self::parent_on_property_changed(this, change);
28
29        if change.property() == Self::dashes_property().as_property() {
30            let new_value = change.get_new_value::<Option<MediaCollection<f64>>>();
31
32            if let Some(subscription) = this.dashes_subscription.take() {
33                subscription.dispose();
34            }
35
36            if let Some(new_value) = new_value {
37                let weak = this.to_ref().downgrade();
38                let subscription = new_value.collection_changed(move |_| {
39                    if let Some(this) = weak.upgrade() {
40                        this.raise_invalidated();
41                    }
42                });
43                *this.dashes_subscription.borrow_mut() = Some(subscription);
44            }
45        }
46    }
47}
48
49crate::ferro_properties! { impl DashStyle {
50    ferro_property!(pub fn dashes_property() -> StyledProperty<Option<MediaCollection<f64>>> {
51        FerroProperty::register::<DashStyle, _>("Dashes", None)
52    });
53
54    ferro_property!(pub fn offset_property() -> StyledProperty<f64> {
55        FerroProperty::register::<DashStyle, _>("Offset", 0.0)
56    });
57} }
58
59impl DashStyle {
60    fn static_constructor() {
61        Self::dashes_property().changed().add_class_handler::<DashStyle>(|x, _| x.raise_invalidated());
62        Self::offset_property().changed().add_class_handler::<DashStyle>(|x, _| x.raise_invalidated());
63    }
64
65    /// Creates the class data.
66    pub fn construct() -> Self {
67        Self { base: Animatable::construct(), invalidated: HandlerList::new(), dashes_subscription: RefCell::new(None) }
68    }
69
70    /// Creates a dash style without dashes.
71    pub fn new() -> Ref<Self> {
72        instantiate(Self::construct())
73    }
74
75    /// Creates a dash style with a copy of the given dashes.
76    pub fn with_dashes(dashes: Option<&[f64]>, offset: f64) -> Ref<Self> {
77        Self::with_dash_list(MediaCollection::from_items(dashes.unwrap_or(&[]).iter().copied()), offset)
78    }
79
80    /// Creates a dash style that shares the given dash list.
81    pub fn with_dash_list(dashes: MediaCollection<f64>, offset: f64) -> Ref<Self> {
82        let result = Self::new();
83        result.set_dashes(Some(dashes));
84        result.set_offset(offset);
85        result
86    }
87
88    /// Represents a dashed dash style.
89    pub fn dash() -> Rc<ImmutableDashStyle> {
90        crate::ferro_thread_local! {
91            static DASH: Rc<ImmutableDashStyle> = Rc::new(ImmutableDashStyle::new(Some(&[2.0, 2.0]), 1.0));
92        }
93        DASH.with(Rc::clone)
94    }
95
96    /// Represents a dotted dash style.
97    pub fn dot() -> Rc<ImmutableDashStyle> {
98        crate::ferro_thread_local! {
99            static DOT: Rc<ImmutableDashStyle> = Rc::new(ImmutableDashStyle::new(Some(&[0.0, 2.0]), 0.0));
100        }
101        DOT.with(Rc::clone)
102    }
103
104    /// Represents a dashed dotted dash style.
105    pub fn dash_dot() -> Rc<ImmutableDashStyle> {
106        crate::ferro_thread_local! {
107            static DASH_DOT: Rc<ImmutableDashStyle> =
108                Rc::new(ImmutableDashStyle::new(Some(&[2.0, 2.0, 0.0, 2.0]), 1.0));
109        }
110        DASH_DOT.with(Rc::clone)
111    }
112
113    /// Represents a dashed double dotted dash style.
114    pub fn dash_dot_dot() -> Rc<ImmutableDashStyle> {
115        crate::ferro_thread_local! {
116            static DASH_DOT_DOT: Rc<ImmutableDashStyle> =
117                Rc::new(ImmutableDashStyle::new(Some(&[2.0, 2.0, 0.0, 2.0, 0.0, 2.0]), 1.0));
118        }
119        DASH_DOT_DOT.with(Rc::clone)
120    }
121
122    /// The length of alternating dashes and gaps.
123    pub fn dashes(&self) -> Option<MediaCollection<f64>> {
124        self.get_value(Self::dashes_property())
125    }
126
127    pub fn set_dashes(&self, value: Option<MediaCollection<f64>>) {
128        self.set_value(Self::dashes_property(), value)
129    }
130
131    /// How far in the dash sequence the stroke will start.
132    pub fn offset(&self) -> f64 {
133        self.get_value(Self::offset_property())
134    }
135
136    pub fn set_offset(&self, value: f64) {
137        self.set_value(Self::offset_property(), value)
138    }
139
140    /// Subscribes to invalidation of the dash style: raised when the dashes
141    /// or the offset change. Disposing the returned handle unsubscribes.
142    pub fn invalidated(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
143        let token = self.invalidated.add(Rc::new(handler));
144        let weak = self.to_ref().downgrade();
145        Disposable::create(move || {
146            if let Some(this) = weak.upgrade() {
147                this.invalidated.remove(token);
148            }
149        })
150    }
151
152    fn raise_invalidated(&self) {
153        if self.invalidated.is_empty() {
154            return;
155        }
156        for (_, handler) in self.invalidated.snapshot().iter() {
157            handler();
158        }
159    }
160
161    /// Returns an immutable clone of the dash style.
162    pub fn to_immutable(&self) -> ImmutableDashStyle {
163        ImmutableDashStyle::new(self.dashes().map(|dashes| dashes.to_vec()).as_deref(), self.offset())
164    }
165}