aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json4
-rw-r--r--include/cru/ui/base.hpp1
-rw-r--r--include/cru/ui/control.hpp16
-rw-r--r--include/cru/ui/render/render_object.hpp35
-rw-r--r--include/cru/ui/render/window_render_object.hpp10
-rw-r--r--include/cru/ui/ui_host.hpp127
-rw-r--r--include/cru/ui/window.hpp98
-rw-r--r--src/ui/CMakeLists.txt4
-rw-r--r--src/ui/content_control.cpp6
-rw-r--r--src/ui/control.cpp68
-rw-r--r--src/ui/layout_control.cpp4
-rw-r--r--src/ui/render/render_object.cpp16
-rw-r--r--src/ui/render/window_render_object.cpp65
-rw-r--r--src/ui/ui_host.cpp360
-rw-r--r--src/ui/window.cpp340
15 files changed, 584 insertions, 570 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9b38e418..7f06bdfc 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -69,6 +69,8 @@
"gsl_util": "cpp",
"multi_span": "cpp",
"span": "cpp",
- "string_span": "cpp"
+ "string_span": "cpp",
+ "numeric": "cpp",
+ "pointers": "cpp"
}
}
diff --git a/include/cru/ui/base.hpp b/include/cru/ui/base.hpp
index fbb87c8c..a1335f90 100644
--- a/include/cru/ui/base.hpp
+++ b/include/cru/ui/base.hpp
@@ -183,6 +183,7 @@ using cru::platform::colors::yellowgreen;
class Window;
class Control;
class ClickDetector;
+class UiHost;
//-------------------- region: basic types --------------------
namespace internal {
diff --git a/include/cru/ui/control.hpp b/include/cru/ui/control.hpp
index f0475dea..30dc589a 100644
--- a/include/cru/ui/control.hpp
+++ b/include/cru/ui/control.hpp
@@ -9,7 +9,7 @@
namespace cru::ui {
class Control : public Object {
- friend class Window;
+ friend UiHost;
protected:
Control();
@@ -26,8 +26,8 @@ class Control : public Object {
//*************** region: tree ***************
public:
- // Get the window if attached, otherwise, return nullptr.
- Window* GetWindow() const { return window_; }
+ // Get the ui host if attached, otherwise, return nullptr.
+ UiHost* GetUiHost() const { return ui_host_; }
Control* GetParent() const { return parent_; }
@@ -37,7 +37,7 @@ class Control : public Object {
void TraverseDescendants(const std::function<void(Control*)>& predicate);
void _SetParent(Control* parent);
- void _SetDescendantWindow(Window* window);
+ void _SetDescendantUiHost(UiHost* host);
private:
static void _TraverseDescendants(
@@ -111,7 +111,6 @@ class Control : public Object {
event::RoutedEvent<event::KeyEventArgs>* KeyUpEvent() {
return &key_up_event_;
}
- event::RoutedEvent<event::CharEventArgs>* CharEvent() { return &char_event_; }
event::RoutedEvent<event::FocusChangeEventArgs>* GainFocusEvent() {
return &gain_focus_event_;
}
@@ -129,7 +128,6 @@ class Control : public Object {
event::RoutedEvent<event::KeyEventArgs> key_down_event_;
event::RoutedEvent<event::KeyEventArgs> key_up_event_;
- event::RoutedEvent<event::CharEventArgs> char_event_;
event::RoutedEvent<event::FocusChangeEventArgs> gain_focus_event_;
event::RoutedEvent<event::FocusChangeEventArgs> lose_focus_event_;
@@ -137,13 +135,13 @@ class Control : public Object {
//*************** region: tree ***************
protected:
virtual void OnParentChanged(Control* old_parent, Control* new_parent);
- virtual void OnAttachToWindow(Window* window);
- virtual void OnDetachToWindow(Window* window);
+ virtual void OnAttachToHost(UiHost* host);
+ virtual void OnDetachFromHost(UiHost* host);
virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) }
private:
- Window* window_ = nullptr;
+ UiHost* ui_host_ = nullptr;
Control* parent_ = nullptr;
private:
diff --git a/include/cru/ui/render/render_object.hpp b/include/cru/ui/render/render_object.hpp
index 33ef3d1a..6a8db52f 100644
--- a/include/cru/ui/render/render_object.hpp
+++ b/include/cru/ui/render/render_object.hpp
@@ -4,27 +4,12 @@
#include "cru/common/event.hpp"
namespace cru::ui::render {
-
-struct AfterLayoutEventArgs {};
-
-struct IRenderHost : Interface {
- // Mark the layout as invalid, and arrange a re-layout later.
- // Note this method might be called more than one times in a message cycle. So
- // implementation should merge multiple request into once.
- virtual void InvalidateLayout() = 0;
-
- // Mark the paint as invalid, and arrange a re-paint later.
- // Note this method might be called more than one times in a message cycle. So
- // implementation should merge multiple request into once.
- virtual void InvalidatePaint() = 0;
-
- virtual IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() = 0;
-};
-
// Render object will not destroy its children when destroyed. Control must
// manage lifecycle of its render objects. Since control will destroy its
// children when destroyed, render objects will be destroyed along with it.
class RenderObject : public Object {
+ friend WindowRenderObject;
+
protected:
enum class ChildMode {
None,
@@ -47,8 +32,7 @@ class RenderObject : public Object {
Control* GetAttachedControl() const { return control_; }
void SetAttachedControl(Control* new_control) { control_ = new_control; }
- IRenderHost* GetRenderHost() const { return render_host_; }
- void SetRenderHost(IRenderHost* render_host) { render_host_ = render_host; }
+ UiHost* GetUiHost() const { return ui_host_; }
RenderObject* GetParent() const { return parent_; }
@@ -83,13 +67,8 @@ class RenderObject : public Object {
virtual RenderObject* HitTest(const Point& point) = 0;
public:
- void InvalidateLayout() const {
- if (render_host_ != nullptr) render_host_->InvalidateLayout();
- }
-
- void InvalidatePaint() const {
- if (render_host_ != nullptr) render_host_->InvalidatePaint();
- }
+ void InvalidateLayout();
+ void InvalidatePaint();
protected:
void SetChildMode(ChildMode mode) { child_mode_ = mode; }
@@ -117,11 +96,11 @@ class RenderObject : public Object {
private:
void SetParent(RenderObject* new_parent);
- void SetRenderHostRecursive(IRenderHost* host);
+ void SetRenderHostRecursive(UiHost* host);
private:
Control* control_ = nullptr;
- IRenderHost* render_host_ = nullptr;
+ UiHost* ui_host_ = nullptr;
RenderObject* parent_ = nullptr;
std::vector<RenderObject*> children_{};
diff --git a/include/cru/ui/render/window_render_object.hpp b/include/cru/ui/render/window_render_object.hpp
index 2f7f1e84..76b17b82 100644
--- a/include/cru/ui/render/window_render_object.hpp
+++ b/include/cru/ui/render/window_render_object.hpp
@@ -4,17 +4,13 @@
namespace cru::ui::render {
class WindowRenderObject : public RenderObject {
public:
- WindowRenderObject(Window* window);
+ WindowRenderObject(UiHost* host);
WindowRenderObject(const WindowRenderObject& other) = delete;
WindowRenderObject(WindowRenderObject&& other) = delete;
WindowRenderObject& operator=(const WindowRenderObject& other) = delete;
WindowRenderObject& operator=(WindowRenderObject&& other) = delete;
~WindowRenderObject() override = default;
- Window* GetWindow() const { return window_; }
-
- void Relayout();
-
void Draw(platform::graph::IPainter* painter) override;
RenderObject* HitTest(const Point& point) override;
@@ -29,10 +25,6 @@ class WindowRenderObject : public RenderObject {
}
private:
- Window* window_;
-
EventRevokerGuard after_layout_event_guard_;
-
- std::unique_ptr<IRenderHost> render_host_;
};
} // namespace cru::ui::render
diff --git a/include/cru/ui/ui_host.hpp b/include/cru/ui/ui_host.hpp
new file mode 100644
index 00000000..96026675
--- /dev/null
+++ b/include/cru/ui/ui_host.hpp
@@ -0,0 +1,127 @@
+#pragma once
+#include "base.hpp"
+
+#include "cru/common/event.hpp"
+#include "cru/common/self_resolvable.hpp"
+#include "render/base.hpp"
+
+namespace cru::ui {
+struct AfterLayoutEventArgs {};
+
+class UiHost : public Object, public SelfResolvable<UiHost> {
+ public:
+ // This will create root window render object and attach it to window.
+ // It will also create and manage a native window.
+ UiHost(Window* window);
+
+ CRU_DELETE_COPY(UiHost)
+ CRU_DELETE_MOVE(UiHost)
+
+ ~UiHost() override;
+
+ public:
+ // Mark the layout as invalid, and arrange a re-layout later.
+ // This method could be called more than one times in a message cycle. But
+ // layout only takes place once.
+ void InvalidateLayout();
+
+ // Mark the paint as invalid, and arrange a re-paint later.
+ // This method could be called more than one times in a message cycle. But
+ // paint only takes place once.
+ void InvalidatePaint();
+
+ IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() {
+ return &after_layout_event_;
+ }
+
+ void Relayout();
+
+ // 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: focus ***************
+
+ // Request focus for specified control.
+ bool RequestFocusFor(Control* control);
+
+ // Get the control that has focus.
+ Control* GetFocusControl();
+
+ //*************** region: focus ***************
+
+ // 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.
+ Control* GetMouseCaptureControl();
+
+ Control* HitTest(const Point& point);
+
+ void UpdateCursor();
+
+ private:
+ //*************** region: native messages ***************
+ void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t);
+ void OnNativePaint(platform::native::INativeWindow* window, std::nullptr_t);
+ void OnNativeResize(platform::native::INativeWindow* window,
+ const Size& size);
+
+ void OnNativeFocus(platform::native::INativeWindow* window,
+ cru::platform::native::FocusChangeType focus);
+
+ void OnNativeMouseEnterLeave(
+ platform::native::INativeWindow* window,
+ cru::platform::native::MouseEnterLeaveType enter);
+ void OnNativeMouseMove(platform::native::INativeWindow* window,
+ const Point& point);
+ void OnNativeMouseDown(
+ platform::native::INativeWindow* window,
+ const platform::native::NativeMouseButtonEventArgs& args);
+ void OnNativeMouseUp(
+ platform::native::INativeWindow* window,
+ const platform::native::NativeMouseButtonEventArgs& args);
+
+ void OnNativeKeyDown(platform::native::INativeWindow* window,
+ const platform::native::NativeKeyEventArgs& args);
+ void OnNativeKeyUp(platform::native::INativeWindow* window,
+ const platform::native::NativeKeyEventArgs& args);
+
+ //*************** region: event dispatcher helper ***************
+
+ void DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point, bool no_leave,
+ bool no_enter);
+
+ private:
+ bool need_layout_ = false;
+
+ Event<AfterLayoutEventArgs> after_layout_event_;
+
+ std::shared_ptr<platform::native::INativeWindowResolver>
+ native_window_resolver_;
+
+ std::vector<EventRevokerGuard> event_revoker_guards_;
+
+ Window* window_control_;
+ std::unique_ptr<render::WindowRenderObject> root_render_object_;
+
+ Control* mouse_hover_control_;
+
+ Control* focus_control_; // "focus_control_" can't be nullptr
+
+ Control* mouse_captured_control_;
+};
+} // namespace cru::ui
diff --git a/include/cru/ui/window.hpp b/include/cru/ui/window.hpp
index 86112189..f15e605b 100644
--- a/include/cru/ui/window.hpp
+++ b/include/cru/ui/window.hpp
@@ -1,13 +1,9 @@
#pragma once
#include "content_control.hpp"
-#include "cru/common/self_resolvable.hpp"
-
namespace cru::ui {
-// TODO: Make Window able to be invalid and handle operations in invalidity
-// situation.
-class Window final : public ContentControl, public SelfResolvable<Window> {
- friend class Control;
+class Window final : public ContentControl {
+ friend UiHost;
public:
static constexpr std::string_view control_type = "Window";
@@ -32,96 +28,18 @@ class Window final : public ContentControl, public SelfResolvable<Window> {
render::RenderObject* GetRenderObject() const override;
- platform::native::INativeWindow* ResolveNativeWindow();
-
- // 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: focus ***************
-
- // Request focus for specified control.
- bool RequestFocusFor(Control* control);
-
- // Get the control that has focus.
- Control* GetFocusControl();
-
- //*************** region: focus ***************
-
- // 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.
- Control* GetMouseCaptureControl();
+ bool IsRetainAfterDestroy() { return retain_after_destroy_; }
+ void SetRetainAfterDestroy(bool destroy) { retain_after_destroy_ = destroy; }
protected:
void OnChildChanged(Control* old_child, Control* new_child) override;
private:
- Control* HitTest(const Point& point);
-
- //*************** region: native messages ***************
-
- void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t);
- void OnNativePaint(platform::native::INativeWindow* window, std::nullptr_t);
- void OnNativeResize(platform::native::INativeWindow* window,
- const Size& size);
-
- void OnNativeFocus(platform::native::INativeWindow* window,
- cru::platform::native::FocusChangeType focus);
-
- void OnNativeMouseEnterLeave(
- platform::native::INativeWindow* window,
- cru::platform::native::MouseEnterLeaveType enter);
- void OnNativeMouseMove(platform::native::INativeWindow* window,
- const Point& point);
- void OnNativeMouseDown(
- platform::native::INativeWindow* window,
- const platform::native::NativeMouseButtonEventArgs& args);
- void OnNativeMouseUp(
- platform::native::INativeWindow* window,
- const platform::native::NativeMouseButtonEventArgs& args);
-
- void OnNativeKeyDown(platform::native::INativeWindow* window,
- const platform::native::NativeKeyEventArgs& args);
- void OnNativeKeyUp(platform::native::INativeWindow* window,
- const platform::native::NativeKeyEventArgs& args);
- void OnNativeChar(platform::native::INativeWindow* window, std::string c);
-
- //*************** region: event dispatcher helper ***************
-
- void DispatchMouseHoverControlChangeEvent(Control* old_control,
- Control* new_control,
- const Point& point, bool no_leave,
- bool no_enter);
-
- void UpdateCursor();
-
- private:
- std::shared_ptr<platform::native::INativeWindowResolver>
- native_window_resolver_;
-
- std::vector<EventRevokerGuard> event_revoker_guards_;
-
- std::unique_ptr<render::WindowRenderObject> render_object_;
-
- Control* mouse_hover_control_;
-
- Control* focus_control_; // "focus_control_" can't be nullptr
+ std::unique_ptr<UiHost> managed_ui_host_;
- Control* mouse_captured_control_;
+ // UiHost is responsible to take care of lifetime of this.
+ render::WindowRenderObject* render_object_;
- bool need_layout_ = false;
+ bool retain_after_destroy_ = false;
};
} // namespace cru::ui
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index e132f3df..a8fd46a5 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -9,7 +9,8 @@ add_library(cru_ui STATIC
control.cpp
helper.cpp
layout_control.cpp
- no_child_control.cpp
+ no_child_control.cpp
+ ui_host.cpp
ui_manager.cpp
window.cpp
controls/button.cpp
@@ -36,6 +37,7 @@ target_sources(cru_ui PUBLIC
${CRU_UI_INCLUDE_DIR}/layout_control.hpp
${CRU_UI_INCLUDE_DIR}/no_child_control.hpp
${CRU_UI_INCLUDE_DIR}/ui_event.hpp
+ ${CRU_UI_INCLUDE_DIR}/ui_host.hpp
${CRU_UI_INCLUDE_DIR}/ui_manager.hpp
${CRU_UI_INCLUDE_DIR}/window.hpp
${CRU_UI_INCLUDE_DIR}/controls/base.hpp
diff --git a/src/ui/content_control.cpp b/src/ui/content_control.cpp
index 5dcd89d9..eb13f4cb 100644
--- a/src/ui/content_control.cpp
+++ b/src/ui/content_control.cpp
@@ -12,16 +12,16 @@ void ContentControl::SetChild(Control* child) {
Expects(!dynamic_cast<Window*>(child)); // Can't add a window as child.
if (child == child_) return;
- const auto window = GetWindow();
+ const auto host = GetUiHost();
const auto old_child = child_;
child_ = child;
if (old_child) {
old_child->_SetParent(nullptr);
- old_child->_SetDescendantWindow(nullptr);
+ old_child->_SetDescendantUiHost(nullptr);
}
if (child) {
child->_SetParent(this);
- child->_SetDescendantWindow(window);
+ child->_SetDescendantUiHost(host);
}
OnChildChanged(old_child, child);
}
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index 180ba476..7691dac2 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -2,9 +2,8 @@
#include "cru/platform/native/cursor.hpp"
#include "cru/platform/native/ui_application.hpp"
-#include "cru/platform/native/window.hpp"
#include "cru/ui/base.hpp"
-#include "cru/ui/window.hpp"
+#include "cru/ui/ui_host.hpp"
#include "routed_event_dispatch.hpp"
#include <memory>
@@ -33,23 +32,23 @@ void Control::_SetParent(Control* parent) {
if (old_parent != new_parent) OnParentChanged(old_parent, new_parent);
}
-void Control::_SetDescendantWindow(Window* window) {
- if (window == nullptr && window_ == nullptr) return;
-
+void Control::_SetDescendantUiHost(UiHost* host) {
// You can only attach or detach window.
- Expects((window != nullptr && window_ == nullptr) ||
- (window == nullptr && window_ != nullptr));
+ Expects((host != nullptr && ui_host_ == nullptr) ||
+ (host == nullptr && ui_host_ != nullptr));
+
+ if (host == nullptr && ui_host_ == nullptr) return;
- if (window == nullptr) {
- const auto old = window_;
+ if (host == nullptr) {
+ const auto old = ui_host_;
TraverseDescendants([old](Control* control) {
- control->window_ = nullptr;
- control->OnDetachToWindow(old);
+ control->ui_host_ = nullptr;
+ control->OnDetachFromHost(old);
});
} else
- TraverseDescendants([window](Control* control) {
- control->window_ = window;
- control->OnAttachToWindow(window);
+ TraverseDescendants([host](Control* control) {
+ control->ui_host_ = host;
+ control->OnAttachToHost(host);
});
}
@@ -65,25 +64,38 @@ void Control::_TraverseDescendants(
}
bool Control::RequestFocus() {
- auto window = GetWindow();
- if (window == nullptr) return false;
+ auto host = GetUiHost();
+ if (host == nullptr) return false;
- return window->RequestFocusFor(this);
+ return host->RequestFocusFor(this);
}
bool Control::HasFocus() {
- auto window = GetWindow();
- if (window == nullptr) return false;
+ auto host = GetUiHost();
+ if (host == nullptr) return false;
- return window->GetFocusControl() == this;
+ return host->GetFocusControl() == this;
}
-bool Control::CaptureMouse() { return GetWindow()->CaptureMouseFor(this); }
+bool Control::CaptureMouse() {
+ auto host = GetUiHost();
+ if (host == nullptr) return false;
+
+ return host->CaptureMouseFor(this);
+}
-bool Control::ReleaseMouse() { return GetWindow()->CaptureMouseFor(nullptr); }
+bool Control::ReleaseMouse() {
+ auto host = GetUiHost();
+ if (host == nullptr) return false;
+
+ return host->CaptureMouseFor(nullptr);
+}
bool Control::IsMouseCaptured() {
- return GetWindow()->GetMouseCaptureControl() == this;
+ auto host = GetUiHost();
+ if (host == nullptr) return false;
+
+ return host->GetMouseCaptureControl() == this;
}
std::shared_ptr<ICursor> Control::GetCursor() { return cursor_; }
@@ -101,9 +113,9 @@ std::shared_ptr<ICursor> Control::GetInheritedCursor() {
void Control::SetCursor(std::shared_ptr<ICursor> cursor) {
cursor_ = std::move(cursor);
- const auto window = GetWindow();
- if (window != nullptr) {
- window->UpdateCursor();
+ const auto host = GetUiHost();
+ if (host != nullptr) {
+ host->UpdateCursor();
}
}
@@ -112,7 +124,7 @@ void Control::OnParentChanged(Control* old_parent, Control* new_parent) {
CRU_UNUSED(new_parent)
}
-void Control::OnAttachToWindow(Window* window) { CRU_UNUSED(window) }
+void Control::OnAttachToHost(UiHost* host) { CRU_UNUSED(host) }
-void Control::OnDetachToWindow(Window* window) { CRU_UNUSED(window) }
+void Control::OnDetachFromHost(UiHost* host) { CRU_UNUSED(host) }
} // namespace cru::ui
diff --git a/src/ui/layout_control.cpp b/src/ui/layout_control.cpp
index 90a825ff..1d5d1ede 100644
--- a/src/ui/layout_control.cpp
+++ b/src/ui/layout_control.cpp
@@ -19,7 +19,7 @@ void LayoutControl::AddChild(Control* control, const Index position) {
children_.insert(this->children_.cbegin() + position, control);
control->_SetParent(this);
- control->_SetDescendantWindow(GetWindow());
+ control->_SetDescendantUiHost(GetUiHost());
OnAddChild(control, position);
}
@@ -36,7 +36,7 @@ void LayoutControl::RemoveChild(const Index position) {
children_.erase(i);
child->_SetParent(nullptr);
- child->_SetDescendantWindow(nullptr);
+ child->_SetDescendantUiHost(nullptr);
OnRemoveChild(child, position);
}
diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp
index e329d150..4a1bedf3 100644
--- a/src/ui/render/render_object.cpp
+++ b/src/ui/render/render_object.cpp
@@ -1,6 +1,7 @@
#include "cru/ui/render/render_object.hpp"
#include "cru/common/logger.hpp"
+#include "cru/ui/ui_host.hpp"
#include <algorithm>
@@ -18,7 +19,7 @@ void RenderObject::AddChild(RenderObject* render_object, const Index position) {
children_.insert(children_.cbegin() + position, render_object);
render_object->SetParent(this);
- render_object->SetRenderHostRecursive(GetRenderHost());
+ render_object->SetRenderHostRecursive(GetUiHost());
OnAddChild(render_object, position);
}
@@ -165,6 +166,14 @@ void RenderObject::SetParent(RenderObject* new_parent) {
OnParentChanged(old_parent, new_parent);
}
+void RenderObject::InvalidateLayout() {
+ if (ui_host_ != nullptr) ui_host_->InvalidateLayout();
+}
+
+void RenderObject::InvalidatePaint() {
+ if (ui_host_ != nullptr) ui_host_->InvalidatePaint();
+}
+
void RenderObject::NotifyAfterLayoutRecursive(RenderObject* render_object) {
render_object->OnAfterLayout();
for (const auto o : render_object->GetChildren()) {
@@ -172,11 +181,10 @@ void RenderObject::NotifyAfterLayoutRecursive(RenderObject* render_object) {
}
}
-void RenderObject::SetRenderHostRecursive(IRenderHost* host) {
- SetRenderHost(host);
+void RenderObject::SetRenderHostRecursive(UiHost* host) {
+ ui_host_ = host;
for (const auto child : GetChildren()) {
child->SetRenderHostRecursive(host);
}
}
-
} // namespace cru::ui::render
diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp
index 29313645..abed6fa4 100644
--- a/src/ui/render/window_render_object.cpp
+++ b/src/ui/render/window_render_object.cpp
@@ -1,74 +1,17 @@
#include "cru/ui/render/window_render_object.hpp"
#include "../helper.hpp"
-#include "cru/common/logger.hpp"
#include "cru/platform/graph/util/painter.hpp"
-#include "cru/platform/native/ui_application.hpp"
-#include "cru/platform/native/window.hpp"
-#include "cru/ui/window.hpp"
+#include "cru/ui/ui_host.hpp"
namespace cru::ui::render {
-class WindowRenderHost : public IRenderHost,
- public SelfResolvable<WindowRenderHost> {
- public:
- WindowRenderHost(WindowRenderObject* render_object)
- : render_object_(render_object) {
- Expects(render_object != nullptr);
- }
-
- void InvalidateLayout() override;
-
- void InvalidatePaint() override {
- if (const auto native_window =
- render_object_->GetWindow()->ResolveNativeWindow())
- native_window->RequestRepaint();
- }
-
- IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() override {
- return &after_layout_event_;
- }
-
- private:
- WindowRenderObject* render_object_;
-
- bool need_layout_ = false;
-
- Event<AfterLayoutEventArgs> after_layout_event_;
-};
-
-void WindowRenderHost::InvalidateLayout() {
- if (!need_layout_) {
- log::Debug("A relayout is required.");
- GetUiApplication()->InvokeLater([resolver = this->CreateResolver()] {
- if (const auto host = resolver.Resolve()) {
- host->render_object_->Relayout();
- host->need_layout_ = false;
- host->after_layout_event_.Raise(AfterLayoutEventArgs{});
- log::Debug("A relayout finished.");
- host->InvalidatePaint();
- }
- });
- need_layout_ = true;
- }
-}
-
-WindowRenderObject::WindowRenderObject(Window* window)
- : window_(window), render_host_(new WindowRenderHost(this)) {
+WindowRenderObject::WindowRenderObject(UiHost* host) {
SetChildMode(ChildMode::Single);
- SetRenderHost(render_host_.get());
- after_layout_event_guard_.Reset(render_host_->AfterLayoutEvent()->AddHandler(
+ ui_host_ = host;
+ after_layout_event_guard_.Reset(host->AfterLayoutEvent()->AddHandler(
[this](auto) { NotifyAfterLayoutRecursive(this); }));
}
-void WindowRenderObject::Relayout() {
- const auto native_window = window_->ResolveNativeWindow();
- const auto client_size = native_window
- ? native_window->GetClientSize()
- : Size{100, 100}; // a reasonable assumed size
- Measure(client_size);
- Layout(Rect{Point{}, client_size});
-}
-
void WindowRenderObject::Draw(platform::graph::IPainter* painter) {
painter->Clear(colors::white);
if (const auto child = GetChild()) {
diff --git a/src/ui/ui_host.cpp b/src/ui/ui_host.cpp
new file mode 100644
index 00000000..c82709dd
--- /dev/null
+++ b/src/ui/ui_host.cpp
@@ -0,0 +1,360 @@
+#include "cru/ui/ui_host.hpp"
+
+#include "cru/common/logger.hpp"
+#include "cru/platform/graph/painter.hpp"
+#include "cru/platform/native/ui_application.hpp"
+#include "cru/platform/native/window.hpp"
+#include "cru/ui/render/window_render_object.hpp"
+#include "cru/ui/window.hpp"
+#include "routed_event_dispatch.hpp"
+
+namespace cru::ui {
+using platform::native::INativeWindow;
+using platform::native::IUiApplication;
+
+namespace event_names {
+#ifdef CRU_DEBUG
+#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = #name;
+#else
+#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = "";
+#endif
+
+CRU_DEFINE_EVENT_NAME(LoseFocus)
+CRU_DEFINE_EVENT_NAME(GainFocus)
+CRU_DEFINE_EVENT_NAME(MouseEnter)
+CRU_DEFINE_EVENT_NAME(MouseLeave)
+CRU_DEFINE_EVENT_NAME(MouseMove)
+CRU_DEFINE_EVENT_NAME(MouseDown)
+CRU_DEFINE_EVENT_NAME(MouseUp)
+CRU_DEFINE_EVENT_NAME(KeyDown)
+CRU_DEFINE_EVENT_NAME(KeyUp)
+CRU_DEFINE_EVENT_NAME(Char)
+
+#undef CRU_DEFINE_EVENT_NAME
+} // namespace event_names
+
+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) {
+ l.push_front(control);
+ control = control->GetParent();
+ }
+ return l;
+}
+
+Control* FindLowestCommonAncestor(Control* left, Control* right) {
+ if (left == nullptr || right == nullptr) return nullptr;
+
+ auto&& left_list = GetAncestorList(left);
+ auto&& right_list = GetAncestorList(right);
+
+ // the root is different
+ if (left_list.front() != right_list.front()) return nullptr;
+
+ // find the last same control or the last control (one is ancestor of the
+ // other)
+ auto left_i = left_list.cbegin();
+ auto right_i = right_list.cbegin();
+
+ while (true) {
+ if (left_i == left_list.cend()) {
+ return *(--left_i);
+ }
+ if (right_i == right_list.cend()) {
+ return *(--right_i);
+ }
+ if (*left_i != *right_i) {
+ return *(--left_i);
+ }
+ ++left_i;
+ ++right_i;
+ }
+}
+} // namespace
+
+namespace {
+template <typename T>
+inline void BindNativeEvent(
+ UiHost* host, INativeWindow* native_window, IEvent<T>* event,
+ void (UiHost::*handler)(INativeWindow*, typename IEvent<T>::EventArgs),
+ std::vector<EventRevokerGuard>& guard_pool) {
+ guard_pool.push_back(EventRevokerGuard(event->AddHandler(
+ std::bind(handler, host, native_window, std::placeholders::_1))));
+}
+} // namespace
+
+UiHost::UiHost(Window* window)
+ : mouse_hover_control_(nullptr),
+ focus_control_(window),
+ mouse_captured_control_(nullptr),
+ window_control_(window) {
+ native_window_resolver_ =
+ IUiApplication::GetInstance()->CreateWindow(nullptr);
+
+ const auto native_window = native_window_resolver_->Resolve();
+ window->_SetDescendantUiHost(this);
+
+ root_render_object_ = std::make_unique<render::WindowRenderObject>(this);
+ root_render_object_->SetAttachedControl(window);
+ window->render_object_ = root_render_object_.get();
+
+ BindNativeEvent(this, native_window, native_window->DestroyEvent(),
+ &UiHost::OnNativeDestroy, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->PaintEvent(),
+ &UiHost::OnNativePaint, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->ResizeEvent(),
+ &UiHost::OnNativeResize, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->FocusEvent(),
+ &UiHost::OnNativeFocus, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->MouseEnterLeaveEvent(),
+ &UiHost::OnNativeMouseEnterLeave, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->MouseMoveEvent(),
+ &UiHost::OnNativeMouseMove, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->MouseDownEvent(),
+ &UiHost::OnNativeMouseDown, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->MouseUpEvent(),
+ &UiHost::OnNativeMouseUp, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->KeyDownEvent(),
+ &UiHost::OnNativeKeyDown, event_revoker_guards_);
+ BindNativeEvent(this, native_window, native_window->KeyUpEvent(),
+ &UiHost::OnNativeKeyUp, event_revoker_guards_);
+}
+
+UiHost::~UiHost() {
+ window_control_->TraverseDescendants(
+ [this](Control* control) { control->OnDetachFromHost(this); });
+}
+
+void UiHost::InvalidatePaint() {
+ if (const auto native_window = native_window_resolver_->Resolve())
+ native_window->RequestRepaint();
+}
+
+void UiHost::InvalidateLayout() {
+ if (!need_layout_) {
+ platform::native::IUiApplication::GetInstance()->InvokeLater(
+ [resolver = this->CreateResolver()] {
+ if (const auto host = resolver.Resolve()) {
+ host->Relayout();
+ host->need_layout_ = false;
+ host->after_layout_event_.Raise(AfterLayoutEventArgs{});
+ log::Debug("A relayout finished.");
+ host->InvalidatePaint();
+ }
+ });
+ need_layout_ = true;
+ }
+}
+
+void UiHost::Relayout() {
+ const auto native_window = native_window_resolver_->Resolve();
+ const auto client_size = native_window
+ ? native_window->GetClientSize()
+ : Size{100, 100}; // a reasonable assumed size
+ root_render_object_->Measure(client_size);
+ root_render_object_->Layout(Rect{Point{}, client_size});
+}
+
+bool UiHost::RequestFocusFor(Control* control) {
+ Expects(control != nullptr); // The control to request focus can't be null.
+ // You can set it as the window.
+
+ if (focus_control_ == control) return true;
+
+ DispatchEvent(event_names::LoseFocus, focus_control_,
+ &Control::LoseFocusEvent, nullptr, false);
+
+ focus_control_ = control;
+
+ DispatchEvent(event_names::GainFocus, control, &Control::GainFocusEvent,
+ nullptr, false);
+
+ return true;
+}
+
+Control* UiHost::GetFocusControl() { return focus_control_; }
+
+bool UiHost::CaptureMouseFor(Control* control) {
+ const auto native_window = native_window_resolver_->Resolve();
+ if (!native_window) return false;
+
+ if (control == mouse_captured_control_) return true;
+
+ if (control == nullptr) {
+ 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_,
+ native_window->GetMousePosition(), true, false);
+ }
+ UpdateCursor();
+ return true;
+ }
+
+ if (mouse_captured_control_) return false;
+
+ mouse_captured_control_ = control;
+ DispatchMouseHoverControlChangeEvent(
+ mouse_hover_control_, mouse_captured_control_,
+ native_window->GetMousePosition(), false, true);
+ UpdateCursor();
+ return true;
+}
+
+Control* UiHost::GetMouseCaptureControl() { return mouse_captured_control_; }
+
+void UiHost::OnNativeDestroy(INativeWindow* window, std::nullptr_t) {
+ CRU_UNUSED(window)
+ delete this; // TODO: Develop this.
+}
+
+void UiHost::OnNativePaint(INativeWindow* window, std::nullptr_t) {
+ auto painter = window->BeginPaint();
+ root_render_object_->Draw(painter.get());
+ painter->EndDraw();
+}
+
+void UiHost::OnNativeResize(INativeWindow* window, const Size& size) {
+ CRU_UNUSED(window)
+ CRU_UNUSED(size)
+
+ InvalidateLayout();
+}
+
+void UiHost::OnNativeFocus(INativeWindow* window,
+ platform::native::FocusChangeType focus) {
+ CRU_UNUSED(window)
+
+ focus == platform::native::FocusChangeType::Gain
+ ? DispatchEvent(event_names::GainFocus, focus_control_,
+ &Control::GainFocusEvent, nullptr, true)
+ : DispatchEvent(event_names::LoseFocus, focus_control_,
+ &Control::LoseFocusEvent, nullptr, true);
+}
+
+void UiHost::OnNativeMouseEnterLeave(
+ INativeWindow* window, platform::native::MouseEnterLeaveType type) {
+ CRU_UNUSED(window)
+
+ if (type == platform::native::MouseEnterLeaveType::Leave) {
+ DispatchEvent(event_names::MouseLeave, mouse_hover_control_,
+ &Control::MouseLeaveEvent, nullptr);
+ mouse_hover_control_ = nullptr;
+ }
+}
+
+void UiHost::OnNativeMouseMove(INativeWindow* window, const Point& point) {
+ CRU_UNUSED(window)
+
+ // Find the first control that hit test succeed.
+ const auto new_mouse_hover_control = HitTest(point);
+ const auto old_mouse_hover_control = mouse_hover_control_;
+ mouse_hover_control_ = new_mouse_hover_control;
+
+ if (mouse_captured_control_) {
+ const auto n = FindLowestCommonAncestor(new_mouse_hover_control,
+ mouse_captured_control_);
+ const auto o = FindLowestCommonAncestor(old_mouse_hover_control,
+ mouse_captured_control_);
+ bool a = IsAncestor(o, n);
+ if (a) {
+ DispatchEvent(event_names::MouseLeave, o, &Control::MouseLeaveEvent, n);
+ } else {
+ DispatchEvent(event_names::MouseEnter, n, &Control::MouseEnterEvent, o,
+ point);
+ }
+ DispatchEvent(event_names::MouseMove, mouse_captured_control_,
+ &Control::MouseMoveEvent, nullptr, point);
+ UpdateCursor();
+ return;
+ }
+
+ DispatchMouseHoverControlChangeEvent(
+ old_mouse_hover_control, new_mouse_hover_control, point, false, false);
+ DispatchEvent(event_names::MouseMove, new_mouse_hover_control,
+ &Control::MouseMoveEvent, nullptr, point);
+ UpdateCursor();
+}
+
+void UiHost::OnNativeMouseDown(
+ INativeWindow* window,
+ const platform::native::NativeMouseButtonEventArgs& args) {
+ CRU_UNUSED(window)
+
+ Control* control =
+ mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
+ DispatchEvent(event_names::MouseDown, control, &Control::MouseDownEvent,
+ nullptr, args.point, args.button, args.modifier);
+}
+
+void UiHost::OnNativeMouseUp(
+ INativeWindow* window,
+ const platform::native::NativeMouseButtonEventArgs& args) {
+ CRU_UNUSED(window)
+
+ Control* control =
+ mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
+ DispatchEvent(event_names::MouseUp, control, &Control::MouseUpEvent, nullptr,
+ args.point, args.button, args.modifier);
+}
+
+void UiHost::OnNativeKeyDown(INativeWindow* window,
+ const platform::native::NativeKeyEventArgs& args) {
+ CRU_UNUSED(window)
+
+ DispatchEvent(event_names::KeyDown, focus_control_, &Control::KeyDownEvent,
+ nullptr, args.key, args.modifier);
+}
+
+void UiHost::OnNativeKeyUp(INativeWindow* window,
+ const platform::native::NativeKeyEventArgs& args) {
+ CRU_UNUSED(window)
+
+ DispatchEvent(event_names::KeyUp, focus_control_, &Control::KeyUpEvent,
+ nullptr, args.key, args.modifier);
+}
+
+void UiHost::DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point,
+ bool no_leave,
+ bool no_enter) {
+ if (new_control != old_control) // if the mouse-hover-on control changed
+ {
+ const auto lowest_common_ancestor =
+ FindLowestCommonAncestor(old_control, new_control);
+ if (!no_leave && old_control != nullptr)
+ DispatchEvent(event_names::MouseLeave, old_control,
+ &Control::MouseLeaveEvent,
+ lowest_common_ancestor); // dispatch mouse leave event.
+ if (!no_enter && new_control != nullptr) {
+ DispatchEvent(event_names::MouseEnter, new_control,
+ &Control::MouseEnterEvent, lowest_common_ancestor,
+ point); // dispatch mouse enter event.
+ }
+ }
+}
+
+void UiHost::UpdateCursor() {
+ if (const auto native_window = native_window_resolver_->Resolve()) {
+ const auto capture = GetMouseCaptureControl();
+ native_window->SetCursor(
+ (capture ? capture : GetMouseHoverControl())->GetInheritedCursor());
+ }
+}
+
+Control* UiHost::HitTest(const Point& point) {
+ return root_render_object_->HitTest(point)->GetAttachedControl();
+}
+} // namespace cru::ui
diff --git a/src/ui/window.cpp b/src/ui/window.cpp
index a3279ba7..4f414385 100644
--- a/src/ui/window.cpp
+++ b/src/ui/window.cpp
@@ -1,357 +1,29 @@
#include "cru/ui/window.hpp"
-#include "cru/platform/graph/painter.hpp"
-#include "cru/platform/native/ui_application.hpp"
-#include "cru/platform/native/window.hpp"
#include "cru/ui/render/window_render_object.hpp"
-#include "routed_event_dispatch.hpp"
-
-#include <list>
+#include "cru/ui/ui_host.hpp"
namespace cru::ui {
-using cru::platform::native::FocusChangeType;
-using cru::platform::native::INativeWindow;
-using cru::platform::native::INativeWindowResolver;
-using cru::platform::native::IUiApplication;
-using cru::platform::native::MouseEnterLeaveType;
-
-namespace event_names {
-#ifdef CRU_DEBUG
-#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = #name;
-#else
-#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = "";
-#endif
-
-CRU_DEFINE_EVENT_NAME(LoseFocus)
-CRU_DEFINE_EVENT_NAME(GainFocus)
-CRU_DEFINE_EVENT_NAME(MouseEnter)
-CRU_DEFINE_EVENT_NAME(MouseLeave)
-CRU_DEFINE_EVENT_NAME(MouseMove)
-CRU_DEFINE_EVENT_NAME(MouseDown)
-CRU_DEFINE_EVENT_NAME(MouseUp)
-CRU_DEFINE_EVENT_NAME(KeyDown)
-CRU_DEFINE_EVENT_NAME(KeyUp)
-CRU_DEFINE_EVENT_NAME(Char)
-
-#undef CRU_DEFINE_EVENT_NAME
-} // namespace event_names
-
-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) {
- l.push_front(control);
- control = control->GetParent();
- }
- return l;
-}
-
-Control* FindLowestCommonAncestor(Control* left, Control* right) {
- if (left == nullptr || right == nullptr) return nullptr;
-
- auto&& left_list = GetAncestorList(left);
- auto&& right_list = GetAncestorList(right);
-
- // the root is different
- if (left_list.front() != right_list.front()) return nullptr;
-
- // find the last same control or the last control (one is ancestor of the
- // other)
- auto left_i = left_list.cbegin();
- auto right_i = right_list.cbegin();
-
- while (true) {
- if (left_i == left_list.cend()) {
- return *(--left_i);
- }
- if (right_i == right_list.cend()) {
- return *(--right_i);
- }
- if (*left_i != *right_i) {
- return *(--left_i);
- }
- ++left_i;
- ++right_i;
- }
-}
-} // namespace
Window* Window::CreateOverlapped() {
return new Window(tag_overlapped_constructor{});
}
-namespace {
-template <typename T>
-inline void BindNativeEvent(
- Window* window, INativeWindow* native_window, IEvent<T>* event,
- void (Window::*handler)(INativeWindow*, typename IEvent<T>::EventArgs),
- std::vector<EventRevokerGuard>& guard_pool) {
- guard_pool.push_back(EventRevokerGuard(event->AddHandler(
- std::bind(handler, window, native_window, std::placeholders::_1))));
-}
-} // namespace
-
-Window::Window(tag_overlapped_constructor)
- : mouse_hover_control_(nullptr),
- focus_control_(this),
- mouse_captured_control_(nullptr) {
- window_ = this;
- native_window_resolver_ =
- IUiApplication::GetInstance()->CreateWindow(nullptr);
-
- const auto native_window = native_window_resolver_->Resolve();
-
- render_object_.reset(new render::WindowRenderObject(this));
- render_object_->SetAttachedControl(this);
-
- BindNativeEvent(this, native_window, native_window->DestroyEvent(),
- &Window::OnNativeDestroy, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->PaintEvent(),
- &Window::OnNativePaint, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->ResizeEvent(),
- &Window::OnNativeResize, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->FocusEvent(),
- &Window::OnNativeFocus, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseEnterLeaveEvent(),
- &Window::OnNativeMouseEnterLeave, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseMoveEvent(),
- &Window::OnNativeMouseMove, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseDownEvent(),
- &Window::OnNativeMouseDown, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseUpEvent(),
- &Window::OnNativeMouseUp, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->KeyDownEvent(),
- &Window::OnNativeKeyDown, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->KeyUpEvent(),
- &Window::OnNativeKeyUp, event_revoker_guards_);
+Window::Window(tag_overlapped_constructor) {
+ managed_ui_host_ = std::make_unique<UiHost>(this);
}
Window::~Window() {
- TraverseDescendants(
- [this](Control* control) { control->OnDetachToWindow(this); });
+ // explicit destroy ui host first.
+ managed_ui_host_.reset();
}
std::string_view Window::GetControlType() const { return control_type; }
-render::RenderObject* Window::GetRenderObject() const {
- return render_object_.get();
-}
-
-platform::native::INativeWindow* Window::ResolveNativeWindow() {
- return native_window_resolver_->Resolve();
-}
-
-bool Window::RequestFocusFor(Control* control) {
- Expects(control != nullptr); // The control to request focus can't be null.
- // You can set it as the window.
-
- if (focus_control_ == control) return true;
-
- DispatchEvent(event_names::LoseFocus, focus_control_,
- &Control::LoseFocusEvent, nullptr, false);
-
- focus_control_ = control;
-
- DispatchEvent(event_names::GainFocus, control, &Control::GainFocusEvent,
- nullptr, false);
-
- return true;
-}
-
-Control* Window::GetFocusControl() { return focus_control_; }
-
-bool Window::CaptureMouseFor(Control* control) {
- const auto native_window = ResolveNativeWindow();
- if (!native_window) return false;
-
- if (control == mouse_captured_control_) return true;
-
- if (control == nullptr) {
- 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_,
- native_window->GetMousePosition(), true, false);
- }
- UpdateCursor();
- return true;
- }
-
- if (mouse_captured_control_) return false;
-
- mouse_captured_control_ = control;
- DispatchMouseHoverControlChangeEvent(
- mouse_hover_control_, mouse_captured_control_,
- native_window->GetMousePosition(), false, true);
- UpdateCursor();
- return true;
-}
-
-Control* Window::GetMouseCaptureControl() { return mouse_captured_control_; }
+render::RenderObject* Window::GetRenderObject() const { return render_object_; }
void Window::OnChildChanged(Control* old_child, Control* new_child) {
if (old_child) render_object_->RemoveChild(0);
if (new_child) render_object_->AddChild(new_child->GetRenderObject(), 0);
}
-
-Control* Window::HitTest(const Point& point) {
- return render_object_->HitTest(point)->GetAttachedControl();
-}
-
-void Window::OnNativeDestroy(INativeWindow* window, std::nullptr_t) {
- CRU_UNUSED(window)
- delete this; // TODO: Finally change this.
-}
-
-void Window::OnNativePaint(INativeWindow* window, std::nullptr_t) {
- auto painter = window->BeginPaint();
- render_object_->Draw(painter.get());
- painter->EndDraw();
-}
-
-void Window::OnNativeResize(INativeWindow* window, const Size& size) {
- CRU_UNUSED(window)
- CRU_UNUSED(size)
-
- render_object_->GetRenderHost()->InvalidateLayout();
-}
-
-void Window::OnNativeFocus(INativeWindow* window, FocusChangeType focus) {
- CRU_UNUSED(window)
-
- focus == FocusChangeType::Gain
- ? DispatchEvent(event_names::GainFocus, focus_control_,
- &Control::GainFocusEvent, nullptr, true)
- : DispatchEvent(event_names::LoseFocus, focus_control_,
- &Control::LoseFocusEvent, nullptr, true);
-}
-
-void Window::OnNativeMouseEnterLeave(INativeWindow* window,
- MouseEnterLeaveType type) {
- CRU_UNUSED(window)
-
- if (type == MouseEnterLeaveType::Leave) {
- DispatchEvent(event_names::MouseLeave, mouse_hover_control_,
- &Control::MouseLeaveEvent, nullptr);
- mouse_hover_control_ = nullptr;
- }
-}
-
-void Window::OnNativeMouseMove(INativeWindow* window, const Point& point) {
- CRU_UNUSED(window)
-
- // Find the first control that hit test succeed.
- const auto new_mouse_hover_control = HitTest(point);
- const auto old_mouse_hover_control = mouse_hover_control_;
- mouse_hover_control_ = new_mouse_hover_control;
-
- if (mouse_captured_control_) {
- const auto n = FindLowestCommonAncestor(new_mouse_hover_control,
- mouse_captured_control_);
- const auto o = FindLowestCommonAncestor(old_mouse_hover_control,
- mouse_captured_control_);
- bool a = IsAncestor(o, n);
- if (a) {
- DispatchEvent(event_names::MouseLeave, o, &Control::MouseLeaveEvent, n);
- } else {
- DispatchEvent(event_names::MouseEnter, n, &Control::MouseEnterEvent, o,
- point);
- }
- DispatchEvent(event_names::MouseMove, mouse_captured_control_,
- &Control::MouseMoveEvent, nullptr, point);
- UpdateCursor();
- return;
- }
-
- DispatchMouseHoverControlChangeEvent(
- old_mouse_hover_control, new_mouse_hover_control, point, false, false);
- DispatchEvent(event_names::MouseMove, new_mouse_hover_control,
- &Control::MouseMoveEvent, nullptr, point);
- UpdateCursor();
-}
-
-void Window::OnNativeMouseDown(
- INativeWindow* window,
- const platform::native::NativeMouseButtonEventArgs& args) {
- CRU_UNUSED(window)
-
- Control* control =
- mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
- DispatchEvent(event_names::MouseDown, control, &Control::MouseDownEvent,
- nullptr, args.point, args.button, args.modifier);
-}
-
-void Window::OnNativeMouseUp(
- INativeWindow* window,
- const platform::native::NativeMouseButtonEventArgs& args) {
- CRU_UNUSED(window)
-
- Control* control =
- mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
- DispatchEvent(event_names::MouseUp, control, &Control::MouseUpEvent, nullptr,
- args.point, args.button, args.modifier);
-}
-
-void Window::OnNativeKeyDown(INativeWindow* window,
- const platform::native::NativeKeyEventArgs& args) {
- CRU_UNUSED(window)
-
- DispatchEvent(event_names::KeyDown, focus_control_, &Control::KeyDownEvent,
- nullptr, args.key, args.modifier);
-}
-
-void Window::OnNativeKeyUp(INativeWindow* window,
- const platform::native::NativeKeyEventArgs& args) {
- CRU_UNUSED(window)
-
- DispatchEvent(event_names::KeyUp, focus_control_, &Control::KeyUpEvent,
- nullptr, args.key, args.modifier);
-}
-
-void Window::OnNativeChar(platform::native::INativeWindow* window,
- std::string c) {
- CRU_UNUSED(window)
-
- DispatchEvent(event_names::Char, focus_control_, &Control::CharEvent, nullptr,
- std::move(c));
-}
-
-void Window::DispatchMouseHoverControlChangeEvent(Control* old_control,
- Control* new_control,
- const Point& point,
- bool no_leave,
- bool no_enter) {
- if (new_control != old_control) // if the mouse-hover-on control changed
- {
- const auto lowest_common_ancestor =
- FindLowestCommonAncestor(old_control, new_control);
- if (!no_leave && old_control != nullptr)
- DispatchEvent(event_names::MouseLeave, old_control,
- &Control::MouseLeaveEvent,
- lowest_common_ancestor); // dispatch mouse leave event.
- if (!no_enter && new_control != nullptr) {
- DispatchEvent(event_names::MouseEnter, new_control,
- &Control::MouseEnterEvent, lowest_common_ancestor,
- point); // dispatch mouse enter event.
- }
- }
-}
-
-void Window::UpdateCursor() {
- if (const auto native_window = ResolveNativeWindow()) {
- const auto capture = GetMouseCaptureControl();
- native_window->SetCursor(
- (capture ? capture : GetMouseHoverControl())->GetInheritedCursor());
- }
-}
} // namespace cru::ui