diff options
| -rw-r--r-- | include/cru/base/Event.h | 18 | ||||
| -rw-r--r-- | include/cru/base/SelfResolvable.h | 153 | ||||
| -rw-r--r-- | include/cru/ui/controls/TextHostControlService.h | 2 | ||||
| -rw-r--r-- | src/ui/components/Menu.cpp | 4 |
4 files changed, 168 insertions, 9 deletions
diff --git a/include/cru/base/Event.h b/include/cru/base/Event.h index c26bea83..0bf54475 100644 --- a/include/cru/base/Event.h +++ b/include/cru/base/Event.h @@ -1,5 +1,6 @@ #pragma once #include "Base.h" +#include "SelfResolvable.h" #include <algorithm> #include <cassert> @@ -11,7 +12,7 @@ namespace cru { class EventHandlerRevoker; -class EventBase : public Object { +class EventBase : public Object, public SelfResolvable<EventBase> { friend EventHandlerRevoker; public: @@ -34,23 +35,28 @@ class EventHandlerRevoker { friend EventBase; private: - EventHandlerRevoker(EventBase* event, EventBase::EventHandlerToken token) - : event_(event), token_(token) {} + EventHandlerRevoker(ObjectResolver<EventBase>&& resolver, + EventBase::EventHandlerToken token) + : resolver_(std::move(resolver)), token_(token) {} public: /** * Revoke the registered handler. If the event has already been destroyed or * the handler is already revoked, nothing will be done. */ - void operator()() const { event_->RemoveHandler(token_); } + void operator()() const { + if (const auto event = resolver_.Resolve()) { + event->RemoveHandler(token_); + } + } private: - EventBase* event_; + ObjectResolver<EventBase> resolver_; EventBase::EventHandlerToken token_; }; inline EventHandlerRevoker EventBase::CreateRevoker(EventHandlerToken token) { - return EventHandlerRevoker(this, token); + return EventHandlerRevoker(CreateResolver(), token); } struct IBaseEvent : public virtual Interface { diff --git a/include/cru/base/SelfResolvable.h b/include/cru/base/SelfResolvable.h new file mode 100644 index 00000000..84fa54f6 --- /dev/null +++ b/include/cru/base/SelfResolvable.h @@ -0,0 +1,153 @@ +#pragma once + +#include <cassert> +#include <functional> +#include <memory> +#include <type_traits> + +namespace cru { +template <typename T> +class SelfResolvable; + +template <typename T> +class ObjectResolver { + friend SelfResolvable<T>; + template <typename U> + friend class ObjectResolver; + + private: + template <typename U> + using Accessor_ = std::function<U*(const std::shared_ptr<void*>&)>; + using ThisAccessor_ = Accessor_<T>; + + explicit ObjectResolver(T* o) + : shared_object_ptr_(new void*(o)), + accessor_([](const std::shared_ptr<void*>& ptr) { + return static_cast<T*>(*ptr); + }) {} + explicit ObjectResolver(std::shared_ptr<void*> ptr, ThisAccessor_ accessor) + : shared_object_ptr_(std::move(ptr)), accessor_(std::move(accessor)) {} + + template <typename U> + static ThisAccessor_ CreateAccessor(Accessor_<U> parent_accessor) { + return [parent_accessor = + std::move(parent_accessor)](const std::shared_ptr<void*>& ptr) { + return static_cast<T*>(parent_accessor(ptr)); + }; + } + + public: + template <typename U, + typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> + ObjectResolver(const ObjectResolver<U>& other) + : shared_object_ptr_(other.shared_object_ptr_), + accessor_(CreateAccessor(other.accessor_)) {} + + template <typename U, + typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> + ObjectResolver(ObjectResolver<U>&& other) + : shared_object_ptr_(std::move(other.shared_object_ptr_)), + accessor_(CreateAccessor(std::move(other.accessor_))) {} + + ObjectResolver(const ObjectResolver&) = default; + ObjectResolver& operator=(const ObjectResolver&) = default; + ObjectResolver(ObjectResolver&&) = default; + ObjectResolver& operator=(ObjectResolver&&) = default; + ~ObjectResolver() = default; + + template <typename U, + typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> + ObjectResolver& operator=(const ObjectResolver<U>& other) { + if (this != &other) { + this->shared_object_ptr_ = other.shared_object_ptr_; + this->accessor_ = CreateAccessor(other.accessor_); + } + return *this; + } + + template <typename U, + typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> + ObjectResolver& operator=(ObjectResolver<U>&& other) { + if (this != &other) { + this->shared_object_ptr_ = std::move(other.shared_object_ptr_); + this->accessor_ = CreateAccessor(std::move(other.shared_object_ptr_)); + } + return *this; + } + + bool IsValid() const { return this->shared_object_ptr_ != nullptr; } + + T* Resolve() const { + assert(IsValid()); + return this->accessor_(this->shared_object_ptr_); + } + + /** + * @remarks So this class can be used as a functor. + */ + T* operator()() const { return Resolve(); } + + template <typename U, + typename = std::enable_if_t<std::is_convertible_v<T*, U*>>> + operator ObjectResolver<U>() const { + return ObjectResolver<U>(*this); + } + + private: + void SetResolvedObject(T* o) { + assert(IsValid()); + *this->shared_object_ptr_ = o; + } + + private: + std::shared_ptr<void*> shared_object_ptr_; + std::function<T*(const std::shared_ptr<void*>&)> accessor_; +}; + +/** + * @remarks + * This class is not copyable and movable since subclass is polymorphic and + * copying is then nonsense. However, you can even delete move capability in + * subclass because it may also be nonsense for subclass. The move capability is + * optional. + * + * Whether this class needs to be thread-safe still has to be considered. + */ +template <typename T> +class SelfResolvable { + public: + SelfResolvable() : resolver_(CastToSubClass()) {} + SelfResolvable(const SelfResolvable&) = delete; + SelfResolvable& operator=(const SelfResolvable&) = delete; + + // Resolvers to old object will resolve to new object. + SelfResolvable(SelfResolvable&& other) + : resolver_(std::move(other.resolver_)) { + this->resolver_.SetResolvedObject(CastToSubClass()); + } + + // Old resolvers for this object will resolve to nullptr. + // Other's resolvers will now resolve to this. + SelfResolvable& operator=(SelfResolvable&& other) { + if (this != &other) { + this->resolver_ = std::move(other.resolver_); + this->resolver_.SetResolvedObject(CastToSubClass()); + } + return *this; + } + + virtual ~SelfResolvable() { + if (this->resolver_.IsValid()) { + this->resolver_.SetResolvedObject(nullptr); + } + } + + ObjectResolver<T> CreateResolver() { return resolver_; } + + private: + T* CastToSubClass() { return static_cast<T*>(this); } + + private: + ObjectResolver<T> resolver_; +}; +} // namespace cru diff --git a/include/cru/ui/controls/TextHostControlService.h b/include/cru/ui/controls/TextHostControlService.h index 40ba017a..e57075d3 100644 --- a/include/cru/ui/controls/TextHostControlService.h +++ b/include/cru/ui/controls/TextHostControlService.h @@ -208,6 +208,6 @@ class CRU_UI_API TextHostControlService : public Object { // true if left mouse is down and selecting bool mouse_move_selecting_ = false; - DeleteLaterPtr<components::PopupMenu> context_menu_; + std::unique_ptr<components::PopupMenu> context_menu_; }; } // namespace cru::ui::controls diff --git a/src/ui/components/Menu.cpp b/src/ui/components/Menu.cpp index c89c7fc9..a80ce92f 100644 --- a/src/ui/components/Menu.cpp +++ b/src/ui/components/Menu.cpp @@ -53,13 +53,13 @@ Component* Menu::RemoveItemAt(Index index) { } void Menu::ClearItems() { + container_.ClearChildren(); + for (auto item : items_) { item->DeleteIfDeleteByParent(); } items_.clear(); - - container_.ClearChildren(); } void Menu::AddTextItemAt(std::string text, Index index, |
