diff --git a/src/pagerouter.cpp b/src/pagerouter.cpp index 80ec9876..96ef2fac 100644 --- a/src/pagerouter.cpp +++ b/src/pagerouter.cpp @@ -1,164 +1,190 @@ #include #include "pagerouter.h" ParsedRoute parseRoute(QJSValue value) { if (value.isUndefined()) { return ParsedRoute{QString(), QVariant()}; } else if (value.isString()) { return ParsedRoute{ value.toString(), QVariant() }; } else { return ParsedRoute{ value.property(QStringLiteral("route")).toString(), value.property(QStringLiteral("data")).toVariant() }; } } QList parseRoutes(QJSValue values) { QList ret; if (values.isArray()) { for (auto route : values.toVariant().toList()) { if (route.toString() != QString()) { ret << ParsedRoute{ route.toString(), QVariant() }; - } else { - ret << parseRoute(route.value()); + } else if (route.canConvert()) { + auto map = route.value(); + ret << ParsedRoute{ + map.value(QStringLiteral("route")).toString(), + map.value(QStringLiteral("data")) + }; } } } else { ret << parseRoute(values); } return ret; } PageRouter::PageRouter(QQuickItem *parent) : QQuickItem(parent) { } PageRouter::~PageRouter() {} void PageRouter::classBegin() { QQmlComponent *component = new QQmlComponent(qmlEngine(this), this); component->setData( QByteArrayLiteral("import QtQuick 2.0; import org.kde.kirigami 2.7 as Kirigami; Kirigami.PageRow { anchors.fill: parent }"), QUrl(QStringLiteral("pagerouter.cpp")) ); m_pageRow = static_cast(component->create(qmlContext(this))); QQmlProperty::write(m_pageRow, QStringLiteral("parent"), QVariant::fromValue(this)); connect(this, &PageRouter::initialRouteChanged, [=]() { QMetaObject::invokeMethod(m_pageRow, "clear"); m_currentRoutes.clear(); push(parseRoute(initialRoute())); }); } void PageRouter::componentComplete() { QMetaObject::invokeMethod(m_pageRow, "clear"); m_currentRoutes.clear(); push(parseRoute(initialRoute())); } bool PageRouter::routesContainsKey(const QString &key) { return m_routes.hasProperty(key); } QQmlComponent* PageRouter::routesValueForKey(const QString &key) { return m_routes.property(key).toVariant().value(); } void PageRouter::push(ParsedRoute route) { if (!routesContainsKey(route.name)) { qCritical() << "Route" << route.name << "not defined"; return; } auto context = qmlContext(this); auto component = routesValueForKey(route.name); if (component->status() == QQmlComponent::Ready) { auto item = component->create(context); // TODO: pester mart about seeing if he knows // how to utilise an attached property instead + item->setParent(this); item->setProperty("routeData", route.data); QMetaObject::invokeMethod(m_pageRow, "push", Q_ARG(QVariant, QVariant::fromValue(item)), Q_ARG(QVariant, QVariant())); m_currentRoutes << route; } else if (component->status() == QQmlComponent::Loading) { connect(component, &QQmlComponent::statusChanged, [=](QQmlComponent::Status status) { if (status != QQmlComponent::Ready) { qCritical() << "Failed to push route:" << component->errors(); } auto item = component->create(context); // TODO: See above + item->setParent(this); item->setProperty("routeData", route.data); QMetaObject::invokeMethod(m_pageRow, "push", Q_ARG(QVariant, QVariant::fromValue(item)), Q_ARG(QVariant, QVariant())); m_currentRoutes << route; }); } else { qCritical() << "Failed to push route:" << component->errors(); } } QJSValue PageRouter::routes() const { return m_routes; } -void PageRouter::setRoutes(const QJSValue &routes) +void PageRouter::setRoutes(QJSValue routes) { m_routes = routes; } QJSValue PageRouter::initialRoute() const { return m_initialRoute; } void PageRouter::setInitialRoute(QJSValue value) { m_initialRoute = value; } void PageRouter::navigateToRoute(QJSValue route) { QMetaObject::invokeMethod(m_pageRow, "clear"); m_currentRoutes.clear(); for (auto route : parseRoutes(route)) { push(route); } } bool PageRouter::isNavigatedToRoute(QJSValue route) { auto parsed = parseRoutes(route); if (parsed.length() > m_currentRoutes.length()) { return false; } for (int i = 0; i < parsed.length(); i++) { if (parsed[i].name != m_currentRoutes[i].name || parsed[i].data != m_currentRoutes[i].data) { return false; } } return true; } void PageRouter::pushRoute(QJSValue route) { push(parseRoute(route)); } void PageRouter::popRoute() { QMetaObject::invokeMethod(m_pageRow, "pop"); -} \ No newline at end of file +} + +PageRouterAttached* PageRouter::qmlAttachedProperties(QObject *object) +{ + auto attached = new PageRouterAttached(object); + auto pointer = object; + while (pointer != nullptr) { + auto casted = qobject_cast(pointer); + if (casted != nullptr) { + attached->m_router = casted; + break; + } + pointer = pointer->parent(); + } + if (attached->m_router.isNull()) { + qCritical() << "PageRouterAttached could not find a parent PageRouter"; + } + return attached; +} + +PageRouterAttached::PageRouterAttached(QObject *parent) : QObject(parent) {} \ No newline at end of file diff --git a/src/pagerouter.h b/src/pagerouter.h index 3192d8b4..4a336d85 100644 --- a/src/pagerouter.h +++ b/src/pagerouter.h @@ -1,51 +1,85 @@ #pragma once #include #include "columnview.h" struct ParsedRoute { QString name; QVariant data; }; +class PageRouterAttached; + class PageRouter : public QQuickItem { Q_OBJECT Q_PROPERTY(QJSValue routes READ routes WRITE setRoutes NOTIFY routesChanged) Q_PROPERTY(QJSValue initialRoute READ initialRoute WRITE setInitialRoute NOTIFY initialRouteChanged) Q_PROPERTY(QQuickItem* pageRow MEMBER m_pageRow) private: QJSValue m_routes; QQuickItem* m_pageRow; QJSValue m_initialRoute; QList m_currentRoutes; void push(ParsedRoute route); bool routesContainsKey(const QString &key); QQmlComponent *routesValueForKey(const QString &key); protected: void classBegin() override; void componentComplete() override; public: PageRouter(QQuickItem *parent = nullptr); ~PageRouter(); QJSValue routes() const; - void setRoutes(const QJSValue &routes); + void setRoutes(QJSValue routes); QJSValue initialRoute() const; void setInitialRoute(QJSValue initialRoute); Q_INVOKABLE void navigateToRoute(QJSValue route); Q_INVOKABLE bool isNavigatedToRoute(QJSValue route); Q_INVOKABLE void pushRoute(QJSValue route); Q_INVOKABLE void popRoute(); + static PageRouterAttached *qmlAttachedProperties(QObject *object); + Q_SIGNALS: void routesChanged(); void initialRouteChanged(); }; + +class PageRouterAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(PageRouter *router READ router NOTIFY routerChanged) + Q_PROPERTY(QVariant data MEMBER m_data NOTIFY dataChanged) + +private: + explicit PageRouterAttached(QObject *parent = nullptr); + + QPointer m_router; + QVariant m_data; + + friend class PageRouter; + +public: + PageRouter* router() const { return m_router; }; + QVariant data() const { return m_data; }; + Q_INVOKABLE void navigateToRoute(QJSValue route) { m_router->navigateToRoute(route); }; + Q_INVOKABLE bool isNavigatedToRoute(QJSValue route) { return m_router->isNavigatedToRoute(route); }; + Q_INVOKABLE void pushRoute(QJSValue route) { m_router->pushRoute(route); }; + Q_INVOKABLE void popRoute() { m_router->popRoute(); }; + +Q_SIGNALS: + void routerChanged(); + void dataChanged(); +}; + +QML_DECLARE_TYPEINFO(PageRouter, QML_HAS_ATTACHED_PROPERTIES) \ No newline at end of file