]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Improve Filelight installation UX
authorFelix Ernst <felixernst@kde.org>
Mon, 8 Jul 2024 11:41:56 +0000 (11:41 +0000)
committerFelix Ernst <felixernst@kde.org>
Mon, 8 Jul 2024 11:41:56 +0000 (11:41 +0000)
Before this commit pressing the free space button when Filelight
is not installed would show a singular action called "Install
Filelight to View Disk Usage Statistics…". Pressing this button
would open the store page for Filelight. This is an okay user
experience, but we can do better.

This commit makes it so pressing the free space button when
Filelight is not installed shows an attractive UI that makes clear
that freeing up disk space can be accomplished nicely by installing
Filelight. The "Install Filelight…" button on this UI is connected
to PackageKit directly, so we do not need to show a separate store
like Discover and instead trigger an installation right then and
there. For this, the recently introduced DolphinPackageInstaller
KJob is used.

Installation progress is reported through the status bar similar to
the progress reporting of slowly loading directories or searches.
Installation failure or success is ultimately shown within Dolphin
as a passive notification above the view.

On Microsoft Windows or when PackageKit is not available, the
install button will only open a store page for Filelight.

CCBUG: 477739

src/CMakeLists.txt
src/config-dolphin.h.cmake
src/dolphinviewcontainer.cpp
src/global.h
src/statusbar/dolphinstatusbar.cpp
src/statusbar/dolphinstatusbar.h
src/statusbar/statusbarspaceinfo.cpp
src/statusbar/statusbarspaceinfo.h

index 6b1d9c7b062931822d0d4e7af543fff58f6783a4..29eef57535a4b4db2bf19402133bb7c68241aac1 100644 (file)
@@ -1,6 +1,7 @@
 include(ECMAddAppIcon)
 
 set(ADMIN_WORKER_PACKAGE_NAME "kio-admin")
+set(FILELIGHT_PACKAGE_NAME "filelight")
 configure_file(config-dolphin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-dolphin.h)
 
 add_definitions(
index 05ae7d2f97f5f6eff8c4be5338ad14892207bb98..871adc9707d0fa1a978b14e859feaf65277e728a 100644 (file)
@@ -7,3 +7,6 @@
 
 /** The name of the package that needs to be installed so URLs starting with "admin:" can be opened in Dolphin. */
 #cmakedefine ADMIN_WORKER_PACKAGE_NAME "@ADMIN_WORKER_PACKAGE_NAME@"
+
+/** The name of the KDE Filelight package. */
+#cmakedefine FILELIGHT_PACKAGE_NAME "@FILELIGHT_PACKAGE_NAME@"
index ce91dbfe85a52cc5f337d3660628bfcdda4e812b..99ba4efa8a36afd57a6342a0cd5c997289f7ae2d 100644 (file)
@@ -165,6 +165,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent)
     });
     connect(m_statusBar, &DolphinStatusBar::stopPressed, this, &DolphinViewContainer::stopDirectoryLoading);
     connect(m_statusBar, &DolphinStatusBar::zoomLevelChanged, this, &DolphinViewContainer::slotStatusBarZoomLevelChanged);
+    connect(m_statusBar, &DolphinStatusBar::showMessage, this, &DolphinViewContainer::showMessage);
 
     m_statusBarTimer = new QTimer(this);
     m_statusBarTimer->setSingleShot(true);
index dd07f9170f246e866cc254c420fc26ecd26994b4..9161ed877a910bd394a995f94811497db311b446 100644 (file)
@@ -49,8 +49,8 @@ QPair<QString, Qt::SortOrder> sortOrderForUrl(QUrl &url);
 /**
  * TODO: Move this somewhere global to all KDE apps, not just Dolphin
  */
-const int VERTICAL_SPACER_HEIGHT = 12;
-const int LAYOUT_SPACING_SMALL = 2;
+constexpr int VERTICAL_SPACER_HEIGHT = 12;
+constexpr int LAYOUT_SPACING_SMALL = 2;
 }
 
 enum Animated { WithAnimation, WithoutAnimation };
index 5a3aa58577ccbf4d07820ef91c277cacdf746a22..c8369febcf8dd7341a9e04ec3a54bf86fc2be0de 100644 (file)
@@ -70,6 +70,13 @@ DolphinStatusBar::DolphinStatusBar(QWidget *parent)
 
     // Initialize space information
     m_spaceInfo = new StatusBarSpaceInfo(contentsContainer);
+    connect(m_spaceInfo, &StatusBarSpaceInfo::showMessage, this, &DolphinStatusBar::showMessage);
+    connect(m_spaceInfo,
+            &StatusBarSpaceInfo::showInstallationProgress,
+            this,
+            [this](const QString &currentlyRunningTaskTitle, int installationProgressPercent) {
+                showProgress(currentlyRunningTaskTitle, installationProgressPercent, CancelLoading::Disallowed);
+            });
 
     // Initialize progress information
     m_stopButton = new QToolButton(contentsContainer);
index 8abbed66be7e6f7a144d4750b291fd46878a72ac..a620a011783466a990347e18db9b61a00ba6a9c0 100644 (file)
@@ -9,6 +9,8 @@
 
 #include "animatedheightwidget.h"
 
+#include <KMessageWidget>
+
 #include <QTime>
 
 class QUrl;
@@ -96,6 +98,11 @@ Q_SIGNALS:
 
     void zoomLevelChanged(int zoomLevel);
 
+    /**
+     * Requests for @p message with the given @p messageType to be shown to the user in a non-modal way.
+     */
+    void showMessage(const QString &message, KMessageWidget::MessageType messageType);
+
 protected:
     void contextMenuEvent(QContextMenuEvent *event) override;
     void paintEvent(QPaintEvent *paintEvent) override;
index 9df4164676d28b2fa689d4280f329d8a8832c1cf..a482bde9cd6785dbfbd166200fb27e728b40a6c2 100644 (file)
@@ -6,6 +6,9 @@
 
 #include "statusbarspaceinfo.h"
 
+#include "config-dolphin.h"
+#include "dolphinpackageinstaller.h"
+#include "global.h"
 #include "spaceinfoobserver.h"
 
 #include <KCapacityBar>
 
 #include <QDesktopServices>
 #include <QHBoxLayout>
+#include <QLabel>
 #include <QMenu>
 #include <QMouseEvent>
+#include <QPushButton>
 #include <QStorageInfo>
 #include <QToolButton>
+#include <QVBoxLayout>
+#include <QWidgetAction>
 
 StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget *parent)
     : QWidget(parent)
     , m_observer(nullptr)
+    , m_installFilelightWidgetAction{nullptr}
 {
     m_capacityBar = new KCapacityBar(KCapacityBar::DrawTextInline, this);
     m_textInfoButton = new QToolButton(this);
@@ -116,17 +124,11 @@ void StatusBarSpaceInfo::updateMenu()
     const KService::Ptr kdiskfree = KService::serviceByDesktopName(QStringLiteral("org.kde.kdf"));
 
     if (!filelight && !kdiskfree) {
-        QAction *installFilelight =
-            m_buttonMenu->addAction(QIcon::fromTheme(QStringLiteral("filelight")), i18n("Install Filelight to View Disk Usage Statistics…"));
-
-        connect(installFilelight, &QAction::triggered, this, [] {
-#ifdef Q_OS_WIN
-            QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
-#else
-            QDesktopServices::openUrl(QUrl("appstream://org.kde.filelight.desktop"));
-#endif
-        });
-
+        // Show an UI to install a tool to free up disk space because this is what a user pressing on a "free space" button would want.
+        if (!m_installFilelightWidgetAction) {
+            initialiseInstallFilelightWidgetAction();
+        }
+        m_buttonMenu->addAction(m_installFilelightWidgetAction);
         return;
     }
 
@@ -177,6 +179,42 @@ void StatusBarSpaceInfo::updateMenu()
     }
 }
 
+void StatusBarSpaceInfo::slotInstallFilelightButtonClicked()
+{
+#ifdef Q_OS_WIN
+    QDesktopServices::openUrl(QUrl("https://apps.kde.org/filelight"));
+#else
+    auto packageInstaller = new DolphinPackageInstaller(
+        FILELIGHT_PACKAGE_NAME,
+        QUrl("appstream://org.kde.filelight.desktop"),
+        []() {
+            return KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
+        },
+        this);
+    connect(packageInstaller, &KJob::result, this, [this](KJob *job) {
+        Q_EMIT showInstallationProgress(QString(), 100); // Hides the progress information in the status bar.
+        if (job->error()) {
+            Q_EMIT showMessage(job->errorString(), KMessageWidget::Error);
+        } else {
+            Q_EMIT showMessage(xi18nc("@info", "<application>Filelight</application> installed successfully."), KMessageWidget::Positive);
+            if (m_textInfoButton->menu()->isVisible()) {
+                m_textInfoButton->menu()->hide();
+                updateMenu();
+                m_textInfoButton->menu()->show();
+            }
+        }
+    });
+    const auto installationTaskText{i18nc("@info:status", "Installing Filelight…")};
+    Q_EMIT showInstallationProgress(installationTaskText, -1);
+    connect(packageInstaller, &KJob::percentChanged, this, [this, installationTaskText](KJob * /* job */, long unsigned int percent) {
+        if (percent < 100) { // Ignore some weird reported values.
+            Q_EMIT showInstallationProgress(installationTaskText, percent);
+        }
+    });
+    packageInstaller->start();
+#endif
+}
+
 void StatusBarSpaceInfo::slotValuesChanged()
 {
     Q_ASSERT(m_observer);
@@ -211,4 +249,53 @@ void StatusBarSpaceInfo::slotValuesChanged()
     }
 }
 
+void StatusBarSpaceInfo::initialiseInstallFilelightWidgetAction()
+{
+    Q_ASSERT(!m_installFilelightWidgetAction);
+
+    auto containerWidget = new QWidget{this};
+    containerWidget->setContentsMargins(Dolphin::VERTICAL_SPACER_HEIGHT,
+                                        Dolphin::VERTICAL_SPACER_HEIGHT,
+                                        Dolphin::VERTICAL_SPACER_HEIGHT, // Using the same value for every spacing in this containerWidget looks nice.
+                                        Dolphin::VERTICAL_SPACER_HEIGHT);
+    auto vLayout = new QVBoxLayout(containerWidget);
+
+    auto installFilelightTitle = new QLabel(i18nc("@title", "Free Up Disk Space"), containerWidget);
+    installFilelightTitle->setAlignment(Qt::AlignCenter);
+    installFilelightTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+    QFont titleFont{installFilelightTitle->font()};
+    titleFont.setPointSize(titleFont.pointSize() + 2);
+    installFilelightTitle->setFont(titleFont);
+    vLayout->addWidget(installFilelightTitle);
+
+    vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
+
+    auto installFilelightBody =
+        // i18n: The new line ("<nl/>") tag is only there to format this text visually pleasing, i.e. to avoid having one very long line.
+        new QLabel(xi18nc("@title", "<para>Install additional software to view disk usage statistics<nl/>and identify big files and folders.</para>"),
+                   containerWidget);
+    installFilelightBody->setAlignment(Qt::AlignCenter);
+    installFilelightBody->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+    vLayout->addWidget(installFilelightBody);
+
+    vLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
+
+    auto installFilelightButton =
+        new QPushButton(QIcon::fromTheme(QStringLiteral("filelight")), i18nc("@action:button", "Install Filelight…"), containerWidget);
+    installFilelightButton->setMinimumWidth(std::max(installFilelightButton->sizeHint().width(), installFilelightTitle->sizeHint().width()));
+    auto buttonLayout = new QHBoxLayout; // The parent is automatically set on addLayout() below.
+    buttonLayout->addWidget(installFilelightButton, 0, Qt::AlignHCenter);
+    vLayout->addLayout(buttonLayout);
+
+    // Make sure one Tab press focuses the button after the UI opened.
+    m_buttonMenu->setFocusProxy(installFilelightButton);
+    containerWidget->setFocusPolicy(Qt::TabFocus);
+    containerWidget->setFocusProxy(installFilelightButton);
+    installFilelightButton->setAccessibleDescription(installFilelightBody->text());
+    connect(installFilelightButton, &QAbstractButton::clicked, this, &StatusBarSpaceInfo::slotInstallFilelightButtonClicked);
+
+    m_installFilelightWidgetAction = new QWidgetAction{this};
+    m_installFilelightWidgetAction->setDefaultWidget(containerWidget); // transfers ownership of containerWidget
+}
+
 #include "moc_statusbarspaceinfo.cpp"
index 23a77d045e4ce3d1f46c4c97ec6af17c1cbab6a0..237192c9cdb06dca6bb3ad06d74047025bafd42e 100644 (file)
@@ -6,6 +6,8 @@
 #ifndef STATUSBARSPACEINFO_H
 #define STATUSBARSPACEINFO_H
 
+#include <KMessageWidget>
+
 #include <QUrl>
 #include <QWidget>
 
@@ -14,6 +16,7 @@ class QShowEvent;
 class QMenu;
 class QMouseEvent;
 class QToolButton;
+class QWidgetAction;
 
 class KCapacityBar;
 
@@ -40,6 +43,19 @@ public:
 
     void update();
 
+Q_SIGNALS:
+    /**
+     * Requests for @p message with the given @p messageType to be shown to the user in a non-modal way.
+     */
+    void showMessage(const QString &message, KMessageWidget::MessageType messageType);
+
+    /**
+     * Requests for a progress update to be shown to the user in a non-modal way.
+     * @param currentlyRunningTaskTitle     The task that is currently progressing.
+     * @param installationProgressPercent   The current percentage of completion.
+     */
+    void showInstallationProgress(const QString &currentlyRunningTaskTitle, int installationProgressPercent);
+
 protected:
     void showEvent(QShowEvent *event) override;
     void hideEvent(QHideEvent *event) override;
@@ -48,13 +64,29 @@ protected:
     void updateMenu();
 
 private Q_SLOTS:
+    /**
+     * Asynchronously starts a Filelight installation using DolphinPackageInstaller. @see DolphinPackageInstaller.
+     * Installation success or failure is reported through showMessage(). @see StatusBarSpaceInfo::showMessage().
+     * Installation progress is reported through showInstallationProgress(). @see StatusBarSpaceInfo::showInstallationProgress().
+     */
+    void slotInstallFilelightButtonClicked();
+
     void slotValuesChanged();
 
+private:
+    /**
+     * Creates a new QWidgetAction that contains a UI to install Filelight.
+     * m_installFilelightWidgetAction is initialised after calling this method once.
+     */
+    void initialiseInstallFilelightWidgetAction();
+
 private:
     QScopedPointer<SpaceInfoObserver> m_observer;
     KCapacityBar *m_capacityBar;
     QToolButton *m_textInfoButton;
     QMenu *m_buttonMenu;
+    /** An action containing a UI to install Filelight. */
+    QWidgetAction *m_installFilelightWidgetAction;
     QUrl m_url;
     bool m_ready;
     bool m_shown;