#pragma once #include #include #include #include #include #include "ResourceObserver.h" class ResourceHandler; namespace Detail { template struct Function : public Function {}; template struct Function : public Function {}; template struct Function { using ReturnType = Ret; using Argument = Arg; }; template struct Function : public Function {}; template struct Function : public Function {}; template struct Function : public Function {}; template struct Function : public Function {}; } /** Frontend class for resources * * Usage: * Resource::create("icon:noaccount")->applyTo(accountsAction); * Resource::create("web:http://asdf.com/image.png")->applyTo(imageLbl)->placeholder(Resource::create("icon:loading")); * * Memory management: * Resource caches ResourcePtrs using weak pointers, so while a resource is still existing * when a new resource is created the resources will be the same (including the same handler). * * ResourceObservers keep a shared pointer to the resource, as does the Resource itself to it's * placeholder (if present). This means a resource stays valid while it's still used ("applied to" etc.) * by something. When nothing uses it anymore it gets deleted. * * \note Always pass resource around using ResourcePtr! Copy and move constructors are disabled for a reason. */ class Resource : public std::enable_shared_from_this { explicit Resource(const QString &resource); Resource(const Resource &) = delete; Resource(Resource &&) = delete; public: using Ptr = std::shared_ptr; ~Resource(); /// The returned pointer needs to be stored until either Resource::then is called, or it is used as the argument to Resource::placeholder. static Ptr create(const QString &resource); /// This can e.g. be used to set a local icon as the placeholder while a slow (remote) icon is fetched Ptr placeholder(Ptr other); /// Use these functions to specify what should happen when e.g. the resource changes Ptr applyTo(ResourceObserver *observer); Ptr applyTo(QObject *target, const char *property = nullptr); template Ptr then(Func &&func) { using Arg = typename std::remove_cv< typename std::remove_reference::Argument>::type >::type; return applyTo(new FunctionResourceObserver< typename Detail::Function::ReturnType, Arg, Func >(std::forward(func))); } /// Retrieve the currently active resource. If it's type is different from T a conversion will be attempted. template T getResource() const { return getResourceInternal(qMetaTypeId()).template value(); } QVariant getResourceInternal(const int typeId) const; template static void registerHandler(const QString &id) { m_handlers.insert(id, [](const QString &res) { return std::make_shared(res); }); } template static void registerTransformer(Func &&func) { using Out = typename Detail::Function::ReturnType; using In = typename std::remove_cv::Argument>::type>::type; static_assert(!std::is_same::value, "It does not make sense to transform a value to itself"); m_transfomers.insert(qMakePair(qMetaTypeId(), qMetaTypeId()), [func](const QVariant &in) { return QVariant::fromValue(func(in.value())); }); } private: friend class ResourceHandler; void reportResult(); void reportFailure(const QString &reason); void reportProgress(const int progress); friend class ResourceObserver; void notifyObserverDeleted(ResourceObserver *observer); private: QList m_observers; std::shared_ptr m_handler = nullptr; Ptr m_placeholder = nullptr; // a list of resource handler factories, registered using registerHandler static QMap(const QString &)>> m_handlers; // a list of resource transformers, registered using registerTransformer static QMap, std::function> m_transfomers; static QMap> m_resources; };