diff options
-rw-r--r-- | include/cru/ui/DeleteLater.h | 5 | ||||
-rw-r--r-- | include/cru/ui/components/Component.h | 3 | ||||
-rw-r--r-- | include/cru/ui/controls/Control.h | 4 | ||||
-rw-r--r-- | include/cru/ui/controls/TextHostControlService.h | 3 | ||||
-rw-r--r-- | include/cru/ui/host/WindowHost.h | 10 | ||||
-rw-r--r-- | src/ui/DeleteLater.cpp | 2 | ||||
-rw-r--r-- | src/ui/components/Component.cpp | 6 | ||||
-rw-r--r-- | src/ui/controls/Control.cpp | 15 | ||||
-rw-r--r-- | src/ui/controls/TextHostControlService.cpp | 21 | ||||
-rw-r--r-- | src/ui/host/RoutedEventDispatch.h | 9 | ||||
-rw-r--r-- | src/ui/host/WindowHost.cpp | 22 |
11 files changed, 78 insertions, 22 deletions
diff --git a/include/cru/ui/DeleteLater.h b/include/cru/ui/DeleteLater.h index 84c0e1c2..34c48c14 100644 --- a/include/cru/ui/DeleteLater.h +++ b/include/cru/ui/DeleteLater.h @@ -8,6 +8,9 @@ class DeleteLaterImpl { public: virtual ~DeleteLaterImpl(); void DeleteLater(); + + protected: + virtual void OnPrepareDelete(); }; template <typename T> @@ -19,7 +22,7 @@ template <typename T> using DeleteLaterPtr = std::unique_ptr<T, DeleteLaterDeleter<T>>; template <typename T, typename... Args> -DeleteLaterPtr<T> CreateDeleteLaterPtr(Args&&... args) { +DeleteLaterPtr<T> MakeDeleteLaterPtr(Args&&... args) { return DeleteLaterPtr<T>(new T(std::forward<Args>(args)...)); } } // namespace cru::ui diff --git a/include/cru/ui/components/Component.h b/include/cru/ui/components/Component.h index 2625f445..a2f83149 100644 --- a/include/cru/ui/components/Component.h +++ b/include/cru/ui/components/Component.h @@ -30,6 +30,9 @@ class CRU_UI_API Component : public Object, if (delete_by_parent_) delete this; } + protected: + void OnPrepareDelete() override; + private: bool delete_by_parent_ = false; }; diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h index 8c9f11d3..38a9a86b 100644 --- a/include/cru/ui/controls/Control.h +++ b/include/cru/ui/controls/Control.h @@ -23,6 +23,8 @@ class CRU_UI_API Control : public Object, public DeleteLaterImpl { friend class RootControl; + CRU_DEFINE_CLASS_LOG_TAG(u"Control") + protected: Control(); @@ -174,6 +176,8 @@ class CRU_UI_API Control : public Object, protected: virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) } + void OnPrepareDelete() override; + private: void OnParentChangedCore(Control* old_parent, Control* new_parent); void OnWindowHostChangedCore(host::WindowHost* old_host, diff --git a/include/cru/ui/controls/TextHostControlService.h b/include/cru/ui/controls/TextHostControlService.h index dec9b3e0..ae86e1e9 100644 --- a/include/cru/ui/controls/TextHostControlService.h +++ b/include/cru/ui/controls/TextHostControlService.h @@ -3,6 +3,7 @@ #include "cru/platform/gui/InputMethod.h" #include "cru/platform/gui/TimerHelper.h" #include "cru/platform/gui/UiApplication.h" +#include "cru/ui/DeleteLater.h" #include "cru/ui/controls/Control.h" #include "cru/ui/helper/ShortcutHub.h" @@ -217,6 +218,6 @@ class CRU_UI_API TextHostControlService : public Object { bool mouse_move_selecting_ = false; bool context_menu_dirty_ = true; - std::unique_ptr<components::PopupMenu> context_menu_; + DeleteLaterPtr<components::PopupMenu> context_menu_; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/host/WindowHost.h b/include/cru/ui/host/WindowHost.h index d88482c4..0d2e0341 100644 --- a/include/cru/ui/host/WindowHost.h +++ b/include/cru/ui/host/WindowHost.h @@ -21,6 +21,14 @@ class CRU_UI_API WindowHost : public Object, public SelfResolvable<WindowHost> { friend controls::Control; CRU_DEFINE_CLASS_LOG_TAG(u"WindowHost") + private: + static int event_handling_depth_; + + public: + static bool IsInEventHandling() { return event_handling_depth_ > 0; } + static void EnterEventHandling(); + static void LeaveEventHandling(); + public: explicit WindowHost(controls::Control* root_control); @@ -139,6 +147,8 @@ class CRU_UI_API WindowHost : public Object, public SelfResolvable<WindowHost> { const Point& point, bool no_leave, bool no_enter); + void OnControlDetach(controls::Control* control); + private: controls::Control* root_control_ = nullptr; render::RenderObject* root_render_object_ = nullptr; diff --git a/src/ui/DeleteLater.cpp b/src/ui/DeleteLater.cpp index 8a40642f..f8911ae1 100644 --- a/src/ui/DeleteLater.cpp +++ b/src/ui/DeleteLater.cpp @@ -9,4 +9,6 @@ DeleteLaterImpl::~DeleteLaterImpl() {} void DeleteLaterImpl::DeleteLater() { GetUiApplication()->SetImmediate([this] { delete this; }); } + +void DeleteLaterImpl::OnPrepareDelete() {} } // namespace cru::ui diff --git a/src/ui/components/Component.cpp b/src/ui/components/Component.cpp index ddf65515..01db8710 100644 --- a/src/ui/components/Component.cpp +++ b/src/ui/components/Component.cpp @@ -1,5 +1,7 @@ #include "cru/ui/components/Component.h" +#include "cru/ui/controls/Control.h" + namespace cru::ui::components { - -} +void Component::OnPrepareDelete() { GetRootControl()->RemoveFromParent(); } +} // namespace cru::ui::components diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp index f2d4760e..3b0d4be3 100644 --- a/src/ui/controls/Control.cpp +++ b/src/ui/controls/Control.cpp @@ -1,5 +1,6 @@ #include "cru/ui/controls/Control.h" +#include "cru/common/log/Logger.h" #include "cru/platform/gui/Cursor.h" #include "cru/platform/gui/UiApplication.h" #include "cru/ui/host/WindowHost.h" @@ -28,6 +29,11 @@ Control::Control() { } Control::~Control() { + if (host::WindowHost::IsInEventHandling()) { + CRU_LOG_ERROR( + u"Control destroyed during event handling. Please use DeleteLater."); + } + in_destruction_ = true; RemoveFromParent(); } @@ -120,12 +126,7 @@ void Control::OnParentChangedCore(Control* old_parent, Control* new_parent) { void Control::OnWindowHostChangedCore(host::WindowHost* old_host, host::WindowHost* new_host) { if (old_host != nullptr) { - if (old_host->GetMouseCaptureControl() == this) { - old_host->CaptureMouseFor(nullptr); - } - if (old_host->GetMouseHoverControl() == this) { - old_host->mouse_hover_control_ = nullptr; - } + old_host->OnControlDetach(this); } if (!in_destruction_) { @@ -136,4 +137,6 @@ void Control::OnWindowHostChangedCore(host::WindowHost* old_host, OnWindowHostChanged(old_host, new_host); } } + +void Control::OnPrepareDelete() { RemoveFromParent(); } } // namespace cru::ui::controls diff --git a/src/ui/controls/TextHostControlService.cpp b/src/ui/controls/TextHostControlService.cpp index 15d9102c..d6c40a36 100644 --- a/src/ui/controls/TextHostControlService.cpp +++ b/src/ui/controls/TextHostControlService.cpp @@ -2,9 +2,9 @@ #include "../Helper.h" #include "cru/common/Base.h" -#include "cru/common/log/Logger.h" #include "cru/common/String.h" #include "cru/common/StringUtil.h" +#include "cru/common/log/Logger.h" #include "cru/platform/graphics/Font.h" #include "cru/platform/gui/Base.h" #include "cru/platform/gui/Clipboard.h" @@ -15,6 +15,7 @@ #include "cru/platform/gui/Window.h" #include "cru/ui/Base.h" #include "cru/ui/DebugFlags.h" +#include "cru/ui/DeleteLater.h" #include "cru/ui/components/Menu.h" #include "cru/ui/events/UiEvents.h" #include "cru/ui/helper/ShortcutHub.h" @@ -149,7 +150,7 @@ std::vector<TextControlMovePattern> TextControlMovePattern::kDefaultPatterns = { TextHostControlService::TextHostControlService(gsl::not_null<Control*> control) : control_(control), text_host_control_(dynamic_cast<ITextHostControl*>(control.get())) { - context_menu_ = std::make_unique<components::PopupMenu>(); + context_menu_ = MakeDeleteLaterPtr<components::PopupMenu>(); SetUpShortcuts(); @@ -335,11 +336,12 @@ void TextHostControlService::SetCaretBlinkDuration(int milliseconds) { void TextHostControlService::ScrollToCaret() { if (const auto scroll_render_object = this->GetScrollRenderObject()) { - this->control_->GetWindowHost()->RunAfterLayoutStable( - [this, scroll_render_object]() { - const auto caret_rect = this->GetTextRenderObject()->GetCaretRect(); - scroll_render_object->ScrollToContain(caret_rect, Thickness{5.f}); - }); + auto window_host = this->control_->GetWindowHost(); + if (window_host) + window_host->RunAfterLayoutStable([this, scroll_render_object]() { + const auto caret_rect = this->GetTextRenderObject()->GetCaretRect(); + scroll_render_object->ScrollToContain(caret_rect, Thickness{5.f}); + }); } } @@ -465,8 +467,7 @@ void TextHostControlService::UpdateInputMethodPosition() { right_bottom.y += 5; if constexpr (debug_flags::text_service) { - CRU_LOG_DEBUG( - u"Calculate input method candidate window position: {}.", + CRU_LOG_DEBUG(u"Calculate input method candidate window position: {}.", right_bottom); } @@ -693,7 +694,7 @@ void TextHostControlService::SetUpShortcuts() { void TextHostControlService::OpenContextMenu(const Point& position, ContextMenuItem items) { - context_menu_ = std::make_unique<components::PopupMenu>(); + context_menu_ = MakeDeleteLaterPtr<components::PopupMenu>(); auto menu = context_menu_->GetMenu(); if (items & ContextMenuItem::kSelectAll) { menu->AddTextItem(u"Select All", [this] { this->SelectAll(); }); diff --git a/src/ui/host/RoutedEventDispatch.h b/src/ui/host/RoutedEventDispatch.h index 2f437b31..5d1c8ce5 100644 --- a/src/ui/host/RoutedEventDispatch.h +++ b/src/ui/host/RoutedEventDispatch.h @@ -3,10 +3,11 @@ #include "cru/common/log/Logger.h" #include "cru/ui/DebugFlags.h" #include "cru/ui/controls/Control.h" +#include "cru/ui/host/WindowHost.h" #include <vector> -namespace cru::ui { +namespace cru::ui::host { // Dispatch the event. // // This will raise routed event of the control and its parent and parent's @@ -40,6 +41,8 @@ void DispatchEvent( return; } + WindowHost::EnterEventHandling(); + std::vector<ObjectResolver<controls::Control>> receive_list; auto parent = original_sender; @@ -119,5 +122,7 @@ void DispatchEvent( if constexpr (debug_flags::routed_event) CRU_LOG_DEBUG(u"Routed event dispatch finished."); + + WindowHost::LeaveEventHandling(); } -} // namespace cru::ui +} // namespace cru::ui::host diff --git a/src/ui/host/WindowHost.cpp b/src/ui/host/WindowHost.cpp index f4a9a928..737a7594 100644 --- a/src/ui/host/WindowHost.cpp +++ b/src/ui/host/WindowHost.cpp @@ -7,6 +7,7 @@ #include "cru/platform/gui/InputMethod.h" #include "cru/platform/gui/UiApplication.h" #include "cru/platform/gui/Window.h" +#include "cru/ui/Base.h" #include "cru/ui/DebugFlags.h" #include "cru/ui/host/LayoutPaintCycler.h" #include "cru/ui/render/MeasureRequirement.h" @@ -105,6 +106,15 @@ inline void BindNativeEvent( } } // namespace +int WindowHost::event_handling_depth_ = 0; + +void WindowHost::EnterEventHandling() { event_handling_depth_++; } + +void WindowHost::LeaveEventHandling() { + Expects(event_handling_depth_ > 0); + event_handling_depth_--; +} + WindowHost::WindowHost(controls::Control* root_control) : root_control_(root_control), focus_control_(root_control) { root_render_object_ = root_control->GetRenderObject(); @@ -438,4 +448,16 @@ void WindowHost::SetOverrideCursor( override_cursor_ = cursor; UpdateCursor(); } + +void WindowHost::OnControlDetach(controls::Control* control) { + if (GetFocusControl() == control) { + SetFocusControl(nullptr); + } + if (GetMouseCaptureControl() == control) { + CaptureMouseFor(nullptr); + } + if (GetMouseHoverControl() == control) { + mouse_hover_control_ = HitTest(GetNativeWindow()->GetMousePosition()); + } +} } // namespace cru::ui::host |