From 4dd7f7fcec627dd40047e5db1f8cc3ff6af6850b Mon Sep 17 00:00:00 2001
From: zty199 <46324746+zty199@users.noreply.github.com>
Date: Wed, 26 Jun 2024 15:59:05 +0800
Subject: [PATCH] feat: adapt to Qt6

adapt to Qt6

Log: adapt to Qt6; bump version to 1.7.1
---
 .gitignore                                    |  17 ++-
 CMakeLists.txt                                |  36 ++++++
 debian/changelog                              |   7 ++
 debian/control                                |  33 ++---
 debian/rules                                  |  39 +++---
 debian/spark-webapp-runtime.postinst          |  12 --
 debian/spark-webapp-runtime.prerm             |   7 --
 spark-webapp-runtime.pro                      |   8 +-
 spark-webapp-runtime/application.cpp          | 111 -----------------
 src/CMakeLists.txt                            |  36 ++++++
 src/application.cpp                           |  84 +++++++++++++
 {spark-webapp-runtime => src}/application.h   |   6 +-
 {spark-webapp-runtime => src}/globaldefine.h  |   3 -
 {spark-webapp-runtime => src}/httpd.cpp       |   0
 {spark-webapp-runtime => src}/httpd.h         |   0
 {spark-webapp-runtime => src}/httplib.h       |   0
 {spark-webapp-runtime => src}/main.cpp        |  25 +---
 {spark-webapp-runtime => src}/mainwindow.cpp  | 114 +++++++++++-------
 {spark-webapp-runtime => src}/mainwindow.h    |  11 +-
 .../resources/css/webkit-scrollbar.css        |   0
 .../resources/help/help.html                  |   0
 .../resources/images/Logo-Spark.png           | Bin
 .../resources/images/go-next-24.svg           |   0
 .../resources/images/go-previous-24.svg       |   0
 .../resources/images/spark-webapp-runtime.svg |   0
 .../resources/images/view-refresh.svg         |   0
 .../resources/resources.qrc                   |   0
 .../spark-webapp-runtime.pro                  |  21 +---
 src/src.cmake                                 |  11 ++
 .../webenginepage.cpp                         |   0
 {spark-webapp-runtime => src}/webenginepage.h |   0
 .../webengineurlrequestinterceptor.cpp        |   0
 .../webengineurlrequestinterceptor.h          |   0
 .../webengineview.cpp                         |   2 +-
 {spark-webapp-runtime => src}/webengineview.h |   0
 {spark-webapp-runtime => src}/widget.cpp      |   2 +-
 {spark-webapp-runtime => src}/widget.h        |   0
 translate_generation.sh                       |   6 -
 translate_update.sh                           |   6 -
 translations/CMakeLists.txt                   |  19 +++
 .../spark-webapp-runtime_zh_CN.ts             | 100 ++++++---------
 41 files changed, 357 insertions(+), 359 deletions(-)
 create mode 100644 CMakeLists.txt
 delete mode 100755 debian/spark-webapp-runtime.postinst
 delete mode 100755 debian/spark-webapp-runtime.prerm
 delete mode 100644 spark-webapp-runtime/application.cpp
 create mode 100644 src/CMakeLists.txt
 create mode 100644 src/application.cpp
 rename {spark-webapp-runtime => src}/application.h (93%)
 rename {spark-webapp-runtime => src}/globaldefine.h (83%)
 rename {spark-webapp-runtime => src}/httpd.cpp (100%)
 rename {spark-webapp-runtime => src}/httpd.h (100%)
 rename {spark-webapp-runtime => src}/httplib.h (100%)
 rename {spark-webapp-runtime => src}/main.cpp (93%)
 rename {spark-webapp-runtime => src}/mainwindow.cpp (80%)
 rename {spark-webapp-runtime => src}/mainwindow.h (88%)
 rename {spark-webapp-runtime => src}/resources/css/webkit-scrollbar.css (100%)
 rename {spark-webapp-runtime => src}/resources/help/help.html (100%)
 rename {spark-webapp-runtime => src}/resources/images/Logo-Spark.png (100%)
 rename {spark-webapp-runtime => src}/resources/images/go-next-24.svg (100%)
 rename {spark-webapp-runtime => src}/resources/images/go-previous-24.svg (100%)
 rename {spark-webapp-runtime => src}/resources/images/spark-webapp-runtime.svg (100%)
 rename {spark-webapp-runtime => src}/resources/images/view-refresh.svg (100%)
 rename {spark-webapp-runtime => src}/resources/resources.qrc (100%)
 rename {spark-webapp-runtime => src}/spark-webapp-runtime.pro (53%)
 create mode 100644 src/src.cmake
 rename {spark-webapp-runtime => src}/webenginepage.cpp (100%)
 rename {spark-webapp-runtime => src}/webenginepage.h (100%)
 rename {spark-webapp-runtime => src}/webengineurlrequestinterceptor.cpp (100%)
 rename {spark-webapp-runtime => src}/webengineurlrequestinterceptor.h (100%)
 rename {spark-webapp-runtime => src}/webengineview.cpp (98%)
 rename {spark-webapp-runtime => src}/webengineview.h (100%)
 rename {spark-webapp-runtime => src}/widget.cpp (98%)
 rename {spark-webapp-runtime => src}/widget.h (100%)
 delete mode 100755 translate_generation.sh
 delete mode 100755 translate_update.sh
 create mode 100644 translations/CMakeLists.txt
 rename {spark-webapp-runtime/translations => translations}/spark-webapp-runtime_zh_CN.ts (64%)

diff --git a/.gitignore b/.gitignore
index 7de47bf..0a8302b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
 *.la
 *.lai
 *.so
+*.so.*
 *.dll
 *.dylib
 
@@ -28,6 +29,8 @@ ui_*.h
 *.jsc
 Makefile*
 *build-*
+*.qm
+*.prl
 
 # Qt unit tests
 target_wrapper.*
@@ -42,13 +45,21 @@ target_wrapper.*
 # QtCreator CMake
 CMakeLists.txt.user*
 
-# Qt qm files
-*.qm
+# QtCreator 4.8< compilation database
+compile_commands.json
 
-# Debian dpkg-buildpackage
+# QtCreator local machine specific files for imported projects
+*creator.user*
+
+*_qmlcache.qrc
+
+# debian
 debian/*.debhelper*
 debian/files
 debian/*.substvars
 debian/spark-webapp-runtime
 
+# Others
 build
+obj-*
+.vscode
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..96aea3b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.12)
+
+project(spark-webapp-runtime)
+add_compile_definitions(PROJECT_NAME="${PROJECT_NAME}")
+
+if(NOT DEFINED VERSION)
+    set(VERSION 1.0)
+endif()
+add_compile_definitions(VERSION="${VERSION}")
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(AUTOMOC_COMPILER_PREDEFINES ON)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall")
+if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "sw_64")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mieee")
+endif ()
+if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
+endif ()
+
+add_compile_definitions(QT_MESSAGELOGCONTEXT)
+
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+    set(CMAKE_INSTALL_PREFIX /usr)
+endif()
+
+include(GNUInstallDirs)
+
+add_subdirectory(src)
+add_subdirectory(translations)
diff --git a/debian/changelog b/debian/changelog
index 2d025ed..5bed044 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+spark-webapp-runtime (1.7.1) stable; urgency=medium
+
+  * adapt to Qt6
+
+ -- shenmo <shenmo@spark-app.store>  Wed, 26 Jun 2024 16:15:00 +0800
+
+
 spark-webapp-runtime (1.7.0-1) stable; urgency=medium
 
   * Transmit web notification to system notification center
diff --git a/debian/control b/debian/control
index 633e39c..964ab74 100644
--- a/debian/control
+++ b/debian/control
@@ -3,37 +3,24 @@ Maintainer: shenmo <shenmo@spark-app.store>
 Section: utils
 Priority: optional
 Build-Depends:
- debhelper (>= 9),
+ debhelper (>= 11),
+ cmake (>= 3.12),
  pkg-config,
  qtchooser (>= 55-gc9562a1-1~),
- libqt5core5a,
- libqt5gui5,
- libqt5widgets5,
- libqt5concurrent5,
- libqt5dbus5,
- libqt5svg5-dev,
- qtwebengine5-dev,
- qttools5-private-dev,
- libdtkcore-dev (>= 5.0),
- libdtkgui-dev (>= 5.0),
- libdtkwidget-dev (>= 5.0)
+ qt6-base-dev,
+ qt6-tools-dev,
+ qt6-webengine-dev,
+ libdtkcommon-dev,
+ libdtk6core-dev,
+ libdtk6gui-dev,
+ libdtk6widget-dev
 Standards-Version: 1.0
 Homepage: https://gitee.com/deepin-community-store/spark-web-app-runtime
 
 
 Package: spark-webapp-runtime
 Architecture: any
-Depends:${shlibs:Depends}, ${misc:Depends},
- libqt5core5a,
- libqt5gui5,
- libqt5widgets5,
- libqt5concurrent5,
- libqt5dbus5,
- libqt5svg5,
- libqt5webenginewidgets5,
- libdtkcore5,
- libdtkgui5,
- libdtkwidget5
+Depends:${shlibs:Depends}, ${misc:Depends}
 Conflicts: store.spark-app.spark-webapp-runtime
 Provides: store.spark-app.spark-webapp-runtime
 Replaces: store.spark-app.spark-webapp-runtime
diff --git a/debian/rules b/debian/rules
index 6b40fdf..f0c668b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,39 +1,28 @@
 #!/usr/bin/make -f
 
-export QT_SELECT=5
 include /usr/share/dpkg/default.mk
 
-DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
-DH_AUTO_ARGS = --parallel --buildsystem=qmake
+export QT_SELECT = qt6
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+
+DEB_CFLAGS ?= $(shell dpkg-buildflags --get CFLAGS)
+DEB_CPPFLAGS ?= $(shell dpkg-buildflags --get CPPFLAGS)
+DEB_CXXFLAGS ?= $(shell dpkg-buildflags --get CXXFLAGS)
 
 # Uncomment this to turn on verbose mode.
-# export DH_VERBOSE=1
+#export DH_VERBOSE = 1
 
 %:
 	dh $@ --parallel
 
-override_dh_auto_clean:
-	rm -rf $(CURDIR)/build
-
-
 override_dh_auto_configure:
-	mkdir -p $(CURDIR)/build
+	dh_auto_configure -- \
+	-DVERSION=$(DEB_VERSION_UPSTREAM) \
+	-DCMAKE_BUILD_TYPE=Release \
+	-DCMAKE_C_FLAGS="$(DEB_CFLAGS) $(DEB_CPPFLAGS)" \
+	-DCMAKE_CXX_FLAGS="$(DEB_CXXFLAGS) $(DEB_CPPFLAGS)"
 
-	dh_auto_configure MAKEFLAGS=-j$(JOBS) -- spark-webapp-runtime.pro \
-	-spec linux-g++ CONFIG+=qtquickcompiler \
-	-o $(CURDIR)/build/
-
-
-override_dh_auto_build:
-	make -C $(CURDIR)/build -j$(JOBS)
-
-
-override_dh_auto_install:
-	make -C $(CURDIR)/build install \
-	INSTALL_ROOT=$(CURDIR)/debian/spark-webapp-runtime
-
-
-# Ignore the dpkg-shlibdeps: warning (it uses none of the library's symbols)
-# Qt Mutidedia lib will ref to network libraray.
+#Ignore the dpkg-shlibdeps: warning (it uses none of the library's symbols)
+#Qt Mutidedia lib will ref to network libraray.
 override_dh_shlibdeps:
 	dh_shlibdeps --dpkg-shlibdeps-params=--warnings=0
diff --git a/debian/spark-webapp-runtime.postinst b/debian/spark-webapp-runtime.postinst
deleted file mode 100755
index de93e44..0000000
--- a/debian/spark-webapp-runtime.postinst
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-case "$1" in
-	configure)
-		# Create symbol links for binary files
-		ln -s -f /opt/durapps/spark-webapp-runtime/bin/spark-webapp-runtime /usr/bin/spark-webapp-runtime
-		ln -s -f /opt/durapps/spark-webapp-runtime/share/spark-webapp-runtime /usr/share/spark-webapp-runtime
-	;;
-
-	*)
-	;;
-esac
diff --git a/debian/spark-webapp-runtime.prerm b/debian/spark-webapp-runtime.prerm
deleted file mode 100755
index b8620df..0000000
--- a/debian/spark-webapp-runtime.prerm
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-if [ "$1" = "remove" -o "$1" = "purge" ] ; then
-	# Remove residual symbol links
-	rm -f /usr/bin/spark-webapp-runtime
-	rm -rf /usr/share/spark-webapp-runtime
-fi
diff --git a/spark-webapp-runtime.pro b/spark-webapp-runtime.pro
index 6585564..d509427 100644
--- a/spark-webapp-runtime.pro
+++ b/spark-webapp-runtime.pro
@@ -4,10 +4,4 @@ TEMPLATE = subdirs
 CONFIG += ordered
 
 SUBDIRS += \
-        spark-webapp-runtime
-
-# Update translation files
-CONFIG(release, debug|release) {
-    system(bash $${PWD}/translate_update.sh)
-    system(bash $${PWD}/translate_generation.sh)
-}
+        src/spark-webapp-runtime.pro
diff --git a/spark-webapp-runtime/application.cpp b/spark-webapp-runtime/application.cpp
deleted file mode 100644
index 290afa7..0000000
--- a/spark-webapp-runtime/application.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#include "application.h"
-#include "globaldefine.h"
-
-#include <DPlatformWindowHandle>
-#include <DAboutDialog>
-
-#include <QStandardPaths>
-
-#include <unistd.h>
-
-Application::Application(int &argc, char **argv)
-    : DApplication(argc, argv)
-{
-    saveLaunchParams(argc, argv);
-
-    loadTranslator();
-
-    setAttribute(Qt::AA_UseHighDpiPixmaps);
-    if (!DPlatformWindowHandle::pluginVersion().isEmpty()) {
-        setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
-    }
-
-    setApplicationVersion(QString(CURRENT_VER));
-    setOrganizationName(ORGANIZATION_NAME); // 添加组织名称,和商店主体的文件夹同在 ~/.local/share/spark-union 文件夹下
-    setApplicationName(APPLICATION_NAME); // 这里不要翻译,否则 ~/.local/share 中文件夹名也会被翻译
-    setProductName(DEFAULT_TITLE);
-    setApplicationDisplayName(DEFAULT_TITLE);
-    setApplicationLicense(" <a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPLv3</a> ");
-}
-
-void Application::handleAboutAction()
-{
-    if (aboutDialog()) {
-        DApplication::handleAboutAction();
-        return;
-    }
-
-    initAboutDialog();
-    DApplication::handleAboutAction();
-}
-
-QStringList Application::launchParams() const
-{
-    return m_argv;
-}
-
-void Application::setMainWindow(MainWindow *window)
-{
-    m_mainWindow = window;
-}
-
-MainWindow *Application::mainWindow()
-{
-    return m_mainWindow;
-}
-
-void Application::saveLaunchParams(int &argc, char **argv)
-{
-    m_argc = argc;
-
-    m_argv.clear();
-    for (int i = 0; i < m_argc; i++) {
-        m_argv.append(argv[i]);
-    }
-
-    qDebug() << Q_FUNC_INFO << m_argc << m_argv;
-}
-
-void Application::initAboutDialog()
-{
-    // Customized DAboutDialog
-    DAboutDialog *dialog = new DAboutDialog(activeWindow());
-    // WindowIcon
-    dialog->setWindowIcon(QIcon(":/images/spark-webapp-runtime.svg"));
-    // ProductIcon
-    dialog->setProductIcon(QIcon(":/images/spark-webapp-runtime.svg"));
-    // ProductName
-    dialog->setProductName(productName());
-    // Version
-    dialog->setVersion(translate("DAboutDialog", "Version: %1").arg(applicationVersion()));
-    // CompanyLogo
-    dialog->setCompanyLogo(QPixmap(":/images/Logo-Spark.png"));
-    // Description
-
-    QString szDefaultDesc = QString("<a href='https://gitee.com/deepin-community-store/spark-web-app-runtime'><span style='font-size:12pt;font-weight:500;'>%1</span></a><br/>"
-                                    "<span style='font-size:12pt;'>%2</span>")
-                                .arg(DEFAULT_TITLE)
-                                .arg(QObject::tr("Presented By Spark developers # HadesStudio"));
-
-    dialog->setDescription(szDefaultDesc);
-    // WebsiteName
-    dialog->setWebsiteName("Spark Project");
-    // WebsiteLink
-    dialog->setWebsiteLink("https://gitee.com/deepin-community-store/");
-    // License
-    dialog->setLicense(translate("DAboutDialog", "%1 is released under %2").arg(productName()).arg(applicationLicense()));
-
-    setAboutDialog(dialog);
-    connect(aboutDialog(), &DAboutDialog::destroyed, this, [=] {
-        setAboutDialog(nullptr);
-    });
-
-    dialog->hide();
-}
-
-void Application::slotMainWindowClose()
-{
-    if (aboutDialog()) {
-        aboutDialog()->close();
-    }
-}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..3b2a537
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,36 @@
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Concurrent WebEngineWidgets)
+find_package(Dtk6 REQUIRED COMPONENTS Core Gui Widget)
+
+include(src.cmake)
+
+add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${QRC_FILE})
+
+target_include_directories(${PROJECT_NAME} PUBLIC
+    Qt6::Core
+    Qt6::Gui
+    Qt6::Widgets
+    Qt6::Concurrent
+    Qt6::WebEngineWidgets
+    Dtk6::Core
+    Dtk6::Gui
+    Dtk6::Widget
+)
+
+target_link_libraries(${PROJECT_NAME} PRIVATE
+    Qt6::Core
+    Qt6::Gui
+    Qt6::Widgets
+    Qt6::Concurrent
+    Qt6::WebEngineWidgets
+    Dtk6::Core
+    Dtk6::Gui
+    Dtk6::Widget
+)
+
+install(TARGETS ${PROJECT_NAME} DESTINATION /opt/durapps/${PROJECT_NAME}/bin)
+
+file(CREATE_LINK /opt/durapps/${PROJECT_NAME}/bin/${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.link SYMBOLIC)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.link DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME ${PROJECT_NAME})
+
+file(CREATE_LINK /opt/durapps/${PROJECT_NAME}/share/${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_datadir.link SYMBOLIC)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_datadir.link DESTINATION ${CMAKE_INSTALL_DATADIR} RENAME ${PROJECT_NAME})
diff --git a/src/application.cpp b/src/application.cpp
new file mode 100644
index 0000000..6f4f36d
--- /dev/null
+++ b/src/application.cpp
@@ -0,0 +1,84 @@
+#include "application.h"
+#include "globaldefine.h"
+
+#include <DPlatformWindowHandle>
+#include <DAboutDialog>
+
+const QString websiteLinkTemplate = "<a href='%1' style='text-decoration: none; color: rgba(0,129,255,0.9);'>%2</a>";
+
+Application::Application(int &argc, char **argv)
+    : DApplication(argc, argv)
+{
+    saveLaunchParams(argc, argv);
+
+    loadTranslator();
+
+    if (!DPlatformWindowHandle::pluginVersion().isEmpty()) {
+        setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
+    }
+
+    setOrganizationName(ORGANIZATION_NAME); // 添加组织名称,和商店主体的文件夹同在 ~/.local/share/spark-union 文件夹下
+    setApplicationName(PROJECT_NAME); // 这里不要翻译,否则 ~/.local/share 中文件夹名也会被翻译
+    setApplicationVersion(VERSION);
+    setApplicationDisplayName(DEFAULT_TITLE);
+    setWindowIcon(QIcon(":/images/spark-webapp-runtime.svg"));
+
+    setProductIcon(QIcon(":/images/spark-webapp-runtime.svg"));
+    setProductName(websiteLinkTemplate.arg("https://gitee.com/deepin-community-store/spark-web-app-runtime", DEFAULT_TITLE));
+    setApplicationDescription(QObject::tr("Presented By Spark developers # HadesStudio"));
+    setApplicationLicense(websiteLinkTemplate.arg("https://gitee.com/spark-store-project/spark-web-app-runtime/blob/master/LICENSE", "GPLv3"));
+}
+
+void Application::triggerAboutAction()
+{
+    handleAboutAction();
+}
+
+QStringList Application::launchParams() const
+{
+    return m_argv;
+}
+
+void Application::setMainWindow(MainWindow *window)
+{
+    m_mainWindow = window;
+}
+
+MainWindow *Application::mainWindow()
+{
+    return m_mainWindow;
+}
+
+void Application::handleAboutAction()
+{
+    DApplication::handleAboutAction();
+
+    DAboutDialog *dialog = aboutDialog();
+    if (dialog) {
+        // CompanyLogo
+        dialog->setCompanyLogo(QPixmap(":/images/Logo-Spark.png"));
+        // WebsiteName
+        dialog->setWebsiteName("Spark Project");
+        // WebsiteLink
+        dialog->setWebsiteLink("https://gitee.com/deepin-community-store/");
+    }
+}
+
+void Application::saveLaunchParams(int &argc, char **argv)
+{
+    m_argc = argc;
+
+    m_argv.clear();
+    for (int i = 0; i < m_argc; i++) {
+        m_argv.append(argv[i]);
+    }
+
+    qDebug() << Q_FUNC_INFO << m_argc << m_argv;
+}
+
+void Application::slotMainWindowClose()
+{
+    if (aboutDialog()) {
+        aboutDialog()->close();
+    }
+}
diff --git a/spark-webapp-runtime/application.h b/src/application.h
similarity index 93%
rename from spark-webapp-runtime/application.h
rename to src/application.h
index 805699a..3e24e38 100644
--- a/spark-webapp-runtime/application.h
+++ b/src/application.h
@@ -13,16 +13,18 @@ class Application : public DApplication
 
 public:
     Application(int &argc, char **argv);
-    void handleAboutAction() override;
+    void triggerAboutAction();
 
     QStringList launchParams() const;
 
     void setMainWindow(MainWindow *window);
     MainWindow *mainWindow();
 
+protected:
+    void handleAboutAction() override;
+
 private:
     void saveLaunchParams(int &argc, char **argv);
-    void initAboutDialog();
 
 signals:
     void sigQuit();
diff --git a/spark-webapp-runtime/globaldefine.h b/src/globaldefine.h
similarity index 83%
rename from spark-webapp-runtime/globaldefine.h
rename to src/globaldefine.h
index af86626..4a5c45f 100644
--- a/spark-webapp-runtime/globaldefine.h
+++ b/src/globaldefine.h
@@ -4,7 +4,6 @@
 #include <QObject>
 
 #define DEFAULT_TITLE QObject::tr("SparkWebAppRuntime")
-#define APPLICATION_NAME QString("spark-webapp-runtime")
 #define ORGANIZATION_NAME QString("spark-union")
 #define DEFAULT_URL QString("qrc:/help/help.html")
 #define DEFAULT_WIDTH (1024)
@@ -18,6 +17,4 @@
 #define DEFAULT_PORT 0
 #define DEFAULT_GPU 1
 
-#define CURRENT_VER QString("1.7.0")
-
 #endif // GLOBALDEFINE_H
diff --git a/spark-webapp-runtime/httpd.cpp b/src/httpd.cpp
similarity index 100%
rename from spark-webapp-runtime/httpd.cpp
rename to src/httpd.cpp
diff --git a/spark-webapp-runtime/httpd.h b/src/httpd.h
similarity index 100%
rename from spark-webapp-runtime/httpd.h
rename to src/httpd.h
diff --git a/spark-webapp-runtime/httplib.h b/src/httplib.h
similarity index 100%
rename from spark-webapp-runtime/httplib.h
rename to src/httplib.h
diff --git a/spark-webapp-runtime/main.cpp b/src/main.cpp
similarity index 93%
rename from spark-webapp-runtime/main.cpp
rename to src/main.cpp
index e1496a8..f150fad 100644
--- a/spark-webapp-runtime/main.cpp
+++ b/src/main.cpp
@@ -8,7 +8,6 @@
 #include "httpd.h"
 
 #include <DSysInfo>
-#include <DApplicationSettings>
 
 #include <QCommandLineParser>
 #include <QCommandLineOption>
@@ -19,27 +18,15 @@
 
 int main(int argc, char *argv[])
 {
-    if (!QString(qgetenv("XDG_CURRENT_DESKTOP")).toLower().startsWith("deepin")) {
-        qputenv("XDG_CURRENT_DESKTOP", "Deepin");
-    }
-
     // 龙芯机器配置,使得 DApplication 能正确加载 QTWEBENGINE
     qputenv("DTK_FORCE_RASTER_WIDGETS", "FALSE");
+
 //    qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-features=UseModernMediaControls");
 //    qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-web-security");
-#ifdef __sw_64__
+#if defined __sw_64__ || __loongarch__
     qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--no-sandbox");
 #endif
 
-    if (!Dtk::Core::DSysInfo::isDDE()) {
-#ifndef DSTORE_NO_DXCBs
-        DApplication::loadDXcbPlugin();
-#endif
-    }
-
-    // 开启 HiDPI 缩放支持
-    DApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-
     // 强制使用 DTK 平台插件
     int fakeArgc = argc + 2;
     QVector<char *> fakeArgv(fakeArgc);
@@ -51,9 +38,6 @@ int main(int argc, char *argv[])
     }
     Application a(fakeArgc, fakeArgv.data());
 
-    // 保存 DTK 主题
-    DApplicationSettings settings;
-
     // 解析命令行启动参数
     QCommandLineParser parser;
 
@@ -180,10 +164,7 @@ int main(int argc, char *argv[])
 #if SSL_SERVER
     quint16 u16sslPort = 0;
 #endif
-    QString szDefaultDesc = QString("<a href='https://gitee.com/deepin-community-store/spark-web-app-runtime'><span style='font-size:12pt;font-weight:500;'>%1</span></a><br/>"
-                                    "<span style='font-size:12pt;'>%2</span>")
-                                .arg(DEFAULT_TITLE)
-                                .arg(QObject::tr("Presented By Spark developers # HadesStudio"));
+    QString szDefaultDesc = QObject::tr("Presented By Spark developers # HadesStudio");
 
     // 解析可能存在的配置文件
     QString szCfgFile = DEFAULT_CFG;
diff --git a/spark-webapp-runtime/mainwindow.cpp b/src/mainwindow.cpp
similarity index 80%
rename from spark-webapp-runtime/mainwindow.cpp
rename to src/mainwindow.cpp
index 446013f..c670ee2 100644
--- a/spark-webapp-runtime/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -3,6 +3,7 @@
 #include "webengineview.h"
 #include "webenginepage.h"
 
+#include <DPlatformWindowHandle>
 #include <DLog>
 #include <DWidgetUtil>
 #include <DTitlebar>
@@ -50,8 +51,8 @@ MainWindow::MainWindow(QString szTitle,
     , m_clearCache(new QAction(QObject::tr("Clear Cache"), this))
     , t_menu(new QMenu(this))
     , t_show(new QAction(QObject::tr("Show MainWindow"), this))
-    , t_about(new QAction(qApp->translate("TitleBarMenu", "About"), this))
-    , t_exit(new QAction(qApp->translate("TitleBarMenu", "Exit"), this))
+    , t_about(new QAction(qApp->translate("TitleBarMenu", QString("About").toUtf8().data()), this))
+    , t_exit(new QAction(qApp->translate("TitleBarMenu", QString("Exit").toUtf8().data()), this))
     , downloadMessage(new DFloatingMessage(DFloatingMessage::ResidentType, this))
     , downloadProgressWidget(new QWidget(downloadMessage))
     , progressBarLayout(new QHBoxLayout(downloadProgressWidget))
@@ -73,9 +74,22 @@ MainWindow::~MainWindow()
 {
 }
 
+bool MainWindow::event(QEvent *event)
+{
+    /**
+     * @bug QWebEngineView in Qt6 will recreate window handle
+     * @ref https://github.com/linuxdeepin/deepin-deepinid-client/commit/2a305926a9047c699cf4d12e3e64aae17e8c367b
+     */
+    if (event->type() == QEvent::WinIdChange) {
+        DPlatformWindowHandle handle(this);
+    }
+
+    return DMainWindow::event(event);
+}
+
 void MainWindow::setIcon(QString szIconPath)
 {
-    if (!QFileInfo(szIconPath).exists()) {
+    if (!QFile::exists(szIconPath)) {
         return;
     }
 
@@ -83,19 +97,13 @@ void MainWindow::setIcon(QString szIconPath)
     setWindowIcon(QIcon(szIconPath));
     m_tray->setIcon(QIcon(szIconPath));
 
-    DAboutDialog *aboutDialog = qobject_cast<Application *>(qApp)->aboutDialog();
-    if (aboutDialog) {
-        aboutDialog->setWindowIcon(QIcon::fromTheme(szIconPath));
-        aboutDialog->setProductIcon(QIcon::fromTheme(szIconPath));
-    }
+    qApp->setWindowIcon(QIcon::fromTheme(szIconPath));
+    qApp->setProductIcon(QIcon::fromTheme(szIconPath));
 }
 
 void MainWindow::setDescription(const QString &desc)
 {
-    DAboutDialog *aboutDialog = qobject_cast<Application *>(qApp)->aboutDialog();
-    if (aboutDialog) {
-        aboutDialog->setDescription(desc);
-    }
+    qApp->setApplicationDescription(desc);
 }
 
 QString MainWindow::title() const
@@ -156,6 +164,7 @@ void MainWindow::initLog()
     DLogManager::setlogFilePath(tmpDir() + "/" + "log");
     DLogManager::registerFileAppender();
     DLogManager::registerConsoleAppender();
+    DLogManager::registerJournalAppender();
 }
 
 void MainWindow::initTmpDir()
@@ -242,7 +251,7 @@ void MainWindow::initDownloadProgressBar()
     btnResume->setFixedSize(80, 32);
     btnCancel->setFixedSize(80, 32);
 
-    progressBarLayout->setMargin(0);
+    progressBarLayout->setContentsMargins(0, 0, 0, 0);
     progressBarLayout->setSpacing(0);
     progressBarLayout->setAlignment(Qt::AlignCenter);
     progressBarLayout->addWidget(downloadProgressBar);
@@ -251,7 +260,7 @@ void MainWindow::initDownloadProgressBar()
     progressBarLayout->addWidget(btnResume);
     progressBarLayout->addWidget(btnCancel);
 
-    downloadMessage->setIcon(QIcon::fromTheme("deepin-download").pixmap(64, 64));
+    downloadMessage->setIcon(QIcon::fromTheme("deepin-download"));
     downloadMessage->setWidget(downloadProgressWidget);
     downloadMessage->hide();
 }
@@ -301,7 +310,7 @@ void MainWindow::initConnections()
         fixSize();
     });
     connect(t_about, &QAction::triggered, this, [=]() {
-        qobject_cast<Application *>(qApp)->handleAboutAction();
+        qobject_cast<Application *>(qApp)->triggerAboutAction();
     });
     connect(t_exit, &QAction::triggered, this, [=]() {
         exit(0);
@@ -322,14 +331,14 @@ void MainWindow::fullScreen()
         m_fixSize->setDisabled(true);
         m_menu->update();
         showFullScreen();
-        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(QObject::tr("%1Fullscreen Mode")).arg("    "));
+        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information"), QString(QObject::tr("%1Fullscreen Mode")).arg("    "));
     } else {
         if (!m_isFixedSize) {
             m_fixSize->setDisabled(false); // 命令行参数没有固定窗口大小时,窗口模式下允许手动选择固定窗口大小
         }
         m_menu->update();
         showNormal();
-        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(QObject::tr("%1Windowed Mode")).arg("    "));
+        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information"), QString(QObject::tr("%1Windowed Mode")).arg("    "));
     }
 }
 
@@ -382,7 +391,7 @@ QString MainWindow::saveAs(QString fileName)
                                                     QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + fileName);
     if (!saveFile.isEmpty()) {
         // 判断上层目录是否可写入
-        if (QFileInfo(QFileInfo(saveFile).absolutePath()).isWritable()) {
+        if (QFileInfo(QFileInfo(saveFile).absoluteDir().canonicalPath()).isWritable()) {
             return saveFile;
         } else {
             return saveAs(fileName);
@@ -412,40 +421,46 @@ void MainWindow::on_trayIconActivated(QSystemTrayIcon::ActivationReason reason)
     }
 }
 
-void MainWindow::on_downloadStart(QWebEngineDownloadItem *item)
+void MainWindow::on_downloadStart(QWebEngineDownloadRequest *request)
 {
     // 尝试加锁互斥量,禁止同时下载多个文件
     if (mutex.tryLock()) {
-        QString fileName = QFileInfo(item->path()).fileName();
+        QString fileName = request->suggestedFileName();
         QString filePath = saveAs(fileName);
         if (filePath.isEmpty()) {
             mutex.unlock();
             return;
         }
-        item->setPath(filePath);
-        filePath = QFileInfo(item->path()).absoluteFilePath();
 
-        connect(item, &QWebEngineDownloadItem::downloadProgress, this, &MainWindow::on_downloadProgress);
-        connect(item, &QWebEngineDownloadItem::finished, this, [=]() {
+        QFileInfo fileInfo(filePath);
+        fileName = fileInfo.fileName();
+        QString dirPath = fileInfo.absoluteDir().canonicalPath();
+        filePath = QDir(dirPath).absoluteFilePath(fileName);
+
+        request->setDownloadDirectory(dirPath);
+        request->setDownloadFileName(fileName);
+
+        connect(request, &QWebEngineDownloadRequest::receivedBytesChanged, this, &MainWindow::on_receivedBytesChanged);
+        connect(request, &QWebEngineDownloadRequest::isFinishedChanged, this, [=]() {
             on_downloadFinish(filePath);
         });
 
         connect(btnPause, &DPushButton::clicked, this, [=]() {
-            on_downloadPause(item);
+            on_downloadPause(request);
         });
         connect(btnResume, &DPushButton::clicked, this, [=]() {
-            on_downloadResume(item);
+            on_downloadResume(request);
         });
         connect(btnCancel, &DPushButton::clicked, this, [=]() {
-            on_downloadCancel(item);
+            on_downloadCancel(request);
         });
 
         DFloatingMessage *message = new DFloatingMessage(DFloatingMessage::TransientType);
-        message->setIcon(QIcon::fromTheme("dialog-information").pixmap(64, 64));
-        message->setMessage(QString(QObject::tr("%1Start downloading %2")).arg("    ").arg(fileName));
+        message->setIcon(QIcon::fromTheme("dialog-information"));
+        message->setMessage(QObject::tr("%1Start downloading %2").arg("    ", fileName));
         DMessageManager::instance()->sendMessage(this, message);
 
-        item->accept();
+        request->accept();
 
         // 重置 DownloadProgressBar 状态
         isCanceled = false;
@@ -453,14 +468,16 @@ void MainWindow::on_downloadStart(QWebEngineDownloadItem *item)
         btnPause->show();
         this->downloadMessage->show(); // 上一次下载完成后隐藏了进度条,这里要重新显示
     } else {
-        DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-cancel").pixmap(64, 64), QString(QObject::tr("%1Wait for previous download to complete!")).arg("    "));
+        DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-cancel"), QString(QObject::tr("%1Wait for previous download to complete!")).arg("    "));
     }
 }
 
-void MainWindow::on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+void MainWindow::on_receivedBytesChanged()
 {
-    int value = int(double(bytesReceived) / double(bytesTotal) * 100.0);
-    downloadProgressBar->setValue(value);
+    QWebEngineDownloadRequest *request = qobject_cast<QWebEngineDownloadRequest *>(sender());
+
+    int value = int(double(request->receivedBytes()) / double(request->totalBytes()) * 100.0);
+    downloadProgressBar->setValue(qBound(downloadProgressBar->value(), value, 100));
 
     downloadMessage->setMessage("    " + QString::number(value) + "%");
 
@@ -469,6 +486,11 @@ void MainWindow::on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
 
 void MainWindow::on_downloadFinish(QString filePath)
 {
+    QWebEngineDownloadRequest *request = qobject_cast<QWebEngineDownloadRequest *>(sender());
+    if (!request->isFinished()) {
+        return;
+    }
+
     mutex.unlock(); // 解锁互斥量,允许下载新文件
 
     downloadMessage->hide();
@@ -478,8 +500,8 @@ void MainWindow::on_downloadFinish(QString filePath)
         DPushButton *button = new DPushButton(QObject::tr("Open"));
 
         DFloatingMessage *message = new DFloatingMessage(DFloatingMessage::ResidentType);
-        message->setIcon(QIcon::fromTheme("dialog-ok").pixmap(64, 64));
-        message->setMessage(QString("    %1 %2 %3").arg(QFileInfo(filePath).fileName()).arg(QObject::tr("download finished.")).arg(QObject::tr("Show in file manager?")));
+        message->setIcon(QIcon::fromTheme("dialog-ok"));
+        message->setMessage(QString("    %1 %2 %3").arg(QFileInfo(filePath).fileName(), QObject::tr("download finished."), QObject::tr("Show in file manager?")));
         message->setWidget(button);
         DMessageManager::instance()->sendMessage(this, message);
 
@@ -491,36 +513,36 @@ void MainWindow::on_downloadFinish(QString filePath)
     }
 }
 
-void MainWindow::on_downloadPause(QWebEngineDownloadItem *item)
+void MainWindow::on_downloadPause(QWebEngineDownloadRequest *request)
 {
-    item->pause();
+    request->pause();
 
-    downloadMessage->setIcon(QIcon::fromTheme("package-download-failed").pixmap(64, 64));
+    downloadMessage->setIcon(QIcon::fromTheme("package-download-failed"));
     btnResume->show();
     btnPause->hide();
 }
 
-void MainWindow::on_downloadResume(QWebEngineDownloadItem *item)
+void MainWindow::on_downloadResume(QWebEngineDownloadRequest *request)
 {
-    item->resume();
+    request->resume();
 
-    downloadMessage->setIcon(QIcon::fromTheme("deepin-download").pixmap(64, 64));
+    downloadMessage->setIcon(QIcon::fromTheme("deepin-download"));
     btnResume->hide();
     btnPause->show();
 }
 
-void MainWindow::on_downloadCancel(QWebEngineDownloadItem *item)
+void MainWindow::on_downloadCancel(QWebEngineDownloadRequest *request)
 {
     isCanceled = true; // 取消下载
-    item->cancel();
+    request->cancel();
 
     mutex.unlock();
 
     downloadMessage->hide();
-    DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-error").pixmap(64, 64), QString(QObject::tr("%1Download canceled!")).arg("    "));
+    DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-error"), QString(QObject::tr("%1Download canceled!")).arg("    "));
 }
 
 void MainWindow::slotLoadErrorOccurred()
 {
-    DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-warning").pixmap(64, 64), QString(QObject::tr("%1Load error occurred!")).arg("    "));
+    DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-warning"), QString(QObject::tr("%1Load error occurred!")).arg("    "));
 }
diff --git a/spark-webapp-runtime/mainwindow.h b/src/mainwindow.h
similarity index 88%
rename from spark-webapp-runtime/mainwindow.h
rename to src/mainwindow.h
index d875b77..45c4192 100644
--- a/spark-webapp-runtime/mainwindow.h
+++ b/src/mainwindow.h
@@ -32,6 +32,7 @@ public:
                bool nHideButtons = false,
                QWidget *parent = nullptr);
     ~MainWindow();
+    bool event(QEvent *event) override;
 
     void setIcon(QString szIconPath);
     void setDescription(const QString &desc);
@@ -70,12 +71,12 @@ public slots:
 private slots:
     void on_trayIconActivated(QSystemTrayIcon::ActivationReason reason);
 
-    void on_downloadStart(QWebEngineDownloadItem *item);
-    void on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+    void on_downloadStart(QWebEngineDownloadRequest *request);
+    void on_receivedBytesChanged();
     void on_downloadFinish(QString filePath);
-    void on_downloadPause(QWebEngineDownloadItem *item);
-    void on_downloadResume(QWebEngineDownloadItem *item);
-    void on_downloadCancel(QWebEngineDownloadItem *item);
+    void on_downloadPause(QWebEngineDownloadRequest *request);
+    void on_downloadResume(QWebEngineDownloadRequest *request);
+    void on_downloadCancel(QWebEngineDownloadRequest *request);
 
     void slotLoadErrorOccurred();
 
diff --git a/spark-webapp-runtime/resources/css/webkit-scrollbar.css b/src/resources/css/webkit-scrollbar.css
similarity index 100%
rename from spark-webapp-runtime/resources/css/webkit-scrollbar.css
rename to src/resources/css/webkit-scrollbar.css
diff --git a/spark-webapp-runtime/resources/help/help.html b/src/resources/help/help.html
similarity index 100%
rename from spark-webapp-runtime/resources/help/help.html
rename to src/resources/help/help.html
diff --git a/spark-webapp-runtime/resources/images/Logo-Spark.png b/src/resources/images/Logo-Spark.png
similarity index 100%
rename from spark-webapp-runtime/resources/images/Logo-Spark.png
rename to src/resources/images/Logo-Spark.png
diff --git a/spark-webapp-runtime/resources/images/go-next-24.svg b/src/resources/images/go-next-24.svg
similarity index 100%
rename from spark-webapp-runtime/resources/images/go-next-24.svg
rename to src/resources/images/go-next-24.svg
diff --git a/spark-webapp-runtime/resources/images/go-previous-24.svg b/src/resources/images/go-previous-24.svg
similarity index 100%
rename from spark-webapp-runtime/resources/images/go-previous-24.svg
rename to src/resources/images/go-previous-24.svg
diff --git a/spark-webapp-runtime/resources/images/spark-webapp-runtime.svg b/src/resources/images/spark-webapp-runtime.svg
similarity index 100%
rename from spark-webapp-runtime/resources/images/spark-webapp-runtime.svg
rename to src/resources/images/spark-webapp-runtime.svg
diff --git a/spark-webapp-runtime/resources/images/view-refresh.svg b/src/resources/images/view-refresh.svg
similarity index 100%
rename from spark-webapp-runtime/resources/images/view-refresh.svg
rename to src/resources/images/view-refresh.svg
diff --git a/spark-webapp-runtime/resources/resources.qrc b/src/resources/resources.qrc
similarity index 100%
rename from spark-webapp-runtime/resources/resources.qrc
rename to src/resources/resources.qrc
diff --git a/spark-webapp-runtime/spark-webapp-runtime.pro b/src/spark-webapp-runtime.pro
similarity index 53%
rename from spark-webapp-runtime/spark-webapp-runtime.pro
rename to src/spark-webapp-runtime.pro
index 0f3d749..ed312f7 100644
--- a/spark-webapp-runtime/spark-webapp-runtime.pro
+++ b/src/spark-webapp-runtime.pro
@@ -1,4 +1,4 @@
-QT += core gui webenginewidgets svg concurrent dbus
+QT += core gui concurrent webenginewidgets
 
 greaterThan(QT_MAJOR_VERSION, 5): QT += widgets
 
@@ -6,8 +6,8 @@ TEMPLATE = app
 
 DEFINES += QT_DEPRECATED_WARNINGS
 
-CONFIG += c++11 link_pkgconfig
-PKGCONFIG += dtkcore dtkgui dtkwidget
+CONFIG += c++17 link_pkgconfig
+PKGCONFIG += dtk6core dtk6gui dtk6widget
 
 HEADERS += \
         mainwindow.h \
@@ -31,18 +31,7 @@ SOURCES += \
         webengineurlrequestinterceptor.cpp
 
 RESOURCES += \
-          resources/resources.qrc
+        resources/resources.qrc
 
 TRANSLATIONS += \
-             translations/spark-webapp-runtime_zh_CN.ts
-
-# Default rules for deployment.
-qnx: target.path = /tmp/$${TARGET}/bin
-else: unix:!android: target.path = /opt/durapps/$${TARGET}/bin
-!isEmpty(target.path): INSTALLS += target
-
-# Rules for deployment
-qm.files += translations/*.qm
-qm.path = /opt/durapps/$${TARGET}/share/$${TARGET}/translations
-
-INSTALLS += qm
+        ../translations/spark-webapp-runtime_zh_CN.ts
diff --git a/src/src.cmake b/src/src.cmake
new file mode 100644
index 0000000..19d5d49
--- /dev/null
+++ b/src/src.cmake
@@ -0,0 +1,11 @@
+# https://cmake.org/cmake/help/v3.12/command/file.html#glob-recurse
+file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS
+    "${CMAKE_CURRENT_SOURCE_DIR}/*.h"
+    "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp"
+)
+
+file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
+    "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
+)
+
+qt6_add_resources(QRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/resources/resources.qrc)
diff --git a/spark-webapp-runtime/webenginepage.cpp b/src/webenginepage.cpp
similarity index 100%
rename from spark-webapp-runtime/webenginepage.cpp
rename to src/webenginepage.cpp
diff --git a/spark-webapp-runtime/webenginepage.h b/src/webenginepage.h
similarity index 100%
rename from spark-webapp-runtime/webenginepage.h
rename to src/webenginepage.h
diff --git a/spark-webapp-runtime/webengineurlrequestinterceptor.cpp b/src/webengineurlrequestinterceptor.cpp
similarity index 100%
rename from spark-webapp-runtime/webengineurlrequestinterceptor.cpp
rename to src/webengineurlrequestinterceptor.cpp
diff --git a/spark-webapp-runtime/webengineurlrequestinterceptor.h b/src/webengineurlrequestinterceptor.h
similarity index 100%
rename from spark-webapp-runtime/webengineurlrequestinterceptor.h
rename to src/webengineurlrequestinterceptor.h
diff --git a/spark-webapp-runtime/webengineview.cpp b/src/webengineview.cpp
similarity index 98%
rename from spark-webapp-runtime/webengineview.cpp
rename to src/webengineview.cpp
index f85e4ac..9b924bc 100644
--- a/spark-webapp-runtime/webengineview.cpp
+++ b/src/webengineview.cpp
@@ -34,7 +34,7 @@ void WebEngineView::handleChromiumFlags()
     DGuiApplicationHelper::ColorType themeType = DGuiApplicationHelper::instance()->themeType();
 
     QString env = qgetenv("QTWEBENGINE_CHROMIUM_FLAGS");
-    QStringList flags = env.split(" ", QString::SkipEmptyParts);
+    QStringList flags = env.split(" ", Qt::SkipEmptyParts);
 
     /**
      * --blink-settings=preferredColorScheme=0 强制 prefers-color-scheme=dark (>= 5.14)
diff --git a/spark-webapp-runtime/webengineview.h b/src/webengineview.h
similarity index 100%
rename from spark-webapp-runtime/webengineview.h
rename to src/webengineview.h
diff --git a/spark-webapp-runtime/widget.cpp b/src/widget.cpp
similarity index 98%
rename from spark-webapp-runtime/widget.cpp
rename to src/widget.cpp
index 61399f6..d928883 100644
--- a/spark-webapp-runtime/widget.cpp
+++ b/src/widget.cpp
@@ -58,7 +58,7 @@ void Widget::initUI()
 
     QWidget *spinnerWidget = new QWidget(this);
     QHBoxLayout *spinnerLayout = new QHBoxLayout(spinnerWidget);
-    spinnerLayout->setMargin(0);
+    spinnerLayout->setContentsMargins(0, 0, 0, 0);
     spinnerLayout->setSpacing(0);
     spinnerLayout->setAlignment(Qt::AlignCenter);
     spinnerLayout->addStretch();
diff --git a/spark-webapp-runtime/widget.h b/src/widget.h
similarity index 100%
rename from spark-webapp-runtime/widget.h
rename to src/widget.h
diff --git a/translate_generation.sh b/translate_generation.sh
deleted file mode 100755
index 71fd098..0000000
--- a/translate_generation.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-# this file is used to auto-generate .qm file from .ts file.
-
-cd $(dirname $0)
-
-lrelease ./spark-webapp-runtime/spark-webapp-runtime.pro
diff --git a/translate_update.sh b/translate_update.sh
deleted file mode 100755
index 3d4edb2..0000000
--- a/translate_update.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-# this file is used to auto-update .ts file.
-
-cd $(dirname $0)
-
-lupdate ./spark-webapp-runtime/spark-webapp-runtime.pro -no-obsolete
diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt
new file mode 100644
index 0000000..89291d4
--- /dev/null
+++ b/translations/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Have to disable cleaning for this folder because cmake deletes .ts files upon clean
+# Not sure what else wont clean up / dirty workaround of Qt bug
+# @ref https://bugreports.qt.io/browse/QTBUG-41736
+# @ref https://stackoverflow.com/a/24245615/1917249
+set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM TRUE)
+
+find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
+
+# Update ts files and release qm files only in Release build
+file(GLOB TS_FILES "*.ts")
+if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
+    set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_SOURCE_DIR})
+    qt6_create_translation(QM_FILES ${CMAKE_SOURCE_DIR}/src ${TS_FILES} OPTIONS -no-obsolete)
+    # https://stackoverflow.com/questions/70665191/cmake-does-not-generate-ts-files
+    add_custom_target(${PROJECT_NAME}_translations DEPENDS ${TS_FILES} ${QM_FILES})
+    add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_translations)
+endif ()
+
+install(FILES ${QM_FILES} DESTINATION /opt/durapps/${PROJECT_NAME}/share/${PROJECT_NAME}/translations)
diff --git a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts b/translations/spark-webapp-runtime_zh_CN.ts
similarity index 64%
rename from spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
rename to translations/spark-webapp-runtime_zh_CN.ts
index dd1df84..26e9d78 100644
--- a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
+++ b/translations/spark-webapp-runtime_zh_CN.ts
@@ -1,214 +1,188 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE TS>
 <TS version="2.1" language="zh_CN">
-<context>
-    <name>DAboutDialog</name>
-    <message>
-        <location filename="../application.cpp" line="82"/>
-        <source>Version: %1</source>
-        <translation>版本:%1</translation>
-    </message>
-    <message>
-        <location filename="../application.cpp" line="98"/>
-        <source>%1 is released under %2</source>
-        <translation>%1遵循%2协议发布</translation>
-    </message>
-</context>
 <context>
     <name>QObject</name>
     <message>
-        <location filename="../application.cpp" line="90"/>
-        <location filename="../main.cpp" line="186"/>
+        <location filename="../src/application.cpp" line="28"/>
+        <location filename="../src/main.cpp" line="167"/>
         <source>Presented By Spark developers # HadesStudio</source>
         <translation>由 星火开发者联盟 @ 花心胡萝卜 提供</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="60"/>
+        <location filename="../src/main.cpp" line="44"/>
         <source>Description: %1</source>
         <translation>描述:%1</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="66"/>
+        <location filename="../src/main.cpp" line="50"/>
         <source>Enable CommandLineParser. Default is false.</source>
         <translation>启用参数解析方式。默认顺序解析方式。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="71"/>
+        <location filename="../src/main.cpp" line="55"/>
         <source>The Title of Application. Default is %1.</source>
         <translation>设置程序的运行标题。默认是 %1。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="78"/>
+        <location filename="../src/main.cpp" line="62"/>
         <source>The target URL. Default is Blank.</source>
         <translation>设置要打开的目标 URL。默认是空。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="85"/>
+        <location filename="../src/main.cpp" line="69"/>
         <source>The Width of Application. Default is %1.</source>
         <translation>设置应用的窗口宽度。默认是 %1。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="92"/>
+        <location filename="../src/main.cpp" line="76"/>
         <source>The Height of Application. Default is %1.</source>
         <translation>设置应用的窗口高度。默认是 %1。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="99"/>
+        <location filename="../src/main.cpp" line="83"/>
         <source>Enable Tray Icon. Default is false.</source>
         <translation>启用托盘图标。默认不启用。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="103"/>
+        <location filename="../src/main.cpp" line="87"/>
         <source>Run in Fullscreen Mode. Default is false.</source>
         <translation>以全屏模式运行。默认关闭该功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="107"/>
+        <location filename="../src/main.cpp" line="91"/>
         <source>Fix Window Size. Default is false.</source>
         <translation>固定窗口大小。默认关闭该功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="111"/>
+        <location filename="../src/main.cpp" line="95"/>
         <source>Hide Control Buttons. Default is false.</source>
         <translation>隐藏控制按钮。默认关闭该此功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="116"/>
+        <location filename="../src/main.cpp" line="100"/>
         <source>The ICON of Application.</source>
         <translation>设置应用的图标。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="123"/>
+        <location filename="../src/main.cpp" line="107"/>
         <source>The Description of Application.</source>
         <translation>设置应用的描述信息。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="130"/>
+        <location filename="../src/main.cpp" line="114"/>
         <source>The Configuration file of Application.</source>
         <translation>设置应用的配置文件。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="137"/>
+        <location filename="../src/main.cpp" line="121"/>
         <source>The root path of the program web service.</source>
         <translation>设置内置 WebServer 的根路径。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="144"/>
+        <location filename="../src/main.cpp" line="128"/>
         <source>The port number of the program web service.</source>
         <translation>设置内置 WebServer 的监听端口号。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="151"/>
+        <location filename="../src/main.cpp" line="135"/>
         <source>To use GPU instead of CPU to decoding. Default True.</source>
         <translation>启用GPU渲染,默认开启。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="159"/>
+        <location filename="../src/main.cpp" line="143"/>
         <source>The ssl port number of the program web service.</source>
         <translation>设置内置 WebServer 的 SSL 协议的监听端口号。</translation>
     </message>
     <message>
-        <location filename="../globaldefine.h" line="6"/>
+        <location filename="../src/globaldefine.h" line="6"/>
         <source>SparkWebAppRuntime</source>
         <translation>星火网页应用运行环境</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="47"/>
+        <location filename="../src/mainwindow.cpp" line="48"/>
         <source>Full Screen</source>
         <translation>全屏显示</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="48"/>
+        <location filename="../src/mainwindow.cpp" line="49"/>
         <source>Fix Size</source>
         <translation>固定大小</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="49"/>
+        <location filename="../src/mainwindow.cpp" line="50"/>
         <source>Hide Buttons</source>
         <translation>隐藏按钮</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="50"/>
+        <location filename="../src/mainwindow.cpp" line="51"/>
         <source>Clear Cache</source>
         <translation>清理缓存</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="52"/>
+        <location filename="../src/mainwindow.cpp" line="53"/>
         <source>Show MainWindow</source>
         <translation>显示主界面</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="59"/>
+        <location filename="../src/mainwindow.cpp" line="60"/>
         <source>Pause</source>
         <translation>暂停</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="60"/>
+        <location filename="../src/mainwindow.cpp" line="61"/>
         <source>Resume</source>
         <translation>继续</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="61"/>
+        <location filename="../src/mainwindow.cpp" line="62"/>
         <source>Cancel</source>
         <translation>取消</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="381"/>
+        <location filename="../src/mainwindow.cpp" line="390"/>
         <source>Save As</source>
         <translation>另存为</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="445"/>
+        <location filename="../src/mainwindow.cpp" line="460"/>
         <source>%1Start downloading %2</source>
         <translation>%1开始下载 %2</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="456"/>
+        <location filename="../src/mainwindow.cpp" line="471"/>
         <source>%1Wait for previous download to complete!</source>
         <translation>%1请等待上一个下载任务完成!</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="478"/>
+        <location filename="../src/mainwindow.cpp" line="500"/>
         <source>Open</source>
         <translation>打开</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="482"/>
+        <location filename="../src/mainwindow.cpp" line="504"/>
         <source>download finished.</source>
         <translation>下载完成。</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="482"/>
+        <location filename="../src/mainwindow.cpp" line="504"/>
         <source>Show in file manager?</source>
         <translation>是否在文件管理器中显示?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="520"/>
+        <location filename="../src/mainwindow.cpp" line="542"/>
         <source>%1Download canceled!</source>
         <translation>%1下载取消!</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="525"/>
+        <location filename="../src/mainwindow.cpp" line="547"/>
         <source>%1Load error occurred!</source>
         <translation>%1加载存在错误!</translation>
     </message>
     <message>
-        <location filename="../webengineview.cpp" line="79"/>
+        <location filename="../src/webengineview.cpp" line="82"/>
         <source>View</source>
         <translation>查看</translation>
     </message>
 </context>
-<context>
-    <name>TitleBarMenu</name>
-    <message>
-        <location filename="../mainwindow.cpp" line="53"/>
-        <source>About</source>
-        <translation>关于</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="54"/>
-        <source>Exit</source>
-        <translation>退出</translation>
-    </message>
-</context>
 </TS>