aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json8
-rw-r--r--include/cru/common/base.hpp67
-rw-r--r--include/cru/common/event.hpp17
-rw-r--r--include/cru/platform/native/basic_types.hpp6
-rw-r--r--include/cru/ui/click_detector.hpp38
-rw-r--r--src/ui/click_detector.cpp11
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;