From 47e37635f50c09b4f9a9ee7699e3120bab3e4088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 10 Apr 2016 04:29:29 +0200 Subject: NOISSUE split GUI stuff from logic library --- libraries/logic/resources/Resource.h | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 libraries/logic/resources/Resource.h (limited to 'libraries/logic/resources/Resource.h') diff --git a/libraries/logic/resources/Resource.h b/libraries/logic/resources/Resource.h new file mode 100644 index 00000000..63e97b88 --- /dev/null +++ b/libraries/logic/resources/Resource.h @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "ResourceObserver.h" +#include "TypeMagic.h" + +#include "multimc_logic_export.h" + +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. + * + * @note Always pass resource around using Resource::Ptr! Copy and move constructors are disabled for a reason. + */ +class MULTIMC_LOGIC_EXPORT Resource : public std::enable_shared_from_this +{ + // only allow creation from Resource::create and disallow passing around non-pointers + 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::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); + + /// 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) + { + // Arg will be the functions argument with references and cv-qualifiers (const, volatile) removed + using Arg = TypeMagic::CleanType::Argument>; + // Ret will be the functions return type + using Ret = typename TypeMagic::Function::ReturnType; + + // FunctionResourceObserver + return applyTo(new FunctionResourceObserver(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(); } + + /// @internal Used by ResourceObserver and ResourceProxyModel + QVariant getResourceInternal(const int typeId) const; + + /** Register a new ResourceHandler. T needs to inherit from ResourceHandler + * Usage: Resource::registerHandler("myid"); + */ + template + static void registerHandler(const QString &id) + { + m_handlers.insert(id, [](const QString &res) { return std::make_shared(res); }); + } + /** 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. + */ + template + static void registerTransformer(Func &&func) + { + using Out = typename TypeMagic::Function::ReturnType; + using In = TypeMagic::CleanType::Argument>; + 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: // half private, implementation details + friend class ResourceHandler; + // 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. + */ + void reportResult(); + void reportFailure(const QString &reason); + void reportProgress(const int progress); + + friend class ResourceObserver; + /// Removes observer from the list of observers so that we don't attempt to notify something that doesn't exist + void notifyObserverDeleted(ResourceObserver *observer); + +private: // truly private + QList m_observers; + std::shared_ptr m_handler = nullptr; + Ptr m_placeholder = nullptr; + const QString m_resource; + + static QString storageIdentifier(const QString &id, Ptr placeholder = nullptr); + QString storageIdentifier() const; + + // 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; + // a list of resources so that we can reuse them + static QMap> m_resources; +}; -- cgit v1.2.3