aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/ui/DeleteLater.h5
-rw-r--r--include/cru/ui/components/Component.h3
-rw-r--r--include/cru/ui/controls/Control.h4
-rw-r--r--include/cru/ui/controls/TextHostControlService.h3
-rw-r--r--include/cru/ui/host/WindowHost.h10
-rw-r--r--src/ui/DeleteLater.cpp2
-rw-r--r--src/ui/components/Component.cpp6
-rw-r--r--src/ui/controls/Control.cpp15
-rw-r--r--src/ui/controls/TextHostControlService.cpp21
-rw-r--r--src/ui/host/RoutedEventDispatch.h9
-rw-r--r--src/ui/host/WindowHost.cpp22
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