Skip to main content

ferroui_controls/split_button/
toggle_split_button.rs

1use super::split_button::PC_CHECKED;
2use super::{SplitButton, SplitButtonImpl, SplitButtonImplExt};
3use crate::metadata::PseudoClassesAttribute;
4use crate::primitives::TemplatedControlImpl;
5use crate::{ContentControlImpl, ControlImpl};
6use ferroui_base::data::BindingMode;
7use ferroui_base::input::InputElementImpl;
8use ferroui_base::interactivity::{
9    Interactive, InteractiveImpl, RoutedEvent, RoutedEventArgs, RoutedEventHandlerToken, RoutingStrategies,
10};
11use ferroui_base::layout::LayoutableImpl;
12use ferroui_base::{
13    ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, instantiate, FerroObjectImpl,
14    FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Ref, StaticType, StyledElementImpl,
15    StyledProperty, StyledPropertyOptions, TypeInfo, VisualImpl,
16};
17
18/// A button with primary and secondary parts that can each be pressed
19/// separately. The primary part behaves like a toggle button with two
20/// states and the secondary part opens a flyout.
21#[repr(C)]
22pub struct ToggleSplitButton {
23    base: SplitButton,
24}
25
26ferro_class! {
27    ToggleSplitButton: SplitButton, virtuals ToggleSplitButtonImpl: SplitButtonImpl {
28        /// Invokes the `IsCheckedChanged` event when the `IsChecked`
29        /// property changes.
30        fn on_is_checked_changed(this);
31    }
32}
33ferroui_base::ferro_class_info!(ToggleSplitButton { new: ToggleSplitButton::new });
34
35ferro_impl_classes!(
36    ToggleSplitButton: VisualImpl,
37    LayoutableImpl,
38    InteractiveImpl,
39    InputElementImpl,
40    TemplatedControlImpl,
41    ContentControlImpl
42);
43
44impl ControlImpl for ToggleSplitButton {
45    fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
46        crate::automation::peers::ToggleSplitButtonAutomationPeer::new(this).upcast()
47    }
48}
49
50impl FerroObjectImpl for ToggleSplitButton {
51    fn on_property_changed(this: &Self, e: &FerroPropertyChangedEventArgs<'_>) {
52        Self::parent_on_property_changed(this, e);
53
54        if e.property() == Self::is_checked_property().as_property() {
55            this.on_is_checked_changed();
56        }
57    }
58}
59
60impl StyledElementImpl for ToggleSplitButton {
61    /// Both the toggle split button and the split button share the same
62    /// exact default style.
63    fn style_key_override(_this: &Self) -> &'static TypeInfo {
64        <SplitButton as StaticType>::TYPE
65    }
66}
67
68impl SplitButtonImpl for ToggleSplitButton {
69    fn internal_is_checked(this: &Self) -> bool {
70        this.is_checked()
71    }
72
73    fn on_click_primary(this: &Self, e: Option<&RoutedEventArgs>) {
74        this.toggle();
75
76        Self::parent_on_click_primary(this, e);
77    }
78}
79
80impl ToggleSplitButtonImpl for ToggleSplitButton {
81    fn on_is_checked_changed(this: &Self) {
82        // IsLoaded check
83        if this.parent().is_some() {
84            let event_args = RoutedEventArgs::with_event(Self::is_checked_changed_event());
85            this.raise_event(&event_args);
86        }
87
88        this.update_pseudo_classes();
89    }
90}
91
92impl ToggleSplitButton {
93    /// The pseudoclasses set by the class.
94    pub const PSEUDO_CLASSES: PseudoClassesAttribute = PseudoClassesAttribute::new(&[PC_CHECKED]);
95
96    ferro_routed_event!(
97        /// Defines the `IsCheckedChanged` event.
98        pub fn is_checked_changed_event() -> RoutedEvent<RoutedEventArgs> {
99            RoutedEvent::register::<ToggleSplitButton, _>("IsCheckedChanged", RoutingStrategies::BUBBLE)
100        }
101    );
102}
103
104ferroui_base::ferro_properties! { impl ToggleSplitButton {
105    ferro_property!(
106        /// Defines the `IsChecked` property.
107        pub fn is_checked_property() -> StyledProperty<bool> {
108            FerroProperty::register_with::<ToggleSplitButton, _>(
109                "IsChecked",
110                StyledPropertyOptions::new(false).default_binding_mode(BindingMode::TwoWay),
111            )
112        }
113    );
114} }
115
116impl ToggleSplitButton {
117    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
118    pub fn construct() -> Self {
119        Self { base: SplitButton::construct() }
120    }
121
122    /// Creates a toggle split button.
123    pub fn new() -> Ref<Self> {
124        instantiate(Self::construct())
125    }
126
127    /// Raised when the `IsChecked` property value changes.
128    pub fn is_checked_changed(
129        &self,
130        handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static,
131    ) -> RoutedEventHandlerToken {
132        self.add_handler(Self::is_checked_changed_event(), handler)
133    }
134
135    /// A value indicating whether the toggle split button is checked.
136    pub fn is_checked(&self) -> bool {
137        self.get_value(Self::is_checked_property())
138    }
139
140    pub fn set_is_checked(&self, value: bool) {
141        self.set_value(Self::is_checked_property(), value)
142    }
143
144    /// Toggles the `IsChecked` property between true and false.
145    pub fn toggle(&self) {
146        self.set_current_value(Self::is_checked_property(), !self.is_checked());
147    }
148
149    /// Toggles the button on behalf of the automation peer (internal in the
150    /// reference).
151    pub(crate) fn toggle_for_automation(&self) {
152        self.on_click_primary(None);
153    }
154}