use crate::primitives::TemplatedControlImpl;
use crate::{Button, ButtonImpl, ContentControlImpl, ControlImpl};
use ferroui_base::input::{
InputElementImpl, InputElementImplExt, Key, KeyEventArgs, MouseButton, PointerPressedEventArgs,
PointerReleasedEventArgs,
};
use ferroui_base::interactivity::InteractiveImpl;
use ferroui_base::layout::LayoutableImpl;
use ferroui_base::threading::DispatcherTimer;
use ferroui_base::{
ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroObjectImplExt,
FerroProperty, FerroPropertyChangedEventArgs, Ref, StyledElementImpl, StyledProperty, VisualImpl,
};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
#[repr(C)]
pub struct RepeatButton {
base: Button,
repeat_timer: RefCell<Option<Rc<DispatcherTimer>>>,
}
ferro_class!(RepeatButton: Button);
ferroui_base::ferro_class_info!(RepeatButton { new: RepeatButton::new });
ferro_impl_classes!(
RepeatButton: StyledElementImpl,
VisualImpl,
LayoutableImpl,
InteractiveImpl,
ControlImpl,
TemplatedControlImpl,
ContentControlImpl,
ButtonImpl
);
impl FerroObjectImpl for RepeatButton {
fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
Self::parent_on_property_changed(this, change);
if change.property() == Button::is_pressed_property().as_property() && !change.get_new_value::<bool>() {
this.stop_timer();
}
}
}
impl InputElementImpl for RepeatButton {
fn on_key_down(this: &Self, e: &KeyEventArgs) {
Self::parent_on_key_down(this, e);
if e.key == Key::Space {
this.start_timer();
}
}
fn on_key_up(this: &Self, e: &KeyEventArgs) {
Self::parent_on_key_up(this, e);
this.stop_timer();
}
fn on_pointer_pressed(this: &Self, e: &PointerPressedEventArgs) {
Self::parent_on_pointer_pressed(this, e);
if e.get_current_point(Some(this)).properties.is_left_button_pressed {
this.start_timer();
}
}
fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
Self::parent_on_pointer_released(this, e);
if e.initial_press_mouse_button() == MouseButton::Left {
this.stop_timer();
}
}
}
ferroui_base::ferro_properties! { impl RepeatButton {
ferro_property!(
pub fn interval_property() -> StyledProperty<i32> {
FerroProperty::register::<RepeatButton, _>("Interval", 100)
}
);
ferro_property!(
pub fn delay_property() -> StyledProperty<i32> {
FerroProperty::register::<RepeatButton, _>("Delay", 300)
}
);
} }
impl RepeatButton {
pub fn construct() -> Self {
Self { base: Button::construct(), repeat_timer: RefCell::new(None) }
}
pub fn new() -> Ref<Self> {
instantiate(Self::construct())
}
pub fn interval(&self) -> i32 {
self.get_value(Self::interval_property())
}
pub fn set_interval(&self, value: i32) {
self.set_value(Self::interval_property(), value)
}
pub fn delay(&self) -> i32 {
self.get_value(Self::delay_property())
}
pub fn set_delay(&self, value: i32) {
self.set_value(Self::delay_property(), value)
}
fn from_milliseconds(milliseconds: i32) -> Duration {
Duration::from_millis(u64::try_from(milliseconds).unwrap_or(0))
}
fn start_timer(&self) {
let existing = self.repeat_timer.borrow().clone();
let repeat_timer = match existing {
Some(repeat_timer) => repeat_timer,
None => {
let repeat_timer = DispatcherTimer::new();
let weak = self.to_ref().downgrade();
let _ = repeat_timer.tick(move |_| {
if let Some(this) = weak.upgrade() {
this.repeat_timer_on_tick();
}
});
*self.repeat_timer.borrow_mut() = Some(repeat_timer.clone());
repeat_timer
}
};
if repeat_timer.is_enabled() {
return;
}
repeat_timer.set_interval(Self::from_milliseconds(self.delay()));
repeat_timer.start();
}
fn repeat_timer_on_tick(&self) {
let interval = Self::from_milliseconds(self.interval());
let repeat_timer = self.repeat_timer.borrow().clone();
if let Some(repeat_timer) = repeat_timer {
if repeat_timer.interval() != interval {
repeat_timer.set_interval(interval);
}
}
self.on_click();
}
fn stop_timer(&self) {
let repeat_timer = self.repeat_timer.borrow().clone();
if let Some(repeat_timer) = repeat_timer {
repeat_timer.stop();
}
}
}
impl Drop for RepeatButton {
fn drop(&mut self) {
if let Some(repeat_timer) = self.repeat_timer.get_mut().take() {
repeat_timer.stop();
}
}
}