diff options
author | 杨宇千 <crupest@outlook.com> | 2019-08-11 22:08:49 +0800 |
---|---|---|
committer | 杨宇千 <crupest@outlook.com> | 2019-08-11 22:08:49 +0800 |
commit | 99a322a6badf5b6d95be4944e80d92fc1cb2589e (patch) | |
tree | 4227de0cbb1fd73ef99de4bc57041492a0450208 | |
parent | 3a8abe0aed9bb72ed64cbfe9f2f83a0db285e14c (diff) | |
download | cru-99a322a6badf5b6d95be4944e80d92fc1cb2589e.tar.gz cru-99a322a6badf5b6d95be4944e80d92fc1cb2589e.tar.bz2 cru-99a322a6badf5b6d95be4944e80d92fc1cb2589e.zip |
...
-rw-r--r-- | .editorconfig | 13 | ||||
-rw-r--r-- | include/cru/common/base.hpp | 16 | ||||
-rw-r--r-- | include/cru/ui/click_detector.hpp | 87 | ||||
-rw-r--r-- | include/cru/ui/control.hpp | 14 | ||||
-rw-r--r-- | include/cru/win/graph/direct/text_layout.hpp | 4 | ||||
-rw-r--r-- | src/ui/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/ui/click_detector.cpp | 32 | ||||
-rw-r--r-- | src/ui/control.cpp | 72 |
8 files changed, 167 insertions, 73 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..a957f24a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +end_of_line = crlf +insert_final_newline = true + +[*.cpp] +indent_style = space +indent_size = 2 + +[*.hpp] +indent_style = space +indent_size = 2 diff --git a/include/cru/common/base.hpp b/include/cru/common/base.hpp index d72b97f2..920fe569 100644 --- a/include/cru/common/base.hpp +++ b/include/cru/common/base.hpp @@ -1,6 +1,22 @@ #pragma once #include "pre_config.hpp" +#define CRU_DEFAULT_COPY(classname) \ + classname(const classname&) = default; \ + classname& operator=(const classname&) = default; + +#define CRU_DEFAULT_MOVE(classname) \ + classname(classname&&) = default; \ + classname& operator=(classname&&) = default; + +#define CRU_DELETE_COPY(classname) \ + classname(const classname&) = delete; \ + classname& operator=(const classname&) = delete; + +#define CRU_DELETE_MOVE(classname) \ + classname(classname&&) = delete; \ + classname& operator=(classname&&) = delete; + namespace cru { class Object { public: diff --git a/include/cru/ui/click_detector.hpp b/include/cru/ui/click_detector.hpp new file mode 100644 index 00000000..e7d5e417 --- /dev/null +++ b/include/cru/ui/click_detector.hpp @@ -0,0 +1,87 @@ +#pragma once +#include "control.hpp" + +#include <cstdlib> +#include <forward_list> +#include <optional> + +namespace cru::ui { +class ClickEventArgs : Object { + public: + ClickEventArgs(Control* sender, const Point& down_point, + const Point& up_point, MouseButton button) + : sender_(sender), + down_point_(down_point), + up_point_(up_point), + button_(button) {} + + CRU_DEFAULT_COPY(ClickEventArgs) + CRU_DEFAULT_MOVE(ClickEventArgs) + + ~ClickEventArgs() override = default; + + Control* GetSender() const { return sender_; } + Point GetDownPoint() const { return down_point_; } + Point GetUpPoint() const { return up_point_; } + MouseButton GetButton() const { return button_; } + + private: + Control* sender_; + Point down_point_; + Point up_point_; + MouseButton button_; +}; + +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; + + ~ClickDetector() override = default; + + Control* GetControl() const { return control_; } + + // Return a union of buttons being pressed. Return 0 if no button is being + // pressed. + MouseButton IsPressing() 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); + } + + IEvent<ClickEventArgs>* ClickEvent() { return &event_; } + + private: + std::optional<Point>& FromButton(MouseButton button) { + switch (button) { + case MouseButton::Left: + return click_map_.left; + case MouseButton::Middle: + return click_map_.middle; + case MouseButton::Right: + return click_map_.right; + default: + std::abort(); + } + } + + private: + Control* control_; + + Event<ClickEventArgs> event_; + + std::forward_list<EventRevokerGuard> event_rovoker_guards_; + + struct { + std::optional<Point> left = std::nullopt; + std::optional<Point> middle = std::nullopt; + std::optional<Point> right = std::nullopt; + } click_map_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/control.hpp b/include/cru/ui/control.hpp index f78557a3..b39b59a0 100644 --- a/include/cru/ui/control.hpp +++ b/include/cru/ui/control.hpp @@ -18,7 +18,7 @@ class Control : public Object { friend class Window; protected: - Control() = default; + Control(); public: Control(const Control& other) = delete; @@ -58,10 +58,14 @@ class Control : public Object { bool HasFocus(); - //*************** region: focus *************** + //*************** region: mouse *************** public: bool IsMouseOver() const { return is_mouse_over_; } + bool CaptureMouse(); // TODO + + bool IsMouseCaptured(); // TODO + //*************** region: events *************** public: // Raised when mouse enter the control. @@ -84,11 +88,6 @@ class Control : public Object { event::RoutedEvent<event::MouseButtonEventArgs>* MouseUpEvent() { return &mouse_up_event_; } - // Raised when a mouse button is pressed in the control and released in the - // control with mouse not leaving it between two operations. - event::RoutedEvent<event::MouseButtonEventArgs>* MouseClickEvent() { - return &mouse_click_event_; - } event::RoutedEvent<event::MouseWheelEventArgs>* MouseWheelEvent() { return &mouse_wheel_event_; } @@ -114,7 +113,6 @@ class Control : public Object { event::RoutedEvent<event::MouseEventArgs> mouse_move_event_; event::RoutedEvent<event::MouseButtonEventArgs> mouse_down_event_; event::RoutedEvent<event::MouseButtonEventArgs> mouse_up_event_; - event::RoutedEvent<event::MouseButtonEventArgs> mouse_click_event_; event::RoutedEvent<event::MouseWheelEventArgs> mouse_wheel_event_; event::RoutedEvent<event::KeyEventArgs> key_down_event_; diff --git a/include/cru/win/graph/direct/text_layout.hpp b/include/cru/win/graph/direct/text_layout.hpp index c7657762..a20591c2 100644 --- a/include/cru/win/graph/direct/text_layout.hpp +++ b/include/cru/win/graph/direct/text_layout.hpp @@ -35,8 +35,8 @@ class DWriteTextLayout : public TextLayout, std::wstring GetText() override; void SetText(std::wstring new_text) override; - std::shared_ptr<Font> GetFont(); - void SetFont(std::shared_ptr<Font> font); + std::shared_ptr<Font> GetFont() override; + void SetFont(std::shared_ptr<Font> font) override; void SetMaxWidth(float max_width) override; void SetMaxHeight(float max_height) override; diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 62ae1775..3186b0f5 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB CRU_UI_INCLUDE_DIR ${CRU_INCLUDE_DIR}/cru/ui) add_library(cru_ui STATIC routed_event_dispatch.hpp + click_detector.cpp content_control.cpp control.cpp layout_control.cpp @@ -20,6 +21,7 @@ add_library(cru_ui STATIC ) target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/base.hpp + ${CRU_UI_INCLUDE_DIR}/click_detector.hpp ${CRU_UI_INCLUDE_DIR}/content_control.hpp ${CRU_UI_INCLUDE_DIR}/control.hpp ${CRU_UI_INCLUDE_DIR}/layout_control.hpp diff --git a/src/ui/click_detector.cpp b/src/ui/click_detector.cpp new file mode 100644 index 00000000..1442b885 --- /dev/null +++ b/src/ui/click_detector.cpp @@ -0,0 +1,32 @@ +#include "cru/ui/click_detector.hpp" + +#include <cassert> +#include <optional> + +namespace cru::ui { +ClickDetector::ClickDetector(Control* control) { + assert(control); + control_ = control; + + event_rovoker_guards_.push_front( + EventRevokerGuard(control->MouseDownEvent()->Direct()->AddHandler( + [this, control](event::MouseButtonEventArgs& args) { + if (!control->CaptureMouse()) return; // capture failed + FromButton(args.GetMouseButton()) = + args.GetPoint(); // save mouse down point + }))); + + event_rovoker_guards_.push_front( + EventRevokerGuard(control->MouseUpEvent()->Direct()->AddHandler( + [this, control](event::MouseButtonEventArgs& args) { + if (!control->IsMouseCaptured()) return; + const auto button = args.GetMouseButton(); + auto& down_point = FromButton(button); + if (down_point.has_value()) { + event_.Raise(ClickEventArgs(control, down_point.value(), + args.GetPoint(), button)); + down_point = std::nullopt; + } + }))); +} +} // namespace cru::ui diff --git a/src/ui/control.cpp b/src/ui/control.cpp index bcf56c5e..d529b687 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -9,74 +9,20 @@ #include <cassert> namespace cru::ui { +Control::Control() { + MouseEnterEvent()->Direct()->AddHandler( + [this](event::MouseEventArgs&) { this->is_mouse_over_ = true; }); + + MouseLeaveEvent()->Direct()->AddHandler( + [this](event::MouseEventArgs&) { this->is_mouse_over_ = false; }); +} + void Control::_SetParent(Control* parent) { const auto old_parent = GetParent(); parent_ = parent; const auto new_parent = GetParent(); if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); - - MouseDownEvent()->Direct()->AddHandler( - [this](event::MouseButtonEventArgs& args) { - switch (args.GetMouseButton()) { - case MouseButton::Left: - click_map_.left = true; - OnMouseClickBegin(MouseButton::Left); - break; - case MouseButton::Middle: - click_map_.middle = true; - OnMouseClickBegin(MouseButton::Middle); - break; - case MouseButton::Right: - click_map_.right = true; - OnMouseClickBegin(MouseButton::Right); - break; - } - }); - - MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { - this->is_mouse_over_ = true; - }); - - MouseLeaveEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { - this->is_mouse_over_ = false; - if (click_map_.left) { - OnMouseClickCancel(MouseButton::Left); - } - if (click_map_.middle) { - OnMouseClickCancel(MouseButton::Middle); - } - if (click_map_.right) { - OnMouseClickCancel(MouseButton::Right); - } - click_map_.left = click_map_.middle = click_map_.right = false; - }); - - MouseUpEvent()->Direct()->AddHandler([this](event::MouseButtonEventArgs& args) { - switch (args.GetMouseButton()) { - case MouseButton::Left: - if (click_map_.left) { - click_map_.left = false; - OnMouseClickEnd(MouseButton::Left); - DispatchEvent(this, &Control::MouseClickEvent, nullptr, args.GetPoint(), args.GetMouseButton()); - } - break; - case MouseButton::Middle: - if (click_map_.middle) { - click_map_.middle = false; - OnMouseClickEnd(MouseButton::Middle); - DispatchEvent(this, &Control::MouseClickEvent, nullptr, args.GetPoint(), args.GetMouseButton()); - } - break; - case MouseButton::Right: - if (click_map_.right) { - click_map_.right = false; - OnMouseClickEnd(MouseButton::Right); - DispatchEvent(this, &Control::MouseClickEvent, nullptr, args.GetPoint(), args.GetMouseButton()); - } - break; - } - }); -} // namespace cru::ui +} void Control::_SetDescendantWindow(Window* window) { if (window == nullptr && window_ == nullptr) return; |