aboutsummaryrefslogtreecommitdiff
path: root/include/cru/ui
diff options
context:
space:
mode:
Diffstat (limited to 'include/cru/ui')
-rw-r--r--include/cru/ui/Base.h2
-rw-r--r--include/cru/ui/controls/Button.h4
-rw-r--r--include/cru/ui/controls/CheckBox.h8
-rw-r--r--include/cru/ui/controls/Control.h45
-rw-r--r--include/cru/ui/controls/ControlHost.h191
-rw-r--r--include/cru/ui/controls/IconButton.h11
-rw-r--r--include/cru/ui/controls/LayoutControl.h67
-rw-r--r--include/cru/ui/controls/NoChildControl.h21
-rw-r--r--include/cru/ui/controls/SingleChildControl.h35
-rw-r--r--include/cru/ui/controls/TextBlock.h10
-rw-r--r--include/cru/ui/controls/TextBox.h8
-rw-r--r--include/cru/ui/controls/TreeView.h11
-rw-r--r--include/cru/ui/controls/Window.h168
-rw-r--r--include/cru/ui/events/RoutedEvent.h2
-rw-r--r--include/cru/ui/render/RenderObject.h2
15 files changed, 272 insertions, 313 deletions
diff --git a/include/cru/ui/Base.h b/include/cru/ui/Base.h
index 9e24cfa2..06dd23c2 100644
--- a/include/cru/ui/Base.h
+++ b/include/cru/ui/Base.h
@@ -35,7 +35,7 @@ namespace colors = cru::platform::colors;
namespace controls {
class Control;
-class Window;
+class ControlHost;
} // namespace controls
//-------------------- region: basic types --------------------
diff --git a/include/cru/ui/controls/Button.h b/include/cru/ui/controls/Button.h
index e8fa50f1..64b9bba0 100644
--- a/include/cru/ui/controls/Button.h
+++ b/include/cru/ui/controls/Button.h
@@ -16,10 +16,6 @@ class CRU_UI_API Button : public SingleChildControl<render::BorderRenderObject>,
public:
Button();
- Button(const Button& other) = delete;
- Button(Button&& other) = delete;
- Button& operator=(const Button& other) = delete;
- Button& operator=(Button&& other) = delete;
~Button() override;
std::string GetControlType() const final { return std::string(kControlType); }
diff --git a/include/cru/ui/controls/CheckBox.h b/include/cru/ui/controls/CheckBox.h
index 8a2c84a0..6f4eae2e 100644
--- a/include/cru/ui/controls/CheckBox.h
+++ b/include/cru/ui/controls/CheckBox.h
@@ -1,13 +1,13 @@
#pragma once
#include "../helper/ClickDetector.h"
#include "../render/BorderRenderObject.h"
+#include "Control.h"
#include "IBorderControl.h"
#include "ICheckableControl.h"
#include "IClickableControl.h"
-#include "NoChildControl.h"
namespace cru::ui::controls {
-class CRU_UI_API CheckBox : public NoChildControl,
+class CRU_UI_API CheckBox : public Control,
public virtual IBorderControl,
public virtual ICheckableControl,
public virtual IClickableControl {
@@ -17,7 +17,9 @@ class CRU_UI_API CheckBox : public NoChildControl,
CheckBox();
~CheckBox() override;
- std::string GetControlType() const override { return std::string(kControlType); }
+ std::string GetControlType() const override {
+ return std::string(kControlType);
+ }
render::RenderObject* GetRenderObject() const override {
return container_render_object_.get();
diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h
index 94de4cdc..9e5e86b8 100644
--- a/include/cru/ui/controls/Control.h
+++ b/include/cru/ui/controls/Control.h
@@ -24,9 +24,9 @@ namespace cru::ui::controls {
class CRU_UI_API Control : public Object,
public DeleteLaterImpl,
public SelfResolvable<Control> {
- friend class RootControl;
+ friend class ControlHost;
- CRU_DEFINE_CLASS_LOG_TAG("Control")
+ CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::Control")
protected:
Control();
@@ -41,24 +41,33 @@ class CRU_UI_API Control : public Object,
//*************** region: tree ***************
public:
- Window* GetWindow();
+ ControlHost* GetControlHost();
- Control* GetParent() const { return parent_; }
- void SetParent(Control* parent);
+ Control* GetParent();
bool HasAncestor(Control* control);
+ const std::vector<Control*>& GetChildren();
+ void RemoveChild(Control* child);
+ void RemoveAllChild();
+ void RemoveFromParent();
- virtual void ForEachChild(const std::function<void(Control*)>& predicate) = 0;
-
- /**
- * \remarks This method should be permissive, which means if the specified
- * child control is not a real child of this then nothing will be done.
- */
- virtual void RemoveChild(Control* child) = 0;
+ template <typename F>
+ void TraverseDescendents(F&& f, bool include_this) {
+ if (include_this) {
+ f(this);
+ }
- void RemoveFromParent();
+ for (auto child : GetChildren()) {
+ child->TraverseDescendents(std::forward<F>(f), true);
+ }
+ }
controls::Control* HitTest(const Point& point);
+ protected:
+ void InsertChildAt(Control* control, Index index);
+ void RemoveChildAt(Index index);
+ void AddChild(Control* control);
+
public:
virtual render::RenderObject* GetRenderObject() const = 0;
@@ -89,7 +98,7 @@ class CRU_UI_API Control : public Object,
//*************** region: mouse ***************
public:
- bool IsMouseOver() const { return is_mouse_over_; }
+ bool IsMouseOver();
bool CaptureMouse();
@@ -137,12 +146,14 @@ class CRU_UI_API Control : public Object,
//*************** region: tree ***************
protected:
- virtual void OnParentChanged(Control* old_parent, Control* new_parent) {}
+ virtual void OnParentChanged(Control* old_parent, Control* new_parent);
+ virtual void OnChildInserted(Control* control, Index index);
+ virtual void OnChildRemoved(Control* control, Index index);
private:
+ ControlHost* host_ = nullptr;
Control* parent_ = nullptr;
-
- bool is_mouse_over_ = false;
+ std::vector<Control*> children_;
std::shared_ptr<platform::gui::ICursor> cursor_ = nullptr;
diff --git a/include/cru/ui/controls/ControlHost.h b/include/cru/ui/controls/ControlHost.h
new file mode 100644
index 00000000..c67e8a72
--- /dev/null
+++ b/include/cru/ui/controls/ControlHost.h
@@ -0,0 +1,191 @@
+#pragma once
+#include "Control.h"
+
+#include <cru/base/Base.h>
+#include <cru/base/Event.h>
+#include <cru/base/Guard.h>
+#include <cru/base/log/Logger.h>
+#include <cru/platform/gui/UiApplication.h>
+#include <cru/platform/gui/Window.h>
+
+namespace cru::ui::controls {
+class CRU_UI_API ControlHost : public Object {
+ CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::ControlHost")
+ friend Control;
+
+ public:
+ explicit ControlHost(Control* root_control);
+ ~ControlHost() override;
+
+ platform::gui::INativeWindow* GetNativeWindow();
+
+ void InvalidateLayout();
+ void InvalidatePaint();
+
+ void Repaint();
+ void Relayout();
+ void RelayoutWithSize(const Size& available_size = Size::Infinite(),
+ bool set_window_size_to_fit_content = false);
+
+ // If true, preferred size of root render object is set to window size when
+ // measure. Default is true.
+ bool IsLayoutPreferToFillWindow() const;
+ void SetLayoutPreferToFillWindow(bool value);
+
+ // 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_; }
+
+ Control* GetFocusControl();
+ void SetFocusControl(Control* control);
+
+ Control* GetMouseCaptureControl();
+ bool SetMouseCaptureControl(Control* control);
+
+ std::shared_ptr<platform::gui::ICursor> GetOverrideCursor();
+ void SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor);
+
+ bool IsInEventHandling();
+
+ CRU_DEFINE_EVENT(AfterLayout, std::nullptr_t)
+
+ private:
+ std::unique_ptr<platform::gui::INativeWindow> CreateNativeWindow();
+
+ void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
+ void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
+ void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);
+ void OnNativeFocus(platform::gui::INativeWindow* window,
+ cru::platform::gui::FocusChangeType focus);
+ void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
+ cru::platform::gui::MouseEnterLeaveType enter);
+ void OnNativeMouseMove(platform::gui::INativeWindow* window,
+ const Point& point);
+ void OnNativeMouseDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args);
+ void OnNativeMouseUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args);
+ void OnNativeMouseWheel(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseWheelEventArgs& args);
+ void OnNativeKeyDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args);
+ void OnNativeKeyUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args);
+
+ void DispatchFocusControlChangeEvent(Control* old_control,
+ Control* new_control, bool is_window);
+ void DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point, bool no_leave,
+ bool no_enter);
+
+ template <typename EventArgs, typename... Args>
+ void DispatchEvent(Control* const original_sender,
+ events::RoutedEvent<EventArgs>* (Control::*event_ptr)(),
+ Control* const last_receiver, Args&&... args) {
+ constexpr auto kLogTag = "cru::ui::controls::DispatchEvent";
+
+ event_handling_count_++;
+ Guard event_handling_count_guard([this] { event_handling_count_--; });
+
+ if (original_sender == nullptr || original_sender == last_receiver) return;
+
+ std::string log = "Begin dispatching routed event " +
+ (original_sender->*event_ptr)()->GetName() +
+ ":\n\tTunnel:";
+
+ Guard logging_guard([&] {
+ log += "\nEnd dispatching routed event " +
+ (original_sender->*event_ptr)()->GetName() + ".";
+ CRU_LOG_TAG_DEBUG("{}", log);
+ });
+
+ std::vector<ObjectResolver<Control>> receive_list;
+
+ auto parent = original_sender;
+ while (parent != last_receiver) {
+ receive_list.push_back(parent->CreateResolver());
+ parent = parent->GetParent();
+ }
+
+ auto handled = false;
+
+ // tunnel
+ for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
+ auto control = i->Resolve();
+ log += " ";
+ if (!control) {
+ log += "(deleted)";
+ continue;
+ }
+ log += control->GetDebugId();
+ EventArgs event_args(control, original_sender,
+ std::forward<Args>(args)...);
+ (control->*event_ptr)()->tunnel_.Raise(event_args);
+ if (event_args.IsHandled()) {
+ log += " marked as handled.";
+ handled = true;
+ break;
+ }
+ }
+
+ // bubble
+ if (!handled) {
+ log += "\n\tBubble:";
+ for (auto resolver : receive_list) {
+ auto control = resolver.Resolve();
+ log += " ";
+ if (!control) {
+ log += "(deleted)";
+ continue;
+ }
+ log += control->GetDebugId();
+ EventArgs event_args(control, original_sender,
+ std::forward<Args>(args)...);
+ (control->*event_ptr)()->bubble_.Raise(event_args);
+ if (event_args.IsHandled()) {
+ log += " marked as handled.";
+ break;
+ }
+ }
+ }
+
+ log += "\n\tDirect:";
+ // direct
+ for (auto resolver : receive_list) {
+ auto control = resolver.Resolve();
+ log += " ";
+ if (!control) {
+ log += "(deleted)";
+ continue;
+ }
+ log += control->GetDebugId();
+ EventArgs event_args(control, original_sender,
+ std::forward<Args>(args)...);
+ (control->*event_ptr)()->direct_.Raise(event_args);
+ }
+ }
+
+ void UpdateCursor();
+ void NotifyControlParentChange(Control* control, Control* old_parent, Control* new_parent);
+
+ private:
+ int event_handling_count_;
+
+ Control* root_control_;
+ std::unique_ptr<platform::gui::INativeWindow> native_window_;
+
+ Control* focus_control_;
+ Control* mouse_hover_control_;
+ Control* mouse_captured_control_;
+
+ std::shared_ptr<platform::gui::ICursor> override_cursor_;
+
+ bool layout_prefer_to_fill_window_;
+
+ platform::gui::TimerAutoCanceler repaint_schedule_canceler_;
+ platform::gui::TimerAutoCanceler relayout_schedule_canceler_;
+};
+} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/IconButton.h b/include/cru/ui/controls/IconButton.h
index e52d2a26..0bbda327 100644
--- a/include/cru/ui/controls/IconButton.h
+++ b/include/cru/ui/controls/IconButton.h
@@ -1,18 +1,17 @@
#pragma once
-#include <memory>
-#include "NoChildControl.h"
-
#include "../helper/ClickDetector.h"
#include "../render/BorderRenderObject.h"
#include "../render/GeometryRenderObject.h"
+#include "Control.h"
#include "IBorderControl.h"
#include "IClickableControl.h"
#include "IContentBrushControl.h"
-#include "cru/base/Event.h"
-#include "cru/platform/graphics/Brush.h"
+
+#include <cru/base/Event.h>
+#include <cru/platform/graphics/Brush.h>
namespace cru::ui::controls {
-class CRU_UI_API IconButton : public NoChildControl,
+class CRU_UI_API IconButton : public Control,
public virtual IClickableControl,
public virtual IBorderControl,
public virtual IContentBrushControl {
diff --git a/include/cru/ui/controls/LayoutControl.h b/include/cru/ui/controls/LayoutControl.h
index 54407a3f..fad86530 100644
--- a/include/cru/ui/controls/LayoutControl.h
+++ b/include/cru/ui/controls/LayoutControl.h
@@ -10,17 +10,14 @@ class LayoutControl : public Control {
}
public:
- LayoutControl(const LayoutControl& other) = delete;
- LayoutControl(LayoutControl&& other) = delete;
- LayoutControl& operator=(const LayoutControl& other) = delete;
- LayoutControl& operator=(LayoutControl&& other) = delete;
- ~LayoutControl() override { ClearChildren(); }
+ using Control::AddChild;
+ using Control::InsertChildAt;
+ using Control::RemoveChildAt;
- public:
- const std::vector<Control*>& GetChildren() const { return children_; }
Index GetChildCount() const { return children_.size(); }
- Control* GetChild(Index index) const { return children_[index]; }
- Index IndexOf(Control* control) const {
+ Control* GetChildAt(Index index) const { return children_[index]; }
+
+ Index IndexOfChild(Control* control) const {
auto it = std::find(children_.begin(), children_.end(), control);
if (it == children_.end()) {
return -1;
@@ -28,19 +25,6 @@ class LayoutControl : public Control {
return it - children_.begin();
}
- void ForEachChild(const std::function<void(Control*)>& callback) override {
- for (auto child : children_) {
- callback(child);
- }
- }
-
- void RemoveChild(Control* child) override {
- auto index = IndexOf(child);
- if (index != -1) {
- RemoveChildAt(index);
- }
- }
-
render::RenderObject* GetRenderObject() const override {
return container_render_object_.get();
}
@@ -49,36 +33,6 @@ class LayoutControl : public Control {
return container_render_object_.get();
}
- void AddChildAt(Control* child, Index position) {
- Expects(child);
- Expects(child->GetParent() == nullptr);
- if (position < 0) position = 0;
- if (position > children_.size()) position = children_.size();
- children_.insert(children_.begin() + position, child);
- child->SetParent(this);
-
- assert(child->GetRenderObject());
- container_render_object_->AddChild(child->GetRenderObject(), position);
- }
-
- void AddChild(Control* child) { AddChildAt(child, GetChildCount()); }
-
- void RemoveChildAt(Index position) {
- if (position < 0 || position >= children_.size()) return;
- auto child = children_[position];
- children_.erase(children_.begin() + position);
- container_render_object_->RemoveChild(position);
- child->SetParent(nullptr);
- }
-
- void ClearChildren() {
- container_render_object_->ClearChildren();
- for (auto child : children_) {
- child->SetParent(nullptr);
- }
- children_.clear();
- }
-
const typename TRenderObject::ChildLayoutData& GetChildLayoutData(
Index position) {
return container_render_object_->GetChildLayoutDataAt(position);
@@ -89,6 +43,15 @@ class LayoutControl : public Control {
container_render_object_->SetChildLayoutDataAt(position, data);
}
+ protected:
+ void OnChildInserted(Control* control, Index index) override {
+ container_render_object_->AddChild(control->GetRenderObject(), index);
+ }
+
+ void OnChildRemoved([[maybe_unused]] Control* control, Index index) override {
+ container_render_object_->RemoveChild(index);
+ }
+
private:
std::unique_ptr<TRenderObject> container_render_object_;
diff --git a/include/cru/ui/controls/NoChildControl.h b/include/cru/ui/controls/NoChildControl.h
deleted file mode 100644
index f22fd85e..00000000
--- a/include/cru/ui/controls/NoChildControl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-#include "Control.h"
-
-namespace cru::ui::controls {
-class CRU_UI_API NoChildControl : public Control {
- protected:
- NoChildControl() = default;
-
- public:
- NoChildControl(const NoChildControl& other) = delete;
- NoChildControl(NoChildControl&& other) = delete;
- NoChildControl& operator=(const NoChildControl& other) = delete;
- NoChildControl& operator=(NoChildControl&& other) = delete;
- ~NoChildControl() override = default;
-
- public:
- void ForEachChild(const std::function<void(Control*)>& callback) override;
-
- void RemoveChild(Control* child) override;
-};
-} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/SingleChildControl.h b/include/cru/ui/controls/SingleChildControl.h
index b2e58c5f..f748a38d 100644
--- a/include/cru/ui/controls/SingleChildControl.h
+++ b/include/cru/ui/controls/SingleChildControl.h
@@ -10,25 +10,17 @@ class SingleChildControl : public Control {
}
public:
- CRU_DELETE_COPY(SingleChildControl)
- CRU_DELETE_MOVE(SingleChildControl)
-
- ~SingleChildControl() override { SetChild(nullptr); }
+ Control* GetChild() {
+ return GetChildren().empty() ? nullptr : GetChildren().front();
+ }
- Control* GetChild() const { return child_; }
void SetChild(Control* child) {
- if (child == child_) return;
-
- assert(child == nullptr || child->GetParent() == nullptr);
-
- if (child_) {
- child_->SetParent(nullptr);
+ if (GetChild() == child) return;
+ if (!GetChildren().empty()) {
+ RemoveChildAt(0);
}
-
- child_ = child;
-
if (child) {
- child->SetParent(this);
+ InsertChildAt(child, 0);
}
container_render_object_->SetChild(
@@ -43,20 +35,7 @@ class SingleChildControl : public Control {
return container_render_object_.get();
}
- void ForEachChild(const std::function<void(Control*)>& predicate) override {
- if (child_) {
- predicate(child_);
- }
- }
-
- void RemoveChild(Control* child) override {
- if (child_ == child) {
- SetChild(nullptr);
- }
- }
-
private:
- Control* child_ = nullptr;
std::unique_ptr<TRenderObject> container_render_object_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/TextBlock.h b/include/cru/ui/controls/TextBlock.h
index a3b6407f..af9865c4 100644
--- a/include/cru/ui/controls/TextBlock.h
+++ b/include/cru/ui/controls/TextBlock.h
@@ -1,15 +1,15 @@
#pragma once
-#include "NoChildControl.h"
-
+#include "Control.h"
#include "../render/TextRenderObject.h"
#include "IContentBrushControl.h"
#include "IFontControl.h"
#include "TextHostControlService.h"
-#include "cru/platform/graphics/Brush.h"
-#include "cru/platform/graphics/Font.h"
+
+#include <cru/platform/graphics/Brush.h>
+#include <cru/platform/graphics/Font.h>
namespace cru::ui::controls {
-class CRU_UI_API TextBlock : public NoChildControl,
+class CRU_UI_API TextBlock : public Control,
public virtual ITextHostControl,
public virtual IFontControl,
public virtual IContentBrushControl {
diff --git a/include/cru/ui/controls/TextBox.h b/include/cru/ui/controls/TextBox.h
index adb9895e..4c6ef384 100644
--- a/include/cru/ui/controls/TextBox.h
+++ b/include/cru/ui/controls/TextBox.h
@@ -1,18 +1,18 @@
#pragma once
-#include "NoChildControl.h"
-
#include "../render/BorderRenderObject.h"
#include "../render/TextRenderObject.h"
+#include "Control.h"
#include "IBorderControl.h"
#include "IContentBrushControl.h"
#include "IFontControl.h"
#include "TextHostControlService.h"
-#include "cru/platform/graphics/Brush.h"
+
+#include <cru/platform/graphics/Brush.h>
#include <memory>
namespace cru::ui::controls {
-class CRU_UI_API TextBox : public NoChildControl,
+class CRU_UI_API TextBox : public Control,
public virtual IBorderControl,
public virtual ITextHostControl,
public virtual IContentBrushControl,
diff --git a/include/cru/ui/controls/TreeView.h b/include/cru/ui/controls/TreeView.h
index f4938259..62b1c69b 100644
--- a/include/cru/ui/controls/TreeView.h
+++ b/include/cru/ui/controls/TreeView.h
@@ -48,6 +48,11 @@ class CRU_UI_API TreeViewItem : public Object {
};
class CRU_UI_API TreeView : public Control {
+ friend TreeViewItem;
+
+ private:
+ using Control::AddChild;
+
public:
constexpr static std::string_view kControlType = "TreeView";
@@ -61,11 +66,11 @@ class CRU_UI_API TreeView : public Control {
}
render::TreeRenderObject* GetRenderObject() { return &render_object_; }
- void ForEachChild(const std::function<void(Control*)>& predicate) override;
- void RemoveChild(Control* control) override;
-
TreeViewItem* GetRootItem() { return &root_item_; }
+ protected:
+ void OnChildRemoved(Control* control, Index index) override;
+
private:
render::TreeRenderObject render_object_;
TreeViewItem root_item_;
diff --git a/include/cru/ui/controls/Window.h b/include/cru/ui/controls/Window.h
index 88320219..a2ae01f9 100644
--- a/include/cru/ui/controls/Window.h
+++ b/include/cru/ui/controls/Window.h
@@ -19,7 +19,6 @@ class CRU_UI_API Window
static constexpr std::string_view kControlType = "Window";
Window();
- ~Window() override;
static Window* CreatePopup();
@@ -29,175 +28,10 @@ class CRU_UI_API Window
platform::gui::INativeWindow* GetNativeWindow();
- void InvalidateLayout();
- void InvalidatePaint();
-
- void Repaint();
- void Relayout();
- void RelayoutWithSize(const Size& available_size = Size::Infinite(),
- bool set_window_size_to_fit_content = false);
-
void SetGainFocusOnCreateAndDestroyWhenLoseFocus(bool value);
- // If true, preferred size of root render object is set to window size when
- // measure. Default is true.
- bool IsLayoutPreferToFillWindow() const;
- void SetLayoutPreferToFillWindow(bool value);
-
- // 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_; }
-
- Control* GetFocusControl();
- void SetFocusControl(Control* control);
-
- Control* GetMouseCaptureControl();
- bool SetMouseCaptureControl(Control* control);
-
- std::shared_ptr<platform::gui::ICursor> GetOverrideCursor();
- void SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor);
-
- bool IsInEventHandling();
-
- CRU_DEFINE_EVENT(AfterLayout, std::nullptr_t)
-
- private:
- std::unique_ptr<platform::gui::INativeWindow> CreateNativeWindow();
-
- void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
- void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
- void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);
- void OnNativeFocus(platform::gui::INativeWindow* window,
- cru::platform::gui::FocusChangeType focus);
- void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
- cru::platform::gui::MouseEnterLeaveType enter);
- void OnNativeMouseMove(platform::gui::INativeWindow* window,
- const Point& point);
- void OnNativeMouseDown(platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseButtonEventArgs& args);
- void OnNativeMouseUp(platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseButtonEventArgs& args);
- void OnNativeMouseWheel(platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseWheelEventArgs& args);
- void OnNativeKeyDown(platform::gui::INativeWindow* window,
- const platform::gui::NativeKeyEventArgs& args);
- void OnNativeKeyUp(platform::gui::INativeWindow* window,
- const platform::gui::NativeKeyEventArgs& args);
-
- void DispatchFocusControlChangeEvent(Control* old_control,
- Control* new_control, bool is_window);
- void DispatchMouseHoverControlChangeEvent(Control* old_control,
- Control* new_control,
- const Point& point, bool no_leave,
- bool no_enter);
-
- template <typename EventArgs, typename... Args>
- void DispatchEvent(Control* const original_sender,
- events::RoutedEvent<EventArgs>* (Control::*event_ptr)(),
- Control* const last_receiver, Args&&... args) {
- constexpr auto kLogTag = "cru::ui::controls::DispatchEvent";
-
- event_handling_count_++;
- Guard event_handling_count_guard([this] { event_handling_count_--; });
-
- if (original_sender == nullptr || original_sender == last_receiver) return;
-
- std::string log = "Begin dispatching routed event " +
- (original_sender->*event_ptr)()->GetName() +
- ":\n\tTunnel:";
-
- Guard logging_guard([&] {
- log += "\nEnd dispatching routed event " +
- (original_sender->*event_ptr)()->GetName() + ".";
- CRU_LOG_TAG_DEBUG("{}", log);
- });
-
- std::vector<ObjectResolver<Control>> receive_list;
-
- auto parent = original_sender;
- while (parent != last_receiver) {
- receive_list.push_back(parent->CreateResolver());
- parent = parent->GetParent();
- }
-
- auto handled = false;
-
- // tunnel
- for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
- auto control = i->Resolve();
- log += " ";
- if (!control) {
- log += "(deleted)";
- continue;
- }
- log += control->GetDebugId();
- EventArgs event_args(control, original_sender,
- std::forward<Args>(args)...);
- (control->*event_ptr)()->tunnel_.Raise(event_args);
- if (event_args.IsHandled()) {
- log += " marked as handled.";
- handled = true;
- break;
- }
- }
-
- // bubble
- if (!handled) {
- log += "\n\tBubble:";
- for (auto resolver : receive_list) {
- auto control = resolver.Resolve();
- log += " ";
- if (!control) {
- log += "(deleted)";
- continue;
- }
- log += control->GetDebugId();
- EventArgs event_args(control, original_sender,
- std::forward<Args>(args)...);
- (control->*event_ptr)()->bubble_.Raise(event_args);
- if (event_args.IsHandled()) {
- log += " marked as handled.";
- break;
- }
- }
- }
-
- log += "\n\tDirect:";
- // direct
- for (auto resolver : receive_list) {
- auto control = resolver.Resolve();
- log += " ";
- if (!control) {
- log += "(deleted)";
- continue;
- }
- log += control->GetDebugId();
- EventArgs event_args(control, original_sender,
- std::forward<Args>(args)...);
- (control->*event_ptr)()->direct_.Raise(event_args);
- }
- }
-
- void UpdateCursor();
- void NotifyControlDestroyed(Control* control);
-
private:
- int event_handling_count_;
-
- std::unique_ptr<platform::gui::INativeWindow> native_window_;
-
- Control* focus_control_;
- Control* mouse_hover_control_;
- Control* mouse_captured_control_;
-
- std::shared_ptr<platform::gui::ICursor> override_cursor_;
-
- bool layout_prefer_to_fill_window_;
-
- platform::gui::TimerAutoCanceler repaint_schedule_canceler_;
- platform::gui::TimerAutoCanceler relayout_schedule_canceler_;
+ std::shared_ptr<ControlHost> control_host_;
Control* attached_control_;
diff --git a/include/cru/ui/events/RoutedEvent.h b/include/cru/ui/events/RoutedEvent.h
index 58e50d63..b14b6e42 100644
--- a/include/cru/ui/events/RoutedEvent.h
+++ b/include/cru/ui/events/RoutedEvent.h
@@ -8,7 +8,7 @@ namespace cru::ui::events {
// EventArgs must be reference because the IsHandled property must be settable.
template <typename TEventArgs>
class CRU_UI_API RoutedEvent {
- friend controls::Window;
+ friend controls::ControlHost;
public:
static_assert(std::is_base_of_v<UiEventArgs, TEventArgs>,
diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h
index c299ea24..4c19ad3e 100644
--- a/include/cru/ui/render/RenderObject.h
+++ b/include/cru/ui/render/RenderObject.h
@@ -137,7 +137,7 @@ class CRU_UI_API RenderObject : public Object {
virtual RenderObject* HitTest(const Point& point) = 0;
public:
- controls::Window* GetWindow();
+ controls::ControlHost* GetControlHost();
void InvalidateLayout();
void InvalidatePaint();