aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-09-08 19:09:38 +0800
committer杨宇千 <crupest@outlook.com>2019-09-08 19:09:38 +0800
commit8b9a306d6f24dbc08aeee16b115260406e79126d (patch)
treedb37b8c3441a28b7150b1f40ddf79c7fa9dcda79
parent2c40085dd30d6e7370a0974ad1f642a61acc6e30 (diff)
downloadcru-8b9a306d6f24dbc08aeee16b115260406e79126d.tar.gz
cru-8b9a306d6f24dbc08aeee16b115260406e79126d.tar.bz2
cru-8b9a306d6f24dbc08aeee16b115260406e79126d.zip
...
-rw-r--r--include/cru/ui/control.hpp26
-rw-r--r--include/cru/ui/window.hpp23
-rw-r--r--src/ui/control.cpp37
-rw-r--r--src/ui/controls/button.cpp14
-rw-r--r--src/ui/window.cpp121
-rw-r--r--src/win/native/cursor.cpp2
-rw-r--r--src/win/native/native_window.cpp2
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;