Skip to main content

ferroui_controls/calendar/
calendar_button.rs

1// (c) Copyright Microsoft Corporation.
2// This source is subject to the Microsoft Public License (Ms-PL).
3// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
4// All other rights reserved.
5//
6// Ported from the Silverlight Toolkit sources as adapted by the upstream
7// project; the license text is in the `NOTICE.md` of this crate.
8
9use super::{Calendar, DateTimeHelper};
10use crate::primitives::{TemplateAppliedEventArgs, TemplatedControlImpl};
11use crate::{Button, ButtonImpl, ContentControl, ContentControlImpl, ControlImpl};
12use ferroui_base::input::{
13    InputElementImpl, InputElementImplExt, MouseButton, PointerPressedEventArgs, PointerReleasedEventArgs,
14};
15use ferroui_base::interactivity::InteractiveImpl;
16use ferroui_base::layout::LayoutableImpl;
17use ferroui_base::reactive::{Disposable, IDisposable};
18use ferroui_base::utilities::HandlerList;
19use ferroui_base::{
20    ferro_class, ferro_class_info, ferro_impl_classes, instantiate, BoxedValue, FerroObjectImpl, FerroObjectImplExt,
21    Ref, StyledElementImpl, VisualImpl, WeakRef,
22};
23use std::cell::{Cell, RefCell};
24use std::rc::Rc;
25
26/// Represents a button on a [`Calendar`].
27#[repr(C)]
28pub struct CalendarButton {
29    base: Button,
30
31    /// A value indicating whether the button is focused.
32    is_calendar_button_focused: Cell<bool>,
33
34    /// A value indicating whether the button is inactive.
35    is_inactive: Cell<bool>,
36
37    /// A value indicating whether the button is selected.
38    is_selected: Cell<bool>,
39
40    /// The Calendar associated with this button.
41    owner: RefCell<Option<WeakRef<Calendar>>>,
42
43    calendar_left_mouse_button_down: HandlerList<dyn Fn(&CalendarButton, &PointerPressedEventArgs)>,
44    calendar_left_mouse_button_up: HandlerList<dyn Fn(&CalendarButton, &PointerReleasedEventArgs)>,
45}
46
47ferro_class!(CalendarButton: Button);
48
49ferro_class_info!(CalendarButton {
50    new: CalendarButton::new,
51    markup: {
52        attributes: [PseudoClasses(":selected", ":inactive", ":btnfocused")],
53    },
54});
55
56ferro_impl_classes!(
57    CalendarButton: StyledElementImpl,
58    VisualImpl,
59    LayoutableImpl,
60    InteractiveImpl,
61    ControlImpl,
62    ContentControlImpl,
63    ButtonImpl
64);
65
66impl FerroObjectImpl for CalendarButton {
67    fn constructed(this: &Self) {
68        Self::parent_constructed(this);
69
70        let content: BoxedValue = Rc::new(DateTimeHelper::get_current_date_format().abbreviated_month_names()[0].clone());
71        this.set_current_value(ContentControl::content_property(), Some(content));
72    }
73}
74
75impl InputElementImpl for CalendarButton {
76    /// Provides class handling for the pointer pressed event that occurs
77    /// when the left mouse button is pressed while the mouse pointer is
78    /// over this control.
79    ///
80    /// This method marks the event as handled and cannot be handled by
81    /// subscribing to the event through the routed event of the control.
82    /// Subscribe through [`CalendarButton::calendar_left_mouse_button_down`]
83    /// instead.
84    fn on_pointer_pressed(this: &Self, e: &PointerPressedEventArgs) {
85        Self::parent_on_pointer_pressed(this, e);
86
87        if e.get_current_point(Some(this)).properties.is_left_button_pressed {
88            for (_, handler) in this.calendar_left_mouse_button_down.snapshot().iter() {
89                handler(this, e);
90            }
91        }
92    }
93
94    /// Provides handling for the pointer released event that occurs when
95    /// the left mouse button is released while the mouse pointer is over
96    /// this control.
97    ///
98    /// This method marks the event as handled and cannot be handled by
99    /// subscribing to the event through the routed event of the control.
100    /// Subscribe through [`CalendarButton::calendar_left_mouse_button_up`]
101    /// instead.
102    fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
103        Self::parent_on_pointer_released(this, e);
104
105        if e.initial_press_mouse_button() == MouseButton::Left {
106            for (_, handler) in this.calendar_left_mouse_button_up.snapshot().iter() {
107                handler(this, e);
108            }
109        }
110    }
111}
112
113impl TemplatedControlImpl for CalendarButton {
114    /// Builds the visual tree for the [`CalendarButton`] when a new
115    /// template is applied.
116    fn on_apply_template(this: &Self, _e: &TemplateAppliedEventArgs) {
117        this.set_pseudo_classes();
118    }
119}
120
121impl CalendarButton {
122    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
123    pub fn construct() -> Self {
124        Self {
125            base: Button::construct(),
126            is_calendar_button_focused: Cell::new(false),
127            is_inactive: Cell::new(false),
128            is_selected: Cell::new(false),
129            owner: RefCell::new(None),
130            calendar_left_mouse_button_down: HandlerList::new(),
131            calendar_left_mouse_button_up: HandlerList::new(),
132        }
133    }
134
135    /// Initializes a new instance of the [`CalendarButton`] class.
136    pub fn new() -> Ref<Self> {
137        instantiate(Self::construct())
138    }
139
140    /// Gets the Calendar associated with this button.
141    #[allow(dead_code)] // the reference declares the getter and reads it nowhere
142    pub(crate) fn owner(&self) -> Option<Ref<Calendar>> {
143        self.owner.borrow().as_ref().and_then(WeakRef::upgrade)
144    }
145
146    /// Sets the Calendar associated with this button.
147    pub(crate) fn set_owner(&self, value: Option<&Ref<Calendar>>) {
148        *self.owner.borrow_mut() = value.map(Ref::downgrade);
149    }
150
151    /// Gets a value indicating whether the button is focused.
152    pub(crate) fn is_calendar_button_focused(&self) -> bool {
153        self.is_calendar_button_focused.get()
154    }
155
156    /// Sets a value indicating whether the button is focused.
157    pub(crate) fn set_is_calendar_button_focused(&self, value: bool) {
158        if self.is_calendar_button_focused.get() != value {
159            self.is_calendar_button_focused.set(value);
160            self.set_pseudo_classes();
161        }
162    }
163
164    /// Gets a value indicating whether the button is inactive.
165    pub(crate) fn is_inactive(&self) -> bool {
166        self.is_inactive.get()
167    }
168
169    /// Sets a value indicating whether the button is inactive.
170    pub(crate) fn set_is_inactive(&self, value: bool) {
171        if self.is_inactive.get() != value {
172            self.is_inactive.set(value);
173            self.set_pseudo_classes();
174        }
175    }
176
177    /// Gets a value indicating whether the button is selected.
178    pub(crate) fn is_selected(&self) -> bool {
179        self.is_selected.get()
180    }
181
182    /// Sets a value indicating whether the button is selected.
183    pub(crate) fn set_is_selected(&self, value: bool) {
184        if self.is_selected.get() != value {
185            self.is_selected.set(value);
186            self.set_pseudo_classes();
187        }
188    }
189
190    /// Sets the pseudo classes of the button from its state.
191    fn set_pseudo_classes(&self) {
192        self.pseudo_classes().set(":selected", self.is_selected());
193        self.pseudo_classes().set(":inactive", self.is_inactive());
194        self.pseudo_classes().set(":btnfocused", self.is_calendar_button_focused() && self.is_enabled());
195    }
196
197    /// Occurs when the left mouse button is pressed (or when the tip of the
198    /// stylus touches the tablet PC) while the mouse pointer is over a UI
199    /// element. The handler receives the button that raises the event.
200    pub fn calendar_left_mouse_button_down(
201        &self,
202        handler: impl Fn(&CalendarButton, &PointerPressedEventArgs) + 'static,
203    ) -> Rc<dyn IDisposable> {
204        let token = self.calendar_left_mouse_button_down.add(Rc::new(handler));
205        let weak = self.to_ref().downgrade();
206        Disposable::create(move || {
207            if let Some(this) = weak.upgrade() {
208                this.calendar_left_mouse_button_down.remove(token);
209            }
210        })
211    }
212
213    /// Occurs when the left mouse button is released (or the tip of the
214    /// stylus is removed from the tablet PC) while the mouse (or the
215    /// stylus) is over a UI element (or while a UI element holds mouse
216    /// capture). The handler receives the button that raises the event.
217    pub fn calendar_left_mouse_button_up(
218        &self,
219        handler: impl Fn(&CalendarButton, &PointerReleasedEventArgs) + 'static,
220    ) -> Rc<dyn IDisposable> {
221        let token = self.calendar_left_mouse_button_up.add(Rc::new(handler));
222        let weak = self.to_ref().downgrade();
223        Disposable::create(move || {
224            if let Some(this) = weak.upgrade() {
225                this.calendar_left_mouse_button_up.remove(token);
226            }
227        })
228    }
229}