2015-05-28 18:38:29 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <QString>
|
|
|
|
#include <QMap>
|
|
|
|
#include <QVariant>
|
|
|
|
#include <functional>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
#include "ResourceObserver.h"
|
2015-06-06 11:30:49 +01:00
|
|
|
#include "TypeMagic.h"
|
2015-05-28 18:38:29 +01:00
|
|
|
|
|
|
|
class ResourceHandler;
|
|
|
|
|
|
|
|
/** 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.
|
|
|
|
*
|
2015-06-06 11:30:49 +01:00
|
|
|
* @note Always pass resource around using Resource::Ptr! Copy and move constructors are disabled for a reason.
|
2015-05-28 18:38:29 +01:00
|
|
|
*/
|
|
|
|
class Resource : public std::enable_shared_from_this<Resource>
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
// only allow creation from Resource::create and disallow passing around non-pointers
|
2015-05-28 18:38:29 +01:00
|
|
|
explicit Resource(const QString &resource);
|
|
|
|
Resource(const Resource &) = delete;
|
|
|
|
Resource(Resource &&) = delete;
|
|
|
|
public:
|
|
|
|
using Ptr = std::shared_ptr<Resource>;
|
|
|
|
|
|
|
|
~Resource();
|
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
/// The returned pointer needs to be stored until either Resource::applyTo or Resource::then is called, or it is passed as
|
|
|
|
/// a placeholder to Resource::create itself.
|
|
|
|
static Ptr create(const QString &resource, Ptr placeholder = nullptr);
|
2015-05-28 18:38:29 +01:00
|
|
|
|
|
|
|
/// 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<typename Func>
|
|
|
|
Ptr then(Func &&func)
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
// Arg will be the functions argument with references and cv-qualifiers (const, volatile) removed
|
|
|
|
using Arg = TypeMagic::CleanType<typename TypeMagic::Function<Func>::Argument>;
|
|
|
|
// Ret will be the functions return type
|
|
|
|
using Ret = typename TypeMagic::Function<Func>::ReturnType;
|
|
|
|
|
|
|
|
// FunctionResourceObserver<ReturnType, ArgumentType, FunctionSignature>
|
|
|
|
return applyTo(new FunctionResourceObserver<Ret, Arg, Func>(std::forward<Func>(func)));
|
2015-05-28 18:38:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve the currently active resource. If it's type is different from T a conversion will be attempted.
|
|
|
|
template<typename T>
|
|
|
|
T getResource() const { return getResourceInternal(qMetaTypeId<T>()).template value<T>(); }
|
2015-06-06 11:30:49 +01:00
|
|
|
|
|
|
|
/// @internal Used by ResourceObserver and ResourceProxyModel
|
2015-05-28 18:38:29 +01:00
|
|
|
QVariant getResourceInternal(const int typeId) const;
|
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
/** Register a new ResourceHandler. T needs to inherit from ResourceHandler
|
|
|
|
* Usage: Resource::registerHandler<MyResourceHandler>("myid");
|
|
|
|
*/
|
2015-05-28 18:38:29 +01:00
|
|
|
template<typename T>
|
|
|
|
static void registerHandler(const QString &id)
|
|
|
|
{
|
|
|
|
m_handlers.insert(id, [](const QString &res) { return std::make_shared<T>(res); });
|
|
|
|
}
|
2015-06-06 11:30:49 +01:00
|
|
|
/** Register a new resource transformer
|
|
|
|
* Resource transformers are functions that are responsible for converting between different types,
|
|
|
|
* for example converting from a QByteArray to a QPixmap. They are registered "externally" because not
|
|
|
|
* all types might be available in this library, for example gui types like QPixmap.
|
|
|
|
*
|
|
|
|
* Usage: Resource::registerTransformer([](const InputType &type) { return OutputType(type); });
|
|
|
|
* This assumes that OutputType has a constructor that takes InputType as an argument. More
|
|
|
|
* complicated transformers can of course also be registered.
|
|
|
|
*
|
|
|
|
* When a ResourceObserver requests a type that's different from the actual resource type, a matching
|
|
|
|
* transformer will be looked up from the list of transformers.
|
|
|
|
* @note Only one-stage transforms will be performed (you can't registerTransformers for QString => int
|
|
|
|
* and int => float and expect QString to automatically be transformed into a float.
|
|
|
|
*/
|
2015-05-28 18:38:29 +01:00
|
|
|
template<typename Func>
|
|
|
|
static void registerTransformer(Func &&func)
|
|
|
|
{
|
2015-06-06 11:30:49 +01:00
|
|
|
using Out = typename TypeMagic::Function<Func>::ReturnType;
|
|
|
|
using In = TypeMagic::CleanType<typename TypeMagic::Function<Func>::Argument>;
|
2015-05-28 18:38:29 +01:00
|
|
|
static_assert(!std::is_same<Out, In>::value, "It does not make sense to transform a value to itself");
|
|
|
|
m_transfomers.insert(qMakePair(qMetaTypeId<In>(), qMetaTypeId<Out>()), [func](const QVariant &in)
|
|
|
|
{
|
|
|
|
return QVariant::fromValue<Out>(func(in.value<In>()));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
private: // half private, implementation details
|
2015-05-28 18:38:29 +01:00
|
|
|
friend class ResourceHandler;
|
2015-06-06 11:30:49 +01:00
|
|
|
// the following three functions are called by ResourceHandlers
|
|
|
|
/** Notifies the observers. They will call Resource::getResourceInternal which will call ResourceHandler::result
|
|
|
|
* or delegate to it's placeholder.
|
|
|
|
*/
|
2015-05-28 18:38:29 +01:00
|
|
|
void reportResult();
|
|
|
|
void reportFailure(const QString &reason);
|
|
|
|
void reportProgress(const int progress);
|
|
|
|
|
|
|
|
friend class ResourceObserver;
|
2015-06-06 11:30:49 +01:00
|
|
|
/// Removes observer from the list of observers so that we don't attempt to notify something that doesn't exist
|
2015-05-28 18:38:29 +01:00
|
|
|
void notifyObserverDeleted(ResourceObserver *observer);
|
|
|
|
|
2015-06-06 11:30:49 +01:00
|
|
|
private: // truly private
|
2015-05-28 18:38:29 +01:00
|
|
|
QList<ResourceObserver *> m_observers;
|
|
|
|
std::shared_ptr<ResourceHandler> m_handler = nullptr;
|
|
|
|
Ptr m_placeholder = nullptr;
|
2015-06-06 11:30:49 +01:00
|
|
|
const QString m_resource;
|
|
|
|
|
|
|
|
static QString storageIdentifier(const QString &id, Ptr placeholder = nullptr);
|
|
|
|
QString storageIdentifier() const;
|
2015-05-28 18:38:29 +01:00
|
|
|
|
|
|
|
// a list of resource handler factories, registered using registerHandler
|
|
|
|
static QMap<QString, std::function<std::shared_ptr<ResourceHandler>(const QString &)>> m_handlers;
|
|
|
|
// a list of resource transformers, registered using registerTransformer
|
|
|
|
static QMap<QPair<int, int>, std::function<QVariant(QVariant)>> m_transfomers;
|
2015-06-06 11:30:49 +01:00
|
|
|
// a list of resources so that we can reuse them
|
2015-05-28 18:38:29 +01:00
|
|
|
static QMap<QString, std::weak_ptr<Resource>> m_resources;
|
|
|
|
};
|