ferroui_controls/primitives/
range_base.rs1use super::{RangeBaseValueChangedEventArgs, TemplatedControl, TemplatedControlImpl};
2use crate::ControlImpl;
3use ferroui_base::data::BindingMode;
4use ferroui_base::input::InputElementImpl;
5use ferroui_base::interactivity::{Interactive, InteractiveImpl, RoutedEvent, RoutedEventHandlerToken, RoutingStrategies};
6use ferroui_base::layout::LayoutableImpl;
7use ferroui_base::utilities::MathUtilities;
8use ferroui_base::{
9 ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, FerroObject, FerroObjectImpl,
10 FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, StyledElementImpl, StyledElementImplExt,
11 StyledProperty, StyledPropertyOptions, VisualImpl,
12};
13use std::cell::Cell;
14
15#[repr(C)]
19pub struct RangeBase {
20 base: TemplatedControl,
21 is_data_context_changing: Cell<bool>,
22}
23
24ferro_class!(RangeBase: TemplatedControl);
25ferro_impl_classes!(
26 RangeBase: VisualImpl,
27 LayoutableImpl,
28 InteractiveImpl,
29 InputElementImpl,
30 ControlImpl,
31 TemplatedControlImpl
32);
33
34impl FerroObjectImpl for RangeBase {
35 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
36 Self::parent_on_property_changed(this, change);
37
38 if change.property() == Self::minimum_property().as_property() {
39 this.on_minimum_changed();
40 } else if change.property() == Self::maximum_property().as_property() {
41 this.on_maximum_changed();
42 } else if change.property() == Self::value_property().as_property() {
43 let (old_value, new_value) = change.get_old_and_new_value::<f64>();
44 let value_changed_event_args =
45 RangeBaseValueChangedEventArgs::new(old_value, new_value, Some(Self::value_changed_event()));
46 this.raise_event(&value_changed_event_args);
47 }
48 }
49}
50
51impl StyledElementImpl for RangeBase {
52 fn on_initialized(this: &Self) {
53 Self::parent_on_initialized(this);
54
55 this.coerce_value(Self::maximum_property().as_property());
56 this.coerce_value(Self::value_property().as_property());
57 }
58
59 fn on_data_context_begin_update(this: &Self) {
60 this.is_data_context_changing.set(true);
61 Self::parent_on_data_context_begin_update(this);
62 }
63
64 fn on_data_context_end_update(this: &Self) {
65 Self::parent_on_data_context_end_update(this);
66 this.is_data_context_changing.set(false);
67 }
68}
69
70ferroui_base::ferro_properties! { impl RangeBase {
71 ferro_property!(
72 pub fn minimum_property() -> StyledProperty<f64> {
74 FerroProperty::register_with::<RangeBase, _>(
75 "Minimum",
76 StyledPropertyOptions::new(0.0).coerce(RangeBase::coerce_minimum),
77 )
78 }
79 );
80
81 ferro_property!(
82 pub fn maximum_property() -> StyledProperty<f64> {
84 FerroProperty::register_with::<RangeBase, _>(
85 "Maximum",
86 StyledPropertyOptions::new(100.0).coerce(RangeBase::coerce_maximum),
87 )
88 }
89 );
90
91 ferro_property!(
92 pub fn value_property() -> StyledProperty<f64> {
94 FerroProperty::register_with::<RangeBase, _>(
95 "Value",
96 StyledPropertyOptions::new(0.0)
97 .default_binding_mode(BindingMode::TwoWay)
98 .coerce(RangeBase::coerce_value_core),
99 )
100 }
101 );
102
103 ferro_property!(
104 pub fn small_change_property() -> StyledProperty<f64> {
106 FerroProperty::register::<RangeBase, _>("SmallChange", 1.0)
107 }
108 );
109
110 ferro_property!(
111 pub fn large_change_property() -> StyledProperty<f64> {
113 FerroProperty::register::<RangeBase, _>("LargeChange", 10.0)
114 }
115 );
116} }
117
118impl RangeBase {
119 ferro_routed_event!(
120 pub fn value_changed_event() -> RoutedEvent<RangeBaseValueChangedEventArgs> {
122 RoutedEvent::register::<RangeBase, _>("ValueChanged", RoutingStrategies::BUBBLE)
123 }
124 );
125
126 pub fn construct() -> Self {
128 Self { base: TemplatedControl::construct(), is_data_context_changing: Cell::new(false) }
129 }
130
131 pub fn value_changed(
133 &self,
134 handler: impl Fn(&Interactive, &RangeBaseValueChangedEventArgs) + 'static,
135 ) -> RoutedEventHandlerToken {
136 self.add_handler(Self::value_changed_event(), handler)
137 }
138
139 pub fn minimum(&self) -> f64 {
141 self.get_value(Self::minimum_property())
142 }
143
144 pub fn set_minimum(&self, value: f64) {
145 self.set_value(Self::minimum_property(), value)
146 }
147
148 fn coerce_minimum(sender: &FerroObject, value: f64) -> f64 {
149 if Self::validate_double(value) {
150 value
151 } else {
152 sender.get_value(Self::minimum_property())
153 }
154 }
155
156 fn on_minimum_changed(&self) {
157 if self.is_initialized() && !self.is_data_context_changing.get() {
158 self.coerce_value(Self::maximum_property().as_property());
159 self.coerce_value(Self::value_property().as_property());
160 }
161 }
162
163 pub fn maximum(&self) -> f64 {
165 self.get_value(Self::maximum_property())
166 }
167
168 pub fn set_maximum(&self, value: f64) {
169 self.set_value(Self::maximum_property(), value)
170 }
171
172 fn coerce_maximum(sender: &FerroObject, value: f64) -> f64 {
173 if Self::validate_double(value) {
174 f64::max(value, sender.get_value(Self::minimum_property()))
175 } else {
176 sender.get_value(Self::maximum_property())
177 }
178 }
179
180 fn on_maximum_changed(&self) {
181 if self.is_initialized() && !self.is_data_context_changing.get() {
182 self.coerce_value(Self::minimum_property().as_property());
183 self.coerce_value(Self::value_property().as_property());
184 }
185 }
186
187 pub fn value(&self) -> f64 {
189 self.get_value(Self::value_property())
190 }
191
192 pub fn set_range_value(&self, value: f64) {
193 self.set_value(Self::value_property(), value)
194 }
195
196 fn coerce_value_core(sender: &FerroObject, value: f64) -> f64 {
197 if Self::validate_double(value) {
198 MathUtilities::clamp(
199 value,
200 sender.get_value(Self::minimum_property()),
201 sender.get_value(Self::maximum_property()),
202 )
203 } else {
204 sender.get_value(Self::value_property())
205 }
206 }
207
208 pub fn small_change(&self) -> f64 {
210 self.get_value(Self::small_change_property())
211 }
212
213 pub fn set_small_change(&self, value: f64) {
214 self.set_value(Self::small_change_property(), value)
215 }
216
217 pub fn large_change(&self) -> f64 {
219 self.get_value(Self::large_change_property())
220 }
221
222 pub fn set_large_change(&self, value: f64) {
223 self.set_value(Self::large_change_property(), value)
224 }
225
226 fn validate_double(value: f64) -> bool {
228 !value.is_infinite() && !value.is_nan()
229 }
230}