Skip to main content

ferroui_base/styling/
style_instance.rs

1use super::activators::{IStyleActivator, IStyleActivatorSink};
2use super::{ISetterInstance, IStyleInstance, SetterInstance, SetterInstanceKind, StyleBase};
3use crate::animation::IAnimation;
4use crate::data::BindingPriority;
5use crate::property_store::{FrameType, ValueFrame, ValueFrameBase};
6use crate::reactive::{IDisposable, IObservable, IObserver, LightweightSubject};
7use crate::{Ref, StyledElement, WeakRef};
8use std::any::Any;
9use std::cell::{Cell, RefCell};
10use std::rc::{Rc, Weak};
11
12/// A style has two setters for the same property: the invalid operation the
13/// managed original raises when the style is instanced on a control.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct DuplicateSetterError {
16    /// The property, as it is displayed.
17    pub property: String,
18    /// The style (or control theme), as it is displayed.
19    pub style: String,
20}
21
22impl std::fmt::Display for DuplicateSetterError {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        write!(f, "Duplicate setter encountered for property '{}' in '{}'.", self.property, self.style)
25    }
26}
27
28impl std::error::Error for DuplicateSetterError {}
29
30/// Stores state for a style that has been instanced on a control.
31///
32/// A style instance is created when a style is applied to a control. It is
33/// responsible for tracking the setter instances of the style and its
34/// activator, and is the value frame that holds the style's values in the
35/// control's value store. A style without an activator whose setters are all
36/// plain values has a single instance, shared by every control it applies to.
37pub struct StyleInstance {
38    base: ValueFrameBase,
39    this: Weak<StyleInstance>,
40    activator: Option<Rc<dyn IStyleActivator>>,
41    is_active: Cell<bool>,
42    setters: RefCell<Vec<Rc<dyn ISetterInstance>>>,
43    animations: RefCell<Option<Vec<Rc<dyn IAnimation>>>>,
44    animation_apply_disposables: RefCell<Option<Vec<Rc<dyn IDisposable>>>>,
45    animation_trigger: RefCell<Option<LightweightSubject<bool>>>,
46    source: WeakRef<StyleBase>,
47}
48
49impl StyleInstance {
50    pub(crate) fn new(
51        style: WeakRef<StyleBase>,
52        activator: Option<Rc<dyn IStyleActivator>>,
53        type_: FrameType,
54    ) -> Rc<Self> {
55        let priority = Self::get_priority(activator.is_some());
56        Rc::new_cyclic(|this| Self {
57            base: ValueFrameBase::new(priority, type_),
58            this: this.clone(),
59            activator,
60            is_active: Cell::new(false),
61            setters: RefCell::new(Vec::new()),
62            animations: RefCell::new(None),
63            animation_apply_disposables: RefCell::new(None),
64            animation_trigger: RefCell::new(None),
65            source: style,
66        })
67    }
68
69    /// The priority of the values of the style instance.
70    pub fn priority(&self) -> BindingPriority {
71        self.base.priority()
72    }
73
74    /// Whether the instance was created by `style`.
75    pub(crate) fn is_source(&self, style: &Ref<StyleBase>) -> bool {
76        self.source.points_to(style)
77    }
78
79    pub(crate) fn source_display(&self) -> String {
80        match self.source.upgrade() {
81            Some(source) => source.to_display_string(),
82            None => "Style".to_string(),
83        }
84    }
85
86    /// Adds the instance of a setter. A second setter for a property the
87    /// style instance already has a value for is an error (the invalid
88    /// operation of the managed original); nothing is added then.
89    pub(crate) fn try_add(&self, instance: SetterInstance) -> Result<(), DuplicateSetterError> {
90        match instance.kind {
91            SetterInstanceKind::Entry(value_entry) => {
92                if self.base.contains(value_entry.property()) {
93                    return Err(DuplicateSetterError {
94                        property: value_entry.property().to_string(),
95                        style: self.source_display(),
96                    });
97                }
98                self.base.add(value_entry);
99            }
100            SetterInstanceKind::Other(instance) => self.setters.borrow_mut().push(instance),
101        }
102        Ok(())
103    }
104
105    /// As [`try_add`](Self::try_add); a duplicate setter panics with the
106    /// message of the error.
107    #[allow(dead_code)]
108    pub(crate) fn add(&self, instance: SetterInstance) {
109        if let Err(error) = self.try_add(instance) {
110            panic!("{error}");
111        }
112    }
113
114    pub(crate) fn add_animations(&self, animations: &[Rc<dyn IAnimation>]) {
115        self.animations.borrow_mut().get_or_insert_with(Vec::new).extend(animations.iter().cloned());
116    }
117
118    /// Applies the animations of the style to `control`: they run while the
119    /// style instance is active.
120    pub(crate) fn apply_animations(&self, control: &StyledElement) {
121        let animations = self.animations.borrow().clone();
122        if let Some(animations) = animations {
123            let trigger = self.animation_trigger.borrow_mut().get_or_insert_with(LightweightSubject::new).clone();
124            let match_: Rc<dyn IObservable<bool>> = Rc::new(trigger.clone());
125            for animation in &animations {
126                let disposable = animation.apply(control, None, match_.clone(), None, false);
127                self.animation_apply_disposables.borrow_mut().get_or_insert_with(Vec::new).push(disposable);
128            }
129
130            if self.activator.is_none() {
131                trigger.on_next(true);
132            }
133        }
134    }
135
136    fn trigger_animations(&self, value: bool) {
137        let trigger = self.animation_trigger.borrow().clone();
138        if let Some(trigger) = trigger {
139            trigger.on_next(value);
140        }
141    }
142
143    pub(crate) fn make_shared(&self) {
144        self.base.make_shared();
145    }
146
147    fn get_priority(has_activator: bool) -> BindingPriority {
148        if has_activator {
149            BindingPriority::StyleTrigger
150        } else {
151            BindingPriority::Style
152        }
153    }
154}
155
156impl IStyleInstance for StyleInstance {
157    fn source(&self) -> Option<Ref<StyleBase>> {
158        self.source.upgrade()
159    }
160
161    #[inline]
162    fn has_activator(&self) -> bool {
163        self.activator.is_some()
164    }
165
166    fn is_active(&self) -> bool {
167        self.is_active.get()
168    }
169}
170
171impl IStyleActivatorSink for StyleInstance {
172    fn on_next(&self, value: bool) {
173        if let Some(owner) = self.base.owner() {
174            if let Some(this) = self.this.upgrade() {
175                let frame: Rc<dyn ValueFrame> = this;
176                owner.values().on_frame_activation_changed(&owner, &frame);
177            }
178        }
179        self.trigger_animations(value);
180    }
181}
182
183impl ValueFrame for StyleInstance {
184    fn base(&self) -> &ValueFrameBase {
185        &self.base
186    }
187
188    fn get_is_active(&self) -> (bool, bool) {
189        let previous = self.is_active.get();
190
191        let is_active = match &self.activator {
192            Some(activator) => {
193                if !activator.is_subscribed() {
194                    let sink: Weak<dyn IStyleActivatorSink> = self.this.clone();
195                    activator.subscribe(sink);
196                    self.trigger_animations(activator.get_is_active());
197                }
198                activator.get_is_active()
199            }
200            None => true,
201        };
202
203        self.is_active.set(is_active);
204        (is_active, is_active != previous)
205    }
206
207    fn dispose(&self) {
208        self.base.unsubscribe_all();
209        if let Some(activator) = &self.activator {
210            activator.dispose();
211        }
212        let disposables = self.animation_apply_disposables.borrow().clone();
213        if let Some(disposables) = disposables {
214            for item in disposables {
215                item.dispose();
216            }
217        }
218    }
219
220    fn as_any(&self) -> &dyn Any {
221        self
222    }
223}