┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/search/popup.cpp
diff options
context:
space:
mode:
authorFelix Ernst <[email protected]>2025-04-07 21:09:00 +0000
committerFelix Ernst <[email protected]>2025-04-07 21:09:00 +0000
commit4102ccb80457eea44ea280f0ace2a419602bc34b (patch)
tree841e039cf9864276c968a397a2ae75c363199342 /src/search/popup.cpp
parentbfc177d3d1bc5a4a241e35d59086e4824e7c0bd3 (diff)
Rewrite search integration
This huge commit is a nearly complete rewrite of the Dolphin search code. It implements most of the improved Dolphin search UI/UX as designed and discussed in a collaborative effort by Kristen McWilliam, Jin Liu, Andy Betts, Tagwerk, a few others and me. See https://invent.kde.org/system/dolphin/-/issues/46. # Notable changes - A toggle to change the search tool is provided as most contributors deemed that useful in https://invent.kde.org/system/dolphin/-/merge_requests/642#note_985112. - The default search is changed to filenamesearch for maximum reliability. - Removing all search parameters will take users back to the view state prior to starting a search instead of keeping the search results open. - The UI for choosing file types or modification dates has been made more powerful with more granularity and more options. - Most search parameters can be configured from a popup menu which gives us extra space for extra clarity. - Labels and help buttons as well as hyperlinks to settings makes sure the user always knows why some search parameters are unavailable in some contexts. - Chips show important search parameters while the popup is closed. They allow quickly removing filters. - The titles of the search and the input field placeholder message change to make clear whether file names or file contents are searched. - When the user actively switches the search tool, whether content should be searched, or whether to search everywhere, this is preserved for the initial state of the search bar when the user opens it the next time after restarting Dolphin. # Architecture - The new DolphinQuery class is independent of the UI and contains all search parameters modifiable in Dolphin as easy setters and getters. - DolphinQuery objects are also used to update the states of every component in the search UI. There is now a clear separation of UI and search configuration/DolphinQuery. - DolphinQuery is responsible for exporting to and importing from search URLs. - The search UI always reflects the currently configured DolphinQuery no matter if the user changed the UI to change the DolphinQuery or loaded a DolphinQuery/older search URL which then is reflected in the UI. - I tried to simplify all classes and their interaction between each other as much as possible. - I added some tests BUG: 386754 CCBUG: 435119 CCBUG: 458761 BUG: 446387 BUG: 470136 CCBUG: 471556 CCBUG: 475439 CCBUG: 477969 BUG: 480001 BUG: 483578 BUG: 488047 BUG: 488845 BUG: 500103 FIXED-IN: 25.08
Diffstat (limited to 'src/search/popup.cpp')
-rw-r--r--src/search/popup.cpp378
1 files changed, 378 insertions, 0 deletions
diff --git a/src/search/popup.cpp b/src/search/popup.cpp
new file mode 100644
index 000000000..ce0fb21a2
--- /dev/null
+++ b/src/search/popup.cpp
@@ -0,0 +1,378 @@
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2024 Felix Ernst <[email protected]>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
+
+#include "popup.h"
+
+#include "config-dolphin.h"
+#include "dolphinpackageinstaller.h"
+#include "dolphinquery.h"
+#include "global.h"
+#include "selectors/dateselector.h"
+#include "selectors/filetypeselector.h"
+#include "selectors/minimumratingselector.h"
+#include "selectors/tagsselector.h"
+
+#include <KContextualHelpButton>
+#include <KDialogJobUiDelegate>
+#include <KIO/ApplicationLauncherJob>
+#include <KIO/CommandLauncherJob>
+#include <KLocalizedString>
+#include <KService>
+
+#include <QButtonGroup>
+#ifdef Q_OS_WIN
+#include <QDesktopServices>
+#endif
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMenu>
+#include <QRadioButton>
+#include <QStandardPaths>
+#include <QToolButton>
+#include <QVBoxLayout>
+
+namespace
+{
+constexpr auto kFindDesktopName = "org.kde.kfind";
+}
+
+using namespace Search;
+
+QString Search::filenamesearchUiName()
+{
+ // i18n: Localized name for the Filenamesearch search tool for use in user interfaces.
+ return i18n("Simple search");
+};
+
+QString Search::balooUiName()
+{
+ // i18n: Localized name for the Baloo search tool for use in user interfaces.
+ return i18n("File Indexing");
+};
+
+Popup::Popup(std::shared_ptr<const DolphinQuery> dolphinQuery, QWidget *parent)
+ : WidgetMenu{parent}
+ , UpdatableStateInterface{dolphinQuery}
+{
+}
+
+QWidget *Popup::init()
+{
+ 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 verticalMainLayout = new QVBoxLayout{containerWidget};
+ verticalMainLayout->setSpacing((2 * Dolphin::VERTICAL_SPACER_HEIGHT) / 3); // A bit less spacing between rows than when adding an explicit spacer.
+
+ /// Add UI to switch between only searching in file names or also in contents.
+ auto searchInLabel = new QLabel{i18nc("@title:group", "Search in:"), containerWidget};
+ searchInLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ verticalMainLayout->addWidget(searchInLabel);
+
+ m_searchInFileNamesRadioButton = new QRadioButton{i18nc("@option:radio Search in:", "File names"), containerWidget};
+ connect(m_searchInFileNamesRadioButton, &QAbstractButton::clicked, this, [this]() {
+ if (m_searchConfiguration->searchThrough() == SearchThrough::FileNames) {
+ return; // Already selected.
+ }
+ SearchSettings::setWhat(QStringLiteral("FileNames"));
+ SearchSettings::self()->save();
+ DolphinQuery searchConfigurationCopy = *m_searchConfiguration;
+ searchConfigurationCopy.setSearchThrough(SearchThrough::FileNames);
+ Q_EMIT configurationChanged(std::move(searchConfigurationCopy));
+ });
+ verticalMainLayout->addWidget(m_searchInFileNamesRadioButton);
+
+ m_searchInFileContentsRadioButton = new QRadioButton{containerWidget};
+ connect(m_searchInFileContentsRadioButton, &QAbstractButton::clicked, this, [this]() {
+ if (m_searchConfiguration->searchThrough() == SearchThrough::FileContents) {
+ return; // Already selected.
+ }
+ SearchSettings::setWhat(QStringLiteral("FileContents"));
+ SearchSettings::self()->save();
+ DolphinQuery searchConfigurationCopy = *m_searchConfiguration;
+ searchConfigurationCopy.setSearchThrough(SearchThrough::FileContents);
+ Q_EMIT configurationChanged(std::move(searchConfigurationCopy));
+ });
+ verticalMainLayout->addWidget(m_searchInFileContentsRadioButton);
+
+ auto searchInButtonGroup = new QButtonGroup{this};
+ searchInButtonGroup->addButton(m_searchInFileNamesRadioButton);
+ searchInButtonGroup->addButton(m_searchInFileContentsRadioButton);
+
+ /// Add UI to switch between search tools.
+ // When we build without Baloo, there is only one search tool available, so we skip adding the UI to switch.
+#if HAVE_BALOO
+ verticalMainLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
+
+ auto searchUsingLabel = new QLabel{i18nc("@title:group", "Search using:"), containerWidget};
+ searchUsingLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ verticalMainLayout->addWidget(searchUsingLabel);
+
+ /// Initialize the Filenamesearch row.
+ m_filenamesearchRadioButton = new QRadioButton{filenamesearchUiName(), containerWidget};
+ connect(m_filenamesearchRadioButton, &QAbstractButton::clicked, this, [this]() {
+ if (m_searchConfiguration->searchTool() == SearchTool::Filenamesearch) {
+ return; // Already selected.
+ }
+ SearchSettings::setSearchTool(QStringLiteral("Filenamesearch"));
+ SearchSettings::self()->save();
+ DolphinQuery searchConfigurationCopy = *m_searchConfiguration;
+ searchConfigurationCopy.setSearchTool(SearchTool::Filenamesearch);
+ Q_EMIT configurationChanged(std::move(searchConfigurationCopy));
+ });
+
+ m_filenamesearchContextualHelpButton = new KContextualHelpButton(
+ xi18nc("@info about a search tool",
+ "<para>For searching in file contents <application>%1</application> attempts to use third-party search tools if they are available on this "
+ "system and are expected to lead to better or faster results. <application>ripgrep</application> and <application>ripgrep-all</application> "
+ "might improve your search experience if they are installed. <application>ripgrep-all</application> in particular enables searches in more "
+ "file types (e.g. pdf, docx, sqlite, jpg, movie subtitles (mkv, mp4)).</para><para>The manner in which these search tools are invoked can be "
+ "configured by editing a script file. Copy it from <filename>%2</filename> to <filename>%3</filename> before modifying your copy. If any "
+ "issues arise, delete your copy <filename>%3</filename> to revert your changes.</para>",
+ filenamesearchUiName(),
+ QStringLiteral("%1/kio_filenamesearch/kio-filenamesearch-grep").arg(KDE_INSTALL_FULL_DATADIR),
+ QStringLiteral("%1/kio_filenamesearch/kio-filenamesearch-grep").arg(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation))),
+ m_filenamesearchRadioButton,
+ containerWidget);
+
+ auto filenamesearchRowLayout = new QHBoxLayout;
+ filenamesearchRowLayout->addWidget(m_filenamesearchRadioButton);
+ filenamesearchRowLayout->addWidget(m_filenamesearchContextualHelpButton);
+ filenamesearchRowLayout->addStretch(); // for left-alignment
+ verticalMainLayout->addLayout(filenamesearchRowLayout);
+
+ /// Initialize the Baloo row.
+ m_balooRadioButton = new QRadioButton{balooUiName(), containerWidget};
+ connect(m_balooRadioButton, &QAbstractButton::clicked, this, [this]() {
+ if (m_searchConfiguration->searchTool() == SearchTool::Baloo) {
+ return; // Already selected.
+ }
+ SearchSettings::setSearchTool(QStringLiteral("Baloo"));
+ SearchSettings::self()->save();
+ DolphinQuery searchConfigurationCopy = *m_searchConfiguration;
+ searchConfigurationCopy.setSearchTool(SearchTool::Baloo);
+ Q_EMIT configurationChanged(std::move(searchConfigurationCopy));
+ });
+
+ m_balooContextualHelpButton = new KContextualHelpButton(QString(), m_balooRadioButton, containerWidget);
+
+ auto balooSettingsButton = new QToolButton{containerWidget};
+ balooSettingsButton->setText(i18nc("@action:button %1 is software name", "Configure %1…", balooUiName()));
+ balooSettingsButton->setIcon(QIcon::fromTheme("configure"));
+ balooSettingsButton->setToolTip(balooSettingsButton->text());
+ balooSettingsButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ balooSettingsButton->setAutoRaise(true);
+ balooSettingsButton->setFixedHeight(m_balooRadioButton->sizeHint().height());
+ connect(balooSettingsButton, &QToolButton::clicked, this, [containerWidget] {
+ // Code taken from KCMLauncher::openSystemSettings() in the KCMUtil KDE framework.
+ constexpr auto systemSettings = "systemsettings";
+ KIO::CommandLauncherJob *openBalooSettingsJob;
+ // Open in System Settings if it's available
+ if (KService::serviceByDesktopName(systemSettings)) {
+ openBalooSettingsJob = new KIO::CommandLauncherJob(systemSettings, {"kcm_baloofile"}, containerWidget);
+ openBalooSettingsJob->setDesktopName(systemSettings);
+ } else {
+ openBalooSettingsJob = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), {"kcm_baloofile"}, containerWidget);
+ }
+ openBalooSettingsJob->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, containerWidget));
+ openBalooSettingsJob->start();
+ });
+
+ auto balooRowLayout = new QHBoxLayout;
+ balooRowLayout->addWidget(m_balooRadioButton);
+ balooRowLayout->addWidget(m_balooContextualHelpButton);
+ balooRowLayout->addWidget(balooSettingsButton);
+ balooRowLayout->addStretch(); // for left-alignment
+ verticalMainLayout->addLayout(balooRowLayout);
+
+ auto searchUsingButtonGroup = new QButtonGroup{this};
+ searchUsingButtonGroup->addButton(m_filenamesearchRadioButton);
+ searchUsingButtonGroup->addButton(m_balooRadioButton);
+
+ verticalMainLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
+
+ /// Add extra search filters like date, tags, rating, etc.
+ m_selectorsLayoutWidget = new QWidget{containerWidget};
+ if (m_searchConfiguration->searchTool() == SearchTool::Filenamesearch) {
+ m_selectorsLayoutWidget->hide();
+ }
+ auto selectorsLayout = new QGridLayout{m_selectorsLayoutWidget};
+ selectorsLayout->setContentsMargins(0, 0, 0, 0);
+ selectorsLayout->setSpacing(verticalMainLayout->spacing());
+
+ auto typeSelectorTitle = new QLabel{i18nc("@title:group for filtering files based on their type", "File Type:"), containerWidget};
+ typeSelectorTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ selectorsLayout->addWidget(typeSelectorTitle, 1, 0);
+
+ m_typeSelector = new FileTypeSelector{m_searchConfiguration, containerWidget};
+ connect(m_typeSelector, &FileTypeSelector::configurationChanged, this, &Popup::configurationChanged);
+ selectorsLayout->addWidget(m_typeSelector, 2, 0);
+
+ auto dateSelectorTitle = new QLabel{i18nc("@title:group for filtering files by modified date", "Modified since:"), containerWidget};
+ dateSelectorTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ selectorsLayout->addWidget(dateSelectorTitle, 1, 1);
+
+ m_dateSelector = new DateSelector{m_searchConfiguration, containerWidget};
+ m_dateSelector->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); // Make sure this button is as wide as the other button in this column.
+ connect(m_dateSelector, &DateSelector::configurationChanged, this, &Popup::configurationChanged);
+ selectorsLayout->addWidget(m_dateSelector, 2, 1);
+
+ auto ratingSelectorTitle = new QLabel{i18nc("@title:group for selecting a minimum rating of search results", "Rating:"), containerWidget};
+ ratingSelectorTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ selectorsLayout->addWidget(ratingSelectorTitle, 3, 0);
+
+ m_ratingSelector = new MinimumRatingSelector{m_searchConfiguration, containerWidget};
+ connect(m_ratingSelector, &MinimumRatingSelector::configurationChanged, this, &Popup::configurationChanged);
+ selectorsLayout->addWidget(m_ratingSelector, 4, 0);
+
+ auto tagsSelectorTitle = new QLabel{i18nc("@title:group for selecting required tags for search results", "Tags:"), containerWidget};
+ tagsSelectorTitle->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ selectorsLayout->addWidget(tagsSelectorTitle, 3, 1);
+
+ m_tagsSelector = new TagsSelector{m_searchConfiguration, containerWidget};
+ m_tagsSelector->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); // Make sure this button is as wide as the other button in this column.
+ connect(m_tagsSelector, &TagsSelector::configurationChanged, this, &Popup::configurationChanged);
+ selectorsLayout->addWidget(m_tagsSelector, 4, 1);
+
+ verticalMainLayout->addWidget(m_selectorsLayoutWidget);
+#endif // HAVE_BALOO
+
+ /**
+ * Dolphin cannot provide every advanced search workflow, so here at the end we need to push users to more dedicated search tools if what Dolphin provides
+ * turns out to be insufficient.
+ */
+ verticalMainLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT);
+
+ auto kfindLabel = new QLabel{i18nc("@label above 'Install KFind'/'Open KFind' button", "For more advanced searches:"), containerWidget};
+ kfindLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard);
+ verticalMainLayout->addWidget(kfindLabel);
+
+ m_kFindButton = new QToolButton{containerWidget};
+ m_kFindButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ connect(m_kFindButton, &QToolButton::clicked, this, &Popup::slotKFindButtonClicked);
+ verticalMainLayout->addWidget(m_kFindButton);
+
+ return containerWidget;
+}
+
+void Popup::updateState(const std::shared_ptr<const DolphinQuery> &dolphinQuery)
+{
+ m_searchInFileNamesRadioButton->setChecked(dolphinQuery->searchThrough() == SearchThrough::FileNames);
+ m_searchInFileContentsRadioButton->setChecked(dolphinQuery->searchThrough() == SearchThrough::FileContents);
+
+ // When we build without Baloo, there is only one search tool available and no UI to switch.
+#if HAVE_BALOO
+ m_filenamesearchRadioButton->setChecked(dolphinQuery->searchTool() == SearchTool::Filenamesearch);
+ m_filenamesearchContextualHelpButton->setVisible(dolphinQuery->searchThrough() == SearchThrough::FileContents);
+
+ if (dolphinQuery->searchLocations() != SearchLocations::Everywhere && !isIndexingEnabledIn(dolphinQuery->searchPath())) {
+ m_balooRadioButton->setToolTip(
+ xi18nc("@info:tooltip",
+ "<para>Searching in <filename>%1</filename> using <application>%2</application> is currently not possible because "
+ "<application>%2</application> is configured to never create a search index of that location.</para>",
+ dolphinQuery->searchPath().adjusted(QUrl::RemoveUserInfo | QUrl::StripTrailingSlash).toString(QUrl::PreferLocalFile),
+ balooUiName()));
+ m_balooRadioButton->setDisabled(true);
+ } else if (dolphinQuery->searchThrough() == SearchThrough::FileContents && !isContentIndexingEnabled()) {
+ m_balooRadioButton->setToolTip(xi18nc("@info:tooltip",
+ "<para>Searching through file contents using <application>%1</application> is currently not possible because "
+ "<application>%1</application> is configured to never create a search index for file contents.</para>",
+ balooUiName()));
+ m_balooRadioButton->setDisabled(true);
+ } else {
+ m_balooRadioButton->setToolTip(QString());
+ m_balooRadioButton->setEnabled(true);
+ }
+ m_balooContextualHelpButton->setContextualHelpText(
+ i18nc("@info make a warning paragraph bold before other paragraphs", "<b>%1</b>", m_balooRadioButton->toolTip())
+ + xi18nc(
+ "@info about a search tool",
+ "<para><application>%1</application> uses a database for searching. The database is created by indexing your files in the background based on "
+ "how <application>%1</application> is configured.<list><item><application>%1</application> provides results extremely "
+ "quickly.</item><item>Allows searching for file types, dates, tags, etc.</item><item>Only searches in indexed folders. Configure which folders "
+ "should be indexed in <application>System Settings</application>.</item><item>When the searched locations contain links to other files or "
+ "folders, those will not be searched or show up in search results.</item><item>Hidden files and folders and their contents might also not be "
+ "searched depending on how <application>%1</application> is configured.</item></list></para>",
+ balooUiName()));
+
+ m_balooRadioButton->setChecked(dolphinQuery->searchTool() == SearchTool::Baloo);
+ m_balooRadioButton->setChecked(false);
+
+ if (m_balooRadioButton->isChecked()) {
+ m_searchInFileContentsRadioButton->setText(i18nc("@option:radio Search in:", "File names and contents"));
+ m_typeSelector->updateStateToMatch(dolphinQuery);
+ m_dateSelector->updateStateToMatch(dolphinQuery);
+ m_ratingSelector->updateStateToMatch(dolphinQuery);
+ m_tagsSelector->updateStateToMatch(dolphinQuery);
+ } else {
+#endif // HAVE_BALOO
+ m_searchInFileContentsRadioButton->setText(i18nc("@option:radio Search in:", "File contents"));
+#if HAVE_BALOO
+ }
+
+ /// Show/Hide Baloo-specific selectors.
+ m_selectorsLayoutWidget->setVisible(m_balooRadioButton->isChecked());
+ const int columnWidth = std::max(
+ {m_typeSelector->sizeHint().width(), m_dateSelector->sizeHint().width(), m_ratingSelector->sizeHint().width(), m_tagsSelector->sizeHint().width()});
+ static_cast<QGridLayout *>(m_selectorsLayoutWidget->layout())->setColumnMinimumWidth(0, columnWidth);
+ static_cast<QGridLayout *>(m_selectorsLayoutWidget->layout())->setColumnMinimumWidth(1, columnWidth);
+ resizeToFitContents();
+#endif // HAVE_BALOO
+
+ KService::Ptr kFind = KService::serviceByDesktopName(kFindDesktopName);
+ if (kFind) {
+ m_kFindButton->setText(i18nc("@action:button 1 is KFind app name", "Open %1", kFind->name()));
+ m_kFindButton->setIcon(QIcon::fromTheme(kFind->icon()));
+ } else {
+ m_kFindButton->setText(i18nc("@action:button", "Install KFind…"));
+ m_kFindButton->setIcon(QIcon::fromTheme(QStringLiteral("kfind"), QIcon::fromTheme(QStringLiteral("install"))));
+ }
+}
+
+void Popup::slotKFindButtonClicked()
+{
+ /// Open KFind if it is installed.
+ KService::Ptr kFind = KService::serviceByDesktopName(kFindDesktopName);
+ if (kFind) {
+ auto *job = new KIO::ApplicationLauncherJob(kFind);
+ job->setUrls({m_searchConfiguration->searchPath()});
+ job->start();
+ return;
+ }
+
+ /// Otherwise, install KFind.
+#ifdef Q_OS_WIN
+ QDesktopServices::openUrl(QUrl("https://apps.kde.org/kfind"));
+#else
+ auto packageInstaller = new DolphinPackageInstaller(
+ KFIND_PACKAGE_NAME,
+ QUrl("appstream://org.kde.kfind.desktop"),
+ []() {
+ return KService::serviceByDesktopName(kFindDesktopName);
+ },
+ 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>KFind</application> installed successfully."), KMessageWidget::Positive);
+ updateStateToMatch(m_searchConfiguration); // Updates m_kfindButton from an "Install KFind…" to an "Open KFind" button.
+ }
+ });
+ const auto installationTaskText{i18nc("@info:status", "Installing KFind")};
+ 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
+}