feat(Storybook): read Figma links directly from pages
This commit is contained in:
parent
158bb87b4a
commit
853641fb89
|
@ -45,11 +45,9 @@ set(PROJECT_LIB "${PROJECT_NAME}Lib")
|
||||||
add_library(${PROJECT_LIB}
|
add_library(${PROJECT_LIB}
|
||||||
cachecleaner.cpp cachecleaner.h
|
cachecleaner.cpp cachecleaner.h
|
||||||
directorieswatcher.cpp directorieswatcher.h
|
directorieswatcher.cpp directorieswatcher.h
|
||||||
figmadecoratormodel.cpp figmadecoratormodel.h
|
|
||||||
figmaio.cpp figmaio.h
|
figmaio.cpp figmaio.h
|
||||||
figmalinks.cpp figmalinks.h
|
figmalinks.cpp figmalinks.h
|
||||||
figmalinksmodel.cpp figmalinksmodel.h
|
figmalinksmodel.cpp figmalinksmodel.h
|
||||||
figmalinkssource.cpp figmalinkssource.h
|
|
||||||
modelutils.cpp modelutils.h
|
modelutils.cpp modelutils.h
|
||||||
pagesmodel.h pagesmodel.cpp
|
pagesmodel.h pagesmodel.cpp
|
||||||
sectionsdecoratormodel.cpp sectionsdecoratormodel.h
|
sectionsdecoratormodel.cpp sectionsdecoratormodel.h
|
||||||
|
@ -99,10 +97,6 @@ add_executable(SectionsDecoratorModelTest tests/tst_SectionsDecoratorModel.cpp)
|
||||||
target_link_libraries(SectionsDecoratorModelTest PRIVATE Qt5::Test ${PROJECT_LIB})
|
target_link_libraries(SectionsDecoratorModelTest PRIVATE Qt5::Test ${PROJECT_LIB})
|
||||||
add_test(NAME SectionsDecoratorModelTest COMMAND SectionsDecoratorModelTest)
|
add_test(NAME SectionsDecoratorModelTest COMMAND SectionsDecoratorModelTest)
|
||||||
|
|
||||||
add_executable(FigmaDecoratorModelTest tests/tst_FigmaDecoratorModel.cpp)
|
|
||||||
target_link_libraries(FigmaDecoratorModelTest PRIVATE Qt5::Test ${PROJECT_LIB})
|
|
||||||
add_test(NAME FigmaModelTest COMMAND FigmaModelTest)
|
|
||||||
|
|
||||||
add_executable(QmlTests
|
add_executable(QmlTests
|
||||||
qmlTests/main.cpp
|
qmlTests/main.cpp
|
||||||
qmlTests/src/TextUtils.cpp qmlTests/src/TextUtils.h
|
qmlTests/src/TextUtils.cpp qmlTests/src/TextUtils.h
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
#include "figmadecoratormodel.h"
|
|
||||||
|
|
||||||
#include "figmalinks.h"
|
|
||||||
#include "figmalinksmodel.h"
|
|
||||||
#include "modelutils.h"
|
|
||||||
|
|
||||||
FigmaDecoratorModel::FigmaDecoratorModel(QObject *parent)
|
|
||||||
: QIdentityProxyModel{parent}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> FigmaDecoratorModel::roleNames() const
|
|
||||||
{
|
|
||||||
auto roles = QIdentityProxyModel::roleNames();
|
|
||||||
roles.insert(FigmaRole, QByteArrayLiteral("figma"));
|
|
||||||
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant FigmaDecoratorModel::data(const QModelIndex &proxyIndex, int role) const
|
|
||||||
{
|
|
||||||
if (!checkIndex(proxyIndex, CheckIndexOption::IndexIsValid))
|
|
||||||
return {};
|
|
||||||
|
|
||||||
if (role == FigmaRole) {
|
|
||||||
static FigmaLinksModel empty({});
|
|
||||||
|
|
||||||
if (!m_titleRole)
|
|
||||||
return QVariant::fromValue(&empty);
|
|
||||||
|
|
||||||
const auto title = data(proxyIndex, m_titleRole.value()).toString();
|
|
||||||
auto it = m_submodels.find(title);
|
|
||||||
|
|
||||||
if (it == m_submodels.end()) {
|
|
||||||
QStringList links;
|
|
||||||
|
|
||||||
if (m_figmaLinks)
|
|
||||||
links = m_figmaLinks->getLinksMap().value(title, {});
|
|
||||||
|
|
||||||
auto linksModel = new FigmaLinksModel(
|
|
||||||
links, const_cast<FigmaDecoratorModel*>(this));
|
|
||||||
it = m_submodels.insert(title, linksModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return QVariant::fromValue(it.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
return QIdentityProxyModel::data(proxyIndex, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
FigmaLinks* FigmaDecoratorModel::getFigmaLinks() const
|
|
||||||
{
|
|
||||||
return m_figmaLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaDecoratorModel::setFigmaLinks(FigmaLinks *figmaLinks)
|
|
||||||
{
|
|
||||||
if (figmaLinks == m_figmaLinks)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_figmaLinks = figmaLinks;
|
|
||||||
const auto& linksMap = m_figmaLinks
|
|
||||||
? m_figmaLinks->getLinksMap()
|
|
||||||
: QMap<QString, QStringList>{};
|
|
||||||
|
|
||||||
auto linksIt = linksMap.constBegin();
|
|
||||||
while (linksIt != linksMap.constEnd()) {
|
|
||||||
if (m_submodels.contains(linksIt.key()))
|
|
||||||
m_submodels.value(linksIt.key())->setContent(linksIt.value());
|
|
||||||
++linksIt;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto submodelsIt = m_submodels.constBegin();
|
|
||||||
while (submodelsIt != m_submodels.constEnd()) {
|
|
||||||
if (!linksMap.contains(submodelsIt.key()))
|
|
||||||
submodelsIt.value()->setContent({});
|
|
||||||
++submodelsIt;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit figmaLinksChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaDecoratorModel::setSourceModel(QAbstractItemModel *sourceModel)
|
|
||||||
{
|
|
||||||
qDeleteAll(m_submodels);
|
|
||||||
m_submodels.clear();
|
|
||||||
|
|
||||||
m_titleRole = ModelUtils::findRole(QByteArrayLiteral("title"), sourceModel);
|
|
||||||
|
|
||||||
if(!m_titleRole)
|
|
||||||
qWarning("The source model is missing title role!");
|
|
||||||
|
|
||||||
QIdentityProxyModel::setSourceModel(sourceModel);
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QIdentityProxyModel>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class FigmaLinks;
|
|
||||||
class FigmaLinksModel;
|
|
||||||
|
|
||||||
class FigmaDecoratorModel : public QIdentityProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(FigmaLinks* figmaLinks READ getFigmaLinks
|
|
||||||
WRITE setFigmaLinks NOTIFY figmaLinksChanged)
|
|
||||||
public:
|
|
||||||
static constexpr auto FigmaRole = Qt::UserRole + 100;
|
|
||||||
|
|
||||||
explicit FigmaDecoratorModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
|
||||||
QVariant data(const QModelIndex &proxyIndex, int role) const override;
|
|
||||||
|
|
||||||
FigmaLinks* getFigmaLinks() const;
|
|
||||||
void setFigmaLinks(FigmaLinks *figmaLinks);
|
|
||||||
|
|
||||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void figmaLinksChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<int> m_titleRole;
|
|
||||||
FigmaLinks* m_figmaLinks = nullptr;
|
|
||||||
mutable QMap<QString, FigmaLinksModel*> m_submodels;
|
|
||||||
};
|
|
|
@ -1,139 +0,0 @@
|
||||||
#include "figmalinkssource.h"
|
|
||||||
|
|
||||||
#include <QQmlEngine>
|
|
||||||
|
|
||||||
#include "figmaio.h"
|
|
||||||
#include "figmalinks.h"
|
|
||||||
|
|
||||||
FigmaLinksSource::FigmaLinksSource(QObject *parent)
|
|
||||||
: QObject{parent}
|
|
||||||
{
|
|
||||||
connect(&m_watcher, &QFileSystemWatcher::fileChanged,
|
|
||||||
this, [this](const QString &path) {
|
|
||||||
this->readFile();
|
|
||||||
|
|
||||||
if (!this->m_watcher.files().contains(path))
|
|
||||||
this->m_watcher.addPath(path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const QUrl& FigmaLinksSource::getFilePath() const
|
|
||||||
{
|
|
||||||
return m_filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaLinksSource::setFilePath(const QUrl& path)
|
|
||||||
{
|
|
||||||
if (path == m_filePath)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_filePath = path;
|
|
||||||
readFile();
|
|
||||||
setupWatcher();
|
|
||||||
emit filePathChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
FigmaLinks* FigmaLinksSource::getFigmaLinks() const
|
|
||||||
{
|
|
||||||
return m_figmaLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaLinksSource::remove(const QString &key, const QList<int> &indexes)
|
|
||||||
{
|
|
||||||
if (m_filePath.isEmpty()) {
|
|
||||||
qWarning("FigmaLinksSource::remove - file path is not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<QString, QStringList> linksMap;
|
|
||||||
|
|
||||||
if (m_figmaLinks)
|
|
||||||
linksMap = m_figmaLinks->getLinksMap();
|
|
||||||
|
|
||||||
auto it = linksMap.find(key);
|
|
||||||
|
|
||||||
if (it == linksMap.end()) {
|
|
||||||
qWarning("FigmaLinksSource::remove - provided key doesn't exist!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto indexesSorted = indexes;
|
|
||||||
std::sort(indexesSorted.begin(), indexesSorted.end());
|
|
||||||
|
|
||||||
if (std::adjacent_find(indexesSorted.cbegin(), indexesSorted.cend())
|
|
||||||
!= indexesSorted.cend()) {
|
|
||||||
qWarning("FigmaLinksSource::remove - provided indexes list contains duplicates!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& linksList = it.value();
|
|
||||||
|
|
||||||
if (indexesSorted.first() < 0 || indexesSorted.last() >= linksList.size()) {
|
|
||||||
qWarning("FigmaLinksSource::remove - at least one provided index is out of range!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (linksList.size() == indexesSorted.size()) {
|
|
||||||
linksMap.erase(it);
|
|
||||||
} else {
|
|
||||||
std::for_each(std::crbegin(indexesSorted), std::crend(indexesSorted),
|
|
||||||
[&linksList](int idx) {
|
|
||||||
linksList.removeAt(idx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FigmaIO::write(m_filePath.path(), linksMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaLinksSource::append(const QString &key, const QList<QString> &links)
|
|
||||||
{
|
|
||||||
QMap<QString, QStringList> linksMap;
|
|
||||||
|
|
||||||
|
|
||||||
if (m_filePath.isEmpty()) {
|
|
||||||
qWarning("FigmaLinksSource::append - file path is not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_figmaLinks)
|
|
||||||
linksMap = m_figmaLinks->getLinksMap();
|
|
||||||
|
|
||||||
linksMap[key].append(links);
|
|
||||||
|
|
||||||
FigmaIO::write(m_filePath.path(), linksMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaLinksSource::updateFigmaLinks(const QMap<QString, QStringList>& map)
|
|
||||||
{
|
|
||||||
FigmaLinks *mapping = new FigmaLinks(map, this);
|
|
||||||
|
|
||||||
if (m_figmaLinks && qjsEngine(m_figmaLinks)) {
|
|
||||||
m_figmaLinks->setParent(nullptr);
|
|
||||||
QQmlEngine::setObjectOwnership(m_figmaLinks, QQmlEngine::JavaScriptOwnership);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_figmaLinks = mapping;
|
|
||||||
emit figmaLinksChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaLinksSource::readFile()
|
|
||||||
{
|
|
||||||
QMap<QString, QStringList> figmaLinks = FigmaIO::read(m_filePath.path());
|
|
||||||
updateFigmaLinks(figmaLinks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FigmaLinksSource::setupWatcher()
|
|
||||||
{
|
|
||||||
auto currentlyWatched = m_watcher.files();
|
|
||||||
|
|
||||||
if (!currentlyWatched.isEmpty())
|
|
||||||
m_watcher.removePaths(currentlyWatched);
|
|
||||||
|
|
||||||
if (m_filePath.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_watcher.addPath(m_filePath.path());
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
class FigmaLinks;
|
|
||||||
|
|
||||||
class FigmaLinksSource : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QUrl filePath READ getFilePath WRITE setFilePath NOTIFY filePathChanged)
|
|
||||||
Q_PROPERTY(FigmaLinks* figmaLinks READ getFigmaLinks NOTIFY figmaLinksChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FigmaLinksSource(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
const QUrl& getFilePath() const;
|
|
||||||
void setFilePath(const QUrl& path);
|
|
||||||
FigmaLinks* getFigmaLinks() const;
|
|
||||||
|
|
||||||
Q_INVOKABLE void remove(const QString &key, const QList<int> &indexes);
|
|
||||||
Q_INVOKABLE void append(const QString &key, const QList<QString> &links);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void filePathChanged();
|
|
||||||
void figmaLinksChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void updateFigmaLinks(const QMap<QString, QStringList>& map);
|
|
||||||
void readFile();
|
|
||||||
void setupWatcher();
|
|
||||||
|
|
||||||
FigmaLinks *m_figmaLinks = nullptr;
|
|
||||||
QUrl m_filePath;
|
|
||||||
QFileSystemWatcher m_watcher;
|
|
||||||
};
|
|
|
@ -4,9 +4,7 @@
|
||||||
|
|
||||||
#include "cachecleaner.h"
|
#include "cachecleaner.h"
|
||||||
#include "directorieswatcher.h"
|
#include "directorieswatcher.h"
|
||||||
#include "figmadecoratormodel.h"
|
|
||||||
#include "figmalinks.h"
|
#include "figmalinks.h"
|
||||||
#include "figmalinkssource.h"
|
|
||||||
#include "pagesmodel.h"
|
#include "pagesmodel.h"
|
||||||
#include "sectionsdecoratormodel.h"
|
#include "sectionsdecoratormodel.h"
|
||||||
|
|
||||||
|
@ -46,8 +44,6 @@ int main(int argc, char *argv[])
|
||||||
engine.rootContext()->setContextProperty(
|
engine.rootContext()->setContextProperty(
|
||||||
"pagesFolder", QML_IMPORT_ROOT + QStringLiteral("/pages"));
|
"pagesFolder", QML_IMPORT_ROOT + QStringLiteral("/pages"));
|
||||||
|
|
||||||
qmlRegisterType<FigmaDecoratorModel>("Storybook", 1, 0, "FigmaDecoratorModel");
|
|
||||||
qmlRegisterType<FigmaLinksSource>("Storybook", 1, 0, "FigmaLinksSource");
|
|
||||||
qmlRegisterType<PagesModelInitialized>("Storybook", 1, 0, "PagesModel");
|
qmlRegisterType<PagesModelInitialized>("Storybook", 1, 0, "PagesModel");
|
||||||
qmlRegisterType<SectionsDecoratorModel>("Storybook", 1, 0, "SectionsDecoratorModel");
|
qmlRegisterType<SectionsDecoratorModel>("Storybook", 1, 0, "SectionsDecoratorModel");
|
||||||
qmlRegisterUncreatableType<FigmaLinks>("Storybook", 1, 0, "FigmaLinks", {});
|
qmlRegisterUncreatableType<FigmaLinks>("Storybook", 1, 0, "FigmaLinks", {});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.14
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import Qt.labs.settings 1.0
|
import Qt.labs.settings 1.0
|
||||||
|
|
||||||
|
@ -78,19 +78,6 @@ ApplicationWindow {
|
||||||
id: pagesModel
|
id: pagesModel
|
||||||
}
|
}
|
||||||
|
|
||||||
FigmaLinksSource {
|
|
||||||
id: figmaLinksSource
|
|
||||||
|
|
||||||
filePath: "figma.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
FigmaDecoratorModel {
|
|
||||||
id: figmaModel
|
|
||||||
|
|
||||||
sourceModel: pagesModel
|
|
||||||
figmaLinks: figmaLinksSource.figmaLinks
|
|
||||||
}
|
|
||||||
|
|
||||||
HotReloader {
|
HotReloader {
|
||||||
id: reloader
|
id: reloader
|
||||||
|
|
||||||
|
@ -212,7 +199,7 @@ ApplicationWindow {
|
||||||
id: currentPageModelItem
|
id: currentPageModelItem
|
||||||
|
|
||||||
model: SingleItemProxyModel {
|
model: SingleItemProxyModel {
|
||||||
sourceModel: figmaModel
|
sourceModel: pagesModel
|
||||||
roleName: "title"
|
roleName: "title"
|
||||||
value: root.currentPage
|
value: root.currentPage
|
||||||
}
|
}
|
||||||
|
@ -320,9 +307,6 @@ Tips:
|
||||||
figmaLinksCache: figmaImageLinksCache
|
figmaLinksCache: figmaImageLinksCache
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoveLinksRequested: figmaLinksSource.remove(pageTitle, indexes)
|
|
||||||
onAppendLinksRequested: figmaLinksSource.append(pageTitle, links)
|
|
||||||
|
|
||||||
onClosing: Qt.callLater(destroy)
|
onClosing: Qt.callLater(destroy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.14
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import Storybook 1.0
|
import Storybook 1.0
|
||||||
|
|
||||||
|
@ -50,64 +50,14 @@ Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
id: selectableCheckBox
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
text: "selectable"
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolSeparator {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: selectionText
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
property string selectionAsString: ""
|
|
||||||
|
|
||||||
text: `selected indexes: [${selectionAsString}]`
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: grid.selection
|
|
||||||
|
|
||||||
function onSelectionChanged() {
|
|
||||||
const indexes = grid.selection.selectedIndexes
|
|
||||||
const rows = indexes.map(idx => idx.row)
|
|
||||||
|
|
||||||
selectionText.selectionAsString = rows.join(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Clear selection"
|
|
||||||
|
|
||||||
onClicked: grid.selection.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImagesGridView {
|
ImagesGridView {
|
||||||
id: grid
|
id: grid
|
||||||
|
|
||||||
Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
selectable: selectableCheckBox.checked
|
|
||||||
clip: true
|
clip: true
|
||||||
model: imagesModel
|
model: imagesModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// category: Components
|
// category: Components
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const auto categoryUncategorized QStringLiteral("Uncategorized");
|
const auto categoryUncategorized QStringLiteral("Uncategorized");
|
||||||
}
|
}
|
||||||
|
@ -15,6 +17,10 @@ PagesModel::PagesModel(const QString &path, QObject *parent)
|
||||||
m_items = load();
|
m_items = load();
|
||||||
readMetadata(m_items);
|
readMetadata(m_items);
|
||||||
|
|
||||||
|
for (const auto& item : qAsConst(m_items)) {
|
||||||
|
setFigmaLinks(item.title, item.figmaLinks);
|
||||||
|
}
|
||||||
|
|
||||||
fsWatcher->addPath(path);
|
fsWatcher->addPath(path);
|
||||||
|
|
||||||
connect(fsWatcher, &QFileSystemWatcher::directoryChanged,
|
connect(fsWatcher, &QFileSystemWatcher::directoryChanged,
|
||||||
|
@ -62,6 +68,21 @@ void PagesModel::readMetadata(PagesModelItem& item) {
|
||||||
? categoryMatch.captured(2).trimmed() : categoryUncategorized;
|
? categoryMatch.captured(2).trimmed() : categoryUncategorized;
|
||||||
|
|
||||||
item.category = category;
|
item.category = category;
|
||||||
|
|
||||||
|
static QRegularExpression figmaRegex(
|
||||||
|
"^(\\/\\/\\s*)((?:https:\\/\\/)?(?:www\\.)?figma\\.com\\/.*)$",
|
||||||
|
QRegularExpression::MultilineOption);
|
||||||
|
|
||||||
|
QRegularExpressionMatchIterator i = figmaRegex.globalMatch(content);
|
||||||
|
QStringList links;
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = i.next();
|
||||||
|
QString link = match.captured(2);
|
||||||
|
links << link;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.figmaLinks = links;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PagesModel::readMetadata(QList<PagesModelItem> &items) {
|
void PagesModel::readMetadata(QList<PagesModelItem> &items) {
|
||||||
|
@ -129,6 +150,7 @@ void PagesModel::reload() {
|
||||||
auto index = std::distance(m_items.begin(), it);
|
auto index = std::distance(m_items.begin(), it);
|
||||||
const auto& previous = *it;
|
const auto& previous = *it;
|
||||||
readMetadata(item);
|
readMetadata(item);
|
||||||
|
setFigmaLinks(item.title, item.figmaLinks);
|
||||||
|
|
||||||
if (previous.category != item.category) {
|
if (previous.category != item.category) {
|
||||||
// For simplicity category change is handled by removing and
|
// For simplicity category change is handled by removing and
|
||||||
|
@ -149,7 +171,8 @@ QHash<int, QByteArray> PagesModel::roleNames() const
|
||||||
{
|
{
|
||||||
static const QHash<int,QByteArray> roles {
|
static const QHash<int,QByteArray> roles {
|
||||||
{ TitleRole, QByteArrayLiteral("title") },
|
{ TitleRole, QByteArrayLiteral("title") },
|
||||||
{ CategoryRole, QByteArrayLiteral("category") }
|
{ CategoryRole, QByteArrayLiteral("category") },
|
||||||
|
{ FigmaRole, QByteArrayLiteral("figma") }
|
||||||
};
|
};
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
|
@ -167,8 +190,28 @@ QVariant PagesModel::data(const QModelIndex &index, int role) const
|
||||||
|
|
||||||
if (role == TitleRole)
|
if (role == TitleRole)
|
||||||
return m_items.at(index.row()).title;
|
return m_items.at(index.row()).title;
|
||||||
|
|
||||||
if (role == CategoryRole)
|
if (role == CategoryRole)
|
||||||
return m_items.at(index.row()).category;
|
return m_items.at(index.row()).category;
|
||||||
|
|
||||||
|
if (role == FigmaRole) {
|
||||||
|
auto title = m_items.at(index.row()).title;
|
||||||
|
auto it = m_figmaSubmodels.find(title);
|
||||||
|
assert(it != m_figmaSubmodels.end());
|
||||||
|
|
||||||
|
return QVariant::fromValue(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PagesModel::setFigmaLinks(const QString& title, const QStringList& links)
|
||||||
|
{
|
||||||
|
auto it = m_figmaSubmodels.find(title);
|
||||||
|
|
||||||
|
if (it == m_figmaSubmodels.end()) {
|
||||||
|
m_figmaSubmodels.insert(title, new FigmaLinksModel(links, this));
|
||||||
|
} else {
|
||||||
|
it.value()->setContent(links);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "figmalinksmodel.h"
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
struct PagesModelItem {
|
struct PagesModelItem {
|
||||||
|
@ -10,6 +12,7 @@ struct PagesModelItem {
|
||||||
QDateTime lastModified;
|
QDateTime lastModified;
|
||||||
QString title;
|
QString title;
|
||||||
QString category;
|
QString category;
|
||||||
|
QStringList figmaLinks;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PagesModel : public QAbstractListModel
|
class PagesModel : public QAbstractListModel
|
||||||
|
@ -20,7 +23,8 @@ public:
|
||||||
|
|
||||||
enum Roles {
|
enum Roles {
|
||||||
TitleRole = Qt::UserRole + 1,
|
TitleRole = Qt::UserRole + 1,
|
||||||
CategoryRole
|
CategoryRole,
|
||||||
|
FigmaRole
|
||||||
};
|
};
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
@ -34,7 +38,10 @@ private:
|
||||||
static void readMetadata(PagesModelItem &item);
|
static void readMetadata(PagesModelItem &item);
|
||||||
static void readMetadata(QList<PagesModelItem> &items);
|
static void readMetadata(QList<PagesModelItem> &items);
|
||||||
|
|
||||||
|
void setFigmaLinks(const QString& title, const QStringList& links);
|
||||||
|
|
||||||
QString m_path;
|
QString m_path;
|
||||||
QList<PagesModelItem> m_items;
|
QList<PagesModelItem> m_items;
|
||||||
|
QMap<QString, FigmaLinksModel*> m_figmaSubmodels;
|
||||||
QFileSystemWatcher* fsWatcher;
|
QFileSystemWatcher* fsWatcher;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
/* required */ property FigmaLinksCache figmaLinksCache
|
required property FigmaLinksCache figmaLinksCache
|
||||||
property alias sourceModel: d.model
|
property alias sourceModel: d.model
|
||||||
|
|
||||||
readonly property Instantiator _d: Instantiator {
|
readonly property Instantiator _d: Instantiator {
|
||||||
|
@ -29,7 +29,7 @@ ListModel {
|
||||||
d.idCounter++
|
d.idCounter++
|
||||||
|
|
||||||
figmaLinksCache.getImageUrl(model.link, link => {
|
figmaLinksCache.getImageUrl(model.link, link => {
|
||||||
if (delegate)
|
if (delegate && link !== null)
|
||||||
root.setProperty(model.index, "imageLink", link)
|
root.setProperty(model.index, "imageLink", link)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQml 2.14
|
import QtQml 2.15
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
|
@ -14,10 +14,6 @@ ApplicationWindow {
|
||||||
signal removeLinksRequested(var indexes)
|
signal removeLinksRequested(var indexes)
|
||||||
signal appendLinksRequested(var links)
|
signal appendLinksRequested(var links)
|
||||||
|
|
||||||
readonly property var urlRegex:
|
|
||||||
/^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/
|
|
||||||
|
|
||||||
|
|
||||||
SwipeView {
|
SwipeView {
|
||||||
id: topSwipeView
|
id: topSwipeView
|
||||||
|
|
||||||
|
@ -40,37 +36,6 @@ ApplicationWindow {
|
||||||
topSwipeView.incrementCurrentIndex()
|
topSwipeView.incrementCurrentIndex()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: ToolBar {
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: removeButton
|
|
||||||
|
|
||||||
readonly property int selectionCount:
|
|
||||||
grid.selection.selectedIndexes.length
|
|
||||||
|
|
||||||
text: "Remove selected"
|
|
||||||
+ (enabled ? ` (${selectionCount})` : "")
|
|
||||||
enabled: grid.selection.hasSelection
|
|
||||||
|
|
||||||
onClicked: removeConfirmDialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
ToolSeparator {}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Add new links"
|
|
||||||
|
|
||||||
onClicked: addNewLinksDialog.open()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -106,81 +71,4 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog {
|
|
||||||
id: removeConfirmDialog
|
|
||||||
|
|
||||||
readonly property var selected: grid.selection.selectedIndexes
|
|
||||||
|
|
||||||
anchors.centerIn: Overlay.overlay
|
|
||||||
|
|
||||||
title: "Links removal"
|
|
||||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
|
||||||
|
|
||||||
Label {
|
|
||||||
text: "Are you sure that you want to remove "
|
|
||||||
+ removeButton.selectionCount + " link(s)?"
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: root.removeLinksRequested(selected.map(idx => idx.row))
|
|
||||||
onSelectedChanged: close()
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialog {
|
|
||||||
id: addNewLinksDialog
|
|
||||||
|
|
||||||
anchors.centerIn: Overlay.overlay
|
|
||||||
|
|
||||||
title: "Add new Figma links"
|
|
||||||
standardButtons: Dialog.Save | Dialog.Cancel
|
|
||||||
|
|
||||||
width: parent.width * 0.8
|
|
||||||
height: parent.height * 0.4
|
|
||||||
|
|
||||||
GroupBox {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
title: "Figma links, 1 per line"
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
id: scrollView
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
contentHeight: linksTextEdit.implicitHeight
|
|
||||||
contentWidth: linksTextEdit.implicitWidth
|
|
||||||
|
|
||||||
TextEdit {
|
|
||||||
id: linksTextEdit
|
|
||||||
|
|
||||||
property var links: []
|
|
||||||
|
|
||||||
width: scrollView.width
|
|
||||||
height: scrollView.height
|
|
||||||
|
|
||||||
font.pixelSize: 13
|
|
||||||
selectByMouse: true
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
const allLines = text.split("\n")
|
|
||||||
const nonEmptyLines = allLines.filter(
|
|
||||||
line => line.trim().length > 0)
|
|
||||||
const trimmed = nonEmptyLines.map(line => line.trim())
|
|
||||||
|
|
||||||
links = trimmed.every(line => root.urlRegex.test(line))
|
|
||||||
? trimmed : []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosed: Qt.callLater(linksTextEdit.clear)
|
|
||||||
onAccepted: root.appendLinksRequested(linksTextEdit.links)
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
standardButton(Dialog.Save).enabled
|
|
||||||
= Qt.binding(() => linksTextEdit.links.length > 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.14
|
import QtQuick.Controls 2.15
|
||||||
import QtQml.Models 2.14
|
import QtQml.Models 2.15
|
||||||
|
|
||||||
GridView {
|
GridView {
|
||||||
id: root
|
id: root
|
||||||
|
@ -10,15 +10,6 @@ GridView {
|
||||||
|
|
||||||
signal clicked(int index)
|
signal clicked(int index)
|
||||||
|
|
||||||
property bool selectable: true
|
|
||||||
readonly property alias selection: selectionModel
|
|
||||||
|
|
||||||
ItemSelectionModel {
|
|
||||||
id: selectionModel
|
|
||||||
|
|
||||||
model: root.model
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
width: root.cellWidth
|
width: root.cellWidth
|
||||||
height: root.cellHeight
|
height: root.cellHeight
|
||||||
|
@ -60,21 +51,6 @@ GridView {
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: model.rawLink
|
ToolTip.text: model.rawLink
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
visible: root.selectable
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
checked: {
|
|
||||||
selectionModel.selection
|
|
||||||
return selectionModel.isSelected(root.model.index(index, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggled: selectionModel.select(root.model.index(index, 0),
|
|
||||||
ItemSelectionModel.Toggle)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,341 +0,0 @@
|
||||||
#include <QSignalSpy>
|
|
||||||
#include <QTest>
|
|
||||||
#include <QTemporaryFile>
|
|
||||||
#include <QStringListModel>
|
|
||||||
|
|
||||||
#include "figmadecoratormodel.h"
|
|
||||||
#include "figmaio.h"
|
|
||||||
#include "figmalinks.h"
|
|
||||||
#include "figmalinksmodel.h"
|
|
||||||
#include "figmalinkssource.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
auto constexpr sampleJson1 = R"(
|
|
||||||
{
|
|
||||||
"Component_1": [
|
|
||||||
"link_1", "link_2"
|
|
||||||
],
|
|
||||||
"Component_2": [
|
|
||||||
"link_3", "link_4"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
auto constexpr sampleJson2 = R"(
|
|
||||||
{
|
|
||||||
"Component_1": [
|
|
||||||
"link_1"
|
|
||||||
],
|
|
||||||
"Component_2": [
|
|
||||||
"link_3", "link_5"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
class TestSourceModel : public QAbstractListModel {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr auto TitleRole = 0;
|
|
||||||
|
|
||||||
TestSourceModel(int count = 1) : m_count(count) {}
|
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
|
|
||||||
return m_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override {
|
|
||||||
if (!index.isValid())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return QString("title_%1").arg(index.row());
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override {
|
|
||||||
QHash<int, QByteArray> roles;
|
|
||||||
roles.insert(TitleRole, QByteArrayLiteral("title"));
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
int m_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // unnamed namespace
|
|
||||||
|
|
||||||
class FigmaDecoratorModelTest: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void figmaIOTest() {
|
|
||||||
QTemporaryFile file;
|
|
||||||
QVERIFY(file.open());
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
const auto readEmpty = FigmaIO::read(file.fileName());
|
|
||||||
QCOMPARE(readEmpty, {});
|
|
||||||
|
|
||||||
const QMap<QString, QStringList> content = {
|
|
||||||
{ "k_1", { "l_1", "l_2"}},
|
|
||||||
{ "k_2", { "l_3", "l_4"}}
|
|
||||||
};
|
|
||||||
|
|
||||||
FigmaIO::write(file.fileName(), content);
|
|
||||||
const auto readContent = FigmaIO::read(file.fileName());
|
|
||||||
|
|
||||||
QCOMPARE(readContent, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
void readingFigmaFileTest() {
|
|
||||||
FigmaLinksSource figmaLinksSource;
|
|
||||||
|
|
||||||
QSignalSpy spy(&figmaLinksSource, &FigmaLinksSource::figmaLinksChanged);
|
|
||||||
|
|
||||||
QCOMPARE(figmaLinksSource.getFigmaLinks(), nullptr);
|
|
||||||
|
|
||||||
QTemporaryFile file;
|
|
||||||
if (file.open()) {
|
|
||||||
QTextStream stream(&file);
|
|
||||||
stream << sampleJson1;
|
|
||||||
}
|
|
||||||
|
|
||||||
figmaLinksSource.setFilePath(file.fileName());
|
|
||||||
|
|
||||||
QVERIFY(figmaLinksSource.getFigmaLinks() != nullptr);
|
|
||||||
|
|
||||||
const FigmaLinks *links = figmaLinksSource.getFigmaLinks();
|
|
||||||
|
|
||||||
QCOMPARE(links->getLinksMap(), (QMap<QString, QStringList> {
|
|
||||||
{{"Component_1"}, {"link_1", "link_2"}},
|
|
||||||
{{"Component_2"}, {"link_3", "link_4"}}}));
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
|
|
||||||
QTemporaryFile file2;
|
|
||||||
if (file2.open()) {
|
|
||||||
QTextStream stream(&file2);
|
|
||||||
stream << sampleJson2;
|
|
||||||
}
|
|
||||||
|
|
||||||
figmaLinksSource.setFilePath(file2.fileName());
|
|
||||||
|
|
||||||
QVERIFY(figmaLinksSource.getFigmaLinks() != nullptr);
|
|
||||||
|
|
||||||
const FigmaLinks *links2 = figmaLinksSource.getFigmaLinks();
|
|
||||||
|
|
||||||
QCOMPARE(links2->getLinksMap(), (QMap<QString, QStringList> {
|
|
||||||
{{"Component_1"}, {"link_1"}},
|
|
||||||
{{"Component_2"}, {"link_3", "link_5"}}}));
|
|
||||||
QCOMPARE(spy.count(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void readingAfterFigmaFileChangedTest() {
|
|
||||||
|
|
||||||
FigmaLinksSource figmaLinksSource;
|
|
||||||
|
|
||||||
QSignalSpy spy(&figmaLinksSource, &FigmaLinksSource::figmaLinksChanged);
|
|
||||||
|
|
||||||
QCOMPARE(figmaLinksSource.getFigmaLinks(), nullptr);
|
|
||||||
|
|
||||||
QTemporaryFile file;
|
|
||||||
if (file.open()) {
|
|
||||||
QTextStream stream(&file);
|
|
||||||
stream << sampleJson1;
|
|
||||||
}
|
|
||||||
|
|
||||||
figmaLinksSource.setFilePath(file.fileName());
|
|
||||||
|
|
||||||
QVERIFY(figmaLinksSource.getFigmaLinks() != nullptr);
|
|
||||||
|
|
||||||
const FigmaLinks *links = figmaLinksSource.getFigmaLinks();
|
|
||||||
|
|
||||||
QCOMPARE(links->getLinksMap(), (QMap<QString, QStringList> {
|
|
||||||
{{"Component_1"}, {"link_1", "link_2"}},
|
|
||||||
{{"Component_2"}, {"link_3", "link_4"}}}));
|
|
||||||
|
|
||||||
QCOMPARE(spy.count(), 1);
|
|
||||||
|
|
||||||
if (QFile f(file.fileName());
|
|
||||||
f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
|
||||||
QTextStream stream(&f);
|
|
||||||
stream << sampleJson2;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVERIFY(spy.wait());
|
|
||||||
QCOMPARE(spy.count(), 2);
|
|
||||||
|
|
||||||
const FigmaLinks *links2 = figmaLinksSource.getFigmaLinks();
|
|
||||||
|
|
||||||
QCOMPARE(links2->getLinksMap(), (QMap<QString, QStringList> {
|
|
||||||
{{"Component_1"}, {"link_1"}},
|
|
||||||
{{"Component_2"}, {"link_3", "link_5"}}}));
|
|
||||||
|
|
||||||
|
|
||||||
if (QFile f(file.fileName());
|
|
||||||
f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
|
||||||
QTextStream stream(&f);
|
|
||||||
stream << sampleJson1;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVERIFY(spy.wait());
|
|
||||||
QCOMPARE(spy.count(), 3);
|
|
||||||
|
|
||||||
const FigmaLinks *links3 = figmaLinksSource.getFigmaLinks();
|
|
||||||
|
|
||||||
QCOMPARE(links3->getLinksMap(), (QMap<QString, QStringList> {
|
|
||||||
{{"Component_1"}, {"link_1", "link_2"}},
|
|
||||||
{{"Component_2"}, {"link_3", "link_4"}}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void emptyFigmaModelTest() {
|
|
||||||
FigmaDecoratorModel model;
|
|
||||||
QCOMPARE(model.rowCount(), 0);
|
|
||||||
QVERIFY(model.roleNames().contains(FigmaDecoratorModel::FigmaRole));
|
|
||||||
QCOMPARE(model.roleNames().value(
|
|
||||||
FigmaDecoratorModel::FigmaRole), QStringLiteral("figma"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void figmaModelWithoutSourceModel() {
|
|
||||||
FigmaLinks links({
|
|
||||||
{{"Component_1"}, {"link_1", "link_2"}},
|
|
||||||
{{"Component_2"}, {"link_3", "link_4"}}
|
|
||||||
});
|
|
||||||
|
|
||||||
FigmaDecoratorModel model;
|
|
||||||
model.setFigmaLinks(&links);
|
|
||||||
|
|
||||||
QCOMPARE(model.rowCount(), 0);
|
|
||||||
QVERIFY(model.roleNames().contains(FigmaDecoratorModel::FigmaRole));
|
|
||||||
QCOMPARE(model.roleNames().value(
|
|
||||||
FigmaDecoratorModel::FigmaRole), QStringLiteral("figma"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void figmaModelWithIncompatibleSourceModel() {
|
|
||||||
QStringListModel stringsModel({"s1", "s2"});
|
|
||||||
FigmaDecoratorModel model;
|
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg,
|
|
||||||
"The source model is missing title role!");
|
|
||||||
model.setSourceModel(&stringsModel);
|
|
||||||
|
|
||||||
QCOMPARE(model.rowCount(), 2);
|
|
||||||
QVERIFY(model.roleNames().contains(FigmaDecoratorModel::FigmaRole));
|
|
||||||
QCOMPARE(model.roleNames().value(
|
|
||||||
FigmaDecoratorModel::FigmaRole), QStringLiteral("figma"));
|
|
||||||
|
|
||||||
QCOMPARE(model.data(
|
|
||||||
model.index(0, 0),
|
|
||||||
FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>()->rowCount(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void figmaModelWithoutLinksTest() {
|
|
||||||
TestSourceModel sourceModel;
|
|
||||||
FigmaDecoratorModel model;
|
|
||||||
|
|
||||||
model.setSourceModel(&sourceModel);
|
|
||||||
|
|
||||||
QCOMPARE(model.rowCount(), 1);
|
|
||||||
QCOMPARE(model.roleNames().count(), 2);
|
|
||||||
QVERIFY(model.roleNames().contains(FigmaDecoratorModel::FigmaRole));
|
|
||||||
QCOMPARE(model.roleNames().value(
|
|
||||||
FigmaDecoratorModel::FigmaRole), QStringLiteral("figma"));
|
|
||||||
|
|
||||||
QCOMPARE(model.data(
|
|
||||||
model.index(0, 0),
|
|
||||||
FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>()->rowCount(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void figmaModelTest() {
|
|
||||||
TestSourceModel sourceModel{2};
|
|
||||||
FigmaLinks links({
|
|
||||||
{{"title_0"}, {"link_1", "link_2"}},
|
|
||||||
{{"title_x"}, {"link_3", "link_4"}}
|
|
||||||
});
|
|
||||||
|
|
||||||
FigmaDecoratorModel model;
|
|
||||||
|
|
||||||
QSignalSpy spy(&model, &FigmaDecoratorModel::dataChanged);
|
|
||||||
|
|
||||||
model.setSourceModel(&sourceModel);
|
|
||||||
|
|
||||||
QCOMPARE(spy.size(), 0);
|
|
||||||
|
|
||||||
QCOMPARE(model.rowCount(), 2);
|
|
||||||
QCOMPARE(model.roleNames().count(), 2);
|
|
||||||
QVERIFY(model.roleNames().contains(FigmaDecoratorModel::FigmaRole));
|
|
||||||
QVERIFY(model.roleNames().contains(TestSourceModel::TitleRole));
|
|
||||||
|
|
||||||
QCOMPARE(model.roleNames().value(
|
|
||||||
FigmaDecoratorModel::FigmaRole), QStringLiteral("figma"));
|
|
||||||
QCOMPARE(model.roleNames().value(
|
|
||||||
TestSourceModel::TitleRole), QStringLiteral("title"));
|
|
||||||
|
|
||||||
QCOMPARE(model.data(model.index(0, 0),
|
|
||||||
TestSourceModel::TitleRole).toString(), "title_0");
|
|
||||||
QCOMPARE(model.data(model.index(1, 0),
|
|
||||||
TestSourceModel::TitleRole).toString(), "title_1");
|
|
||||||
|
|
||||||
auto figmaLinksModel1 = model.data(model.index(0, 0), FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>();
|
|
||||||
|
|
||||||
QVERIFY(figmaLinksModel1 != nullptr);
|
|
||||||
QCOMPARE(figmaLinksModel1->rowCount(), 0);
|
|
||||||
|
|
||||||
auto figmaLinksModel2 = model.data(model.index(1, 0), FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>();
|
|
||||||
|
|
||||||
QVERIFY(figmaLinksModel2 != nullptr);
|
|
||||||
QCOMPARE(figmaLinksModel2->rowCount(), 0);
|
|
||||||
|
|
||||||
QSignalSpy linksModelspy1(figmaLinksModel1,
|
|
||||||
&QAbstractItemModel::modelReset);
|
|
||||||
QSignalSpy linksModelspy2(figmaLinksModel2,
|
|
||||||
&QAbstractItemModel::modelReset);
|
|
||||||
|
|
||||||
model.setFigmaLinks(&links);
|
|
||||||
|
|
||||||
QCOMPARE(spy.size(), 0);
|
|
||||||
|
|
||||||
QCOMPARE(linksModelspy1.size(), 1);
|
|
||||||
QCOMPARE(linksModelspy2.size(), 0);
|
|
||||||
|
|
||||||
QCOMPARE(model.data(model.index(0, 0), FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>(), figmaLinksModel1);
|
|
||||||
QCOMPARE(model.data(model.index(1, 0), FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>(), figmaLinksModel2);
|
|
||||||
|
|
||||||
QCOMPARE(figmaLinksModel1->rowCount(), 2);
|
|
||||||
QCOMPARE(figmaLinksModel2->rowCount(), 0);
|
|
||||||
|
|
||||||
QCOMPARE(model.data(model.index(0, 0),
|
|
||||||
TestSourceModel::TitleRole).toString(), "title_0");
|
|
||||||
QCOMPARE(model.data(model.index(1, 0),
|
|
||||||
TestSourceModel::TitleRole).toString(), "title_1");
|
|
||||||
|
|
||||||
QCOMPARE(figmaLinksModel1->roleNames().size(), 1);
|
|
||||||
QCOMPARE(figmaLinksModel2->roleNames().size(), 1);
|
|
||||||
|
|
||||||
QCOMPARE(figmaLinksModel1->data(figmaLinksModel1->index(0, 0),
|
|
||||||
FigmaLinksModel::LinkRole).toString(), "link_1");
|
|
||||||
QCOMPARE(figmaLinksModel1->data(figmaLinksModel1->index(1, 0),
|
|
||||||
FigmaLinksModel::LinkRole).toString(), "link_2");
|
|
||||||
|
|
||||||
model.setFigmaLinks(nullptr);
|
|
||||||
|
|
||||||
QCOMPARE(spy.size(), 0);
|
|
||||||
QCOMPARE(linksModelspy1.size(), 2);
|
|
||||||
QCOMPARE(linksModelspy2.size(), 0);
|
|
||||||
|
|
||||||
QCOMPARE(model.data(model.index(0, 0), FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>(), figmaLinksModel1);
|
|
||||||
QCOMPARE(model.data(model.index(1, 0), FigmaDecoratorModel::FigmaRole)
|
|
||||||
.value<QAbstractItemModel*>(), figmaLinksModel2);
|
|
||||||
|
|
||||||
QCOMPARE(figmaLinksModel1->rowCount(), 0);
|
|
||||||
QCOMPARE(figmaLinksModel2->rowCount(), 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
QTEST_MAIN(FigmaDecoratorModelTest)
|
|
||||||
#include "tst_FigmaDecoratorModel.moc"
|
|
Loading…
Reference in New Issue