From 4b3cc4276d08106d40710accecf51d85ac955ef8 Mon Sep 17 00:00:00 2001
From: zty199 <1282441920@qq.com>
Date: Sun, 29 Nov 2020 02:59:26 +0800
Subject: [PATCH] Improve Features

Support run in Fullscreen mode;
Suppport "save as" when downloading files.
---
 spark-webapp-runtime/help/help.html           |   2 +
 spark-webapp-runtime/main.cpp                 |  33 ++++-
 spark-webapp-runtime/mainwindow.cpp           | 127 +++++++++++++++++-
 spark-webapp-runtime/mainwindow.h             |  20 +++
 .../spark-webapp-runtime_zh_CN.ts             |  71 +++++++---
 spark-webapp-runtime/webenginepage.cpp        |  32 +----
 spark-webapp-runtime/webenginepage.h          |  10 --
 spark-webapp-runtime/widget.cpp               |   5 +
 spark-webapp-runtime/widget.h                 |   1 +
 9 files changed, 234 insertions(+), 67 deletions(-)

diff --git a/spark-webapp-runtime/help/help.html b/spark-webapp-runtime/help/help.html
index d3affe9..6cc2913 100644
--- a/spark-webapp-runtime/help/help.html
+++ b/spark-webapp-runtime/help/help.html
@@ -28,6 +28,7 @@
                   -u, --url <url>        设置要打开的目标 URL。默认为空。
                   -w, --width <width>    设置应用的窗口宽度。默认为 1024。
                   -H, --height <height>  设置应用的窗口高度。默认为 768。
+                  --full-screen          以全屏模式运行。默认关闭该功能。
                   --fix-size             固定窗口大小。默认关闭该功能。
                   --hide-buttons         隐藏控制按钮。默认关闭该功能。
                   -i, --ico <ico>        设置应用的图标。
@@ -63,6 +64,7 @@
                   -u, --url <url>        The target URL. Default is Blank.
                   -w, --width <width>    The Width of Application. Default is 1024.
                   -H, --height <height>  The Height of Application. Default is 768.
+                  --full-screen          Run in Fullscreen Mode. Default is false.
                   --fix-size             Fix Window Size. Default is false.
                   --hide-buttons         Hide Control Buttons. Default is false.
                   -i, --ico <ico>        The ICON of Application.
diff --git a/spark-webapp-runtime/main.cpp b/spark-webapp-runtime/main.cpp
index 392b13e..84cd894 100644
--- a/spark-webapp-runtime/main.cpp
+++ b/spark-webapp-runtime/main.cpp
@@ -86,6 +86,10 @@ int main(int argc, char *argv[])
                                  QString::number(DEFAULT_HEIGHT));
     parser.addOption(optHeight);
 
+    QCommandLineOption optFullScreen("full-screen",
+                                  QObject::tr("Run in Fullscreen Mode. Default is false."));
+    parser.addOption(optFullScreen);
+
     QCommandLineOption optFixSize("fix-size",
                                   QObject::tr("Fix Window Size. Default is false."));
     parser.addOption(optFixSize);
@@ -139,6 +143,7 @@ int main(int argc, char *argv[])
     QString szUrl = DEFAULT_URL;
     int width = DEFAULT_WIDTH;
     int height = DEFAULT_HEIGHT;
+    bool fullScreen = false;
     bool fixSize = false;
     bool hideButtons = false;
     QString szIcon = DEFAULT_ICON;
@@ -163,6 +168,7 @@ int main(int argc, char *argv[])
                 szUrl = settings.value("SparkWebAppRuntime/URL", DEFAULT_TITLE).toString();
                 width = settings.value("SparkWebAppRuntime/Width", DEFAULT_WIDTH).toUInt();
                 height = settings.value("SparkWebAppRuntime/Height", DEFAULT_HEIGHT).toUInt();
+                fullScreen = settings.value("SparkWebAppRunTime/FullScreen", false).toBool();
                 fixSize = settings.value("SparkWebAppRunTime/FixSize", false).toBool();
                 hideButtons = settings.value("SparkWebAppRunTime/HideButtons", false).toBool();
                 szIcon = settings.value("SparkWebAppRuntime/Ico", DEFAULT_ICON).toString();
@@ -196,6 +202,10 @@ int main(int argc, char *argv[])
         height = parser.value(optHeight).toInt();
     }
 
+    if (parser.isSet(optFullScreen))
+    {
+        fullScreen = true;
+    }
     if (parser.isSet(optFixSize))
     {
         fixSize = true;
@@ -251,38 +261,47 @@ int main(int argc, char *argv[])
 
         if (argc > 5)
         {
-            fixSize = true;
+            fullScreen = true;
         }
         if (argc > 6)
+        {
+            fixSize = true;
+        }
+        if (argc > 7)
         {
             hideButtons = true;
         }
 
-        if (argc > 7)
+        if (argc > 8)
         {
             szIcon = QString(argv[7]);
         }
-        if (argc > 8)
+        if (argc > 9)
         {
             szDesc = QString("%1<br/><br/>%2").arg(QString(argv[8])).arg(szDefaultDesc);
         }
-        if (argc > 9)
+        if (argc > 10)
         {
             szRootPath = QString(argv[9]);
         }
-        if (argc > 10)
+        if (argc > 11)
         {
             u16Port = QString(argv[10]).toUInt();
         }
 #if SSL_SERVER
-        if (argc > 11)
+        if (argc > 12)
         {
             u16sslPort = QString(argv[11]).toUInt();
         }
 #endif
     }
 
-    MainWindow w(szTitle, szUrl, width, height, fixSize, hideButtons, dialog);
+    if(fixSize)
+    {
+        fullScreen = false; //  固定窗口大小时禁用全屏模式,避免标题栏按钮 BUG
+    }
+
+    MainWindow w(szTitle, szUrl, width, height, fullScreen, fixSize, hideButtons, dialog);
 
 #if SSL_SERVER
     if (!szRootPath.isEmpty() && u16Port > 0 && u16sslPort > 0)
diff --git a/spark-webapp-runtime/mainwindow.cpp b/spark-webapp-runtime/mainwindow.cpp
index 8aea10f..d44baf1 100644
--- a/spark-webapp-runtime/mainwindow.cpp
+++ b/spark-webapp-runtime/mainwindow.cpp
@@ -3,16 +3,23 @@
 #include <DMainWindow>
 #include <DWidgetUtil>
 #include <DTitlebar>
+#include <DMessageManager>
+#include <DPushButton>
 
 #include <QLayout>
 #include <QFileInfo>
+#include <QFileDialog>
+#include <QDir>
 #include <QCloseEvent>
 #include <QDebug>
 
+#include "webenginepage.h"
+
 MainWindow::MainWindow(QString szTitle,
                        QString szUrl,
                        int nWidth,
                        int nHeight,
+                       bool nFullScreen,
                        bool nFixSize,
                        bool nHideButtons,
                        DAboutDialog *dialog,
@@ -24,8 +31,12 @@ MainWindow::MainWindow(QString szTitle,
     , btnForward(new DToolButton(titlebar()))
     , btnRefresh(new DToolButton(titlebar()))
     , m_menu(new QMenu)
+    , m_fullScreen(new QAction("Full Screen"))
     , m_fixSize(new QAction(tr("Fix Size")))
     , m_hideButtons(new QAction(tr("Hide Buttons")))
+    , bar(new DProgressBar)
+    , message(new DFloatingMessage(DFloatingMessage::ResidentType))
+    , process(new QProcess)
     , m_width(nWidth)
     , m_height(nHeight)
 {
@@ -52,16 +63,23 @@ MainWindow::MainWindow(QString szTitle,
     titlebar()->addWidget(btnForward, Qt::AlignLeft);
     titlebar()->addWidget(btnRefresh, Qt::AlignLeft);
 
+    m_fullScreen->setCheckable(true);
+    m_fullScreen->setChecked(nFullScreen);
+    m_fullScreen->setDisabled(nFixSize);    //  固定窗口大小时禁用全屏模式,避免标题栏按钮 BUG
     m_fixSize->setCheckable(true);
     m_fixSize->setChecked(nFixSize);
-    m_fixSize->setDisabled(nFixSize);   // 建议使用命令行参数 --fix-size 或者 --hide-buttons 时直接禁止在 GUI 修改选项,主要因为最大化按钮无法刷新存在状态,干脆都禁用了......
+    m_fixSize->setDisabled(nFixSize);
     m_hideButtons->setCheckable(true);
     m_hideButtons->setChecked(nHideButtons);
     m_hideButtons->setDisabled(nHideButtons);
+    m_menu->addAction(m_fullScreen);
     m_menu->addAction(m_fixSize);
     m_menu->addAction(m_hideButtons);
     titlebar()->setMenu(m_menu);
 
+    titlebar()->setAutoHideOnFullscreen(true);
+
+    fullScreen();
     fixSize();
     hideButtons();
 
@@ -78,6 +96,10 @@ MainWindow::MainWindow(QString szTitle,
         m_widget->refresh();
     });
 
+    connect(m_fullScreen, &QAction::triggered, this, [=]()
+    {
+        fullScreen();
+    });
     connect(m_fixSize, &QAction::triggered, this, [=]()
     {
         fixSize();
@@ -86,6 +108,8 @@ MainWindow::MainWindow(QString szTitle,
     {
         hideButtons();
     });
+
+    connect(m_widget->getPage()->profile(), &QWebEngineProfile::downloadRequested, this, &MainWindow::on_downloadStart);
 }
 
 MainWindow::~MainWindow()
@@ -110,6 +134,20 @@ void MainWindow::setIcon(QString szIconPath)
     }
 }
 
+void MainWindow::fullScreen()
+{
+    if(m_fullScreen->isChecked())
+    {
+        showFullScreen();
+        DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(tr("%1Fullscreen Mode")).arg("    "));
+    }
+    else
+    {
+        showNormal();
+        DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(tr("%1Windowed Mode")).arg("    "));
+    }
+}
+
 void MainWindow::fixSize()
 {
     if(m_fixSize->isChecked())
@@ -139,8 +177,95 @@ void MainWindow::hideButtons()
     }
 }
 
+QString MainWindow::saveAs(QString fileName)
+{
+    QString saveFile = QFileDialog::getSaveFileName(this, tr("Save As"), QDir::homePath() + "/Downloads/" + fileName);
+    if(!saveFile.isEmpty())
+    {
+        //  判断上层目录是否可写入
+        if(QFileInfo(QFileInfo(saveFile).absolutePath()).permissions().testFlag(QFile::WriteUser))
+        {
+            return saveFile;
+        }
+        else
+        {
+            saveAs(fileName);
+        }
+    }
+    return nullptr;
+}
+
+void MainWindow::keyPressEvent(QKeyEvent *event)
+{
+    if(m_fixSize->isEnabled())
+    {
+        if(event->key() == Qt::Key_F11)
+        {
+            m_fullScreen->trigger();
+            m_menu->update();
+        }
+        event->accept();
+    }
+}
+
 void MainWindow::closeEvent(QCloseEvent *event)
 {
     m_dialog->close();
     event->accept();
 }
+
+void MainWindow::on_downloadStart(QWebEngineDownloadItem *item)
+
+{
+    QString fileName = QFileInfo(item->path()).fileName();
+    if(saveAs(fileName).isEmpty())
+    {
+        return;
+    }
+    item->setPath(saveAs(fileName));
+    QString filePath = QFileInfo(item->path()).absoluteFilePath();
+
+    connect(item, &QWebEngineDownloadItem::downloadProgress, this, &MainWindow::on_downloadProgress);
+    connect(item, &QWebEngineDownloadItem::finished, this, [=]
+    {
+        on_downloadFinish(filePath);
+    });
+
+    DFloatingMessage *message = new DFloatingMessage(DFloatingMessage::TransientType);
+    message->setIcon(QIcon::fromTheme("dialog-information").pixmap(64, 64));
+    message->setMessage(QString(tr("%1Start downloading %2")).arg("    ").arg(fileName));
+    DMessageManager::instance()->sendMessage(this, message);
+
+    item->accept();
+}
+
+void MainWindow::on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+    int value = int(double(bytesReceived) / double(bytesTotal) * 100.0);
+
+    bar->setFixedSize(250, 8);
+    bar->setValue(value);
+    message->setIcon(QIcon::fromTheme("deepin-download").pixmap(64, 64));
+    message->setMessage("    " + QString::number(value) + "%");
+    message->setWidget(bar);
+    DMessageManager::instance()->sendMessage(this, message);
+}
+
+void MainWindow::on_downloadFinish(QString filePath)
+{
+    message->hide();
+
+    DPushButton *button = new DPushButton(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(tr("download finished.")).arg(tr("Show in file manager?")));
+    message->setWidget(button);
+    DMessageManager::instance()->sendMessage(this, message);
+
+    connect(button, &DPushButton::clicked, this, [=]
+    {
+        process->start("dde-file-manager --show-item " + filePath);
+        message->hide();
+    });
+}
diff --git a/spark-webapp-runtime/mainwindow.h b/spark-webapp-runtime/mainwindow.h
index 630db36..a808ca5 100644
--- a/spark-webapp-runtime/mainwindow.h
+++ b/spark-webapp-runtime/mainwindow.h
@@ -4,6 +4,10 @@
 #include <DMainWindow>
 #include <DAboutDialog>
 #include <DToolButton>
+#include <DProgressBar>
+#include <DFloatingMessage>
+
+#include <QProcess>
 
 #include "widget.h"
 #include "globaldefine.h"
@@ -19,6 +23,7 @@ public:
                QString szUrl = DEFAULT_URL,
                int nWidth = DEFAULT_WIDTH,
                int nHeight = DEFAULT_HEIGHT,
+               bool nFullScreen = false,
                bool nFixSize = false,
                bool nHideButtons = false,
                DAboutDialog *dialog = nullptr,
@@ -36,16 +41,31 @@ private:
     DToolButton *btnRefresh;
 
     QMenu *m_menu;
+    QAction *m_fullScreen;
     QAction *m_fixSize;
     QAction *m_hideButtons;
 
+    DProgressBar *bar;
+    DFloatingMessage *message;
+
+    QProcess *process;
+
     int m_width, m_height;
 
+    void fullScreen();
     void fixSize();
     void hideButtons();
 
+    QString saveAs(QString fileName);
+
+    void keyPressEvent(QKeyEvent *event);
     void closeEvent(QCloseEvent *event);
 
+private slots:
+    void on_downloadStart(QWebEngineDownloadItem *item);
+    void on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+    void on_downloadFinish(QString filePath);
+
 signals:
     void sigQuit();
 
diff --git a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts b/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
index 65e24b1..b14a0c9 100644
--- a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
+++ b/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
@@ -4,15 +4,50 @@
 <context>
     <name>MainWindow</name>
     <message>
-        <location filename="../mainwindow.cpp" line="27"/>
+        <location filename="../mainwindow.cpp" line="35"/>
         <source>Fix Size</source>
         <translation>固定大小</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="28"/>
+        <location filename="../mainwindow.cpp" line="36"/>
         <source>Hide Buttons</source>
         <translation>隐藏按钮</translation>
     </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="142"/>
+        <source>%1Fullscreen Mode</source>
+        <translation>%1全屏模式</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="147"/>
+        <source>%1Windowed Mode</source>
+        <translation>%1窗口模式</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="182"/>
+        <source>Save As</source>
+        <translation>另存为</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="236"/>
+        <source>%1Start downloading %2</source>
+        <translation>%1开始下载 %2</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="258"/>
+        <source>Open</source>
+        <translation>打开</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="262"/>
+        <source>download finished.</source>
+        <translation>下载完成。</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="262"/>
+        <source>Show in file manager?</source>
+        <translation>是否在文件管理器中显示?</translation>
+    </message>
 </context>
 <context>
     <name>QObject</name>
@@ -63,41 +98,46 @@
     </message>
     <message>
         <location filename="../main.cpp" line="90"/>
+        <source>Run in Fullscreen Mode. Default is false.</source>
+        <translation>以全屏模式运行。默认关闭该功能。</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="94"/>
         <source>Fix Window Size. Default is false.</source>
         <translation>固定窗口大小。默认关闭该功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="94"/>
+        <location filename="../main.cpp" line="98"/>
         <source>Hide Control Buttons. Default is false.</source>
         <translation>隐藏控制按钮。默认关闭该此功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="98"/>
+        <location filename="../main.cpp" line="102"/>
         <source>The ICON of Application.</source>
         <translation>设置应用的图标。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="104"/>
+        <location filename="../main.cpp" line="108"/>
         <source>The Description of Application.</source>
         <translation>设置应用的描述信息。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="110"/>
+        <location filename="../main.cpp" line="114"/>
         <source>The Configuration file of Application.</source>
         <translation>设置应用的配置文件。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="116"/>
+        <location filename="../main.cpp" line="120"/>
         <source>The root path of the program web service.</source>
         <translation>设置内置 WebServer 的根路径。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="123"/>
+        <location filename="../main.cpp" line="127"/>
         <source>The port number of the program web service.</source>
         <translation>设置内置 WebServer 的监听端口号。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="130"/>
+        <location filename="../main.cpp" line="134"/>
         <source>The ssl port number of the program web service.</source>
         <translation>设置内置 WebServer 的 SSL 协议的监听端口号。</translation>
     </message>
@@ -107,17 +147,4 @@
         <translation>星火网页应用运行环境</translation>
     </message>
 </context>
-<context>
-    <name>WebEnginePage</name>
-    <message>
-        <location filename="../webenginepage.cpp" line="38"/>
-        <source>Start downloading...</source>
-        <translation>开始下载...</translation>
-    </message>
-    <message>
-        <location filename="../webenginepage.cpp" line="48"/>
-        <source>Download finished!</source>
-        <translation>下载完成!</translation>
-    </message>
-</context>
 </TS>
diff --git a/spark-webapp-runtime/webenginepage.cpp b/spark-webapp-runtime/webenginepage.cpp
index 66e7144..29bc26e 100644
--- a/spark-webapp-runtime/webenginepage.cpp
+++ b/spark-webapp-runtime/webenginepage.cpp
@@ -3,18 +3,19 @@
 #include <QDir>
 #include <QDebug>
 
+#include <DMessageManager>
+
+DWIDGET_USE_NAMESPACE
+
 WebEnginePage::WebEnginePage(QObject *parent)
     : QWebEnginePage(parent)
-    , m_profile(this->profile())
-    , process(new QProcess)
 {
-    connect(m_profile, &QWebEngineProfile::downloadRequested, this, &WebEnginePage::on_download);
 }
 
 QWebEnginePage *WebEnginePage::createWindow(QWebEnginePage::WebWindowType type)
 {
     Q_UNUSED(type)
-    WebEnginePage *page = new WebEnginePage();
+    WebEnginePage *page = new WebEnginePage;
     connect(page, &QWebEnginePage::urlChanged, this, &WebEnginePage::on_urlChanged);
     return page;
 }
@@ -24,26 +25,3 @@ void WebEnginePage::on_urlChanged(const QUrl url)
     setUrl(url);
     sender()->deleteLater();
 }
-
-void WebEnginePage::on_download(QWebEngineDownloadItem *item)
-
-{
-    connect(item, &QWebEngineDownloadItem::downloadProgress, this, &WebEnginePage::on_downloadProgress);
-    connect(item, &QWebEngineDownloadItem::finished, this, &WebEnginePage::on_downloadFinished);
-
-    //  无法指定下载位置,原因未知;默认位置为 ~/Downloads
-    //  item->setPath(QDir::homePath() + "/Downloads/");
-    item->accept();
-
-    process->start(QString("notify-send -a spark-webapp-runtime -t 5000 \"%1\"").arg(tr("Start downloading...")));
-}
-
-void WebEnginePage::on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
-{
-    qDebug() << "Download Progress:\tbytesReceived: " << bytesReceived << "\tbytesTotal: " << bytesTotal;
-}
-
-void WebEnginePage::on_downloadFinished()
-{
-    process->start(QString("notify-send -a spark-webapp-runtime -t 5000 \"%1\"").arg(tr("Download finished!")));
-}
diff --git a/spark-webapp-runtime/webenginepage.h b/spark-webapp-runtime/webenginepage.h
index 19b0df3..f36bd17 100644
--- a/spark-webapp-runtime/webenginepage.h
+++ b/spark-webapp-runtime/webenginepage.h
@@ -2,9 +2,7 @@
 #define WEBENGINEPAGE_H
 
 #include <QWebEnginePage>
-#include <QWebEngineProfile>
 #include <QObject>
-#include <QProcess>
 
 class WebEnginePage : public QWebEnginePage
 {
@@ -14,16 +12,8 @@ public:
 protected:
     QWebEnginePage *createWindow(WebWindowType type) override;
 
-private:
-    QWebEngineProfile *m_profile;
-
-    QProcess *process;
-
 private slots:
     void on_urlChanged(const QUrl url);
-    void on_download(QWebEngineDownloadItem *item);
-    void on_downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
-    void on_downloadFinished();
 
 };
 
diff --git a/spark-webapp-runtime/widget.cpp b/spark-webapp-runtime/widget.cpp
index 8e18f76..d861dc1 100644
--- a/spark-webapp-runtime/widget.cpp
+++ b/spark-webapp-runtime/widget.cpp
@@ -32,6 +32,11 @@ Widget::~Widget()
 {
 }
 
+WebEnginePage *Widget::getPage()
+{
+    return m_page;
+}
+
 void Widget::goBack()
 {
     m_webEngineView->back();
diff --git a/spark-webapp-runtime/widget.h b/spark-webapp-runtime/widget.h
index 062de66..c0748c2 100644
--- a/spark-webapp-runtime/widget.h
+++ b/spark-webapp-runtime/widget.h
@@ -21,6 +21,7 @@ public:
     explicit Widget(QString szUrl = nullptr, QWidget *parent = nullptr);
     ~Widget();
 
+    WebEnginePage *getPage();
     void goBack();
     void goForward();
     void refresh();