feat(Storybook): Hot reloading for pages

Closes: #7975
This commit is contained in:
Michał Cieślak 2022-10-18 20:47:41 +02:00 committed by Michał
parent 803bf48e99
commit 8981b8615a
15 changed files with 149 additions and 29 deletions

View File

@ -20,14 +20,15 @@ find_package(
COMPONENTS Core Quick QuickControls2
REQUIRED)
file(GLOB_RECURSE QML_FILES "stubs/*.qml" "mocks/*.qml" "Storybook/*.qml" "../ui/StatusQ/*.qml" "../ui/app/*.qml")
file(GLOB_RECURSE QML_FILES "stubs/*.qml" "mocks/*.qml" "pages/*.qml" "src/*.qml" "src/qmldir" "../ui/StatusQ/*.qml" "../ui/app/*.qml")
file(GLOB_RECURSE JS_FILES "../ui/StatusQ/*.js" "../ui/app/*.js")
add_executable(
${PROJECT_NAME}
main.cpp
qml.qrc
${QML_FILES}
cachecleaner.cpp cachecleaner.h
directorieswatcher.cpp directorieswatcher.h
${QML_FILES} main.qml
${JS_FILES}
)
@ -37,7 +38,7 @@ target_link_libraries(
${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick Qt5::QuickControls2
SortFilterProxyModel)
set(QML_IMPORT_PATH ${CMAKE_SOURCE_DIR} CACHE STRING "" FORCE)
set(QML_IMPORT_PATH "${CMAKE_SOURCE_DIR}/src" CACHE STRING "" FORCE)
if (APPLE)
find_library(AppKit AppKit)

View File

@ -0,0 +1,12 @@
#include "cachecleaner.h"
#include <QQmlEngine>
CacheCleaner::CacheCleaner(QQmlEngine* engine)
: engine(engine)
{
}
void CacheCleaner::clearComponentCache() const {
engine->clearComponentCache();
}

16
storybook/cachecleaner.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <QObject>
class QQmlEngine;
class CacheCleaner : public QObject
{
Q_OBJECT
public:
explicit CacheCleaner(QQmlEngine* engine);
Q_INVOKABLE void clearComponentCache() const;
private:
QQmlEngine* engine;
};

View File

@ -0,0 +1,29 @@
#include "directorieswatcher.h"
#include <QFileSystemWatcher>
#include <QDirIterator>
DirectoriesWatcher::DirectoriesWatcher(QObject *parent)
: QObject{parent}, fsWatcher(new QFileSystemWatcher(this))
{
connect(fsWatcher, &QFileSystemWatcher::directoryChanged,
this, &DirectoriesWatcher::changed);
}
void DirectoriesWatcher::addPaths(const QStringList &paths)
{
for (auto& path : paths) {
QDirIterator it(path, QDir::AllDirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (it.hasNext()) {
const auto& subpath = it.filePath();
if (!subpath.isEmpty())
fsWatcher->addPath(subpath);
it.next();
}
fsWatcher->addPath(path);
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <QObject>
class QFileSystemWatcher;
class DirectoriesWatcher : public QObject
{
Q_OBJECT
public:
explicit DirectoriesWatcher(QObject *parent = nullptr);
void addPaths(const QStringList &paths);
signals:
void changed();
private:
QFileSystemWatcher* fsWatcher;
};

View File

@ -1,6 +1,10 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlEngine>
#include <cachecleaner.h>
#include <directorieswatcher.h>
int main(int argc, char *argv[])
{
@ -14,14 +18,36 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine;
engine.addImportPath(QStringLiteral(":/"));
engine.addImportPath(SRC_DIR + QStringLiteral("/../ui/StatusQ/src"));
engine.addImportPath(SRC_DIR + QStringLiteral("/../ui/app"));
engine.addImportPath(SRC_DIR + QStringLiteral("/../ui/imports"));
engine.addImportPath(SRC_DIR + QStringLiteral("/stubs"));
engine.addImportPath(SRC_DIR + QStringLiteral("/mocks"));
QStringList additionalImportPaths {
SRC_DIR + QStringLiteral("/../ui/StatusQ/src"),
SRC_DIR + QStringLiteral("/../ui/app"),
SRC_DIR + QStringLiteral("/../ui/imports"),
SRC_DIR + QStringLiteral("/src"),
SRC_DIR + QStringLiteral("/pages"),
SRC_DIR + QStringLiteral("/stubs"),
SRC_DIR + QStringLiteral("/mocks"),
};
const QUrl url(QStringLiteral("qrc:/main.qml"));
for (auto& path : additionalImportPaths)
engine.addImportPath(path);
auto watcherFactory = [additionalImportPaths](QQmlEngine*, QJSEngine*) {
auto watcher = new DirectoriesWatcher();
watcher->addPaths(additionalImportPaths);
return watcher;
};
qmlRegisterSingletonType<DirectoriesWatcher>(
"Storybook", 1, 0, "SourceWatcher", watcherFactory);
auto cleanerFactory = [](QQmlEngine* engine, QJSEngine*) {
return new CacheCleaner(engine);
};
qmlRegisterSingletonType<CacheCleaner>(
"Storybook", 1, 0, "CacheCleaner", cleanerFactory);
const QUrl url(SRC_DIR + QStringLiteral("/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)

View File

@ -18,6 +18,13 @@ ApplicationWindow {
font.pixelSize: 13
HotReloader {
loader: viewLoader
enabled: hotReloadingCheckBox.checked
onReloaded: reloadingAnimation.restart()
}
ListModel {
id: pagesModel
@ -63,6 +70,31 @@ ApplicationWindow {
}
}
CheckBox {
id: hotReloadingCheckBox
Layout.fillWidth: true
text: "Hot reloading"
Rectangle {
anchors.fill: parent
border.color: "red"
border.width: 2
opacity: 0
OpacityAnimator on opacity {
id: reloadingAnimation
running: false
from: 1
to: 0
duration: 500
easing.type: Easing.InQuad
}
}
}
Pane {
Layout.fillWidth: true
Layout.fillHeight: true
@ -87,7 +119,7 @@ ApplicationWindow {
anchors.fill: parent
clip: true
source: Qt.resolvedUrl(`./pages/${root.currentPage}Page.qml`)
source: `pages/${root.currentPage}Page.qml`
asynchronous: loadAsyncCheckBox.checked
visible: status === Loader.Ready
@ -109,5 +141,6 @@ ApplicationWindow {
property alias currentPage: root.currentPage
property alias loadAsynchronously: loadAsyncCheckBox.checked
property alias darkMode: darkModeCheckBox.checked
property alias hotReloading: hotReloadingCheckBox.checked
}
}

View File

@ -1,17 +0,0 @@
<RCC>
<qresource prefix="/">
<file>Storybook/ImageSelectPopup.qml</file>
<file>Storybook/Logs.qml</file>
<file>Storybook/LogsAndControlsPanel.qml</file>
<file>Storybook/LogsView.qml</file>
<file>Storybook/PagesList.qml</file>
<file>Storybook/StorybookUtils.qml</file>
<file>Storybook/qmldir</file>
<file>main.qml</file>
<file>pages/CommunitiesPortalDummyModel.qml</file>
<file>pages/CommunitiesPortalLayoutPage.qml</file>
<file>pages/CommunitiesPortalModelEditor.qml</file>
<file>pages/LoginViewPage.qml</file>
<file>pages/AboutViewPage.qml</file>
</qresource>
</RCC>

View File

@ -1,3 +1,4 @@
HotReloader 1.0 HotReloader.qml
ImageSelectPopup 1.0 ImageSelectPopup.qml
Logs 1.0 Logs.qml
LogsAndControlsPanel 1.0 LogsAndControlsPanel.qml