diff options
author | 杨宇千 <crupest@outlook.com> | 2019-09-08 19:09:38 +0800 |
---|---|---|
committer | 杨宇千 <crupest@outlook.com> | 2019-09-08 19:09:38 +0800 |
commit | 8b9a306d6f24dbc08aeee16b115260406e79126d (patch) | |
tree | db37b8c3441a28b7150b1f40ddf79c7fa9dcda79 | |
parent | 2c40085dd30d6e7370a0974ad1f642a61acc6e30 (diff) | |
download | cru-8b9a306d6f24dbc08aeee16b115260406e79126d.tar.gz cru-8b9a306d6f24dbc08aeee16b115260406e79126d.tar.bz2 cru-8b9a306d6f24dbc08aeee16b115260406e79126d.zip |
...
-rw-r--r-- | include/cru/ui/control.hpp | 26 | ||||
-rw-r--r-- | include/cru/ui/window.hpp | 23 | ||||
-rw-r--r-- | src/ui/control.cpp | 37 | ||||
-rw-r--r-- | src/ui/controls/button.cpp | 14 | ||||
-rw-r--r-- | src/ui/window.cpp | 121 | ||||
-rw-r--r-- | src/win/native/cursor.cpp | 2 | ||||
-rw-r--r-- | src/win/native/native_window.cpp | 2 |
7 files changed, 143 insertions, 82 deletions
diff --git a/include/cru/ui/control.hpp b/include/cru/ui/control.hpp index f312272e..cf4f9053 100644 --- a/include/cru/ui/control.hpp +++ b/include/cru/ui/control.hpp @@ -3,8 +3,10 @@ #include "cru/common/base.hpp" #include "cru/platform/native/basic_types.hpp" +#include "cru/platform/native/cursor.hpp" #include "event/ui_event.hpp" +#include <memory> #include <string_view> #include <vector> @@ -68,13 +70,31 @@ class Control : public Object { bool IsMouseCaptured(); + //*************** region: cursor *************** + // Cursor is inherited from parent recursively if not set. + public: + // null for not set + std::shared_ptr<platform::native::Cursor> GetCursor(); + + // will not return nullptr + std::shared_ptr<platform::native::Cursor> GetInheritedCursor(); + + // null to unset + void SetCursor(std::shared_ptr<platform::native::Cursor> cursor); + //*************** region: events *************** public: - // Raised when mouse enter the control. + // Raised when mouse enter the control. Even when the control itself captures + // the mouse, this event is raised as regular. But if mouse is captured by + // another control, the control will not receive any mouse enter event. You + // can use `IsMouseCaptured` to get more info. event::RoutedEvent<event::MouseEventArgs>* MouseEnterEvent() { return &mouse_enter_event_; } - // Raised when mouse is leave the control. + // Raised when mouse is leave the control. Even when the control itself + // captures the mouse, this event is raised as regular. But if mouse is + // captured by another control, the control will not receive any mouse leave + // event. You can use `IsMouseCaptured` to get more info. event::RoutedEvent<event::MouseEventArgs>* MouseLeaveEvent() { return &mouse_leave_event_; } @@ -142,5 +162,7 @@ class Control : public Object { bool middle; bool right; } click_map_; + + std::shared_ptr<platform::native::Cursor> cursor_ = nullptr; }; } // namespace cru::ui diff --git a/include/cru/ui/window.hpp b/include/cru/ui/window.hpp index ee6d2176..1325100f 100644 --- a/include/cru/ui/window.hpp +++ b/include/cru/ui/window.hpp @@ -18,6 +18,8 @@ class WindowRenderObject; } class Window final : public ContentControl, public SelfResolvable<Window> { + friend class Control; + public: static constexpr auto control_type = L"Window"; @@ -45,6 +47,10 @@ class Window final : public ContentControl, public SelfResolvable<Window> { return native_window_; } + // Get current control that mouse hovers on. This ignores the mouse-capture + // control. Even when mouse is captured by another control, this function + // return the control under cursor. You can use `GetMouseCaptureControl` to + // get more info. Control* GetMouseHoverControl() const { return mouse_hover_control_; } //*************** region: layout *************** @@ -62,7 +68,16 @@ class Window final : public ContentControl, public SelfResolvable<Window> { //*************** region: focus *************** - // Pass nullptr to release capture. + // Pass nullptr to release capture. If mouse is already capture by a control, + // this capture will fail and return false. If control is identical to the + // capturing control, capture is not changed and this function will return + // true. + // + // When capturing control changes, + // appropriate event will be sent. If mouse is not on the capturing control + // and capture is released, mouse enter event will be sent to the mouse-hover + // control. If mouse is not on the capturing control and capture is set, mouse + // leave event will be sent to the mouse-hover control. bool CaptureMouseFor(Control* control); // Return null if not captured. @@ -94,10 +109,12 @@ class Window final : public ContentControl, public SelfResolvable<Window> { //*************** region: event dispatcher helper *************** - // dispatch enter is useful when mouse is captured. void DispatchMouseHoverControlChangeEvent(Control* old_control, Control* new_control, - const Point& point); + const Point& point, bool no_leave, + bool no_enter); + + void UpdateCursor(); private: platform::native::NativeWindow* native_window_; diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 7bd33d84..50628883 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,14 +1,22 @@ #include "cru/ui/control.hpp" #include "cru/platform/native/basic_types.hpp" +#include "cru/platform/native/cursor.hpp" +#include "cru/platform/native/native_window.hpp" +#include "cru/platform/native/ui_applicaition.hpp" #include "cru/ui/base.hpp" #include "cru/ui/event/ui_event.hpp" #include "cru/ui/window.hpp" #include "routed_event_dispatch.hpp" #include <cassert> +#include <memory> namespace cru::ui { +using platform::native::Cursor; +using platform::native::SystemCursor; +using platform::native::UiApplication; + Control::Control() { MouseEnterEvent()->Direct()->AddHandler( [this](event::MouseEventArgs&) { this->is_mouse_over_ = true; }); @@ -69,18 +77,35 @@ bool Control::HasFocus() { return window->GetFocusControl() == this; } -bool Control::CaptureMouse() { - return GetWindow()->CaptureMouseFor(this); -} +bool Control::CaptureMouse() { return GetWindow()->CaptureMouseFor(this); } -bool Control::ReleaseMouse() { - return GetWindow()->CaptureMouseFor(nullptr); -} +bool Control::ReleaseMouse() { return GetWindow()->CaptureMouseFor(nullptr); } bool Control::IsMouseCaptured() { return GetWindow()->GetMouseCaptureControl() == this; } +std::shared_ptr<Cursor> Control::GetCursor() { return cursor_; } + +std::shared_ptr<Cursor> Control::GetInheritedCursor() { + Control* control = this; + while (control != nullptr) { + const auto cursor = control->GetCursor(); + if (cursor != nullptr) return cursor; + control = control->GetParent(); + } + return UiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( + SystemCursor::Arrow); +} + +void Control::SetCursor(std::shared_ptr<Cursor> cursor) { + cursor_ = std::move(cursor); + const auto window = GetWindow(); + if (window != nullptr) { + window->UpdateCursor(); + } +} + void Control::OnParentChanged(Control* old_parent, Control* new_parent) {} void Control::OnAttachToWindow(Window* window) {} diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index 42a08e33..38ce75a8 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -3,12 +3,17 @@ #include "cru/platform/graph/brush.hpp" #include "cru/platform/graph/graph_factory.hpp" +#include "cru/platform/native/cursor.hpp" #include "cru/platform/native/native_window.hpp" +#include "cru/platform/native/ui_applicaition.hpp" #include "cru/ui/render/border_render_object.hpp" #include "cru/ui/ui_manager.hpp" #include "cru/ui/window.hpp" namespace cru::ui::controls { +using platform::native::SystemCursor; +using platform::native::UiApplication; + Button::Button() : click_detector_(this) { // const auto predefined_resource = // UiManager::GetInstance()->GetPredefineResources(); @@ -39,10 +44,15 @@ Button::Button() : click_detector_(this) { } else { SetState(ButtonState::Hover); } + SetCursor(UiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( + SystemCursor::Hand)); }); - MouseLeaveEvent()->Direct()->AddHandler( - [this](event::MouseEventArgs& args) { SetState(ButtonState::Normal); }); + MouseLeaveEvent()->Direct()->AddHandler([this](event::MouseEventArgs& args) { + SetState(ButtonState::Normal); + SetCursor(UiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( + SystemCursor::Arrow)); + }); click_detector_.ClickBeginEvent()->AddHandler([this](MouseButton button) { if (button & trigger_button_) { diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 1ec34c1d..b599e479 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -11,6 +11,14 @@ namespace cru::ui { namespace { +bool IsAncestor(Control* control, Control* ancestor) { + while (control != nullptr) { + if (control == ancestor) return true; + control = control->GetParent(); + } + return false; +} + std::list<Control*> GetAncestorList(Control* control) { std::list<Control*> l; while (control != nullptr) { @@ -20,20 +28,7 @@ std::list<Control*> GetAncestorList(Control* control) { return l; } -constexpr int in_neither = 0; -constexpr int in_left = 1; -constexpr int in_right = 2; - -// if find_control is not nullptr, then find_result will be set as where the -// find_control is located. in_neither means mouse hover state of it is not -// changed. in_left means mouse move out it. in_right means mouse move in it. -// This is useful for mouse capture. -Control* FindLowestCommonAncestor(Control* left, Control* right, - Control* find_control, int* find_result) { - if (find_control) { - *find_result = in_neither; - } - +Control* FindLowestCommonAncestor(Control* left, Control* right) { if (left == nullptr || right == nullptr) return nullptr; auto&& left_list = GetAncestorList(left); @@ -49,45 +44,13 @@ Control* FindLowestCommonAncestor(Control* left, Control* right, while (true) { if (left_i == left_list.cend()) { - Control* result = *(--left_i); - while (right_i != right_list.cend()) { - if (*right_i == find_control) { - *find_result = in_right; - return result; - } - ++right_i; - } - return result; + return *(--left_i); } if (right_i == right_list.cend()) { - Control* result = *(--right_i); - while (left_i != left_list.cend()) { - if (*left_i == find_control) { - *find_result = in_left; - return result; - } - ++left_i; - } - return result; + return *(--right_i); } if (*left_i != *right_i) { - Control* result = *(--left_i); - ++left_i; - while (right_i != right_list.cend()) { - if (*right_i == find_control) { - *find_result = in_right; - return result; - } - ++right_i; - } - while (left_i != left_list.cend()) { - if (*left_i == find_control) { - *find_result = in_left; - return result; - } - ++left_i; - } - return result; + return *(--left_i); } ++left_i; ++right_i; @@ -185,19 +148,28 @@ bool Window::RequestFocusFor(Control* control) { Control* Window::GetFocusControl() { return focus_control_; } bool Window::CaptureMouseFor(Control* control) { + if (control == mouse_captured_control_) return true; + if (control == nullptr) { - if (mouse_captured_control_) { - mouse_captured_control_ = nullptr; - OnNativeMouseMove(GetNativeWindow()->GetMousePosition()); + const auto old_capture_control = mouse_captured_control_; + mouse_captured_control_ = + nullptr; // update this in case this is used in event handlers + if (old_capture_control != mouse_hover_control_) { + DispatchMouseHoverControlChangeEvent( + old_capture_control, mouse_hover_control_, + GetNativeWindow()->GetMousePosition(), true, false); } + UpdateCursor(); return true; } if (mouse_captured_control_) return false; + mouse_captured_control_ = control; - const auto c = - FindLowestCommonAncestor(control, mouse_hover_control_, nullptr, nullptr); - DispatchEvent(mouse_hover_control_, &Control::MouseLeaveEvent, c); + DispatchMouseHoverControlChangeEvent( + mouse_hover_control_, mouse_captured_control_, + GetNativeWindow()->GetMousePosition(), false, true); + UpdateCursor(); return true; } @@ -244,10 +216,22 @@ void Window::OnNativeMouseMove(const Point& point) { const auto old_control_mouse_hover = mouse_hover_control_; mouse_hover_control_ = new_control_mouse_hover; - DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, - new_control_mouse_hover, point); + if (mouse_captured_control_) { + DispatchMouseHoverControlChangeEvent( + mouse_captured_control_, new_control_mouse_hover, point, false, true); + DispatchMouseHoverControlChangeEvent( + old_control_mouse_hover, mouse_captured_control_, point, true, false); + DispatchEvent(mouse_captured_control_, &Control::MouseMoveEvent, nullptr, + point); + UpdateCursor(); + return; + } + + DispatchMouseHoverControlChangeEvent( + old_control_mouse_hover, new_control_mouse_hover, point, false, false); DispatchEvent(new_control_mouse_hover, &Control::MouseMoveEvent, nullptr, point); + UpdateCursor(); } void Window::OnNativeMouseDown( @@ -276,24 +260,27 @@ void Window::OnNativeKeyUp(int virtual_code) { void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, Control* new_control, - const Point& point) { + const Point& point, + bool no_leave, + bool no_enter) { if (new_control != old_control) // if the mouse-hover-on control changed { - int find_result; - const auto lowest_common_ancestor = FindLowestCommonAncestor( - old_control, new_control, mouse_captured_control_, &find_result); - if (old_control != nullptr && // if last mouse-hover-on control exists - (mouse_captured_control_ == nullptr || // and if mouse is not captured - find_result == in_left)) // or mouse is captured andc mouse is move - // out of capturing control + const auto lowest_common_ancestor = + FindLowestCommonAncestor(old_control, new_control); + if (!no_leave && old_control != nullptr) DispatchEvent(old_control, &Control::MouseLeaveEvent, lowest_common_ancestor); // dispatch mouse leave event. - if (new_control != nullptr && - (mouse_captured_control_ == nullptr || find_result == in_right)) { + if (!no_enter && new_control != nullptr) { DispatchEvent(new_control, &Control::MouseEnterEvent, lowest_common_ancestor, point); // dispatch mouse enter event. } } } + +void Window::UpdateCursor() { + const auto capture = GetMouseCaptureControl(); + GetNativeWindow()->SetCursor( + (capture ? capture : GetMouseHoverControl())->GetInheritedCursor()); +} } // namespace cru::ui diff --git a/src/win/native/cursor.cpp b/src/win/native/cursor.cpp index 987d2d54..8c0e393b 100644 --- a/src/win/native/cursor.cpp +++ b/src/win/native/cursor.cpp @@ -25,7 +25,7 @@ WinCursor::~WinCursor() { namespace { WinCursor* LoadWinCursor(const wchar_t* name) { - const auto handle = ::LoadCursorW(NULL, name); + const auto handle = static_cast<HCURSOR>(::LoadImageW(NULL, name, IMAGE_CURSOR, SM_CYCURSOR, SM_CYCURSOR, LR_SHARED)); if (handle == NULL) { throw Win32Error(::GetLastError(), "Failed to get system cursor."); } diff --git a/src/win/native/native_window.cpp b/src/win/native/native_window.cpp index bda84ea9..6a02c2fc 100644 --- a/src/win/native/native_window.cpp +++ b/src/win/native/native_window.cpp @@ -173,7 +173,7 @@ void WinNativeWindow::SetCursor(std::shared_ptr<Cursor> cursor) { L"Failed to set cursor. Last error code: {}.", ::GetLastError()))); }; - if (!::SetWindowLongPtrW(hwnd_, GCLP_HCURSOR, + if (!::SetClassLongPtrW(hwnd_, GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(c->GetHandle()))) { outputError(); return; |