diff options
32 files changed, 844 insertions, 830 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(); diff --git a/src/ThemeBuilder/components/StyleRuleEditor.cpp b/src/ThemeBuilder/components/StyleRuleEditor.cpp index f3de97b4..f57e4654 100644 --- a/src/ThemeBuilder/components/StyleRuleEditor.cpp +++ b/src/ThemeBuilder/components/StyleRuleEditor.cpp @@ -43,7 +43,7 @@ ui::style::StyleRule StyleRuleEditor::GetValue() const { void StyleRuleEditor::SetValue(const ui::style::StyleRule& style_rule, bool trigger_change) { - body_layout_.ClearChildren(); + body_layout_.RemoveAllChild(); condition_editor_ = components::conditions::CreateConditionEditor(style_rule.GetCondition()); styler_editor_ = diff --git a/src/ThemeBuilder/components/StyleRuleSetEditor.cpp b/src/ThemeBuilder/components/StyleRuleSetEditor.cpp index f2509f4f..fd159a44 100644 --- a/src/ThemeBuilder/components/StyleRuleSetEditor.cpp +++ b/src/ThemeBuilder/components/StyleRuleSetEditor.cpp @@ -80,7 +80,7 @@ void StyleRuleSetEditor::UpdateView( }); style_rule_editors_.insert(style_rule_editors_.cbegin() + i, std::move(style_rule_editor)); - rules_layout_.AddChildAt(style_rule_editors_.back()->GetRootControl(), + rules_layout_.InsertChildAt(style_rule_editors_.back()->GetRootControl(), i); } break; diff --git a/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp b/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp index b9b1fdef..d7324350 100644 --- a/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp +++ b/src/ThemeBuilder/components/conditions/CompoundConditionEditor.cpp @@ -63,7 +63,8 @@ CompoundConditionEditor::CompoundConditionEditor() { if (editor) { ConnectChangeEvent(editor.get()); editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { - auto index = this->children_container_.IndexOf(c->GetRootControl()); + auto index = + this->children_container_.IndexOfChild(c->GetRootControl()); this->children_.erase(this->children_.begin() + index); this->children_container_.RemoveChildAt(index); RaiseChangeEvent(); @@ -88,13 +89,13 @@ CompoundConditionEditor::GetChildren() { void CompoundConditionEditor::SetChildren( std::vector<ClonePtr<ui::style::Condition>> children, bool trigger_change) { - children_container_.ClearChildren(); + children_container_.RemoveAllChild(); children_.clear(); for (const auto& condition : children) { auto editor = CreateConditionEditor(condition.get()); ConnectChangeEvent(editor.get()); editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { - auto index = this->children_container_.IndexOf(c->GetRootControl()); + auto index = this->children_container_.IndexOfChild(c->GetRootControl()); this->children_.erase(this->children_.begin() + index); this->children_container_.RemoveChildAt(index); RaiseChangeEvent(); diff --git a/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp b/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp index 1a20bf70..90d19a68 100644 --- a/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp +++ b/src/ThemeBuilder/components/stylers/CompoundStylerEditor.cpp @@ -65,7 +65,8 @@ CompoundStylerEditor::CompoundStylerEditor() { if (editor) { ConnectChangeEvent(editor.get()); editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { - auto index = this->children_container_.IndexOf(c->GetRootControl()); + auto index = + this->children_container_.IndexOfChild(c->GetRootControl()); this->children_.erase(this->children_.begin() + index); this->children_container_.RemoveChildAt(index); RaiseChangeEvent(); @@ -94,7 +95,7 @@ void CompoundStylerEditor::SetValue(ui::style::CompoundStyler* value, auto editor = CreateStylerEditor(styler.get()); ConnectChangeEvent(editor.get()); editor->RemoveEvent()->AddSpyOnlyHandler([this, c = editor.get()] { - auto index = this->children_container_.IndexOf(c->GetRootControl()); + auto index = this->children_container_.IndexOfChild(c->GetRootControl()); this->children_.erase(this->children_.begin() + index); this->children_container_.RemoveChildAt(index); RaiseChangeEvent(); diff --git a/src/platform/gui/win/Window.cpp b/src/platform/gui/win/Window.cpp index dfbe0f96..38e4a0b6 100644 --- a/src/platform/gui/win/Window.cpp +++ b/src/platform/gui/win/Window.cpp @@ -79,7 +79,6 @@ WinNativeWindow::~WinNativeWindow() { void WinNativeWindow::Close() { if (hwnd_) ::DestroyWindow(hwnd_); - application_->UnregisterWindow(this); } void WinNativeWindow::SetParent(INativeWindow* parent) { diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 84849d44..e931b678 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -12,9 +12,9 @@ add_library(CruUi controls/CheckBox.cpp controls/Container.cpp controls/Control.cpp + controls/ControlHost.cpp controls/FlexLayout.cpp controls/IconButton.cpp - controls/NoChildControl.cpp controls/ScrollView.cpp controls/StackLayout.cpp controls/TextBlock.cpp diff --git a/src/ui/components/Menu.cpp b/src/ui/components/Menu.cpp index a80ce92f..380da911 100644 --- a/src/ui/components/Menu.cpp +++ b/src/ui/components/Menu.cpp @@ -3,6 +3,7 @@ #include "cru/ui/ThemeManager.h" #include "cru/ui/controls/Button.h" #include "cru/ui/controls/Control.h" +#include "cru/ui/controls/ControlHost.h" #include "cru/ui/controls/FlexLayout.h" #include "cru/ui/controls/TextBlock.h" #include "cru/ui/controls/Window.h" @@ -38,7 +39,7 @@ void Menu::AddItemAt(Component* item, Index index) { Expects(index >= 0 && index <= GetItemCount()); items_.insert(items_.cbegin() + index, item); - container_.AddChildAt(item->GetRootControl(), index); + container_.InsertChildAt(item->GetRootControl(), index); } Component* Menu::RemoveItemAt(Index index) { @@ -53,7 +54,7 @@ Component* Menu::RemoveItemAt(Index index) { } void Menu::ClearItems() { - container_.ClearChildren(); + container_.RemoveAllChild(); for (auto item : items_) { item->DeleteIfDeleteByParent(); @@ -79,7 +80,7 @@ PopupMenu::PopupMenu(controls::Control* attached_control) menu_.SetOnItemClick([this](Index) { popup_->GetNativeWindow()->Close(); }); popup_ = controls::Window::CreatePopup(); popup_->SetAttachedControl(attached_control); - popup_->AddChildAt(menu_.GetRootControl(), 0); + popup_->InsertChildAt(menu_.GetRootControl(), 0); } PopupMenu::~PopupMenu() { delete popup_; } @@ -94,7 +95,7 @@ void PopupMenu::SetPosition(const Point& position) { void PopupMenu::Show() { auto native_window = popup_->GetNativeWindow(); native_window->SetVisibility(platform::gui::WindowVisibilityType::Show); - popup_->RelayoutWithSize(Size::Infinite(), true); + popup_->GetControlHost()->RelayoutWithSize(Size::Infinite(), true); native_window->RequestFocus(); native_window->SetToForeground(); } diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp index 9c0fc537..02148b72 100644 --- a/src/ui/controls/Control.cpp +++ b/src/ui/controls/Control.cpp @@ -1,11 +1,12 @@ #include "cru/ui/controls/Control.h" +#include "cru/base/Base.h" #include "cru/base/log/Logger.h" -#include "cru/ui/controls/Window.h" - #include "cru/platform/gui/Cursor.h" #include "cru/platform/gui/UiApplication.h" +#include "cru/ui/controls/ControlHost.h" #include "cru/ui/style/StyleRuleSet.h" +#include <algorithm> #include <format> namespace cru::ui::controls { @@ -17,25 +18,14 @@ Control::Control() { style_rule_set_ = std::make_shared<style::StyleRuleSet>(); style_rule_set_bind_ = std::make_unique<style::StyleRuleSetBind>(this, style_rule_set_); - - MouseEnterEvent()->Direct()->AddHandler( - [this](events::MouseEventArgs&) { this->is_mouse_over_ = true; }); - - MouseLeaveEvent()->Direct()->AddHandler( - [this](events::MouseEventArgs&) { this->is_mouse_over_ = false; }); } Control::~Control() { - if (auto window = GetWindow()) { - if (window->IsInEventHandling()) { - CRU_LOG_TAG_WARN( - "Better use delete later to delete control during event handling."); - } + if (host_ && host_->IsInEventHandling()) { + CRU_LOG_TAG_WARN( + "Better use delete later to delete control during event handling."); } - if (auto window = GetWindow()) { - window->NotifyControlDestroyed(this); - } RemoveFromParent(); } @@ -44,23 +34,9 @@ std::string Control::GetDebugId() const { static_cast<const void*>(this)); } -Window* Control::GetWindow() { - auto parent = this; - while (parent) { - if (auto window = dynamic_cast<Window*>(parent)) { - return window; - } - parent = parent->GetParent(); - } - return nullptr; -} +ControlHost* Control::GetControlHost() { return host_; } -void Control::SetParent(Control* parent) { - if (parent_ == parent) return; - auto old_parent = parent_; - parent_ = parent; - OnParentChanged(old_parent, parent); -} +Control* Control::GetParent() { return parent_; } bool Control::HasAncestor(Control* control) { auto parent = this; @@ -71,6 +47,21 @@ bool Control::HasAncestor(Control* control) { return false; } +const std::vector<Control*>& Control::GetChildren() { return children_; } + +void Control::RemoveChild(Control* child) { + auto iter = std::ranges::find(children_, child); + if (iter != children_.cend()) { + RemoveChildAt(iter - children_.cbegin()); + } +} + +void Control::RemoveAllChild() { + while (!GetChildren().empty()) { + RemoveChildAt(GetChildren().size() - 1); + } +} + void Control::RemoveFromParent() { if (parent_) { parent_->RemoveChild(this); @@ -87,39 +78,85 @@ controls::Control* Control::HitTest(const Point& point) { return nullptr; } -bool Control::HasFocus() { - auto window = GetWindow(); - if (window == nullptr) return false; +void Control::InsertChildAt(Control* control, Index index) { + if (index < 0 || index > children_.size()) { + throw Exception("Child control index out of range."); + } + + if (control->parent_) { + throw Exception("Control already has a parent."); + } + + children_.insert(children_.cbegin() + index, control); + control->parent_ = this; + + TraverseDescendents([this](Control* control) { control->host_ = host_; }, + false); + if (host_) { + host_->NotifyControlParentChange(control, nullptr, this); + } + control->OnParentChanged(nullptr, this); + OnChildInserted(control, index); - return window->GetFocusControl() == this; + if (host_) { + host_->InvalidateLayout(); + } } -bool Control::CaptureMouse() { - auto window = GetWindow(); - if (window == nullptr) return false; +void Control::RemoveChildAt(Index index) { + if (index < 0 || index >= children_.size()) { + throw Exception("Child control index out of range."); + } + + auto control = children_[index]; + children_.erase(children_.cbegin() + index); + control->parent_ = nullptr; + TraverseDescendents([this](Control* control) { control->host_ = nullptr; }, + false); + if (host_) { + host_->NotifyControlParentChange(control, this, nullptr); + } + control->OnParentChanged(this, nullptr); + OnChildRemoved(control, index); - return window->SetMouseCaptureControl(this); + if (host_) { + host_->InvalidateLayout(); + } +} + +void Control::AddChild(Control* control) { + InsertChildAt(control, GetChildren().size()); +} + +bool Control::HasFocus() { + if (!host_) return false; + return host_->GetFocusControl() == this; } void Control::SetFocus() { - auto window = GetWindow(); - if (window == nullptr) return; + if (!host_) return; + host_->SetFocusControl(this); +} - window->SetFocusControl(this); +bool Control::IsMouseOver() { + if (!host_) return false; + return host_->GetMouseHoverControl() == this; +} + +bool Control::CaptureMouse() { + if (!host_) return false; + return host_->SetMouseCaptureControl(this); } bool Control::ReleaseMouse() { - auto window = GetWindow(); - if (window == nullptr) return false; - if (window->GetMouseCaptureControl() != this) return false; - return window->SetMouseCaptureControl(nullptr); + if (!host_) return false; + if (!IsMouseCaptured()) return false; + return host_->SetMouseCaptureControl(nullptr); } bool Control::IsMouseCaptured() { - auto window = GetWindow(); - if (window == nullptr) return false; - - return window->GetMouseCaptureControl() == this; + if (!host_) return false; + return host_->GetMouseCaptureControl() == this; } std::shared_ptr<ICursor> Control::GetCursor() { return cursor_; } @@ -137,13 +174,16 @@ 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(); + if (host_) { + host_->UpdateCursor(); } } std::shared_ptr<style::StyleRuleSet> Control::GetStyleRuleSet() { return style_rule_set_; } + +void Control::OnParentChanged(Control* old_parent, Control* new_parent) {} +void Control::OnChildInserted(Control* control, Index index) {} +void Control::OnChildRemoved(Control* control, Index index) {} } // namespace cru::ui::controls diff --git a/src/ui/controls/ControlHost.cpp b/src/ui/controls/ControlHost.cpp new file mode 100644 index 00000000..09639465 --- /dev/null +++ b/src/ui/controls/ControlHost.cpp @@ -0,0 +1,424 @@ +#include "cru/ui/controls/ControlHost.h" + +#include "cru/platform/gui/UiApplication.h" +#include "cru/platform/gui/Window.h" +#include "cru/ui/Base.h" + +#include <cassert> + +namespace cru::ui::controls { +ControlHost::ControlHost(Control* root_control) + : event_handling_count_(0), + root_control_(root_control), + native_window_(CreateNativeWindow()), + focus_control_(root_control), + mouse_hover_control_(nullptr), + mouse_captured_control_(nullptr), + layout_prefer_to_fill_window_(true) { + root_control->TraverseDescendents( + [this](Control* control) { control->host_ = this; }, true); +} + +ControlHost::~ControlHost() {} + +platform::gui::INativeWindow* ControlHost::GetNativeWindow() { + return native_window_.get(); +} + +namespace { +template <typename T> +inline void BindNativeEvent( + ControlHost* host, platform::gui::INativeWindow* native_window, + IEvent<T>* event, + void (ControlHost::*handler)(platform::gui::INativeWindow*, + typename IEvent<T>::Args)) { + event->AddHandler( + std::bind(handler, host, native_window, std::placeholders::_1)); +} +} // namespace + +namespace { +bool IsAncestor(Control* control, Control* ancestor) { + while (control != nullptr) { + if (control == ancestor) return true; + control = control->GetParent(); + } + return false; +} + +// Ancestor at last. +std::vector<Control*> GetAncestorList(Control* control) { + if (control == nullptr) return {}; + + std::vector<Control*> l; + while (control != nullptr) { + l.push_back(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.back() != right_list.back()) return nullptr; + + // find the last same control or the last control (one is ancestor of the + // other) + auto left_iter = left_list.crbegin(); + auto right_iter = right_list.crbegin(); + + while (true) { + if (left_iter == left_list.crend()) { + return left_list.front(); + } + if (right_iter == right_list.crend()) { + return right_list.front(); + } + if (*left_iter != *right_iter) { + return *(--left_iter); + } + ++left_iter; + ++right_iter; + } +} +} // namespace + +std::unique_ptr<platform::gui::INativeWindow> +ControlHost::CreateNativeWindow() { + const auto ui_application = platform::gui::IUiApplication::GetInstance(); + + auto native_window = ui_application->CreateWindow(); + assert(native_window); + + BindNativeEvent(this, native_window, native_window->DestroyEvent(), + &ControlHost::OnNativeDestroy); + BindNativeEvent(this, native_window, native_window->PaintEvent(), + &ControlHost::OnNativePaint); + BindNativeEvent(this, native_window, native_window->ResizeEvent(), + &ControlHost::OnNativeResize); + BindNativeEvent(this, native_window, native_window->FocusEvent(), + &ControlHost::OnNativeFocus); + BindNativeEvent(this, native_window, native_window->MouseEnterLeaveEvent(), + &ControlHost::OnNativeMouseEnterLeave); + BindNativeEvent(this, native_window, native_window->MouseMoveEvent(), + &ControlHost::OnNativeMouseMove); + BindNativeEvent(this, native_window, native_window->MouseDownEvent(), + &ControlHost::OnNativeMouseDown); + BindNativeEvent(this, native_window, native_window->MouseUpEvent(), + &ControlHost::OnNativeMouseUp); + BindNativeEvent(this, native_window, native_window->MouseWheelEvent(), + &ControlHost::OnNativeMouseWheel); + BindNativeEvent(this, native_window, native_window->KeyDownEvent(), + &ControlHost::OnNativeKeyDown); + BindNativeEvent(this, native_window, native_window->KeyUpEvent(), + &ControlHost::OnNativeKeyUp); + + return std::unique_ptr<platform::gui::INativeWindow>(native_window); +} + +void ControlHost::InvalidatePaint() { + repaint_schedule_canceler_.Reset( + platform::gui::IUiApplication::GetInstance()->SetImmediate( + [this] { Repaint(); })); +} + +void ControlHost::InvalidateLayout() { + relayout_schedule_canceler_.Reset( + platform::gui::IUiApplication::GetInstance()->SetImmediate( + [this] { Relayout(); })); +} + +bool ControlHost::IsLayoutPreferToFillWindow() const { + return layout_prefer_to_fill_window_; +} + +void ControlHost::SetLayoutPreferToFillWindow(bool value) { + if (value == layout_prefer_to_fill_window_) return; + layout_prefer_to_fill_window_ = value; + InvalidateLayout(); +} + +void ControlHost::Repaint() { + auto painter = native_window_->BeginPaint(); + painter->Clear(colors::white); + root_control_->GetRenderObject()->Draw(painter.get()); + painter->EndDraw(); +} + +void ControlHost::Relayout() { + RelayoutWithSize(native_window_->GetClientSize()); +} + +void ControlHost::RelayoutWithSize(const Size& available_size, + bool set_window_size_to_fit_content) { + auto render_object = root_control_->GetRenderObject(); + render_object->Measure( + render::MeasureRequirement{ + available_size, + !set_window_size_to_fit_content && IsLayoutPreferToFillWindow() + ? render::MeasureSize(available_size) + : render::MeasureSize::NotSpecified()}, + render::MeasureSize::NotSpecified()); + + if (set_window_size_to_fit_content) { + native_window_->SetClientSize(render_object->GetDesiredSize()); + } + + render_object->Layout(Point{}); + CRU_LOG_TAG_DEBUG("A relayout is finished."); + + AfterLayoutEvent_.Raise(nullptr); + + InvalidatePaint(); +} + +Control* ControlHost::GetFocusControl() { return focus_control_; } + +void ControlHost::SetFocusControl(Control* control) { + if (control == nullptr) control = root_control_; + if (focus_control_ == control) return; + + const auto old_focus_control = focus_control_; + + focus_control_ = control; + + DispatchFocusControlChangeEvent(old_focus_control, focus_control_, false); +} + +Control* ControlHost::GetMouseCaptureControl() { + return mouse_captured_control_; +} + +bool ControlHost::SetMouseCaptureControl(Control* control) { + if (!native_window_->CaptureMouse()) return false; + + if (control == mouse_captured_control_) return true; + + if (control == nullptr) { + native_window_->ReleaseMouse(); + 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; +} + +std::shared_ptr<platform::gui::ICursor> ControlHost::GetOverrideCursor() { + return override_cursor_; +} + +void ControlHost::SetOverrideCursor( + std::shared_ptr<platform::gui::ICursor> cursor) { + if (cursor == override_cursor_) return; + override_cursor_ = cursor; + UpdateCursor(); +} + +bool ControlHost::IsInEventHandling() { return event_handling_count_; } + +void ControlHost::OnNativeDestroy(platform::gui::INativeWindow* window, + std::nullptr_t) { + CRU_UNUSED(window) +} + +void ControlHost::OnNativePaint(platform::gui::INativeWindow* window, + std::nullptr_t) { + CRU_UNUSED(window) + InvalidatePaint(); +} + +void ControlHost::OnNativeResize(platform::gui::INativeWindow* window, + const Size& size) { + CRU_UNUSED(window) + CRU_UNUSED(size) + + InvalidateLayout(); +} + +void ControlHost::OnNativeFocus(platform::gui::INativeWindow* window, + platform::gui::FocusChangeType focus) { + CRU_UNUSED(window) + + focus == platform::gui::FocusChangeType::Gain + ? DispatchEvent(focus_control_, &Control::GainFocusEvent, nullptr, true) + : DispatchEvent(focus_control_, &Control::LoseFocusEvent, nullptr, true); +} + +void ControlHost::OnNativeMouseEnterLeave( + platform::gui::INativeWindow* window, + platform::gui::MouseEnterLeaveType type) { + CRU_UNUSED(window) + + if (type == platform::gui::MouseEnterLeaveType::Leave) { + DispatchEvent(mouse_hover_control_, &Control::MouseLeaveEvent, nullptr); + mouse_hover_control_ = nullptr; + } +} + +void ControlHost::OnNativeMouseMove(platform::gui::INativeWindow* window, + const Point& point) { + CRU_UNUSED(window) + + // Find the first control that hit test succeed. + const auto new_mouse_hover_control = root_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(o, &Control::MouseLeaveEvent, n); + } else { + DispatchEvent(n, &Control::MouseEnterEvent, o, point); + } + DispatchEvent(mouse_captured_control_, &Control::MouseMoveEvent, nullptr, + point); + UpdateCursor(); + return; + } + + DispatchMouseHoverControlChangeEvent( + old_mouse_hover_control, new_mouse_hover_control, point, false, false); + DispatchEvent(new_mouse_hover_control, &Control::MouseMoveEvent, nullptr, + point); + UpdateCursor(); +} + +void ControlHost::OnNativeMouseDown( + platform::gui::INativeWindow* window, + const platform::gui::NativeMouseButtonEventArgs& args) { + CRU_UNUSED(window) + + Control* control = mouse_captured_control_ + ? mouse_captured_control_ + : root_control_->HitTest(args.point); + DispatchEvent(control, &Control::MouseDownEvent, nullptr, args.point, + args.button, args.modifier); +} + +void ControlHost::OnNativeMouseUp( + platform::gui::INativeWindow* window, + const platform::gui::NativeMouseButtonEventArgs& args) { + CRU_UNUSED(window) + + Control* control = mouse_captured_control_ + ? mouse_captured_control_ + : root_control_->HitTest(args.point); + DispatchEvent(control, &Control::MouseUpEvent, nullptr, args.point, + args.button, args.modifier); +} + +void ControlHost::OnNativeMouseWheel( + platform::gui::INativeWindow* window, + const platform::gui::NativeMouseWheelEventArgs& args) { + CRU_UNUSED(window) + + Control* control = mouse_captured_control_ + ? mouse_captured_control_ + : root_control_->HitTest(args.point); + DispatchEvent(control, &Control::MouseWheelEvent, nullptr, args.point, + args.delta, args.modifier); +} + +void ControlHost::OnNativeKeyDown( + platform::gui::INativeWindow* window, + const platform::gui::NativeKeyEventArgs& args) { + CRU_UNUSED(window) + + DispatchEvent(focus_control_, &Control::KeyDownEvent, nullptr, args.key, + args.modifier); +} + +void ControlHost::OnNativeKeyUp(platform::gui::INativeWindow* window, + const platform::gui::NativeKeyEventArgs& args) { + CRU_UNUSED(window) + + DispatchEvent(focus_control_, &Control::KeyUpEvent, nullptr, args.key, + args.modifier); +} + +void ControlHost::DispatchFocusControlChangeEvent(Control* old_control, + Control* new_control, + bool is_window) { + if (new_control != old_control) { + const auto lowest_common_ancestor = + FindLowestCommonAncestor(old_control, new_control); + DispatchEvent(old_control, &Control::LoseFocusEvent, lowest_common_ancestor, + is_window); + DispatchEvent(new_control, &Control::GainFocusEvent, lowest_common_ancestor, + is_window); + } +} + +void ControlHost::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(old_control, &Control::MouseLeaveEvent, + lowest_common_ancestor); // dispatch mouse leave event. + if (!no_enter && new_control != nullptr) { + DispatchEvent(new_control, &Control::MouseEnterEvent, + lowest_common_ancestor, + point); // dispatch mouse enter event. + } + } +} + +void ControlHost::UpdateCursor() { + if (override_cursor_) { + native_window_->SetCursor(override_cursor_); + } else { + const auto capture = GetMouseCaptureControl(); + native_window_->SetCursor( + (capture ? capture : GetMouseHoverControl())->GetInheritedCursor()); + } +} + +void ControlHost::NotifyControlParentChange(Control* control, + Control* old_parent, + Control* new_parent) { + if (new_parent == nullptr) { + if (focus_control_->HasAncestor(control)) { + focus_control_ = old_parent; + } + + if (mouse_captured_control_->HasAncestor(control)) { + mouse_captured_control_ = old_parent; + } + + if (mouse_hover_control_->HasAncestor(control)) { + mouse_hover_control_ = old_parent; + } + } +} +} // namespace cru::ui::controls diff --git a/src/ui/controls/NoChildControl.cpp b/src/ui/controls/NoChildControl.cpp deleted file mode 100644 index 382a5d18..00000000 --- a/src/ui/controls/NoChildControl.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "cru/ui/controls/NoChildControl.h" - -namespace cru::ui::controls { -void NoChildControl::ForEachChild( - const std::function<void(Control*)>& callback) { - CRU_UNUSED(callback); -} - -void NoChildControl::RemoveChild(Control* child) { CRU_UNUSED(child); } -} // namespace cru::ui::controls diff --git a/src/ui/controls/TextHostControlService.cpp b/src/ui/controls/TextHostControlService.cpp index f51199c9..5908852a 100644 --- a/src/ui/controls/TextHostControlService.cpp +++ b/src/ui/controls/TextHostControlService.cpp @@ -13,7 +13,7 @@ #include "cru/ui/Base.h" #include "cru/ui/DebugFlags.h" #include "cru/ui/components/Menu.h" -#include "cru/ui/controls/Window.h" +#include "cru/ui/controls/ControlHost.h" #include "cru/ui/helper/ShortcutHub.h" #include "cru/ui/render/ScrollRenderObject.h" #include "cru/ui/render/TextRenderObject.h" @@ -293,9 +293,9 @@ void TextHostControlService::DeleteText(TextRange range, platform::gui::IInputMethodContext* TextHostControlService ::GetInputMethodContext() { - Window* window = this->control_->GetWindow(); - if (!window) return nullptr; - platform::gui::INativeWindow* native_window = window->GetNativeWindow(); + auto host = this->control_->GetControlHost(); + if (!host) return nullptr; + platform::gui::INativeWindow* native_window = host->GetNativeWindow(); if (!native_window) return nullptr; return native_window->GetInputMethodContext(); } @@ -588,11 +588,10 @@ void TextHostControlService::GainFocusHandler( this->ReplaceSelectedText(text); }); - auto window = control_->GetWindow(); - if (window) - input_method_context_event_guard_ += - window->AfterLayoutEvent()->AddHandler( - [this](auto) { this->UpdateInputMethodPosition(); }); + auto host = control_->GetControlHost(); + if (host) + input_method_context_event_guard_ += host->AfterLayoutEvent()->AddHandler( + [this](auto) { this->UpdateInputMethodPosition(); }); SetCaretVisible(true); } } diff --git a/src/ui/controls/TreeView.cpp b/src/ui/controls/TreeView.cpp index 659aef00..89613763 100644 --- a/src/ui/controls/TreeView.cpp +++ b/src/ui/controls/TreeView.cpp @@ -9,7 +9,7 @@ TreeViewItem::TreeViewItem(TreeView* tree_view, TreeViewItem* parent, TreeViewItem::~TreeViewItem() { if (control_) { - control_->SetParent(nullptr); + tree_view_->RemoveChild(control_); } for (auto item : children_) { @@ -46,12 +46,12 @@ void TreeViewItem::RemoveItem(Index position) { void TreeViewItem::SetControl(Control* control) { if (control_) { - control_->SetParent(nullptr); + tree_view_->RemoveChild(control); render_object_item_->SetRenderObject(nullptr); } control_ = control; if (control) { - control->SetParent(tree_view_); + tree_view_->AddChild(tree_view_); render_object_item_->SetRenderObject(control->GetRenderObject()); } } @@ -69,18 +69,10 @@ TreeView::TreeView() TreeView::~TreeView() {} -void TreeView::ForEachChild(const std::function<void(Control*)>& predicate) { - root_item_.TraverseDescendants([&predicate](TreeViewItem* item) { - if (auto control = item->GetControl()) { - predicate(control); - } - }); -} - -void TreeView::RemoveChild(Control* control) { - root_item_.TraverseDescendants([&control](TreeViewItem* item) { +void TreeView::OnChildRemoved(Control* control, Index index) { + root_item_.TraverseDescendants([control](TreeViewItem* item) { if (item->GetControl() == control) { - item->SetControl(nullptr); + item->RemoveFromParent(); } }); } diff --git a/src/ui/controls/Window.cpp b/src/ui/controls/Window.cpp index c82b2485..a5fbf05f 100644 --- a/src/ui/controls/Window.cpp +++ b/src/ui/controls/Window.cpp @@ -1,26 +1,18 @@ #include "cru/ui/controls/Window.h" - #include "cru/platform/gui/UiApplication.h" #include "cru/platform/gui/Window.h" #include "cru/ui/Base.h" +#include "cru/ui/controls/ControlHost.h" #include <cassert> namespace cru::ui::controls { Window::Window() - : event_handling_count_(0), - native_window_(CreateNativeWindow()), - focus_control_(this), - mouse_hover_control_(nullptr), - mouse_captured_control_(nullptr), - layout_prefer_to_fill_window_(true), - attached_control_(nullptr) { + : control_host_(new ControlHost(this)), attached_control_(nullptr) { GetContainerRenderObject()->SetDefaultHorizontalAlignment(Alignment::Stretch); GetContainerRenderObject()->SetDefaultVertialAlignment(Alignment::Stretch); } -Window::~Window() {} - Window* Window::CreatePopup() { auto window = new Window(); window->GetNativeWindow()->SetStyleFlag( @@ -36,409 +28,27 @@ void Window::SetAttachedControl(Control* control) { } platform::gui::INativeWindow* Window::GetNativeWindow() { - return native_window_.get(); + return control_host_->GetNativeWindow(); } void Window::SetGainFocusOnCreateAndDestroyWhenLoseFocus(bool value) { gain_focus_on_create_and_destroy_when_lose_focus_event_guard_.Clear(); if (value) { gain_focus_on_create_and_destroy_when_lose_focus_event_guard_ += - native_window_->VisibilityChangeEvent()->AddHandler( + GetNativeWindow()->VisibilityChangeEvent()->AddHandler( [this](platform::gui::WindowVisibilityType type) { if (type == platform::gui::WindowVisibilityType::Show) { - native_window_->RequestFocus(); + GetNativeWindow()->RequestFocus(); } }); gain_focus_on_create_and_destroy_when_lose_focus_event_guard_ += - native_window_->FocusEvent()->AddHandler( + GetNativeWindow()->FocusEvent()->AddHandler( [this](platform::gui::FocusChangeType type) { if (type == platform::gui::FocusChangeType::Lose) { - native_window_->Close(); + GetNativeWindow()->Close(); } }); } } - -namespace { -template <typename T> -inline void BindNativeEvent( - Window* window, platform::gui::INativeWindow* native_window, - IEvent<T>* event, - void (Window::*handler)(platform::gui::INativeWindow*, - typename IEvent<T>::Args)) { - event->AddHandler( - std::bind(handler, window, native_window, std::placeholders::_1)); -} -} // namespace - -namespace { -bool IsAncestor(Control* control, Control* ancestor) { - while (control != nullptr) { - if (control == ancestor) return true; - control = control->GetParent(); - } - return false; -} - -// Ancestor at last. -std::vector<Control*> GetAncestorList(Control* control) { - if (control == nullptr) return {}; - - std::vector<Control*> l; - while (control != nullptr) { - l.push_back(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.back() != right_list.back()) return nullptr; - - // find the last same control or the last control (one is ancestor of the - // other) - auto left_iter = left_list.crbegin(); - auto right_iter = right_list.crbegin(); - - while (true) { - if (left_iter == left_list.crend()) { - return left_list.front(); - } - if (right_iter == right_list.crend()) { - return right_list.front(); - } - if (*left_iter != *right_iter) { - return *(--left_iter); - } - ++left_iter; - ++right_iter; - } -} -} // namespace - -std::unique_ptr<platform::gui::INativeWindow> Window::CreateNativeWindow() { - const auto ui_application = platform::gui::IUiApplication::GetInstance(); - - auto native_window = ui_application->CreateWindow(); - assert(native_window); - - BindNativeEvent(this, native_window, native_window->DestroyEvent(), - &Window::OnNativeDestroy); - BindNativeEvent(this, native_window, native_window->PaintEvent(), - &Window::OnNativePaint); - BindNativeEvent(this, native_window, native_window->ResizeEvent(), - &Window::OnNativeResize); - BindNativeEvent(this, native_window, native_window->FocusEvent(), - &Window::OnNativeFocus); - BindNativeEvent(this, native_window, native_window->MouseEnterLeaveEvent(), - &Window::OnNativeMouseEnterLeave); - BindNativeEvent(this, native_window, native_window->MouseMoveEvent(), - &Window::OnNativeMouseMove); - BindNativeEvent(this, native_window, native_window->MouseDownEvent(), - &Window::OnNativeMouseDown); - BindNativeEvent(this, native_window, native_window->MouseUpEvent(), - &Window::OnNativeMouseUp); - BindNativeEvent(this, native_window, native_window->MouseWheelEvent(), - &Window::OnNativeMouseWheel); - BindNativeEvent(this, native_window, native_window->KeyDownEvent(), - &Window::OnNativeKeyDown); - BindNativeEvent(this, native_window, native_window->KeyUpEvent(), - &Window::OnNativeKeyUp); - - return std::unique_ptr<platform::gui::INativeWindow>(native_window); -} - -void Window::InvalidatePaint() { - repaint_schedule_canceler_.Reset( - platform::gui::IUiApplication::GetInstance()->SetImmediate( - [this] { Repaint(); })); -} - -void Window::InvalidateLayout() { - relayout_schedule_canceler_.Reset( - platform::gui::IUiApplication::GetInstance()->SetImmediate( - [this] { Relayout(); })); -} - -bool Window::IsLayoutPreferToFillWindow() const { - return layout_prefer_to_fill_window_; -} - -void Window::SetLayoutPreferToFillWindow(bool value) { - if (value == layout_prefer_to_fill_window_) return; - layout_prefer_to_fill_window_ = value; - InvalidateLayout(); -} - -void Window::Repaint() { - auto painter = native_window_->BeginPaint(); - painter->Clear(colors::white); - GetRenderObject()->Draw(painter.get()); - painter->EndDraw(); -} - -void Window::Relayout() { RelayoutWithSize(native_window_->GetClientSize()); } - -void Window::RelayoutWithSize(const Size& available_size, - bool set_window_size_to_fit_content) { - auto render_object = GetRenderObject(); - render_object->Measure( - render::MeasureRequirement{ - available_size, - !set_window_size_to_fit_content && IsLayoutPreferToFillWindow() - ? render::MeasureSize(available_size) - : render::MeasureSize::NotSpecified()}, - render::MeasureSize::NotSpecified()); - - if (set_window_size_to_fit_content) { - native_window_->SetClientSize(render_object->GetDesiredSize()); - } - - render_object->Layout(Point{}); - CRU_LOG_TAG_DEBUG("A relayout is finished."); - - AfterLayoutEvent_.Raise(nullptr); - - InvalidatePaint(); -} - -Control* Window::GetFocusControl() { return focus_control_; } - -void Window::SetFocusControl(Control* control) { - if (control == nullptr) control = this; - if (focus_control_ == control) return; - - const auto old_focus_control = focus_control_; - - focus_control_ = control; - - DispatchFocusControlChangeEvent(old_focus_control, focus_control_, false); -} - -Control* Window::GetMouseCaptureControl() { return mouse_captured_control_; } - -bool Window::SetMouseCaptureControl(Control* control) { - if (!native_window_->CaptureMouse()) return false; - - if (control == mouse_captured_control_) return true; - - if (control == nullptr) { - native_window_->ReleaseMouse(); - 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; -} - -std::shared_ptr<platform::gui::ICursor> Window::GetOverrideCursor() { - return override_cursor_; -} - -void Window::SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor) { - if (cursor == override_cursor_) return; - override_cursor_ = cursor; - UpdateCursor(); -} - -bool Window::IsInEventHandling() { return event_handling_count_; } - -void Window::OnNativeDestroy(platform::gui::INativeWindow* window, - std::nullptr_t) { - CRU_UNUSED(window) -} - -void Window::OnNativePaint(platform::gui::INativeWindow* window, - std::nullptr_t) { - CRU_UNUSED(window) - InvalidatePaint(); -} - -void Window::OnNativeResize(platform::gui::INativeWindow* window, - const Size& size) { - CRU_UNUSED(window) - CRU_UNUSED(size) - - InvalidateLayout(); -} - -void Window::OnNativeFocus(platform::gui::INativeWindow* window, - platform::gui::FocusChangeType focus) { - CRU_UNUSED(window) - - focus == platform::gui::FocusChangeType::Gain - ? DispatchEvent(focus_control_, &Control::GainFocusEvent, nullptr, true) - : DispatchEvent(focus_control_, &Control::LoseFocusEvent, nullptr, true); -} - -void Window::OnNativeMouseEnterLeave(platform::gui::INativeWindow* window, - platform::gui::MouseEnterLeaveType type) { - CRU_UNUSED(window) - - if (type == platform::gui::MouseEnterLeaveType::Leave) { - DispatchEvent(mouse_hover_control_, &Control::MouseLeaveEvent, nullptr); - mouse_hover_control_ = nullptr; - } -} - -void Window::OnNativeMouseMove(platform::gui::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(o, &Control::MouseLeaveEvent, n); - } else { - DispatchEvent(n, &Control::MouseEnterEvent, o, point); - } - DispatchEvent(mouse_captured_control_, &Control::MouseMoveEvent, nullptr, - point); - UpdateCursor(); - return; - } - - DispatchMouseHoverControlChangeEvent( - old_mouse_hover_control, new_mouse_hover_control, point, false, false); - DispatchEvent(new_mouse_hover_control, &Control::MouseMoveEvent, nullptr, - point); - UpdateCursor(); -} - -void Window::OnNativeMouseDown( - platform::gui::INativeWindow* window, - const platform::gui::NativeMouseButtonEventArgs& args) { - CRU_UNUSED(window) - - Control* control = - mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point); - DispatchEvent(control, &Control::MouseDownEvent, nullptr, args.point, - args.button, args.modifier); -} - -void Window::OnNativeMouseUp( - platform::gui::INativeWindow* window, - const platform::gui::NativeMouseButtonEventArgs& args) { - CRU_UNUSED(window) - - Control* control = - mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point); - DispatchEvent(control, &Control::MouseUpEvent, nullptr, args.point, - args.button, args.modifier); -} - -void Window::OnNativeMouseWheel( - platform::gui::INativeWindow* window, - const platform::gui::NativeMouseWheelEventArgs& args) { - CRU_UNUSED(window) - - Control* control = - mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point); - DispatchEvent(control, &Control::MouseWheelEvent, nullptr, args.point, - args.delta, args.modifier); -} - -void Window::OnNativeKeyDown(platform::gui::INativeWindow* window, - const platform::gui::NativeKeyEventArgs& args) { - CRU_UNUSED(window) - - DispatchEvent(focus_control_, &Control::KeyDownEvent, nullptr, args.key, - args.modifier); -} - -void Window::OnNativeKeyUp(platform::gui::INativeWindow* window, - const platform::gui::NativeKeyEventArgs& args) { - CRU_UNUSED(window) - - DispatchEvent(focus_control_, &Control::KeyUpEvent, nullptr, args.key, - args.modifier); -} - -void Window::DispatchFocusControlChangeEvent(Control* old_control, - Control* new_control, - bool is_window) { - if (new_control != old_control) { - const auto lowest_common_ancestor = - FindLowestCommonAncestor(old_control, new_control); - DispatchEvent(old_control, &Control::LoseFocusEvent, lowest_common_ancestor, - is_window); - DispatchEvent(new_control, &Control::GainFocusEvent, lowest_common_ancestor, - is_window); - } -} - -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(old_control, &Control::MouseLeaveEvent, - lowest_common_ancestor); // dispatch mouse leave event. - if (!no_enter && new_control != nullptr) { - DispatchEvent(new_control, &Control::MouseEnterEvent, - lowest_common_ancestor, - point); // dispatch mouse enter event. - } - } -} - -void Window::UpdateCursor() { - if (override_cursor_) { - native_window_->SetCursor(override_cursor_); - } else { - const auto capture = GetMouseCaptureControl(); - native_window_->SetCursor( - (capture ? capture : GetMouseHoverControl())->GetInheritedCursor()); - } -} - -void Window::NotifyControlDestroyed(Control* control) { - if (focus_control_->HasAncestor(control)) { - focus_control_ = control->GetParent(); - } - - if (mouse_captured_control_->HasAncestor(control)) { - mouse_captured_control_ = control->GetParent(); - } - - if (mouse_hover_control_->HasAncestor(control)) { - mouse_hover_control_ = control->GetParent(); - } -} } // namespace cru::ui::controls diff --git a/src/ui/events/MouseEventArgs.cpp b/src/ui/events/MouseEventArgs.cpp index bbbf5cd4..ff3a2372 100644 --- a/src/ui/events/MouseEventArgs.cpp +++ b/src/ui/events/MouseEventArgs.cpp @@ -1,7 +1,7 @@ #include "cru/ui/events/MouseEventArgs.h" #include "cru/ui/controls/Control.h" -#include "cru/ui/controls/Window.h" +#include "cru/ui/controls/ControlHost.h" #include "cru/ui/render/RenderObject.h" namespace cru::ui::events { @@ -17,9 +17,8 @@ Point MouseEventArgs::GetPointToContent( Point MouseEventArgs::GetPointOfScreen() const { auto sender = GetSender(); if (auto control = dynamic_cast<controls::Control*>(sender)) { - if (auto window = control->GetWindow()) - return GetPoint() + - window->GetNativeWindow()->GetClientRect().GetLeftTop(); + if (auto host = control->GetControlHost()) + return GetPoint() + host->GetNativeWindow()->GetClientRect().GetLeftTop(); } return GetPoint(); } diff --git a/src/ui/helper/ClickDetector.cpp b/src/ui/helper/ClickDetector.cpp index be1cbca2..0ac0515f 100644 --- a/src/ui/helper/ClickDetector.cpp +++ b/src/ui/helper/ClickDetector.cpp @@ -3,14 +3,13 @@ #include "cru/base/log/Logger.h" #include "cru/ui/DebugFlags.h" #include "cru/ui/controls/Control.h" -#include "cru/ui/controls/Window.h" +#include "cru/ui/controls/ControlHost.h" namespace cru::ui::helper { Point ClickEventArgs::GetDownPointOfScreen() const { - auto window = sender_->GetWindow(); - if (window != nullptr) { - return down_point_ + - window->GetNativeWindow()->GetClientRect().GetLeftTop(); + auto host = sender_->GetControlHost(); + if (host != nullptr) { + return down_point_ + host->GetNativeWindow()->GetClientRect().GetLeftTop(); } else { return down_point_; } diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index a4da2414..fbb7c292 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -4,7 +4,7 @@ #include "cru/platform/GraphicsBase.h" #include "cru/ui/DebugFlags.h" #include "cru/ui/controls/Control.h" -#include "cru/ui/controls/Window.h" +#include "cru/ui/controls/ControlHost.h" namespace cru::ui::render { const BoxConstraint BoxConstraint::kNotLimit{Size::kMax, Size::kZero}; @@ -265,21 +265,21 @@ Rect RenderObject::GetContentRect() const { return rect; } -controls::Window* RenderObject::GetWindow() { +controls::ControlHost* RenderObject::GetControlHost() { if (control_) { - return control_->GetWindow(); + return control_->GetControlHost(); } return nullptr; } void RenderObject::InvalidateLayout() { - if (auto window = GetWindow()) { - window->InvalidateLayout(); + if (auto host = GetControlHost()) { + host->InvalidateLayout(); } } void RenderObject::InvalidatePaint() { - if (auto window = GetWindow()) { + if (auto window = GetControlHost()) { window->InvalidatePaint(); } } diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index 3834fffb..4343f15a 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -8,7 +8,7 @@ #include "cru/platform/gui/Cursor.h" #include "cru/ui/Base.h" #include "cru/ui/ThemeManager.h" -#include "cru/ui/controls/Window.h" +#include "cru/ui/controls/ControlHost.h" #include "cru/ui/render/ScrollRenderObject.h" #include <algorithm> @@ -317,8 +317,8 @@ void ScrollBar::OnDraw(platform::graphics::IPainter* painter, void ScrollBar::SetCursor() { if (const auto control = render_object_->GetAttachedControl()) { - if (const auto window = control->GetWindow()) { - window->SetOverrideCursor( + if (const auto host = control->GetControlHost()) { + host->SetOverrideCursor( GetUiApplication()->GetCursorManager()->GetSystemCursor( platform::gui::SystemCursorType::Arrow)); cursor_overridden_ = true; @@ -329,8 +329,8 @@ void ScrollBar::SetCursor() { void ScrollBar::RestoreCursor() { if (cursor_overridden_) { if (const auto control = render_object_->GetAttachedControl()) { - if (const auto window = control->GetWindow()) { - window->SetOverrideCursor(nullptr); + if (const auto host = control->GetControlHost()) { + host->SetOverrideCursor(nullptr); } } cursor_overridden_ = false; |
