From e8a5ecfa3b85ee0edd2706be08dfa52f0da1ea98 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 15 Mar 2022 18:15:50 +0300 Subject: [PATCH] Support ui::button repeating callback when kept pressed --- libs/ui/include/psemek/ui/button.hpp | 26 ++++++++++ libs/ui/source/button.cpp | 76 +++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/libs/ui/include/psemek/ui/button.hpp b/libs/ui/include/psemek/ui/button.hpp index 62ee03b7..69091135 100644 --- a/libs/ui/include/psemek/ui/button.hpp +++ b/libs/ui/include/psemek/ui/button.hpp @@ -15,6 +15,11 @@ namespace psemek::ui bool on_event(mouse_move const & e) override; bool on_event(mouse_click const & e) override; + void update(float dt) override; + + void set_enabled(bool value); + void set_hidden(bool value); + using on_click_callback = std::function; void on_click(on_click_callback callback) @@ -22,6 +27,9 @@ namespace psemek::ui callback_ = std::move(callback); } + virtual void set_repeat(float wait, float period); + virtual void set_no_repeat(); + protected: enum class state_t { @@ -33,11 +41,29 @@ namespace psemek::ui virtual state_t state() const { return state_; } virtual void on_state_changed(state_t old); + virtual void post_on_click(); private: state_t state_ = state_t::normal; on_click_callback callback_; + + struct repeat + { + float wait; + float period; + + enum class state_t + { + idle, + wait, + repeat, + } state = state_t::idle; + + float timer = 0.f; + }; + + std::optional repeat_; }; } diff --git a/libs/ui/source/button.cpp b/libs/ui/source/button.cpp index 2e8412dc..4f0863bd 100644 --- a/libs/ui/source/button.cpp +++ b/libs/ui/source/button.cpp @@ -41,8 +41,7 @@ namespace psemek::ui if (e.down) { state_ = state_t::mousedown; - if (callback_) - post(callback_); + post_on_click(); on_state_changed(state_t::mouseover); return true; } @@ -60,12 +59,85 @@ namespace psemek::ui return false; } + void button::update(float dt) + { + if (repeat_) + { + repeat_->timer += dt; + + switch (repeat_->state) + { + case repeat::state_t::idle: + break; + case repeat::state_t::wait: + if (repeat_->timer >= repeat_->wait) + { + repeat_->timer -= repeat_->wait; + repeat_->state = repeat::state_t::repeat; + post_on_click(); + } + break; + case repeat::state_t::repeat: + if (repeat_->timer >= repeat_->period) + { + repeat_->timer -= repeat_->period; + repeat_->state = repeat::state_t::repeat; + post_on_click(); + } + break; + } + } + } + + void button::set_enabled(bool value) + { + element::set_enabled(value); + if (!value && repeat_) + repeat_->state = repeat::state_t::idle; + } + + void button::set_hidden(bool value) + { + element::set_hidden(value); + if (value && repeat_) + repeat_->state = repeat::state_t::idle; + } + + void button::set_repeat(float wait, float period) + { + repeat_ = repeat{wait, period}; + } + + void button::set_no_repeat() + { + repeat_ = std::nullopt; + } + void button::on_state_changed(state_t old) { + if (repeat_) + { + if (state() == state_t::mousedown) + { + repeat_->timer = 0.f; + repeat_->state = repeat::state_t::wait; + } + else + { + repeat_->state = repeat::state_t::idle; + } + } + if (state() == state_t::mousedown || old == state_t::mousedown) { post_reshape(); } } + void button::post_on_click() + { + if (callback_) + post(callback_); + } + }