aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/cru/base/Event.h18
-rw-r--r--include/cru/base/SelfResolvable.h153
-rw-r--r--include/cru/ui/controls/TextHostControlService.h2
3 files changed, 166 insertions, 7 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