aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2018-11-24 00:41:38 +0800
committercrupest <crupest@outlook.com>2018-11-24 00:41:38 +0800
commitd4658bfd97e1770e7ab4de356b3fd8c0d1999493 (patch)
treeaa91d04d7abfe776625ed2980bcae82fbfd516d0
parente8589550140d20b675fa7736441d7cdd1daee4d7 (diff)
downloadcru-d4658bfd97e1770e7ab4de356b3fd8c0d1999493.tar.gz
cru-d4658bfd97e1770e7ab4de356b3fd8c0d1999493.tar.bz2
cru-d4658bfd97e1770e7ab4de356b3fd8c0d1999493.zip
Improve hit test for clip.
-rw-r--r--CruUI-Generate/cru_ui.cpp49
-rw-r--r--CruUI-Generate/cru_ui.hpp14
-rw-r--r--src/ui/control.cpp26
-rw-r--r--src/ui/control.hpp2
-rw-r--r--src/ui/window.cpp23
-rw-r--r--src/ui/window.hpp12
6 files changed, 58 insertions, 68 deletions
diff --git a/CruUI-Generate/cru_ui.cpp b/CruUI-Generate/cru_ui.cpp
index f2cd94d3..ecfeec7d 100644
--- a/CruUI-Generate/cru_ui.cpp
+++ b/CruUI-Generate/cru_ui.cpp
@@ -1005,6 +1005,30 @@ namespace cru::ui
return false;
}
+ Control* Control::HitTest(const Point& point)
+ {
+ const auto point_inside = IsPointInside(point);
+
+ if (!point_inside && IsClipToPadding())
+ return nullptr; // if clip then don't test children.
+
+ const auto& children = GetChildren();
+
+ for (auto i = children.crbegin(); i != children.crend(); ++i)
+ {
+ const auto&& lefttop = (*i)->GetPositionRelative();
+ const auto&& coerced_point = Point(point.x - lefttop.x, point.y - lefttop.y);
+ const auto child_hit_test_result = (*i)->HitTest(coerced_point);
+ if (child_hit_test_result != nullptr)
+ return child_hit_test_result;
+ }
+
+ if (!point_inside)
+ return nullptr;
+
+ return this;
+ }
+
void Control::SetClipToPadding(const bool clip)
{
if (clip_to_padding_ == clip)
@@ -1179,7 +1203,6 @@ namespace cru::ui
child->TraverseDescendants([window](Control* control) {
control->OnAttachToWindow(window);
});
- window->RefreshControlList();
InvalidateLayout();
}
}
@@ -1191,7 +1214,6 @@ namespace cru::ui
child->TraverseDescendants([window](Control* control) {
control->OnDetachToWindow(window);
});
- window->RefreshControlList();
InvalidateLayout();
}
}
@@ -2242,7 +2264,8 @@ namespace cru::ui
return new Window(tag_popup_constructor{}, parent, caption);
}
- Window::Window(tag_overlapped_constructor) : Control(WindowConstructorTag{}, this), control_list_({ this }) {
+ Window::Window(tag_overlapped_constructor) : Control(WindowConstructorTag{}, this)
+ {
const auto window_manager = WindowManager::GetInstance();
hwnd_ = CreateWindowEx(0,
@@ -2258,7 +2281,7 @@ namespace cru::ui
AfterCreateHwnd(window_manager);
}
- Window::Window(tag_popup_constructor, Window* parent, const bool caption) : Control(WindowConstructorTag{}, this), control_list_({ this })
+ Window::Window(tag_popup_constructor, Window* parent, const bool caption) : Control(WindowConstructorTag{}, this)
{
if (parent != nullptr && !parent->IsWindowValid())
throw std::runtime_error("Parent window is not valid.");
@@ -2616,24 +2639,6 @@ namespace cru::ui
is_layout_invalid_ = false;
}
- void Window::RefreshControlList() {
- control_list_.clear();
- TraverseDescendants([this](Control* control) {
- this->control_list_.push_back(control);
- });
- }
-
- Control * Window::HitTest(const Point & point)
- {
- for (auto i = control_list_.crbegin(); i != control_list_.crend(); ++i) {
- auto control = *i;
- if (control->IsPointInside(control->WindowToControl(point))) {
- return control;
- }
- }
- return nullptr;
- }
-
bool Window::RequestFocusFor(Control * control)
{
if (control == nullptr)
diff --git a/CruUI-Generate/cru_ui.hpp b/CruUI-Generate/cru_ui.hpp
index ab070ba1..62a2158a 100644
--- a/CruUI-Generate/cru_ui.hpp
+++ b/CruUI-Generate/cru_ui.hpp
@@ -597,7 +597,6 @@ namespace cru
//--------------------------------------------------------
#include <map>
-#include <list>
#include <memory>
//--------------------------------------------------------
@@ -1619,6 +1618,8 @@ namespace cru::ui
// fill and stroke with width of border.
virtual bool IsPointInside(const Point& point);
+ // Get the top control among all descendants (including self) in local coordinate.
+ Control* HitTest(const Point& point);
//*************** region: graphic ***************
@@ -2165,15 +2166,6 @@ namespace cru::ui
void SetSizeFitContent(const Size& max_size = Size(1000, 1000));
- //*************** region: functions ***************
-
- //Refresh control list.
- //It should be invoked every time a control is added or removed from the tree.
- void RefreshControlList();
-
- //Get the most top control at "point".
- Control* HitTest(const Point& point);
-
//*************** region: focus ***************
@@ -2277,8 +2269,6 @@ namespace cru::ui
Window* parent_window_ = nullptr;
std::shared_ptr<graph::WindowRenderTarget> render_target_{};
- std::list<Control*> control_list_{};
-
Control* mouse_hover_control_ = nullptr;
bool window_focus_ = false;
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index 9afa5497..91b5aea3 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -211,6 +211,30 @@ namespace cru::ui
return false;
}
+ Control* Control::HitTest(const Point& point)
+ {
+ const auto point_inside = IsPointInside(point);
+
+ if (!point_inside && IsClipToPadding())
+ return nullptr; // if clip then don't test children.
+
+ const auto& children = GetChildren();
+
+ for (auto i = children.crbegin(); i != children.crend(); ++i)
+ {
+ const auto&& lefttop = (*i)->GetPositionRelative();
+ const auto&& coerced_point = Point(point.x - lefttop.x, point.y - lefttop.y);
+ const auto child_hit_test_result = (*i)->HitTest(coerced_point);
+ if (child_hit_test_result != nullptr)
+ return child_hit_test_result;
+ }
+
+ if (!point_inside)
+ return nullptr;
+
+ return this;
+ }
+
void Control::SetClipToPadding(const bool clip)
{
if (clip_to_padding_ == clip)
@@ -385,7 +409,6 @@ namespace cru::ui
child->TraverseDescendants([window](Control* control) {
control->OnAttachToWindow(window);
});
- window->RefreshControlList();
InvalidateLayout();
}
}
@@ -397,7 +420,6 @@ namespace cru::ui
child->TraverseDescendants([window](Control* control) {
control->OnDetachToWindow(window);
});
- window->RefreshControlList();
InvalidateLayout();
}
}
diff --git a/src/ui/control.hpp b/src/ui/control.hpp
index 4374983b..6de7f450 100644
--- a/src/ui/control.hpp
+++ b/src/ui/control.hpp
@@ -121,6 +121,8 @@ namespace cru::ui
// fill and stroke with width of border.
virtual bool IsPointInside(const Point& point);
+ // Get the top control among all descendants (including self) in local coordinate.
+ Control* HitTest(const Point& point);
//*************** region: graphic ***************
diff --git a/src/ui/window.cpp b/src/ui/window.cpp
index 87656cdc..ceabddef 100644
--- a/src/ui/window.cpp
+++ b/src/ui/window.cpp
@@ -132,7 +132,8 @@ namespace cru::ui
return new Window(tag_popup_constructor{}, parent, caption);
}
- Window::Window(tag_overlapped_constructor) : Control(WindowConstructorTag{}, this), control_list_({ this }) {
+ Window::Window(tag_overlapped_constructor) : Control(WindowConstructorTag{}, this)
+ {
const auto window_manager = WindowManager::GetInstance();
hwnd_ = CreateWindowEx(0,
@@ -148,7 +149,7 @@ namespace cru::ui
AfterCreateHwnd(window_manager);
}
- Window::Window(tag_popup_constructor, Window* parent, const bool caption) : Control(WindowConstructorTag{}, this), control_list_({ this })
+ Window::Window(tag_popup_constructor, Window* parent, const bool caption) : Control(WindowConstructorTag{}, this)
{
if (parent != nullptr && !parent->IsWindowValid())
throw std::runtime_error("Parent window is not valid.");
@@ -506,24 +507,6 @@ namespace cru::ui
is_layout_invalid_ = false;
}
- void Window::RefreshControlList() {
- control_list_.clear();
- TraverseDescendants([this](Control* control) {
- this->control_list_.push_back(control);
- });
- }
-
- Control * Window::HitTest(const Point & point)
- {
- for (auto i = control_list_.crbegin(); i != control_list_.crend(); ++i) {
- auto control = *i;
- if (control->IsPointInside(control->WindowToControl(point))) {
- return control;
- }
- }
- return nullptr;
- }
-
bool Window::RequestFocusFor(Control * control)
{
if (control == nullptr)
diff --git a/src/ui/window.hpp b/src/ui/window.hpp
index d98e60e2..a3fba57a 100644
--- a/src/ui/window.hpp
+++ b/src/ui/window.hpp
@@ -2,7 +2,6 @@
#include "system_headers.hpp"
#include <map>
-#include <list>
#include <memory>
#include "control.hpp"
@@ -208,15 +207,6 @@ namespace cru::ui
void SetSizeFitContent(const Size& max_size = Size(1000, 1000));
- //*************** region: functions ***************
-
- //Refresh control list.
- //It should be invoked every time a control is added or removed from the tree.
- void RefreshControlList();
-
- //Get the most top control at "point".
- Control* HitTest(const Point& point);
-
//*************** region: focus ***************
@@ -320,8 +310,6 @@ namespace cru::ui
Window* parent_window_ = nullptr;
std::shared_ptr<graph::WindowRenderTarget> render_target_{};
- std::list<Control*> control_list_{};
-
Control* mouse_hover_control_ = nullptr;
bool window_focus_ = false;