diff options
author | crupest <crupest@outlook.com> | 2020-01-01 17:38:45 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-01-01 17:38:45 +0800 |
commit | ae6f797561cdfa438ebef1fbbf94d784d315e655 (patch) | |
tree | 33659ea0448a6068c4d07b15ca488ab5495906ce | |
parent | f3719a5128ef911ec61c43a6eed34203f3b9bbb4 (diff) | |
download | cru-ae6f797561cdfa438ebef1fbbf94d784d315e655.tar.gz cru-ae6f797561cdfa438ebef1fbbf94d784d315e655.tar.bz2 cru-ae6f797561cdfa438ebef1fbbf94d784d315e655.zip |
...
-rw-r--r-- | .vscode/settings.json | 8 | ||||
-rw-r--r-- | include/cru/common/base.hpp | 67 | ||||
-rw-r--r-- | include/cru/common/event.hpp | 17 | ||||
-rw-r--r-- | include/cru/platform/native/basic_types.hpp | 6 | ||||
-rw-r--r-- | include/cru/ui/click_detector.hpp | 38 | ||||
-rw-r--r-- | src/ui/click_detector.cpp | 11 |
6 files changed, 116 insertions, 31 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index 2b5cd528..7dd396d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -57,6 +57,12 @@ "condition_variable": "cpp", "mutex": "cpp", "shared_mutex": "cpp", - "thread": "cpp" + "thread": "cpp", + "bitset": "cpp", + "iomanip": "cpp", + "set": "cpp", + "sstream": "cpp", + "xlocmon": "cpp", + "xloctime": "cpp" } } diff --git a/include/cru/common/base.hpp b/include/cru/common/base.hpp index 4264142d..d4f164bf 100644 --- a/include/cru/common/base.hpp +++ b/include/cru/common/base.hpp @@ -2,6 +2,7 @@ #include "pre_config.hpp" #include <stdexcept> +#include <type_traits> #define CRU_UNUSED(entity) static_cast<void>(entity); @@ -40,4 +41,70 @@ struct Interface { [[noreturn]] inline void UnreachableCode() { throw std::runtime_error("Unreachable code."); } + } // namespace cru + +template <typename Enum> +struct enable_bitmask_operators : std::false_type {}; + +#define CRU_ENABLE_BITMASK_OPERATORS(enumname) \ + template <> \ + struct enable_bitmask_operators<enumname> : std::true_type {}; + +template <typename Enum> +auto operator|(Enum lhs, Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum> { + using underlying = typename std::underlying_type<Enum>::type; + return static_cast<Enum>(static_cast<underlying>(lhs) | + static_cast<underlying>(rhs)); +} + +template <typename Enum> +auto operator&(Enum lhs, Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum> { + using underlying = typename std::underlying_type<Enum>::type; + return static_cast<Enum>(static_cast<underlying>(lhs) & + static_cast<underlying>(rhs)); +} + +template <typename Enum> +auto operator^(Enum lhs, Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum> { + using underlying = typename std::underlying_type<Enum>::type; + return static_cast<Enum>(static_cast<underlying>(lhs) ^ + static_cast<underlying>(rhs)); +} + +template <typename Enum> +auto operator~(Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum> { + using underlying = typename std::underlying_type<Enum>::type; + return static_cast<Enum>(~static_cast<underlying>(rhs)); +} + +template <typename Enum> +auto operator|=(Enum& lhs, Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum&> { + using underlying = typename std::underlying_type<Enum>::type; + lhs = static_cast<Enum>(static_cast<underlying>(lhs) | + static_cast<underlying>(rhs)); + return lhs; +} + +template <typename Enum> +auto operator&=(Enum& lhs, Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum&> { + using underlying = typename std::underlying_type<Enum>::type; + lhs = static_cast<Enum>(static_cast<underlying>(lhs) & + static_cast<underlying>(rhs)); + return lhs; +} + +template <typename Enum> +auto operator^=(Enum& lhs, Enum rhs) + -> std::enable_if_t<enable_bitmask_operators<Enum>::value, Enum&> { + using underlying = typename std::underlying_type<Enum>::type; + lhs = static_cast<Enum>(static_cast<underlying>(lhs) ^ + static_cast<underlying>(rhs)); + return lhs; +} diff --git a/include/cru/common/event.hpp b/include/cru/common/event.hpp index 33b2e3f6..55a00d7e 100644 --- a/include/cru/common/event.hpp +++ b/include/cru/common/event.hpp @@ -3,6 +3,7 @@ #include "self_resolvable.hpp" +#include <cassert> #include <forward_list> #include <functional> #include <memory> @@ -99,7 +100,8 @@ struct IEvent { IEvent(IEvent&& other) = delete; IEvent& operator=(const IEvent& other) = delete; IEvent& operator=(IEvent&& other) = delete; - ~IEvent() = default; + ~IEvent() = default; // Note that user can't destroy a Event via IEvent. So + // destructor should be protected. public: virtual EventRevoker AddHandler(const EventHandler& handler) = 0; @@ -130,14 +132,16 @@ class Event : public details::EventBase, public IEvent<TEventArgs> { EventRevoker AddHandler(const EventHandler& handler) override { const auto token = current_token_++; - this->handler_data_list_.emplace_after(this->last_handler_iterator_ ,token, handler); + this->handler_data_list_.emplace_after(this->last_handler_iterator_, token, + handler); ++(this->last_handler_iterator_); return CreateRevoker(token); } EventRevoker AddHandler(EventHandler&& handler) override { const auto token = current_token_++; - this->handler_data_list_.emplace_after(this->last_handler_iterator_ ,token, std::move(handler)); + this->handler_data_list_.emplace_after(this->last_handler_iterator_, token, + std::move(handler)); ++(this->last_handler_iterator_); return CreateRevoker(token); } @@ -163,12 +167,15 @@ class Event : public details::EventBase, public IEvent<TEventArgs> { protected: void RemoveHandler(EventHandlerToken token) override { this->handler_data_list_.remove_if( - [token](const HandlerData& data) { return data.token == token; }); + [token](const HandlerData& data) { return data.token == token; }); } private: std::forward_list<HandlerData> handler_data_list_{}; - typename std::forward_list<HandlerData>::const_iterator last_handler_iterator_ = this->handler_data_list_.cbefore_begin(); // remember the last handler to make push back O(1) + typename std::forward_list< + HandlerData>::const_iterator last_handler_iterator_ = + this->handler_data_list_ + .cbefore_begin(); // remember the last handler to make push back O(1) EventHandlerToken current_token_ = 0; }; diff --git a/include/cru/platform/native/basic_types.hpp b/include/cru/platform/native/basic_types.hpp index a53fa671..247df06d 100644 --- a/include/cru/platform/native/basic_types.hpp +++ b/include/cru/platform/native/basic_types.hpp @@ -1,5 +1,5 @@ #pragma once -#include "cru/common/pre_config.hpp" +#include "cru/common/base.hpp" namespace cru::platform::native { struct Dpi { @@ -7,5 +7,7 @@ struct Dpi { float y; }; -enum MouseButton : unsigned { Left = 0b1, Right = 0b10, Middle = 0b100 }; +enum class MouseButton : unsigned { Left = 0b1, Right = 0b10, Middle = 0b100 }; } // namespace cru::platform::native + +CRU_ENABLE_BITMASK_OPERATORS(::cru::platform::native::MouseButton) diff --git a/include/cru/ui/click_detector.hpp b/include/cru/ui/click_detector.hpp index 1a88b8a6..d5e4ac78 100644 --- a/include/cru/ui/click_detector.hpp +++ b/include/cru/ui/click_detector.hpp @@ -1,9 +1,8 @@ #pragma once #include "control.hpp" -#include <cstdlib> -#include <forward_list> #include <optional> +#include <vector> namespace cru::ui { class ClickEventArgs : Object { @@ -32,33 +31,30 @@ class ClickEventArgs : Object { MouseButton button_; }; +enum class ClickState { + None, // Mouse is outside the control. + Hover, // Mouse hovers on the control but not pressed + Press, // Mouse is pressed and if released click is done. + PressInactive // Mouse is pressed but if released click is canceled. +}; + class ClickDetector : public Object { public: explicit ClickDetector(Control* control); - ClickDetector(const ClickDetector& other) = delete; - ClickDetector& operator=(const ClickDetector& other) = delete; - ClickDetector(ClickDetector&& other) = delete; - ClickDetector& operator=(ClickDetector&& other) = delete; + CRU_DELETE_COPY(ClickDetector) + CRU_DELETE_MOVE(ClickDetector) ~ClickDetector() override = default; Control* GetControl() const { return control_; } - // Return a union of buttons being pressed. Return 0 if no button is being - // pressed. - MouseButton GetPressingButton() const { - unsigned result = 0; - if (click_map_.left.has_value()) result |= MouseButton::Left; - if (click_map_.middle.has_value()) result |= MouseButton::Middle; - if (click_map_.right.has_value()) result |= MouseButton::Right; - return static_cast<MouseButton>(result); - } + MouseButton GetTriggerButton() const { return trigger_button_; } + void SetTriggerButton(MouseButton trigger_button); IEvent<ClickEventArgs>* ClickEvent() { return &event_; } - IEvent<MouseButton>* ClickBeginEvent() { return &begin_event_; } - IEvent<MouseButton>* ClickEndEvent() { return &end_event_; } + IEvent<ClickState>* StateChangeEvent() { return &state_change_event_; } private: std::optional<Point>& FromButton(MouseButton button) { @@ -77,12 +73,12 @@ class ClickDetector : public Object { private: Control* control_; - Event<ClickEventArgs> event_; + MouseButton trigger_button_ = MouseButton::Left | MouseButton::Right; - Event<MouseButton> begin_event_; - Event<MouseButton> end_event_; + Event<ClickEventArgs> event_; + Event<ClickState> state_change_event_; - std::forward_list<EventRevokerGuard> event_rovoker_guards_; + std::vector<EventRevokerGuard> event_rovoker_guards_; struct { std::optional<Point> left = std::nullopt; diff --git a/src/ui/click_detector.cpp b/src/ui/click_detector.cpp index b335f3b5..05d6dce1 100644 --- a/src/ui/click_detector.cpp +++ b/src/ui/click_detector.cpp @@ -8,7 +8,14 @@ ClickDetector::ClickDetector(Control* control) { assert(control); control_ = control; - event_rovoker_guards_.push_front( + event_rovoker_guards_.push_back( + EventRevokerGuard(control->MouseEnterEvent()->Direct()->AddHandler( + [this, control](event::MouseEventArgs& args) { + if + + }))); + + event_rovoker_guards_.push_back( EventRevokerGuard(control->MouseDownEvent()->Direct()->AddHandler( [this, control](event::MouseButtonEventArgs& args) { if (!control->CaptureMouse()) return; // capture failed @@ -17,7 +24,7 @@ ClickDetector::ClickDetector(Control* control) { begin_event_.Raise(button); }))); - event_rovoker_guards_.push_front( + event_rovoker_guards_.push_back( EventRevokerGuard(control->MouseUpEvent()->Direct()->AddHandler( [this, control](event::MouseButtonEventArgs& args) { if (!control->IsMouseCaptured()) return; |