NOISSUE re/move some dead code and unused build system parts
This commit is contained in:
		
							
								
								
									
										3
									
								
								api/dead/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								api/dead/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| This stuff is dead code I collected from the repository that still looks like it might be useful for something. | ||||
|  | ||||
| Moved on 10. April 2016 - if it hasn't been moved or used in a while, delete this. | ||||
							
								
								
									
										133
									
								
								api/dead/src/AbstractCommonModel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								api/dead/src/AbstractCommonModel.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| /* Copyright 2015 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include "AbstractCommonModel.h" | ||||
|  | ||||
| BaseAbstractCommonModel::BaseAbstractCommonModel(const Qt::Orientation orientation, QObject *parent) | ||||
| 	: QAbstractListModel(parent), m_orientation(orientation) | ||||
| { | ||||
| } | ||||
|  | ||||
| int BaseAbstractCommonModel::rowCount(const QModelIndex &parent) const | ||||
| { | ||||
| 	return m_orientation == Qt::Horizontal ? entryCount() : size(); | ||||
| } | ||||
| int BaseAbstractCommonModel::columnCount(const QModelIndex &parent) const | ||||
| { | ||||
| 	return m_orientation == Qt::Horizontal ? size() : entryCount(); | ||||
| } | ||||
| QVariant BaseAbstractCommonModel::data(const QModelIndex &index, int role) const | ||||
| { | ||||
| 	if (!hasIndex(index.row(), index.column(), index.parent())) | ||||
| 	{ | ||||
| 		return QVariant(); | ||||
| 	} | ||||
| 	const int i = m_orientation == Qt::Horizontal ? index.column() : index.row(); | ||||
| 	const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column(); | ||||
| 	return formatData(i, role, get(i, entry, role)); | ||||
| } | ||||
| QVariant BaseAbstractCommonModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
| 	if (orientation != m_orientation && role == Qt::DisplayRole) | ||||
| 	{ | ||||
| 		return entryTitle(section); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return QVariant(); | ||||
| 	} | ||||
| } | ||||
| bool BaseAbstractCommonModel::setData(const QModelIndex &index, const QVariant &value, int role) | ||||
| { | ||||
| 	const int i = m_orientation == Qt::Horizontal ? index.column() : index.row(); | ||||
| 	const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column(); | ||||
| 	const bool result = set(i, entry, role, sanetizeData(i, role, value)); | ||||
| 	if (result) | ||||
| 	{ | ||||
| 		emit dataChanged(index, index, QVector<int>() << role); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
| Qt::ItemFlags BaseAbstractCommonModel::flags(const QModelIndex &index) const | ||||
| { | ||||
| 	if (!hasIndex(index.row(), index.column(), index.parent())) | ||||
| 	{ | ||||
| 		return Qt::NoItemFlags; | ||||
| 	} | ||||
|  | ||||
| 	const int entry = m_orientation == Qt::Horizontal ? index.row() : index.column(); | ||||
| 	if (canSet(entry)) | ||||
| 	{ | ||||
| 		return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return Qt::ItemIsEnabled | Qt::ItemIsSelectable; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseAbstractCommonModel::notifyAboutToAddObject(const int at) | ||||
| { | ||||
| 	if (m_orientation == Qt::Horizontal) | ||||
| 	{ | ||||
| 		beginInsertColumns(QModelIndex(), at, at); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		beginInsertRows(QModelIndex(), at, at); | ||||
| 	} | ||||
| } | ||||
| void BaseAbstractCommonModel::notifyObjectAdded() | ||||
| { | ||||
| 	if (m_orientation == Qt::Horizontal) | ||||
| 	{ | ||||
| 		endInsertColumns(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		endInsertRows(); | ||||
| 	} | ||||
| } | ||||
| void BaseAbstractCommonModel::notifyAboutToRemoveObject(const int at) | ||||
| { | ||||
| 	if (m_orientation == Qt::Horizontal) | ||||
| 	{ | ||||
| 		beginRemoveColumns(QModelIndex(), at, at); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		beginRemoveRows(QModelIndex(), at, at); | ||||
| 	} | ||||
| } | ||||
| void BaseAbstractCommonModel::notifyObjectRemoved() | ||||
| { | ||||
| 	if (m_orientation == Qt::Horizontal) | ||||
| 	{ | ||||
| 		endRemoveColumns(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		endRemoveRows(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseAbstractCommonModel::notifyBeginReset() | ||||
| { | ||||
| 	beginResetModel(); | ||||
| } | ||||
| void BaseAbstractCommonModel::notifyEndReset() | ||||
| { | ||||
| 	endResetModel(); | ||||
| } | ||||
							
								
								
									
										462
									
								
								api/dead/src/AbstractCommonModel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										462
									
								
								api/dead/src/AbstractCommonModel.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,462 @@ | ||||
| /* Copyright 2015 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <QAbstractListModel> | ||||
| #include <type_traits> | ||||
| #include <functional> | ||||
| #include <memory> | ||||
|  | ||||
| class BaseAbstractCommonModel : public QAbstractListModel | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	explicit BaseAbstractCommonModel(const Qt::Orientation orientation, QObject *parent = nullptr); | ||||
|  | ||||
| 	// begin QAbstractItemModel interface | ||||
| 	int rowCount(const QModelIndex &parent = QModelIndex()) const override; | ||||
| 	int columnCount(const QModelIndex &parent = QModelIndex()) const override; | ||||
| 	QVariant data(const QModelIndex &index, int role) const override; | ||||
| 	QVariant headerData(int section, Qt::Orientation orientation, int role) const override; | ||||
| 	bool setData(const QModelIndex &index, const QVariant &value, int role) override; | ||||
| 	Qt::ItemFlags flags(const QModelIndex &index) const override; | ||||
| 	// end QAbstractItemModel interface | ||||
|  | ||||
| 	virtual int size() const = 0; | ||||
| 	virtual int entryCount() const = 0; | ||||
|  | ||||
| 	virtual QVariant formatData(const int index, int role, const QVariant &data) const { return data; } | ||||
| 	virtual QVariant sanetizeData(const int index, int role, const QVariant &data) const { return data; } | ||||
|  | ||||
| protected: | ||||
| 	virtual QVariant get(const int index, const int entry, const int role) const = 0; | ||||
| 	virtual bool set(const int index, const int entry, const int role, const QVariant &value) = 0; | ||||
| 	virtual bool canSet(const int entry) const = 0; | ||||
| 	virtual QString entryTitle(const int entry) const = 0; | ||||
|  | ||||
| 	void notifyAboutToAddObject(const int at); | ||||
| 	void notifyObjectAdded(); | ||||
| 	void notifyAboutToRemoveObject(const int at); | ||||
| 	void notifyObjectRemoved(); | ||||
| 	void notifyBeginReset(); | ||||
| 	void notifyEndReset(); | ||||
|  | ||||
| 	const Qt::Orientation m_orientation; | ||||
| }; | ||||
|  | ||||
| template<typename Object> | ||||
| class AbstractCommonModel : public BaseAbstractCommonModel | ||||
| { | ||||
| public: | ||||
| 	explicit AbstractCommonModel(const Qt::Orientation orientation) | ||||
| 		: BaseAbstractCommonModel(orientation) {} | ||||
| 	virtual ~AbstractCommonModel() {} | ||||
|  | ||||
| 	int size() const override { return m_objects.size(); } | ||||
| 	int entryCount() const override { return m_entries.size(); } | ||||
|  | ||||
| 	void append(const Object &object) | ||||
| 	{ | ||||
| 		notifyAboutToAddObject(size()); | ||||
| 		m_objects.append(object); | ||||
| 		notifyObjectAdded(); | ||||
| 	} | ||||
| 	void prepend(const Object &object) | ||||
| 	{ | ||||
| 		notifyAboutToAddObject(0); | ||||
| 		m_objects.prepend(object); | ||||
| 		notifyObjectAdded(); | ||||
| 	} | ||||
| 	void insert(const Object &object, const int index) | ||||
| 	{ | ||||
| 		if (index >= size()) | ||||
| 		{ | ||||
| 			prepend(object); | ||||
| 		} | ||||
| 		else if (index <= 0) | ||||
| 		{ | ||||
| 			append(object); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			notifyAboutToAddObject(index); | ||||
| 			m_objects.insert(index, object); | ||||
| 			notifyObjectAdded(); | ||||
| 		} | ||||
| 	} | ||||
| 	void remove(const int index) | ||||
| 	{ | ||||
| 		notifyAboutToRemoveObject(index); | ||||
| 		m_objects.removeAt(index); | ||||
| 		notifyObjectRemoved(); | ||||
| 	} | ||||
| 	Object get(const int index) const | ||||
| 	{ | ||||
| 		return m_objects.at(index); | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	friend class CommonModel; | ||||
| 	QVariant get(const int index, const int entry, const int role) const override | ||||
| 	{ | ||||
| 		if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) | ||||
| 		{ | ||||
| 			return QVariant(); | ||||
| 		} | ||||
| 		return m_entries[entry].second.value(role)->get(m_objects.at(index)); | ||||
| 	} | ||||
| 	bool set(const int index, const int entry, const int role, const QVariant &value) override | ||||
| 	{ | ||||
| 		if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		IEntry *e = m_entries[entry].second.value(role); | ||||
| 		if (!e->canSet()) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		e->set(m_objects[index], value); | ||||
| 		return true; | ||||
| 	} | ||||
| 	bool canSet(const int entry) const override | ||||
| 	{ | ||||
| 		if (m_entries.size() < entry || !m_entries[entry].second.contains(Qt::EditRole)) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		IEntry *e = m_entries[entry].second.value(Qt::EditRole); | ||||
| 		return e->canSet(); | ||||
| 	} | ||||
|  | ||||
| 	QString entryTitle(const int entry) const override | ||||
| 	{ | ||||
| 		return m_entries.at(entry).first; | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	struct IEntry | ||||
| 	{ | ||||
| 		virtual ~IEntry() {} | ||||
| 		virtual void set(Object &object, const QVariant &value) = 0; | ||||
| 		virtual QVariant get(const Object &object) const = 0; | ||||
| 		virtual bool canSet() const = 0; | ||||
| 	}; | ||||
| 	template<typename T> | ||||
| 	struct VariableEntry : public IEntry | ||||
| 	{ | ||||
| 		typedef T (Object::*Member); | ||||
|  | ||||
| 		explicit VariableEntry(Member member) | ||||
| 			: m_member(member) {} | ||||
|  | ||||
| 		void set(Object &object, const QVariant &value) override | ||||
| 		{ | ||||
| 			object.*m_member = value.value<T>(); | ||||
| 		} | ||||
| 		QVariant get(const Object &object) const override | ||||
| 		{ | ||||
| 			return QVariant::fromValue<T>(object.*m_member); | ||||
| 		} | ||||
| 		bool canSet() const override { return true; } | ||||
|  | ||||
| 	private: | ||||
| 		Member m_member; | ||||
| 	}; | ||||
| 	template<typename T> | ||||
| 	struct FunctionEntry : public IEntry | ||||
| 	{ | ||||
| 		typedef T (Object::*Getter)() const; | ||||
| 		typedef void (Object::*Setter)(T); | ||||
|  | ||||
| 		explicit FunctionEntry(Getter getter, Setter setter) | ||||
| 			: m_getter(m_getter), m_setter(m_setter) {} | ||||
|  | ||||
| 		void set(Object &object, const QVariant &value) override | ||||
| 		{ | ||||
| 			object.*m_setter(value.value<T>()); | ||||
| 		} | ||||
| 		QVariant get(const Object &object) const override | ||||
| 		{ | ||||
| 			return QVariant::fromValue<T>(object.*m_getter()); | ||||
| 		} | ||||
| 		bool canSet() const override { return !!m_setter; } | ||||
|  | ||||
| 	private: | ||||
| 		Getter m_getter; | ||||
| 		Setter m_setter; | ||||
| 	}; | ||||
|  | ||||
| 	QList<Object> m_objects; | ||||
| 	QVector<QPair<QString, QMap<int, IEntry *>>> m_entries; | ||||
|  | ||||
| 	void addEntryInternal(IEntry *e, const int entry, const int role) | ||||
| 	{ | ||||
| 		if (m_entries.size() <= entry) | ||||
| 		{ | ||||
| 			m_entries.resize(entry + 1); | ||||
| 		} | ||||
| 		m_entries[entry].second.insert(role, e); | ||||
| 	} | ||||
|  | ||||
| protected: | ||||
| 	template<typename Getter, typename Setter> | ||||
| 	typename std::enable_if<std::is_member_function_pointer<Getter>::value && std::is_member_function_pointer<Getter>::value, void>::type | ||||
| 	addEntry(Getter getter, Setter setter, const int entry, const int role) | ||||
| 	{ | ||||
| 		addEntryInternal(new FunctionEntry<typename std::result_of<Getter>::type>(getter, setter), entry, role); | ||||
| 	} | ||||
| 	template<typename Getter> | ||||
| 	typename std::enable_if<std::is_member_function_pointer<Getter>::value, void>::type | ||||
| 	addEntry(Getter getter, const int entry, const int role) | ||||
| 	{ | ||||
| 		addEntryInternal(new FunctionEntry<typename std::result_of<Getter>::type>(getter, nullptr), entry, role); | ||||
| 	} | ||||
| 	template<typename T> | ||||
| 	typename std::enable_if<!std::is_member_function_pointer<T (Object::*)>::value, void>::type | ||||
| 	addEntry(T (Object::*member), const int entry, const int role) | ||||
| 	{ | ||||
| 		addEntryInternal(new VariableEntry<T>(member), entry, role); | ||||
| 	} | ||||
|  | ||||
| 	void setEntryTitle(const int entry, const QString &title) | ||||
| 	{ | ||||
| 		m_entries[entry].first = title; | ||||
| 	} | ||||
| }; | ||||
| template<typename Object> | ||||
| class AbstractCommonModel<Object *> : public BaseAbstractCommonModel | ||||
| { | ||||
| public: | ||||
| 	explicit AbstractCommonModel(const Qt::Orientation orientation) | ||||
| 		: BaseAbstractCommonModel(orientation) {} | ||||
| 	virtual ~AbstractCommonModel() | ||||
| 	{ | ||||
| 		qDeleteAll(m_objects); | ||||
| 	} | ||||
|  | ||||
| 	int size() const override { return m_objects.size(); } | ||||
| 	int entryCount() const override { return m_entries.size(); } | ||||
|  | ||||
| 	void append(Object *object) | ||||
| 	{ | ||||
| 		notifyAboutToAddObject(size()); | ||||
| 		m_objects.append(object); | ||||
| 		notifyObjectAdded(); | ||||
| 	} | ||||
| 	void prepend(Object *object) | ||||
| 	{ | ||||
| 		notifyAboutToAddObject(0); | ||||
| 		m_objects.prepend(object); | ||||
| 		notifyObjectAdded(); | ||||
| 	} | ||||
| 	void insert(Object *object, const int index) | ||||
| 	{ | ||||
| 		if (index >= size()) | ||||
| 		{ | ||||
| 			prepend(object); | ||||
| 		} | ||||
| 		else if (index <= 0) | ||||
| 		{ | ||||
| 			append(object); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			notifyAboutToAddObject(index); | ||||
| 			m_objects.insert(index, object); | ||||
| 			notifyObjectAdded(); | ||||
| 		} | ||||
| 	} | ||||
| 	void remove(const int index) | ||||
| 	{ | ||||
| 		notifyAboutToRemoveObject(index); | ||||
| 		m_objects.removeAt(index); | ||||
| 		notifyObjectRemoved(); | ||||
| 	} | ||||
| 	Object *get(const int index) const | ||||
| 	{ | ||||
| 		return m_objects.at(index); | ||||
| 	} | ||||
| 	int find(Object * const obj) const | ||||
| 	{ | ||||
| 		return m_objects.indexOf(obj); | ||||
| 	} | ||||
|  | ||||
| 	QList<Object *> getAll() const | ||||
| 	{ | ||||
| 		return m_objects; | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	friend class CommonModel; | ||||
| 	QVariant get(const int index, const int entry, const int role) const override | ||||
| 	{ | ||||
| 		if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) | ||||
| 		{ | ||||
| 			return QVariant(); | ||||
| 		} | ||||
| 		return m_entries[entry].second.value(role)->get(m_objects.at(index)); | ||||
| 	} | ||||
| 	bool set(const int index, const int entry, const int role, const QVariant &value) override | ||||
| 	{ | ||||
| 		if (m_entries.size() < entry || !m_entries[entry].second.contains(role)) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		IEntry *e = m_entries[entry].second.value(role); | ||||
| 		if (!e->canSet()) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		e->set(m_objects[index], value); | ||||
| 		return true; | ||||
| 	} | ||||
| 	bool canSet(const int entry) const override | ||||
| 	{ | ||||
| 		if (m_entries.size() < entry || !m_entries[entry].second.contains(Qt::EditRole)) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		IEntry *e = m_entries[entry].second.value(Qt::EditRole); | ||||
| 		return e->canSet(); | ||||
| 	} | ||||
|  | ||||
| 	QString entryTitle(const int entry) const override | ||||
| 	{ | ||||
| 		return m_entries.at(entry).first; | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	struct IEntry | ||||
| 	{ | ||||
| 		virtual ~IEntry() {} | ||||
| 		virtual void set(Object *object, const QVariant &value) = 0; | ||||
| 		virtual QVariant get(Object *object) const = 0; | ||||
| 		virtual bool canSet() const = 0; | ||||
| 	}; | ||||
| 	template<typename T> | ||||
| 	struct VariableEntry : public IEntry | ||||
| 	{ | ||||
| 		typedef T (Object::*Member); | ||||
|  | ||||
| 		explicit VariableEntry(Member member) | ||||
| 			: m_member(member) {} | ||||
|  | ||||
| 		void set(Object *object, const QVariant &value) override | ||||
| 		{ | ||||
| 			object->*m_member = value.value<T>(); | ||||
| 		} | ||||
| 		QVariant get(Object *object) const override | ||||
| 		{ | ||||
| 			return QVariant::fromValue<T>(object->*m_member); | ||||
| 		} | ||||
| 		bool canSet() const override { return true; } | ||||
|  | ||||
| 	private: | ||||
| 		Member m_member; | ||||
| 	}; | ||||
| 	template<typename T> | ||||
| 	struct FunctionEntry : public IEntry | ||||
| 	{ | ||||
| 		typedef T (Object::*Getter)() const; | ||||
| 		typedef void (Object::*Setter)(T); | ||||
|  | ||||
| 		explicit FunctionEntry(Getter getter, Setter setter) | ||||
| 			: m_getter(getter), m_setter(setter) {} | ||||
|  | ||||
| 		void set(Object *object, const QVariant &value) override | ||||
| 		{ | ||||
| 			(object->*m_setter)(value.value<T>()); | ||||
| 		} | ||||
| 		QVariant get(Object *object) const override | ||||
| 		{ | ||||
| 			return QVariant::fromValue<T>((object->*m_getter)()); | ||||
| 		} | ||||
| 		bool canSet() const override { return !!m_setter; } | ||||
|  | ||||
| 	private: | ||||
| 		Getter m_getter; | ||||
| 		Setter m_setter; | ||||
| 	}; | ||||
| 	template<typename T> | ||||
| 	struct LambdaEntry : public IEntry | ||||
| 	{ | ||||
| 		using Getter = std::function<T(Object *)>; | ||||
|  | ||||
| 		explicit LambdaEntry(Getter getter) | ||||
| 			: m_getter(getter) {} | ||||
|  | ||||
| 		void set(Object *object, const QVariant &value) override {} | ||||
| 		QVariant get(Object *object) const override | ||||
| 		{ | ||||
| 			return QVariant::fromValue<T>(m_getter(object)); | ||||
| 		} | ||||
| 		bool canSet() const override { return false; } | ||||
|  | ||||
| 	private: | ||||
| 		Getter m_getter; | ||||
| 	}; | ||||
|  | ||||
| 	QList<Object *> m_objects; | ||||
| 	QVector<QPair<QString, QMap<int, IEntry *>>> m_entries; | ||||
|  | ||||
| 	void addEntryInternal(IEntry *e, const int entry, const int role) | ||||
| 	{ | ||||
| 		if (m_entries.size() <= entry) | ||||
| 		{ | ||||
| 			m_entries.resize(entry + 1); | ||||
| 		} | ||||
| 		m_entries[entry].second.insert(role, e); | ||||
| 	} | ||||
|  | ||||
| protected: | ||||
| 	template<typename Getter, typename Setter> | ||||
| 	typename std::enable_if<std::is_member_function_pointer<Getter>::value && std::is_member_function_pointer<Getter>::value, void>::type | ||||
| 	addEntry(const int entry, const int role, Getter getter, Setter setter) | ||||
| 	{ | ||||
| 		addEntryInternal(new FunctionEntry<typename std::result_of<Getter>::type>(getter, setter), entry, role); | ||||
| 	} | ||||
| 	template<typename T> | ||||
| 	typename std::enable_if<std::is_member_function_pointer<typename FunctionEntry<T>::Getter>::value, void>::type | ||||
| 	addEntry(const int entry, const int role, typename FunctionEntry<T>::Getter getter) | ||||
| 	{ | ||||
| 		addEntryInternal(new FunctionEntry<T>(getter, nullptr), entry, role); | ||||
| 	} | ||||
| 	template<typename T> | ||||
| 	typename std::enable_if<!std::is_member_function_pointer<T (Object::*)>::value, void>::type | ||||
| 	addEntry(const int entry, const int role, T (Object::*member)) | ||||
| 	{ | ||||
| 		addEntryInternal(new VariableEntry<T>(member), entry, role); | ||||
| 	} | ||||
| 	template<typename T> | ||||
| 	void addEntry(const int entry, const int role, typename LambdaEntry<T>::Getter lambda) | ||||
| 	{ | ||||
| 		addEntryInternal(new LambdaEntry<T>(lambda), entry, role); | ||||
| 	} | ||||
|  | ||||
| 	void setEntryTitle(const int entry, const QString &title) | ||||
| 	{ | ||||
| 		m_entries[entry].first = title; | ||||
| 	} | ||||
|  | ||||
| 	void setAll(const QList<Object *> objects) | ||||
| 	{ | ||||
| 		notifyBeginReset(); | ||||
| 		qDeleteAll(m_objects); | ||||
| 		m_objects = objects; | ||||
| 		notifyEndReset(); | ||||
| 	} | ||||
| }; | ||||
							
								
								
									
										103
									
								
								api/dead/src/BaseConfigObject.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								api/dead/src/BaseConfigObject.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| /* Copyright 2015 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include "BaseConfigObject.h" | ||||
|  | ||||
| #include <QTimer> | ||||
| #include <QFile> | ||||
| #include <QCoreApplication> | ||||
| #include <QDebug> | ||||
|  | ||||
| #include "Exception.h" | ||||
| #include "FileSystem.h" | ||||
|  | ||||
| BaseConfigObject::BaseConfigObject(const QString &filename) | ||||
| 	: m_filename(filename) | ||||
| { | ||||
| 	m_saveTimer = new QTimer; | ||||
| 	m_saveTimer->setSingleShot(true); | ||||
| 	// cppcheck-suppress pureVirtualCall | ||||
| 	QObject::connect(m_saveTimer, &QTimer::timeout, [this](){saveNow();}); | ||||
| 	setSaveTimeout(250); | ||||
|  | ||||
| 	m_initialReadTimer = new QTimer; | ||||
| 	m_initialReadTimer->setSingleShot(true); | ||||
| 	QObject::connect(m_initialReadTimer, &QTimer::timeout, [this]() | ||||
| 	{ | ||||
| 		loadNow(); | ||||
| 		m_initialReadTimer->deleteLater(); | ||||
| 		m_initialReadTimer = 0; | ||||
| 	}); | ||||
| 	m_initialReadTimer->start(0); | ||||
|  | ||||
| 	// cppcheck-suppress pureVirtualCall | ||||
| 	m_appQuitConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this](){saveNow();}); | ||||
| } | ||||
| BaseConfigObject::~BaseConfigObject() | ||||
| { | ||||
| 	delete m_saveTimer; | ||||
| 	if (m_initialReadTimer) | ||||
| 	{ | ||||
| 		delete m_initialReadTimer; | ||||
| 	} | ||||
| 	QObject::disconnect(m_appQuitConnection); | ||||
| } | ||||
|  | ||||
| void BaseConfigObject::setSaveTimeout(int msec) | ||||
| { | ||||
| 	m_saveTimer->setInterval(msec); | ||||
| } | ||||
|  | ||||
| void BaseConfigObject::scheduleSave() | ||||
| { | ||||
| 	m_saveTimer->stop(); | ||||
| 	m_saveTimer->start(); | ||||
| } | ||||
| void BaseConfigObject::saveNow() | ||||
| { | ||||
| 	if (m_saveTimer->isActive()) | ||||
| 	{ | ||||
| 		m_saveTimer->stop(); | ||||
| 	} | ||||
| 	if (m_disableSaving) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		FS::write(m_filename, doSave()); | ||||
| 	} | ||||
| 	catch (Exception & e) | ||||
| 	{ | ||||
| 		qCritical() << e.cause(); | ||||
| 	} | ||||
| } | ||||
| void BaseConfigObject::loadNow() | ||||
| { | ||||
| 	if (m_saveTimer->isActive()) | ||||
| 	{ | ||||
| 		saveNow(); | ||||
| 	} | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
| 		doLoad(FS::read(m_filename)); | ||||
| 	} | ||||
| 	catch (Exception & e) | ||||
| 	{ | ||||
| 		qWarning() << "Error loading" << m_filename << ":" << e.cause(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										50
									
								
								api/dead/src/BaseConfigObject.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								api/dead/src/BaseConfigObject.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| /* Copyright 2015 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <QObject> | ||||
|  | ||||
| class QTimer; | ||||
|  | ||||
| class BaseConfigObject | ||||
| { | ||||
| public: | ||||
| 	void setSaveTimeout(int msec); | ||||
|  | ||||
| protected: | ||||
| 	explicit BaseConfigObject(const QString &filename); | ||||
| 	virtual ~BaseConfigObject(); | ||||
|  | ||||
| 	// cppcheck-suppress pureVirtualCall | ||||
| 	virtual QByteArray doSave() const = 0; | ||||
| 	virtual void doLoad(const QByteArray &data) = 0; | ||||
|  | ||||
| 	void setSavingDisabled(bool savingDisabled) { m_disableSaving = savingDisabled; } | ||||
|  | ||||
| 	QString fileName() const { return m_filename; } | ||||
|  | ||||
| public: | ||||
| 	void scheduleSave(); | ||||
| 	void saveNow(); | ||||
| 	void loadNow(); | ||||
|  | ||||
| private: | ||||
| 	QTimer *m_saveTimer; | ||||
| 	QTimer *m_initialReadTimer; | ||||
| 	QString m_filename; | ||||
| 	QMetaObject::Connection m_appQuitConnection; | ||||
| 	bool m_disableSaving = false; | ||||
| }; | ||||
							
								
								
									
										37
									
								
								api/dead/src/TypeMagic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/dead/src/TypeMagic.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
|  | ||||
| namespace TypeMagic | ||||
| { | ||||
| /** "Cleans" the given type T by stripping references (&) and cv-qualifiers (const, volatile) from it | ||||
|  * const int => int | ||||
|  * QString & => QString | ||||
|  * const unsigned long long & => unsigned long long | ||||
|  * | ||||
|  * Usage: | ||||
|  *   using Cleaned = Detail::CleanType<const int>; | ||||
|  *   static_assert(std::is_same<Cleaned, int>, "Cleaned == int"); | ||||
|  */ | ||||
| // the order of remove_cv and remove_reference matters! | ||||
| template <typename T> | ||||
| using CleanType = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | ||||
|  | ||||
| /// For functors (structs with operator()), including lambdas, which in **most** cases are functors | ||||
| /// "Calls" Function<Ret(*)(Arg)> or Function<Ret(C::*)(Arg)> | ||||
| template <typename T> struct Function : public Function<decltype(&T::operator())> {}; | ||||
| /// For function pointers (&function), including static members (&Class::member) | ||||
| template <typename Ret, typename Arg> struct Function<Ret(*)(Arg)> : public Function<Ret(Arg)> {}; | ||||
| /// Default specialization used by others. | ||||
| template <typename Ret, typename Arg> struct Function<Ret(Arg)> | ||||
| { | ||||
| 	using ReturnType = Ret; | ||||
| 	using Argument = Arg; | ||||
| }; | ||||
| /// For member functions. Also used by the lambda overload if the lambda captures [this] | ||||
| template <class C, typename Ret, typename Arg> struct Function<Ret(C::*)(Arg)> : public Function<Ret(Arg)> {}; | ||||
| template <class C, typename Ret, typename Arg> struct Function<Ret(C::*)(Arg) const> : public Function<Ret(Arg)> {}; | ||||
| /// Overload for references | ||||
| template <typename F> struct Function<F&> : public Function<F> {}; | ||||
| /// Overload for rvalues | ||||
| template <typename F> struct Function<F&&> : public Function<F> {}; | ||||
| // for more info: https://functionalcpp.wordpress.com/2013/08/05/function-traits/ | ||||
| } | ||||
							
								
								
									
										37
									
								
								api/dead/src/handlers/IconResourceHandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/dead/src/handlers/IconResourceHandler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #include "IconResourceHandler.h" | ||||
| #include <xdgicon.h> | ||||
|  | ||||
| #include <QDir> | ||||
| #include <QDebug> | ||||
|  | ||||
| QList<std::weak_ptr<IconResourceHandler>> IconResourceHandler::m_iconHandlers; | ||||
|  | ||||
| IconResourceHandler::IconResourceHandler(const QString &key) | ||||
| 	: m_key(key) | ||||
| { | ||||
| } | ||||
|  | ||||
| void IconResourceHandler::setTheme(const QString &theme) | ||||
| { | ||||
| 	// notify everyone | ||||
| 	for (auto handler : m_iconHandlers) | ||||
| 	{ | ||||
| 		std::shared_ptr<IconResourceHandler> ptr = handler.lock(); | ||||
| 		if (ptr) | ||||
| 		{ | ||||
| 			ptr->setResult(ptr->get()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IconResourceHandler::init(std::shared_ptr<ResourceHandler> &ptr) | ||||
| { | ||||
| 	m_iconHandlers.append(std::dynamic_pointer_cast<IconResourceHandler>(ptr)); | ||||
| 	// we always have a result, so lets report it now! | ||||
| 	setResult(get()); | ||||
| } | ||||
|  | ||||
| QVariant IconResourceHandler::get() const | ||||
| { | ||||
| 	return XdgIcon::fromTheme(m_key); | ||||
| } | ||||
							
								
								
									
										23
									
								
								api/dead/src/handlers/IconResourceHandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								api/dead/src/handlers/IconResourceHandler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include <resources/ResourceHandler.h> | ||||
|  | ||||
| class IconResourceHandler : public ResourceHandler | ||||
| { | ||||
| public: | ||||
| 	explicit IconResourceHandler(const QString &key); | ||||
|  | ||||
| 	/// Sets the current theme and notifies all IconResourceHandlers of the change | ||||
| 	static void setTheme(const QString &theme); | ||||
|  | ||||
| private: | ||||
| 	// we need to keep track of all IconResourceHandlers so that we can update them if the theme changes | ||||
| 	void init(std::shared_ptr<ResourceHandler> &ptr) override; | ||||
| 	static QList<std::weak_ptr<IconResourceHandler>> m_iconHandlers; | ||||
|  | ||||
| 	QString m_key; | ||||
|  | ||||
| 	// the workhorse, returns QVariantMap (filename => size) for m_key/m_theme | ||||
| 	QVariant get() const; | ||||
| }; | ||||
							
								
								
									
										68
									
								
								api/dead/src/handlers/WebResourceHandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								api/dead/src/handlers/WebResourceHandler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| #include "WebResourceHandler.h" | ||||
|  | ||||
| #include "net/CacheDownload.h" | ||||
| #include "net/HttpMetaCache.h" | ||||
| #include "net/NetJob.h" | ||||
| #include "FileSystem.h" | ||||
| #include "Env.h" | ||||
|  | ||||
| //FIXME: wrong. needs to be done elsewhere. | ||||
| QMap<QString, NetJob *> WebResourceHandler::m_activeDownloads; | ||||
|  | ||||
| WebResourceHandler::WebResourceHandler(const QString &url) | ||||
| 	: QObject(), m_url(url) | ||||
| { | ||||
| 	MetaEntryPtr entry = ENV.metacache()->resolveEntry("icons", url); | ||||
| 	if (!entry->isStale()) | ||||
| 	{ | ||||
| 		setResultFromFile(entry->getFullPath()); | ||||
| 	} | ||||
| 	else if (m_activeDownloads.contains(url)) | ||||
| 	{ | ||||
| 		NetJob *job = m_activeDownloads.value(url); | ||||
| 		connect(job, &NetJob::succeeded, this, &WebResourceHandler::succeeded); | ||||
| 		connect(job, &NetJob::failed, this, [job, this]() {setFailure(job->failReason());}); | ||||
| 		connect(job, &NetJob::progress, this, &WebResourceHandler::progress); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		NetJob *job = new NetJob("Icon download"); | ||||
| 		job->addNetAction(CacheDownload::make(QUrl(url), entry)); | ||||
| 		connect(job, &NetJob::succeeded, this, &WebResourceHandler::succeeded); | ||||
| 		connect(job, &NetJob::failed, this, [job, this]() {setFailure(job->failReason());}); | ||||
| 		connect(job, &NetJob::progress, this, &WebResourceHandler::progress); | ||||
| 		connect(job, &NetJob::finished, job, [job](){m_activeDownloads.remove(m_activeDownloads.key(job));job->deleteLater();}); | ||||
| 		m_activeDownloads.insert(url, job); | ||||
| 		job->start(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void WebResourceHandler::succeeded() | ||||
| { | ||||
| 	MetaEntryPtr entry = ENV.metacache()->resolveEntry("icons", m_url); | ||||
| 	setResultFromFile(entry->getFullPath()); | ||||
| 	m_activeDownloads.remove(m_activeDownloads.key(qobject_cast<NetJob *>(sender()))); | ||||
| } | ||||
| void WebResourceHandler::progress(qint64 current, qint64 total) | ||||
| { | ||||
| 	if (total == 0) | ||||
| 	{ | ||||
| 		setProgress(101); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		setProgress(current / total); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void WebResourceHandler::setResultFromFile(const QString &file) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		setResult(FS::read(file)); | ||||
| 	} | ||||
| 	catch (Exception &e) | ||||
| 	{ | ||||
| 		setFailure(e.cause()); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										23
									
								
								api/dead/src/handlers/WebResourceHandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								api/dead/src/handlers/WebResourceHandler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QObject> | ||||
| #include <resources/ResourceHandler.h> | ||||
|  | ||||
| class NetJob; | ||||
|  | ||||
| class WebResourceHandler : public QObject, public ResourceHandler | ||||
| { | ||||
| public: | ||||
| 	explicit WebResourceHandler(const QString &url); | ||||
|  | ||||
| private slots: | ||||
| 	void succeeded(); | ||||
| 	void progress(qint64 current, qint64 total); | ||||
|  | ||||
| private: | ||||
| 	static QMap<QString, NetJob *> m_activeDownloads; | ||||
|  | ||||
| 	QString m_url; | ||||
|  | ||||
| 	void setResultFromFile(const QString &file); | ||||
| }; | ||||
							
								
								
									
										155
									
								
								api/dead/src/resources/Resource.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								api/dead/src/resources/Resource.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| #include "Resource.h" | ||||
|  | ||||
| #include <QDebug> | ||||
|  | ||||
| #include "ResourceObserver.h" | ||||
| #include "ResourceHandler.h" | ||||
|  | ||||
| // definition of static members of Resource | ||||
| QMap<QString, std::function<std::shared_ptr<ResourceHandler>(const QString &)>> Resource::m_handlers; | ||||
| QMap<QPair<int, int>, std::function<QVariant(QVariant)>> Resource::m_transfomers; | ||||
| QMap<QString, std::weak_ptr<Resource>> Resource::m_resources; | ||||
|  | ||||
| struct NullResourceResult {}; | ||||
| Q_DECLARE_METATYPE(NullResourceResult) | ||||
| class NullResourceHandler : public ResourceHandler | ||||
| { | ||||
| public: | ||||
| 	explicit NullResourceHandler() | ||||
| 	{ | ||||
| 		setResult(QVariant::fromValue<NullResourceResult>(NullResourceResult())); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| Resource::Resource(const QString &resource) | ||||
| 	: m_resource(resource) | ||||
| { | ||||
| 	if (!resource.isEmpty()) | ||||
| 	{ | ||||
| 		// a valid resource identifier has the format <id>:<data> | ||||
| 		Q_ASSERT(resource.contains(':')); | ||||
| 		// "parse" the resource identifier into id and data | ||||
| 		const QString resourceId = resource.left(resource.indexOf(':')); | ||||
| 		const QString resourceData = resource.mid(resource.indexOf(':') + 1); | ||||
|  | ||||
| 		// create and set up the handler | ||||
| 		Q_ASSERT(m_handlers.contains(resourceId)); | ||||
| 		m_handler = m_handlers.value(resourceId)(resourceData); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		m_handler = std::make_shared<NullResourceHandler>(); | ||||
| 	} | ||||
|  | ||||
| 	Q_ASSERT(m_handler); | ||||
| 	m_handler->init(m_handler); | ||||
| 	m_handler->setResource(this); | ||||
| } | ||||
| Resource::~Resource() | ||||
| { | ||||
| 	qDeleteAll(m_observers); | ||||
| } | ||||
|  | ||||
| Resource::Ptr Resource::create(const QString &resource, Ptr placeholder) | ||||
| { | ||||
| 	const QString storageId = storageIdentifier(resource, placeholder); | ||||
|  | ||||
| 	// do we already have a resource? even if m_resources contains it it might not be valid any longer (weak_ptr) | ||||
| 	Resource::Ptr ptr = m_resources.contains(storageId) | ||||
| 			? m_resources.value(storageId).lock() | ||||
| 			: nullptr; | ||||
| 	// did we have one? and is it still valid? | ||||
| 	if (!ptr) | ||||
| 	{ | ||||
| 		/* We don't want Resource to have a public constructor, but std::make_shared needs it, | ||||
| 		 * so we create a subclass of Resource here that exposes the constructor as public. | ||||
| 		 * The alternative would be making the allocator for std::make_shared a friend, but it | ||||
| 		 * differs between different STL implementations, so that would be a pain. | ||||
| 		 */ | ||||
| 		struct ConstructableResource : public Resource | ||||
| 		{ | ||||
| 			explicit ConstructableResource(const QString &resource) | ||||
| 				: Resource(resource) {} | ||||
| 		}; | ||||
| 		ptr = std::make_shared<ConstructableResource>(resource); | ||||
| 		ptr->m_placeholder = placeholder; | ||||
| 		m_resources.insert(storageId, ptr); | ||||
| 	} | ||||
| 	return ptr; | ||||
| } | ||||
|  | ||||
| Resource::Ptr Resource::applyTo(ResourceObserver *observer) | ||||
| { | ||||
| 	m_observers.append(observer); | ||||
| 	observer->setSource(shared_from_this()); // give the observer a shared_ptr for us so we don't get deleted | ||||
| 	observer->resourceUpdated(); // ask the observer to poll us immediently, we might already have data | ||||
| 	return shared_from_this(); // allow chaining | ||||
| } | ||||
| Resource::Ptr Resource::applyTo(QObject *target, const char *property) | ||||
| { | ||||
| 	// the cast to ResourceObserver* is required to ensure the right overload gets choosen, | ||||
| 	// since QObjectResourceObserver also inherits from QObject | ||||
| 	return applyTo(static_cast<ResourceObserver *>(new QObjectResourceObserver(target, property))); | ||||
| } | ||||
|  | ||||
| QVariant Resource::getResourceInternal(const int typeId) const | ||||
| { | ||||
| 	// no result (yet), but a placeholder? delegate to the placeholder. | ||||
| 	if (m_handler->result().isNull() && m_placeholder) | ||||
| 	{ | ||||
| 		return m_placeholder->getResourceInternal(typeId); | ||||
| 	} | ||||
| 	const QVariant variant = m_handler->result(); | ||||
| 	const auto typePair = qMakePair(int(variant.type()), typeId); | ||||
|  | ||||
| 	// do we have an explicit transformer? use it. | ||||
| 	if (m_transfomers.contains(typePair)) | ||||
| 	{ | ||||
| 		return m_transfomers.value(typePair)(variant); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// we do not have an explicit transformer, so we just pass the QVariant, which will automatically | ||||
| 		// transform some types for us (different numbers to each other etc.) | ||||
| 		return variant; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Resource::reportResult() | ||||
| { | ||||
| 	for (ResourceObserver *observer : m_observers) | ||||
| 	{ | ||||
| 		observer->resourceUpdated(); | ||||
| 	} | ||||
| } | ||||
| void Resource::reportFailure(const QString &reason) | ||||
| { | ||||
| 	for (ResourceObserver *observer : m_observers) | ||||
| 	{ | ||||
| 		observer->setFailure(reason); | ||||
| 	} | ||||
| } | ||||
| void Resource::reportProgress(const int progress) | ||||
| { | ||||
| 	for (ResourceObserver *observer : m_observers) | ||||
| 	{ | ||||
| 		observer->setProgress(progress); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Resource::notifyObserverDeleted(ResourceObserver *observer) | ||||
| { | ||||
| 	m_observers.removeAll(observer); | ||||
| } | ||||
|  | ||||
| QString Resource::storageIdentifier(const QString &id, Resource::Ptr placeholder) | ||||
| { | ||||
| 	if (placeholder) | ||||
| 	{ | ||||
| 		return id + '#' + storageIdentifier(placeholder->m_resource, placeholder->m_placeholder); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return id; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										132
									
								
								api/dead/src/resources/Resource.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								api/dead/src/resources/Resource.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QString> | ||||
| #include <QMap> | ||||
| #include <QVariant> | ||||
| #include <functional> | ||||
| #include <memory> | ||||
|  | ||||
| #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<Resource> | ||||
| { | ||||
| 	// 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>; | ||||
|  | ||||
| 	~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<typename Func> | ||||
| 	Ptr then(Func &&func) | ||||
| 	{ | ||||
| 		// 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))); | ||||
| 	} | ||||
|  | ||||
| 	/// 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>(); } | ||||
|  | ||||
| 	/// @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<MyResourceHandler>("myid"); | ||||
| 	 */ | ||||
| 	template<typename T> | ||||
| 	static void registerHandler(const QString &id) | ||||
| 	{ | ||||
| 		m_handlers.insert(id, [](const QString &res) { return std::make_shared<T>(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<typename Func> | ||||
| 	static void registerTransformer(Func &&func) | ||||
| 	{ | ||||
| 		using Out = typename TypeMagic::Function<Func>::ReturnType; | ||||
| 		using In = TypeMagic::CleanType<typename TypeMagic::Function<Func>::Argument>; | ||||
| 		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>())); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 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<ResourceObserver *> m_observers; | ||||
| 	std::shared_ptr<ResourceHandler> 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<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; | ||||
| 	// a list of resources so that we can reuse them | ||||
| 	static QMap<QString, std::weak_ptr<Resource>> m_resources; | ||||
| }; | ||||
							
								
								
									
										28
									
								
								api/dead/src/resources/ResourceHandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								api/dead/src/resources/ResourceHandler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| #include "ResourceHandler.h" | ||||
|  | ||||
| #include "Resource.h" | ||||
|  | ||||
| void ResourceHandler::setResult(const QVariant &result) | ||||
| { | ||||
| 	m_result = result; | ||||
| 	if (m_resource) | ||||
| 	{ | ||||
| 		m_resource->reportResult(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ResourceHandler::setFailure(const QString &reason) | ||||
| { | ||||
| 	if (m_resource) | ||||
| 	{ | ||||
| 		m_resource->reportFailure(reason); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ResourceHandler::setProgress(const int progress) | ||||
| { | ||||
| 	if (m_resource) | ||||
| 	{ | ||||
| 		m_resource->reportProgress(progress); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										36
									
								
								api/dead/src/resources/ResourceHandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								api/dead/src/resources/ResourceHandler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QVariant> | ||||
| #include <memory> | ||||
|  | ||||
| #include "multimc_logic_export.h" | ||||
|  | ||||
| class Resource; | ||||
|  | ||||
| /** Base class for things that can retrieve a resource. | ||||
|  * | ||||
|  * Subclass, provide a constructor that takes a single QString as argument, and | ||||
|  * call Resource::registerHandler<MyResourceHandler>("<id>"), where <id> is the | ||||
|  * prefix of the resource ("web", "icon", etc.) | ||||
|  */ | ||||
| class MULTIMC_LOGIC_EXPORT ResourceHandler | ||||
| { | ||||
| public: | ||||
| 	virtual ~ResourceHandler() {} | ||||
|  | ||||
| 	void setResource(Resource *resource) { m_resource = resource; } | ||||
| 	/// reimplement this if you need to do something after you have been put in a shared pointer | ||||
| 	// we do this instead of inheriting from std::enable_shared_from_this | ||||
| 	virtual void init(std::shared_ptr<ResourceHandler>&) {} | ||||
|  | ||||
| 	QVariant result() const { return m_result; } | ||||
|  | ||||
| protected: // use these methods to notify the resource of changes | ||||
| 	void setResult(const QVariant &result); | ||||
| 	void setFailure(const QString &reason); | ||||
| 	void setProgress(const int progress); | ||||
|  | ||||
| private: | ||||
| 	QVariant m_result; | ||||
| 	Resource *m_resource = nullptr; | ||||
| }; | ||||
							
								
								
									
										55
									
								
								api/dead/src/resources/ResourceObserver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								api/dead/src/resources/ResourceObserver.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| #include "ResourceObserver.h" | ||||
|  | ||||
| #include <QDebug> | ||||
|  | ||||
| #include "Resource.h" | ||||
|  | ||||
| static const char *defaultPropertyForTarget(QObject *target) | ||||
| { | ||||
| 	if (target->inherits("QLabel")) | ||||
| 	{ | ||||
| 		return "pixmap"; | ||||
| 	} | ||||
| 	else if (target->inherits("QAction") || | ||||
| 			 target->inherits("QMenu") || | ||||
| 			 target->inherits("QAbstractButton")) | ||||
| 	{ | ||||
| 		return "icon"; | ||||
| 	} | ||||
| 	// for unit tests | ||||
| 	else if (target->inherits("DummyObserverObject")) | ||||
| 	{ | ||||
| 		return "property"; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		Q_ASSERT_X(false, "ResourceObserver.cpp: defaultPropertyForTarget", "Unrecognized QObject subclass"); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QObjectResourceObserver::QObjectResourceObserver(QObject *target, const char *property) | ||||
| 	: QObject(target), m_target(target) | ||||
| { | ||||
| 	const QMetaObject *mo = m_target->metaObject(); | ||||
| 	m_property = mo->property(mo->indexOfProperty( | ||||
| 								  property ? | ||||
| 									  property | ||||
| 									: defaultPropertyForTarget(target))); | ||||
| } | ||||
| void QObjectResourceObserver::resourceUpdated() | ||||
| { | ||||
| 	m_property.write(m_target, getInternal(m_property.type())); | ||||
| } | ||||
|  | ||||
|  | ||||
| ResourceObserver::~ResourceObserver() | ||||
| { | ||||
| 	m_resource->notifyObserverDeleted(this); | ||||
| } | ||||
|  | ||||
| QVariant ResourceObserver::getInternal(const int typeId) const | ||||
| { | ||||
| 	Q_ASSERT(m_resource); | ||||
| 	return m_resource->getResourceInternal(typeId); | ||||
| } | ||||
							
								
								
									
										73
									
								
								api/dead/src/resources/ResourceObserver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								api/dead/src/resources/ResourceObserver.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include <functional> | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QMetaProperty> | ||||
| #include "multimc_logic_export.h" | ||||
|  | ||||
| class QVariant; | ||||
| class Resource; | ||||
|  | ||||
| /// Base class for things that can use a resource | ||||
| class MULTIMC_LOGIC_EXPORT ResourceObserver | ||||
| { | ||||
| public: | ||||
| 	virtual ~ResourceObserver(); | ||||
|  | ||||
| protected: // these methods are called by the Resource when something changes | ||||
| 	virtual void resourceUpdated() = 0; | ||||
| 	virtual void setFailure(const QString &) {} | ||||
| 	virtual void setProgress(const int) {} | ||||
|  | ||||
| private: | ||||
| 	friend class Resource; | ||||
| 	void setSource(std::shared_ptr<Resource> resource) { m_resource = resource; } | ||||
|  | ||||
| protected: | ||||
| 	template<typename T> | ||||
| 	T get() const { return getInternal(qMetaTypeId<T>()).template value<T>(); } | ||||
| 	QVariant getInternal(const int typeId) const; | ||||
|  | ||||
| private: | ||||
| 	std::shared_ptr<Resource> m_resource; | ||||
| }; | ||||
|  | ||||
| /** Observer for QObject properties | ||||
|  * | ||||
|  * Give it a target and the name of a property, and that property will be set when the resource changes. | ||||
|  * | ||||
|  * If no name is given an attempt to find a default property for some common classes is done. | ||||
|  */ | ||||
| class MULTIMC_LOGIC_EXPORT QObjectResourceObserver : public QObject, public ResourceObserver | ||||
| { | ||||
| public: | ||||
| 	explicit QObjectResourceObserver(QObject *target, const char *property = nullptr); | ||||
|  | ||||
| 	void resourceUpdated() override; | ||||
|  | ||||
| private: | ||||
| 	QObject *m_target; | ||||
| 	QMetaProperty m_property; | ||||
| }; | ||||
|  | ||||
| /** Observer for functions, lambdas etc. | ||||
|  * Template arguments: | ||||
|  *   * We need Ret and Arg in order to create the std::function | ||||
|  *   * We need Func in order to std::forward the function | ||||
|  */ | ||||
| template <typename Ret, typename Arg, typename Func> | ||||
| class FunctionResourceObserver : public ResourceObserver | ||||
| { | ||||
| 	std::function<Ret(Arg)> m_function; | ||||
| public: | ||||
| 	template <typename T> | ||||
| 	explicit FunctionResourceObserver(T &&func) | ||||
| 		: m_function(std::forward<Func>(func)) {} | ||||
|  | ||||
| 	void resourceUpdated() override | ||||
| 	{ | ||||
| 		m_function(get<Arg>()); | ||||
| 	} | ||||
| }; | ||||
							
								
								
									
										89
									
								
								api/dead/src/resources/ResourceProxyModel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								api/dead/src/resources/ResourceProxyModel.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| #include "ResourceProxyModel.h" | ||||
|  | ||||
| #include <QItemSelectionRange> | ||||
|  | ||||
| #include "Resource.h" | ||||
| #include "ResourceObserver.h" | ||||
|  | ||||
| class ModelResourceObserver : public ResourceObserver | ||||
| { | ||||
| public: | ||||
| 	explicit ModelResourceObserver(const QModelIndex &index, const int role) | ||||
| 		: m_index(index), m_role(role) | ||||
| 	{ | ||||
| 		qRegisterMetaType<QVector<int>>("QVector<int>"); | ||||
| 	} | ||||
|  | ||||
| 	void resourceUpdated() override | ||||
| 	{ | ||||
| 		if (m_index.isValid()) | ||||
| 		{ | ||||
| 			// the resource changed, pretend to be the model and notify the views of the update. they will re-poll the model which will return the new resource value | ||||
| 			QMetaObject::invokeMethod(const_cast<QAbstractItemModel *>(m_index.model()), | ||||
| 									  "dataChanged", Qt::QueuedConnection, | ||||
| 									  Q_ARG(QModelIndex, m_index), Q_ARG(QModelIndex, m_index), Q_ARG(QVector<int>, QVector<int>() << m_role)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	QPersistentModelIndex m_index; | ||||
| 	int m_role; | ||||
| }; | ||||
|  | ||||
| ResourceProxyModel::ResourceProxyModel(const int resultTypeId, QObject *parent) | ||||
| 	: QIdentityProxyModel(parent), m_resultTypeId(resultTypeId) | ||||
| { | ||||
| } | ||||
|  | ||||
| QVariant ResourceProxyModel::data(const QModelIndex &proxyIndex, int role) const | ||||
| { | ||||
| 	const QModelIndex mapped = mapToSource(proxyIndex); | ||||
| 	// valid cell that's a Qt::DecorationRole and that contains a non-empty string | ||||
| 	if (mapped.isValid() && role == Qt::DecorationRole && !mapToSource(proxyIndex).data(role).toString().isEmpty()) | ||||
| 	{ | ||||
| 		// do we already have a resource for this index? | ||||
| 		if (!m_resources.contains(mapped)) | ||||
| 		{ | ||||
| 			Resource::Ptr placeholder; | ||||
| 			const QVariant placeholderIdentifier = mapped.data(PlaceholderRole); | ||||
| 			if (!placeholderIdentifier.isNull() && placeholderIdentifier.type() == QVariant::String) | ||||
| 			{ | ||||
| 				placeholder = Resource::create(placeholderIdentifier.toString()); | ||||
| 			} | ||||
|  | ||||
| 			// create the Resource and apply the observer for models | ||||
| 			Resource::Ptr res = Resource::create(mapToSource(proxyIndex).data(role).toString(), placeholder) | ||||
| 					->applyTo(new ModelResourceObserver(proxyIndex, role)); | ||||
|  | ||||
| 			m_resources.insert(mapped, res); | ||||
| 		} | ||||
|  | ||||
| 		return m_resources.value(mapped)->getResourceInternal(m_resultTypeId); | ||||
| 	} | ||||
| 	// otherwise fall back to the source model | ||||
| 	return mapped.data(role); | ||||
| } | ||||
|  | ||||
| void ResourceProxyModel::setSourceModel(QAbstractItemModel *model) | ||||
| { | ||||
| 	if (sourceModel()) | ||||
| 	{ | ||||
| 		disconnect(sourceModel(), 0, this, 0); | ||||
| 	} | ||||
| 	if (model) | ||||
| 	{ | ||||
| 		connect(model, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &tl, const QModelIndex &br, const QVector<int> &roles) | ||||
| 		{ | ||||
| 			// invalidate resources so that they will be re-created | ||||
| 			if (roles.contains(Qt::DecorationRole) || roles.contains(PlaceholderRole) || roles.isEmpty()) | ||||
| 			{ | ||||
| 				const QItemSelectionRange range(tl, br); | ||||
| 				for (const QModelIndex &index : range.indexes()) | ||||
| 				{ | ||||
| 					m_resources.remove(index); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 	QIdentityProxyModel::setSourceModel(model); | ||||
| } | ||||
							
								
								
									
										39
									
								
								api/dead/src/resources/ResourceProxyModel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								api/dead/src/resources/ResourceProxyModel.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QIdentityProxyModel> | ||||
| #include <memory> | ||||
|  | ||||
| #include "multimc_logic_export.h" | ||||
|  | ||||
| /// Convenience proxy model that transforms resource identifiers (strings) for Qt::DecorationRole into other types. | ||||
| class MULTIMC_LOGIC_EXPORT ResourceProxyModel : public QIdentityProxyModel | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	// resultTypeId is found using qMetaTypeId<T>() | ||||
| 	explicit ResourceProxyModel(const int resultTypeId, QObject *parent = nullptr); | ||||
|  | ||||
| 	enum | ||||
| 	{ | ||||
| 		// provide this role from your model if you want to show a placeholder | ||||
| 		PlaceholderRole = Qt::UserRole + 0xabc // some random offset to not collide with other stuff | ||||
| 	}; | ||||
|  | ||||
| 	QVariant data(const QModelIndex &proxyIndex, int role) const override; | ||||
| 	void setSourceModel(QAbstractItemModel *model) override; | ||||
|  | ||||
| 	/// Helper function, usage: m_view->setModel(ResourceProxyModel::mixin<QIcon>(m_model)); | ||||
| 	template <typename T> | ||||
| 	static QAbstractItemModel *mixin(QAbstractItemModel *model) | ||||
| 	{ | ||||
| 		ResourceProxyModel *proxy = new ResourceProxyModel(qMetaTypeId<T>(), model); | ||||
| 		proxy->setSourceModel(model); | ||||
| 		return proxy; | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	// mutable because it needs to be available from the const data() | ||||
| 	mutable QMap<QPersistentModelIndex, std::shared_ptr<class Resource>> m_resources; | ||||
|  | ||||
| 	const int m_resultTypeId; | ||||
| }; | ||||
							
								
								
									
										116
									
								
								api/dead/test/tst_Resource.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								api/dead/test/tst_Resource.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| #include <QTest> | ||||
| #include "TestUtil.h" | ||||
|  | ||||
| #include "resources/Resource.h" | ||||
| #include "resources/ResourceHandler.h" | ||||
| #include "resources/ResourceObserver.h" | ||||
|  | ||||
| class DummyStringResourceHandler : public ResourceHandler | ||||
| { | ||||
| public: | ||||
| 	explicit DummyStringResourceHandler(const QString &key) | ||||
| 		: m_key(key) {} | ||||
|  | ||||
| 	void init(std::shared_ptr<ResourceHandler> &) override | ||||
| 	{ | ||||
| 		setResult(m_key); | ||||
| 	} | ||||
|  | ||||
| 	QString m_key; | ||||
| }; | ||||
| class DummyObserver : public ResourceObserver | ||||
| { | ||||
| public: | ||||
| 	void resourceUpdated() override | ||||
| 	{ | ||||
| 		values += get<QString>(); | ||||
| 	} | ||||
|  | ||||
| 	QStringList values; | ||||
| }; | ||||
| class DummyObserverObject : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
| 	Q_PROPERTY(QString property MEMBER property) | ||||
|  | ||||
| public: | ||||
| 	explicit DummyObserverObject(QObject *parent = nullptr) : QObject(parent) {} | ||||
|  | ||||
| 	QString property; | ||||
| }; | ||||
|  | ||||
| class ResourceTest : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
| 	private | ||||
| 	slots: | ||||
| 	void initTestCase() | ||||
| 	{ | ||||
| 		Resource::registerHandler<DummyStringResourceHandler>("dummy"); | ||||
| 	} | ||||
| 	void cleanupTestCase() | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	void test_Then() | ||||
| 	{ | ||||
| 		QString val; | ||||
| 		Resource::create("dummy:test_Then") | ||||
| 				->then([&val](const QString &key) { val = key; }); | ||||
| 		QCOMPARE(val, QStringLiteral("test_Then")); | ||||
| 	} | ||||
| 	void test_Object() | ||||
| 	{ | ||||
| 		DummyObserver *observer = new DummyObserver; | ||||
| 		Resource::create("dummy:test_Object")->applyTo(observer); | ||||
| 		QCOMPARE(observer->values, QStringList() << "test_Object"); | ||||
| 	} | ||||
| 	void test_QObjectProperty() | ||||
| 	{ | ||||
| 		DummyObserverObject *object = new DummyObserverObject; | ||||
| 		Resource::create("dummy:test_QObjectProperty")->applyTo(object); | ||||
| 		QCOMPARE(object->property, QStringLiteral("test_QObjectProperty")); | ||||
| 	} | ||||
|  | ||||
| 	void test_DontRequestPlaceholder() | ||||
| 	{ | ||||
| 		// since dummy:asdf immediently gives a value we should not get the placeholder | ||||
| 		Resource::create("dummy:asdf", Resource::create("dummy:fdsa")) | ||||
| 				->then([](const QString &key) { QCOMPARE(key, QStringLiteral("asdf")); }); | ||||
| 	} | ||||
|  | ||||
| 	void test_MergedResources() | ||||
| 	{ | ||||
| 		auto r1 = Resource::create("dummy:asdf"); | ||||
| 		auto r2 = Resource::create("dummy:asdf"); | ||||
| 		auto r3 = Resource::create("dummy:fdsa"); | ||||
| 		auto r4 = Resource::create("dummy:asdf"); | ||||
|  | ||||
| 		QCOMPARE(r1, r2); | ||||
| 		QCOMPARE(r1, r4); | ||||
| 		QVERIFY(r1 != r3); | ||||
| 		QVERIFY(r2 != r3); | ||||
| 		QVERIFY(r4 != r3); | ||||
| 	} | ||||
|  | ||||
| 	void test_MergedResourceWithPlaceholder() | ||||
| 	{ | ||||
| 		auto p1 = Resource::create("dummy:placeA"); | ||||
| 		auto p2 = Resource::create("dummy:placeB"); | ||||
|  | ||||
| 		auto r1 = Resource::create("dummy:asdf"); | ||||
| 		auto r2 = Resource::create("dummy:asdf", p1); | ||||
| 		auto r3 = Resource::create("dummy:asdf", p2); | ||||
| 		auto r4 = Resource::create("dummy:asdf", p1); | ||||
|  | ||||
| 		QCOMPARE(r2, r4); | ||||
| 		QVERIFY(r1 != r2); | ||||
| 		QVERIFY(r1 != r3); | ||||
| 		QVERIFY(r1 != r4); | ||||
| 		QVERIFY(r2 != r3); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| QTEST_GUILESS_MAIN(ResourceTest) | ||||
|  | ||||
| #include "tst_Resource.moc" | ||||
		Reference in New Issue
	
	Block a user