1use super::ink_toolbar_buttons::{strings, toolbar_is_inking};
3use super::{
4 InkToolbar, InkToolbarFlyoutItem, InkToolbarFlyoutItemKind, InkToolbarMenuButton,
5 InkToolbarStencilButton, InkToolbarStencilKind, InkToolbarStrokeWidthSlider, InkToolbarTool,
6 InkToolbarToolButton,
7};
8use ferroui_controls::automation::peers::{
9 AutomationControlType, AutomationPeer, AutomationPeerImpl, AutomationPeerImplExt,
10 ControlAutomationPeer, ControlAutomationPeerImpl, RadioButtonAutomationPeer,
11 RadioButtonAutomationPeerImpl, RangeBaseAutomationPeerImpl, RangeBaseAutomationPeerImplExt,
12 SliderAutomationPeer, ToggleButtonAutomationPeer,
13};
14use ferroui_controls::automation::provider::{
15 IExpandCollapseProvider, IInvokeProvider, ISelectionItemProvider, ISelectionProvider,
16 IToggleProvider, IValueProvider, ProviderError,
17};
18use ferroui_controls::automation::{
19 AutomationProperties, ElementNotEnabledException, ExpandCollapsePatternIdentifiers,
20 ExpandCollapseState, ValuePatternIdentifiers,
21};
22use ferroui_controls::primitives::{RangeBase, ToggleButton};
23use ferroui_controls::{Button, Panel};
24use ferroui_base::data::core::ValueType;
25use ferroui_base::{
26 ferro_class, ferro_class_info, ferro_impl_classes, instantiate, BoxedValue, FerroObjectImpl,
27 FerroObjectImplExt, FerroPropertyChangedEventArgs, ObjectType, Ref,
28};
29use std::rc::Rc;
30
31struct Provider<T: ObjectType>(Ref<T>);
36
37macro_rules! conversions {
38 ($($name:ident: $provider:ident),* $(,)?) => {
39 impl<T: ObjectType> Provider<T> {
40 $(
41 fn $name(peer: Ref<T>) -> Rc<dyn $provider>
42 where
43 Self: $provider,
44 {
45 Rc::new(Self(peer))
46 }
47 )*
48 }
49 };
50}
51
52conversions! {
53 as_expand_collapse_provider: IExpandCollapseProvider,
54 as_invoke_provider: IInvokeProvider,
55 as_selection_item_provider: ISelectionItemProvider,
56 as_value_provider: IValueProvider,
57}
58
59fn button_state(owner: &Button) -> ExpandCollapseState {
60 match owner.flyout() {
61 None => ExpandCollapseState::LeafNode,
62 Some(f) if f.is_open() => ExpandCollapseState::Expanded,
63 _ => ExpandCollapseState::Collapsed,
64 }
65}
66fn append_text(base: Option<String>, suffix: String) -> Option<String> {
67 Some(match base.filter(|value| !value.is_empty()) {
68 Some(base) if !suffix.is_empty() => format!("{base}, {suffix}"),
69 Some(base) => base,
70 None => suffix,
71 })
72}
73fn is_builtin_pen(owner: &Button) -> bool {
74 owner
75 .to_ref()
76 .cast::<InkToolbarToolButton>()
77 .is_some_and(|owner| {
78 matches!(
79 owner.tool_kind(),
80 InkToolbarTool::BallpointPen | InkToolbarTool::Pencil | InkToolbarTool::Highlighter
81 )
82 })
83}
84fn menu_has_dropdown(owner: &Button) -> bool {
85 owner.flyout().is_some()
86 && owner
87 .to_ref()
88 .cast::<InkToolbarStencilButton>()
89 .is_none_or(|owner| owner.is_ruler_item_visible() && owner.is_protractor_item_visible())
90}
91fn is_radio_item(owner: &InkToolbarFlyoutItem) -> bool {
92 matches!(
93 owner.kind(),
94 InkToolbarFlyoutItemKind::Radio | InkToolbarFlyoutItemKind::RadioCheck
95 )
96}
97fn invalid_selection() -> ElementNotEnabledException {
101 ElementNotEnabledException::with_message("The requested selection operation is not supported.")
102}
103fn expand(owner: &Button) -> Result<(), ElementNotEnabledException> {
104 if !owner.is_effectively_enabled() {
105 return Err(ElementNotEnabledException::new());
106 }
107 if let Some(tool) = owner.to_ref().cast::<InkToolbarToolButton>() {
112 tool.set_current_value(ToggleButton::is_checked_property(), Some(true));
113 if !tool.can_open_configuration() {
114 return Ok(());
115 }
116 } else if let Some(menu) = owner.to_ref().cast::<InkToolbarMenuButton>() {
117 menu.set_current_value(ToggleButton::is_checked_property(), Some(true));
118 if toolbar_is_inking(&menu) {
119 return Ok(());
120 }
121 }
122 if let Some(flyout) = owner.flyout() {
123 flyout.show_at(owner);
124 }
125 Ok(())
126}
127fn collapse(owner: &Button) -> Result<(), ElementNotEnabledException> {
128 if !owner.is_effectively_enabled() {
129 return Err(ElementNotEnabledException::new());
130 }
131 if let Some(flyout) = owner.flyout() {
132 flyout.hide();
133 }
134 Ok(())
135}
136fn observe_flyout(peer: &Ref<AutomationPeer>, owner: &Button) {
137 if let Some(flyout) = owner.flyout() {
138 let weak_peer = peer.downgrade();
139 let weak_owner = owner.to_ref().downgrade();
140 let weak_flyout = flyout.downgrade();
141 flyout.opened(move || {
142 if let (Some(peer), Some(owner), Some(flyout)) = (
143 weak_peer.upgrade(),
144 weak_owner.upgrade(),
145 weak_flyout.upgrade(),
146 ) {
147 if owner.flyout().as_ref() == Some(&flyout) {
148 peer.raise_property_changed_event(
149 ExpandCollapsePatternIdentifiers::expand_collapse_state_property(),
150 Some(Rc::new(ExpandCollapseState::Collapsed)),
151 Some(Rc::new(ExpandCollapseState::Expanded)),
152 );
153 }
154 }
155 });
156 let weak_peer = peer.downgrade();
157 let weak_owner = owner.to_ref().downgrade();
158 let weak_flyout = flyout.downgrade();
159 flyout.closed(move || {
160 if let (Some(peer), Some(owner), Some(flyout)) = (
161 weak_peer.upgrade(),
162 weak_owner.upgrade(),
163 weak_flyout.upgrade(),
164 ) {
165 if owner.flyout().as_ref() == Some(&flyout) {
166 peer.raise_property_changed_event(
167 ExpandCollapsePatternIdentifiers::expand_collapse_state_property(),
168 Some(Rc::new(ExpandCollapseState::Expanded)),
169 Some(Rc::new(ExpandCollapseState::Collapsed)),
170 );
171 }
172 }
173 });
174 }
175}
176fn subscribe_flyout(peer: &Ref<AutomationPeer>, owner: &Button) {
177 observe_flyout(peer, owner);
178 let weak_peer = peer.downgrade();
179 let weak_owner = owner.to_ref().downgrade();
180 owner.property_changed(move |change| {
181 if change.property() == Button::flyout_property().as_property() {
182 if let (Some(peer), Some(owner)) = (weak_peer.upgrade(), weak_owner.upgrade()) {
183 observe_flyout(&peer, &owner);
184 }
185 }
186 });
187}
188
189#[repr(C)]
190pub struct InkToolbarAutomationPeer {
191 base: ControlAutomationPeer,
192}
193ferro_class!(InkToolbarAutomationPeer:ControlAutomationPeer);
194ferro_class_info!(InkToolbarAutomationPeer {});
195ferro_impl_classes!(InkToolbarAutomationPeer:FerroObjectImpl,ControlAutomationPeerImpl);
196impl AutomationPeerImpl for InkToolbarAutomationPeer {
197 fn get_class_name_core(_this: &Self) -> String {
198 "InkToolbar".to_owned()
199 }
200 fn get_automation_control_type_core(_this: &Self) -> AutomationControlType {
201 AutomationControlType::Pane
203 }
204}
205impl InkToolbarAutomationPeer {
206 pub fn construct(owner: &InkToolbar) -> Self {
207 Self {
208 base: ControlAutomationPeer::construct(owner),
209 }
210 }
211 pub fn new(owner: &InkToolbar) -> Ref<Self> {
212 instantiate(Self::construct(owner))
213 }
214}
215
216#[repr(C)]
217pub struct InkToolbarToolButtonAutomationPeer {
218 base: RadioButtonAutomationPeer,
219}
220ferro_class!(InkToolbarToolButtonAutomationPeer:RadioButtonAutomationPeer);
221ferro_class_info!(InkToolbarToolButtonAutomationPeer{interfaces:[Rc<dyn IExpandCollapseProvider> => Provider::as_expand_collapse_provider,Rc<dyn ISelectionItemProvider> => Provider::as_selection_item_provider]});
222ferro_impl_classes!(InkToolbarToolButtonAutomationPeer:ControlAutomationPeerImpl,RadioButtonAutomationPeerImpl);
223impl FerroObjectImpl for InkToolbarToolButtonAutomationPeer {
224 fn constructed(this: &Self) {
225 Self::parent_constructed(this);
226 subscribe_flyout(&this.to_ref().upcast(), &this.owner_button());
227 }
228}
229impl AutomationPeerImpl for InkToolbarToolButtonAutomationPeer {
230 fn get_class_name_core(_this: &Self) -> String {
231 "InkToolbarToolButton".to_owned()
232 }
233 fn get_automation_control_type_core(this: &Self) -> AutomationControlType {
234 if is_builtin_pen(&this.owner_button()) {
235 AutomationControlType::Button
236 } else {
237 AutomationControlType::Custom
238 }
239 }
240 fn get_localized_control_type_core(this: &Self) -> String {
241 let owner = this.owner_button();
242 let key = if is_builtin_pen(&owner) && owner.flyout().is_some() {
243 "InkToolbarDropDownButtonControlTypeName"
244 } else {
245 "InkToolbarToolButtonControlTypeName"
246 };
247 strings::text(&owner, key)
248 }
249 fn get_help_text_core(this: &Self) -> Option<String> {
250 let owner = this.owner_button();
251 if is_builtin_pen(&owner) {
252 append_text(
255 AutomationProperties::get_help_text(&owner),
256 strings::text(&owner, "InkToolbarColorPaletteHelpText"),
257 )
258 } else {
259 Self::parent_get_help_text_core(this)
260 }
261 }
262 fn get_name_core(this: &Self) -> Option<String> {
263 let owner = this.owner_button();
264 let name = Self::parent_get_name_core(this);
265 if owner
266 .cast::<InkToolbarToolButton>()
267 .is_some_and(|owner| owner.is_checked() == Some(true))
268 {
269 append_text(
270 name,
271 strings::text(&owner, "InkToolbarToolButtonSelectedStateName"),
272 )
273 } else {
274 name
275 }
276 }
277}
278impl InkToolbarToolButtonAutomationPeer {
279 pub fn construct(owner: &InkToolbarToolButton) -> Self {
280 Self {
281 base: RadioButtonAutomationPeer::construct(owner),
282 }
283 }
284 pub fn new(owner: &InkToolbarToolButton) -> Ref<Self> {
285 instantiate(Self::construct(owner))
286 }
287 fn owner_button(&self) -> Ref<Button> {
288 ControlAutomationPeer::owner(self)
289 .cast()
290 .expect("tool button peer owner")
291 }
292}
293impl IExpandCollapseProvider for Provider<InkToolbarToolButtonAutomationPeer> {
294 fn peer(&self) -> Ref<AutomationPeer> {
295 self.0.clone().upcast()
296 }
297 fn expand_collapse_state(&self) -> ExpandCollapseState {
298 match button_state(&self.0.owner_button()) {
300 ExpandCollapseState::LeafNode => ExpandCollapseState::Collapsed,
301 state => state,
302 }
303 }
304 fn shows_menu(&self) -> bool {
305 true
306 }
307 fn expand(&self) -> Result<(), ElementNotEnabledException> {
308 expand(&self.0.owner_button())
309 }
310 fn collapse(&self) -> Result<(), ElementNotEnabledException> {
311 collapse(&self.0.owner_button())
312 }
313}
314impl ISelectionItemProvider for Provider<InkToolbarToolButtonAutomationPeer> {
315 fn peer(&self) -> Ref<AutomationPeer> {
316 self.0.clone().upcast()
317 }
318 fn is_selected(&self) -> bool {
319 self.0.is_selected()
320 }
321 fn selection_container(&self) -> Option<Rc<dyn ISelectionProvider>> {
322 self.0.selection_container()
323 }
324 fn add_to_selection(&self) -> Result<(), ElementNotEnabledException> {
325 let owner = self.0.owner_button();
326 if !owner.is_effectively_enabled() {
327 return Err(ElementNotEnabledException::new());
328 }
329 if self.is_selected() {
330 Ok(())
331 } else {
332 Err(invalid_selection())
333 }
334 }
335 fn remove_from_selection(&self) -> Result<(), ElementNotEnabledException> {
336 let owner = self.0.owner_button();
337 if !owner.is_effectively_enabled() {
338 return Err(ElementNotEnabledException::new());
339 }
340 if self.is_selected() {
341 Err(invalid_selection())
342 } else {
343 Ok(())
344 }
345 }
346 fn select(&self) -> Result<(), ElementNotEnabledException> {
347 let owner = self.0.owner_button();
348 if !owner.is_effectively_enabled() {
349 return Err(ElementNotEnabledException::new());
350 }
351 owner
352 .cast::<InkToolbarToolButton>()
353 .expect("tool peer owner")
354 .set_is_checked(Some(true));
355 Ok(())
356 }
357}
358
359#[repr(C)]
360pub struct InkToolbarMenuButtonAutomationPeer {
361 base: ToggleButtonAutomationPeer,
362}
363ferro_class!(InkToolbarMenuButtonAutomationPeer:ToggleButtonAutomationPeer);
364ferro_class_info!(InkToolbarMenuButtonAutomationPeer{interfaces:[Rc<dyn IExpandCollapseProvider> => Provider::as_expand_collapse_provider]});
365ferro_impl_classes!(InkToolbarMenuButtonAutomationPeer:ControlAutomationPeerImpl);
366impl FerroObjectImpl for InkToolbarMenuButtonAutomationPeer {
367 fn constructed(this: &Self) {
368 Self::parent_constructed(this);
369 subscribe_flyout(&this.to_ref().upcast(), &this.owner_button());
370 }
371}
372impl AutomationPeerImpl for InkToolbarMenuButtonAutomationPeer {
373 fn get_class_name_core(_this: &Self) -> String {
374 "InkToolbarMenuButton".to_owned()
375 }
376 fn get_automation_control_type_core(_this: &Self) -> AutomationControlType {
377 AutomationControlType::Button
378 }
379 fn get_localized_control_type_core(this: &Self) -> String {
380 let owner = this.owner_button();
381 strings::text(
382 &owner,
383 if menu_has_dropdown(&owner) {
384 "InkToolbarStencilDropDownControlTypeName"
385 } else {
386 "InkToolbarToolButtonControlTypeName"
387 },
388 )
389 }
390 fn get_name_core(this: &Self) -> Option<String> {
391 let owner = this.owner_button();
392 let persistent = strings::text(&owner, "InkToolbarStencilButtonName");
393 let mut name = Self::parent_get_name_core(this);
394 if let Some(stencil) = owner.cast::<InkToolbarStencilButton>() {
395 if name
396 .as_deref()
397 .is_none_or(|value| value.is_empty() || value == persistent)
398 {
399 name = Some(strings::text(
400 &owner,
401 match stencil.selected_stencil() {
402 InkToolbarStencilKind::Ruler => "InkToolbarStencilRulerName",
403 InkToolbarStencilKind::Protractor => "InkToolbarStencilProtractorName",
404 },
405 ));
406 }
407 }
408 append_text(Some(persistent), name.unwrap_or_default())
409 }
410 fn get_provider_core(this: &Self, provider_type: ValueType) -> Option<BoxedValue> {
411 let dropdown = menu_has_dropdown(&this.owner_button());
412 if (provider_type == ValueType::of::<Rc<dyn IExpandCollapseProvider>>() && !dropdown)
413 || (provider_type == ValueType::of::<Rc<dyn IToggleProvider>>() && dropdown)
414 {
415 return None;
416 }
417 Self::parent_get_provider_core(this, provider_type)
418 }
419}
420impl InkToolbarMenuButtonAutomationPeer {
421 pub fn construct(owner: &InkToolbarMenuButton) -> Self {
422 Self {
423 base: ToggleButtonAutomationPeer::construct(owner),
424 }
425 }
426 pub fn new(owner: &InkToolbarMenuButton) -> Ref<Self> {
427 instantiate(Self::construct(owner))
428 }
429 fn owner_button(&self) -> Ref<Button> {
430 ControlAutomationPeer::owner(self)
431 .cast()
432 .expect("menu button peer owner")
433 }
434}
435impl IExpandCollapseProvider for Provider<InkToolbarMenuButtonAutomationPeer> {
436 fn peer(&self) -> Ref<AutomationPeer> {
437 self.0.clone().upcast()
438 }
439 fn expand_collapse_state(&self) -> ExpandCollapseState {
440 let owner = self.0.owner_button();
441 if menu_has_dropdown(&owner) {
442 button_state(&owner)
443 } else {
444 ExpandCollapseState::LeafNode
445 }
446 }
447 fn shows_menu(&self) -> bool {
448 true
449 }
450 fn expand(&self) -> Result<(), ElementNotEnabledException> {
451 let owner = self.0.owner_button();
452 if menu_has_dropdown(&owner) {
453 expand(&owner)
454 } else {
455 Ok(())
456 }
457 }
458 fn collapse(&self) -> Result<(), ElementNotEnabledException> {
459 collapse(&self.0.owner_button())
460 }
461}
462
463#[repr(C)]
464pub struct InkToolbarFlyoutItemAutomationPeer {
465 base: ToggleButtonAutomationPeer,
466}
467ferro_class!(InkToolbarFlyoutItemAutomationPeer:ToggleButtonAutomationPeer);
468ferro_class_info!(InkToolbarFlyoutItemAutomationPeer{interfaces:[Rc<dyn ISelectionItemProvider> => Provider::as_selection_item_provider,Rc<dyn IInvokeProvider> => Provider::as_invoke_provider]});
469ferro_impl_classes!(InkToolbarFlyoutItemAutomationPeer:FerroObjectImpl,ControlAutomationPeerImpl);
470impl AutomationPeerImpl for InkToolbarFlyoutItemAutomationPeer {
471 fn get_class_name_core(_this: &Self) -> String {
472 "InkToolbarFlyoutItem".to_owned()
473 }
474 fn get_automation_control_type_core(_this: &Self) -> AutomationControlType {
475 AutomationControlType::MenuItem
476 }
477 fn get_provider_core(this: &Self, provider_type: ValueType) -> Option<BoxedValue> {
478 if provider_type == ValueType::of::<Rc<dyn IToggleProvider>>() {
479 return None;
482 }
483 if provider_type == ValueType::of::<Rc<dyn ISelectionItemProvider>>()
484 && !is_radio_item(&this.owner_item())
485 {
486 return None;
487 }
488 Self::parent_get_provider_core(this, provider_type)
489 }
490}
491impl InkToolbarFlyoutItemAutomationPeer {
492 pub fn construct(owner: &InkToolbarFlyoutItem) -> Self {
493 Self {
494 base: ToggleButtonAutomationPeer::construct(owner),
495 }
496 }
497 pub fn new(owner: &InkToolbarFlyoutItem) -> Ref<Self> {
498 instantiate(Self::construct(owner))
499 }
500 fn owner_item(&self) -> Ref<InkToolbarFlyoutItem> {
501 ControlAutomationPeer::owner(self)
502 .cast()
503 .expect("flyout item peer owner")
504 }
505 fn ensure_selection_enabled(
506 &self,
507 ) -> Result<Ref<InkToolbarFlyoutItem>, ElementNotEnabledException> {
508 let owner = self.owner_item();
509 if !owner.is_effectively_enabled() {
510 return Err(ElementNotEnabledException::new());
511 }
512 if !is_radio_item(&owner) {
513 return Err(invalid_selection());
514 }
515 Ok(owner)
516 }
517}
518impl IInvokeProvider for Provider<InkToolbarFlyoutItemAutomationPeer> {
519 fn peer(&self) -> Ref<AutomationPeer> {
520 self.0.clone().upcast()
521 }
522 fn invoke(&self) -> Result<(), ElementNotEnabledException> {
523 let owner = self.0.owner_item();
524 if !owner.is_effectively_enabled() {
525 return Err(ElementNotEnabledException::new());
526 }
527 owner.on_click();
528 Ok(())
529 }
530}
531impl ISelectionItemProvider for Provider<InkToolbarFlyoutItemAutomationPeer> {
532 fn peer(&self) -> Ref<AutomationPeer> {
533 self.0.clone().upcast()
534 }
535 fn is_selected(&self) -> bool {
536 let owner = self.0.owner_item();
537 is_radio_item(&owner) && owner.is_checked() == Some(true)
538 }
539 fn selection_container(&self) -> Option<Rc<dyn ISelectionProvider>> {
540 None
541 }
542 fn add_to_selection(&self) -> Result<(), ElementNotEnabledException> {
543 let owner = self.0.ensure_selection_enabled()?;
544 if owner.is_checked() == Some(true) {
545 return Ok(());
546 }
547 if owner
548 .visual_parent()
549 .and_then(|parent| parent.cast::<Panel>())
550 .is_some_and(|panel| {
551 panel
552 .children()
553 .snapshot()
554 .iter()
555 .filter_map(|child| child.cast::<InkToolbarFlyoutItem>())
556 .any(|other| {
557 other != owner && is_radio_item(&other) && other.is_checked() == Some(true)
558 })
559 })
560 {
561 return Err(invalid_selection());
562 }
563 owner.on_click();
564 Ok(())
565 }
566 fn remove_from_selection(&self) -> Result<(), ElementNotEnabledException> {
567 let owner = self.0.ensure_selection_enabled()?;
568 if owner.is_checked() == Some(true) {
569 if owner.kind() == InkToolbarFlyoutItemKind::Radio {
570 return Err(invalid_selection());
571 }
572 owner.on_click();
573 }
574 Ok(())
575 }
576 fn select(&self) -> Result<(), ElementNotEnabledException> {
577 let owner = self.0.ensure_selection_enabled()?;
578 if owner.is_checked() != Some(true) {
579 owner.on_click();
580 }
581 Ok(())
582 }
583}
584
585#[repr(C)]
586pub struct InkToolbarStrokeWidthSliderAutomationPeer {
587 base: SliderAutomationPeer,
588}
589ferro_class!(InkToolbarStrokeWidthSliderAutomationPeer:SliderAutomationPeer);
590ferro_class_info!(InkToolbarStrokeWidthSliderAutomationPeer{interfaces:[Rc<dyn IValueProvider> => Provider::as_value_provider]});
591ferro_impl_classes!(InkToolbarStrokeWidthSliderAutomationPeer:FerroObjectImpl,ControlAutomationPeerImpl);
592impl RangeBaseAutomationPeerImpl for InkToolbarStrokeWidthSliderAutomationPeer {
593 fn owner_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
594 Self::parent_owner_property_changed(this, change);
595 if change.property() == RangeBase::value_property().as_property() {
596 this.raise_property_changed_event(
597 ValuePatternIdentifiers::value_property(),
598 change
599 .get_old_value::<f64>()
600 .map(|value| Rc::new(value.to_string()) as BoxedValue),
601 Some(Rc::new(change.get_new_value::<f64>().to_string()) as BoxedValue),
602 );
603 }
604 }
605}
606impl AutomationPeerImpl for InkToolbarStrokeWidthSliderAutomationPeer {
607 fn get_class_name_core(_this: &Self) -> String {
608 "InkToolbarStrokeWidthSlider".to_owned()
609 }
610 fn get_name_core(this: &Self) -> Option<String> {
611 let owner = this.owner_slider();
612 let range = strings::text(&owner, "InkToolbarStrokeWidthSliderRangeFormat")
613 .replace("%1!s!", &owner.minimum().to_string())
614 .replace("%2!s!", &owner.maximum().to_string());
615 append_text(Self::parent_get_name_core(this), range)
616 }
617}
618impl InkToolbarStrokeWidthSliderAutomationPeer {
619 pub fn construct(owner: &InkToolbarStrokeWidthSlider) -> Self {
620 Self {
621 base: SliderAutomationPeer::construct(owner),
622 }
623 }
624 pub fn new(owner: &InkToolbarStrokeWidthSlider) -> Ref<Self> {
625 instantiate(Self::construct(owner))
626 }
627 fn owner_slider(&self) -> Ref<InkToolbarStrokeWidthSlider> {
628 ControlAutomationPeer::owner(self)
629 .cast()
630 .expect("stroke width peer owner")
631 }
632}
633impl IValueProvider for Provider<InkToolbarStrokeWidthSliderAutomationPeer> {
634 fn peer(&self) -> Ref<AutomationPeer> {
635 self.0.clone().upcast()
636 }
637 fn is_read_only(&self) -> bool {
638 !self.0.owner_slider().is_effectively_enabled()
639 }
640 fn value(&self) -> Option<String> {
641 Some(self.0.owner_slider().value().to_string())
642 }
643 fn set_value(&self, value: Option<&str>) -> Result<(), ProviderError> {
644 let owner = self.0.owner_slider();
645 if !owner.is_effectively_enabled() {
646 return Err(Box::new(ElementNotEnabledException::new()));
647 }
648 let value = value.unwrap_or("").parse::<f64>()?;
649 if !value.is_finite() {
650 return Err(Box::new(super::InkError::InvalidInput(
651 "Stroke width must be finite",
652 )));
653 }
654 owner.set_range_value(value.clamp(owner.minimum(), owner.maximum()));
655 Ok(())
656 }
657}