diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,8 @@ DocTools Package Declarative + Activities + ActivitiesStats OPTIONAL_COMPONENTS KHtml QUIET diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -30,6 +30,7 @@ KF5::KIOWidgets KF5::Service KF5::IconThemes + KF5::Activities ) set_target_properties( systemsettingsview PROPERTIES SOVERSION 3 ) diff --git a/core/ModuleView.cpp b/core/ModuleView.cpp --- a/core/ModuleView.cpp +++ b/core/ModuleView.cpp @@ -43,6 +43,8 @@ #include #include +#include + #include "MenuItem.h" class ModuleView::Private { @@ -341,6 +343,13 @@ } // We need to get the state of the now active module stateChanged(); + + KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); + if (activeModule) { + KActivities::ResourceInstance::notifyAccessed(QUrl("kcm:" + activeModule->moduleInfo().service()->storageId()), + "org.kde.systemsettings"); + qWarning()<moduleInfo().service()->storageId()); + } } void ModuleView::stateChanged() diff --git a/sidebar/CMakeLists.txt b/sidebar/CMakeLists.txt --- a/sidebar/CMakeLists.txt +++ b/sidebar/CMakeLists.txt @@ -17,6 +17,7 @@ KF5::XmlGui KF5::Package KF5::Declarative + KF5::ActivitiesStats Qt5::Qml Qt5::Quick Qt5::QuickWidgets diff --git a/sidebar/SidebarMode.h b/sidebar/SidebarMode.h --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -34,6 +34,7 @@ Q_PROPERTY(QAbstractItemModel *categoryModel READ categoryModel CONSTANT) Q_PROPERTY(QAbstractItemModel *subCategoryModel READ subCategoryModel CONSTANT) + Q_PROPERTY(QAbstractItemModel *mostUsedModel READ mostUsedModel CONSTANT) Q_PROPERTY(int activeCategory READ activeCategory WRITE setActiveCategory NOTIFY activeCategoryChanged) Q_PROPERTY(int activeSubCategory READ activeSubCategory WRITE setActiveSubCategory NOTIFY activeSubCategoryChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) @@ -49,6 +50,7 @@ ModuleView * moduleView() const; QAbstractItemModel *categoryModel() const; QAbstractItemModel *subCategoryModel() const; + QAbstractItemModel *mostUsedModel() const; int activeCategory() const; void setActiveCategory(int cat); @@ -61,6 +63,7 @@ Q_INVOKABLE void triggerGlobalAction(const QString &name); Q_INVOKABLE void requestToolTip(int index, const QRectF &rect); Q_INVOKABLE void hideToolTip(); + Q_INVOKABLE void loadMostUsed(int index); protected: QList views() const; diff --git a/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -51,6 +52,15 @@ #include #include +#include +#include +#include + +namespace KAStats = KActivities::Stats; + +using namespace KAStats; +using namespace KAStats::Terms; + K_PLUGIN_FACTORY( SidebarModeFactory, registerPlugin(); ) class SubcategoryModel : public QStandardItemModel @@ -86,6 +96,104 @@ QAbstractItemModel *m_parentModel; }; +class MostUsedModel : public QSortFilterProxyModel +{ +public: + MostUsedModel(QObject *parent = 0) + : QSortFilterProxyModel (parent) + { + sort(0, Qt::DescendingOrder); + setSortRole(ResultModel::ScoreRole); + setDynamicSortFilter(true); + //prepare default items + m_defaultModel = new QStandardItemModel(this); + QStandardItem *item = new QStandardItem(); + item->setData(QUrl(QStringLiteral("kcm:kcm_lookandfeel.desktop")), ResultModel::ResourceRole); + m_defaultModel->appendRow(item); + item = new QStandardItem(); + item->setData(QUrl(QStringLiteral("kcm:user_manager.desktop")), ResultModel::ResourceRole); + m_defaultModel->appendRow(item); + item = new QStandardItem(); + item->setData(QUrl(QStringLiteral("kcm:screenlocker.desktop")), ResultModel::ResourceRole); + m_defaultModel->appendRow(item); + item = new QStandardItem(); + item->setData(QUrl(QStringLiteral("kcm:powerdevilprofilesconfig.desktop")), ResultModel::ResourceRole); + m_defaultModel->appendRow(item); + item = new QStandardItem(); + item->setData(QUrl(QStringLiteral("kcm:kcm_kscreen.desktop")), ResultModel::ResourceRole); + m_defaultModel->appendRow(item); + } + + void setResultModel(ResultModel *model) + { + if (m_resultModel == model) { + return; + } + + auto updateModel = [this]() { + if (m_resultModel->rowCount() >= 5) { + setSourceModel(m_resultModel); + } else { + setSourceModel(m_defaultModel); + } + }; + + m_resultModel = model; + + connect(m_resultModel, &QAbstractItemModel::rowsInserted, this, updateModel); + connect(m_resultModel, &QAbstractItemModel::rowsRemoved, this, updateModel); + + updateModel(); + } + + QHash roleNames() const + { + QHash roleNames; + roleNames.insert(Qt::DisplayRole, "display"); + roleNames.insert(Qt::DecorationRole, "decoration"); + roleNames.insert(ResultModel::ScoreRole, "score"); + return roleNames; + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + MenuItem *mi; + const QModelIndex &mappedIndex = mapToSource(index); + const QString desktopName = sourceModel()->data(mappedIndex, ResultModel::ResourceRole).toUrl().path(); + + if (m_menuItems.contains(desktopName)) { + mi = m_menuItems.value(desktopName); + } else { + mi = new MenuItem(false, nullptr); + const_cast(this)->m_menuItems.insert(desktopName, mi); + } + KService::Ptr service = KService::serviceByStorageId(desktopName); + + if (!service || !service->isValid()) { + return QVariant(); + } + mi->setService(service); + + switch (role) { + case Qt::UserRole: + return QVariant::fromValue(mi); + case Qt::DisplayRole: + return service->name(); + case Qt::DecorationRole: + return service->icon(); + case ResultModel::ScoreRole: + return sourceModel()->data(mappedIndex, ResultModel::ScoreRole).toInt(); + default: + return QVariant(); + } + } + +private: + QHash m_menuItems; + QStandardItemModel *m_defaultModel; + ResultModel *m_resultModel; +}; + class SidebarMode::Private { public: Private() @@ -104,6 +212,7 @@ QQuickWidget * quickWidget; KPackage::Package package; SubcategoryModel * subCategoryModel; + MostUsedModel * mostUsedModel; QWidget * mainWidget; QQuickWidget * placeHolderWidget; QHBoxLayout * mainLayout; @@ -163,6 +272,11 @@ return d->subCategoryModel; } +QAbstractItemModel * SidebarMode::mostUsedModel() const +{ + return d->mostUsedModel; +} + QList SidebarMode::views() const { QList list; @@ -183,6 +297,8 @@ d->proxyModel->setFilterHighlightsEntries( false ); connect( d->proxyModel, &MenuProxyModel::filterRegExpChanged, this, &SidebarMode::activeCategoryChanged ); + d->mostUsedModel = new MostUsedModel( this ); + d->subCategoryModel = new SubcategoryModel( d->proxyModel, this ); d->mainWidget = new QWidget(); d->mainWidget->installEventFilter(this); @@ -219,6 +335,13 @@ d->toolTipManager->hideToolTip(); } +Q_INVOKABLE void SidebarMode::loadMostUsed(int index) +{ + const QModelIndex idx = d->mostUsedModel->index(index, 0); + d->moduleView->closeModules(); + d->moduleView->loadModule( idx ); +} + void SidebarMode::changeModule( const QModelIndex& activeModule ) { d->moduleView->closeModules(); @@ -334,6 +457,8 @@ d->mainLayout->addWidget( d->moduleView ); d->mainLayout->addWidget( d->placeHolderWidget ); emit changeToolBarItems(BaseMode::NoItems); + + d->mostUsedModel->setResultModel(new ResultModel( AllResources | Agent("org.kde.systemsettings") | HighScoredFirst | Limit(5), this)); } bool SidebarMode::eventFilter(QObject* watched, QEvent* event) diff --git a/sidebar/package/contents/ui/introPage.qml b/sidebar/package/contents/ui/IntroIcon.qml copy from sidebar/package/contents/ui/introPage.qml copy to sidebar/package/contents/ui/IntroIcon.qml --- a/sidebar/package/contents/ui/introPage.qml +++ b/sidebar/package/contents/ui/IntroIcon.qml @@ -20,30 +20,30 @@ import QtQuick.Layouts 1.1 import org.kde.kirigami 2.1 as Kirigami -Rectangle { - color: Kirigami.Theme.backgroundColor + +MouseArea { + property alias icon: iconItem.source + property alias text: label.text + property string module + implicitWidth: column.implicitWidth + implicitHeight: column.implicitHeight + cursorShape: Qt.PointingHandCursor + Layout.fillWidth: true + + onClicked: systemsettings.loadMostUsed(index); ColumnLayout { - anchors { - top: parent.top - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - } - Item { - Layout.fillHeight: true - } + id: column + anchors.centerIn: parent Kirigami.Icon { + id: iconItem Layout.alignment: Qt.AlignHCenter - source: "systemsettings" - width: Kirigami.Units.iconSizes.enormous + width: Kirigami.Units.iconSizes.huge height: width - opacity: 0.3 } Kirigami.Label { Layout.alignment: Qt.AlignHCenter - text: i18n("Select an item from the list to see the available options") - } - Item { - Layout.fillHeight: true + id: label } } } + diff --git a/sidebar/package/contents/ui/introPage.qml b/sidebar/package/contents/ui/introPage.qml --- a/sidebar/package/contents/ui/introPage.qml +++ b/sidebar/package/contents/ui/introPage.qml @@ -21,16 +21,14 @@ import org.kde.kirigami 2.1 as Kirigami Rectangle { + id: root color: Kirigami.Theme.backgroundColor ColumnLayout { anchors { - top: parent.top - bottom: parent.bottom + bottom: separator.top + bottomMargin: Kirigami.Units.largeSpacing horizontalCenter: parent.horizontalCenter } - Item { - Layout.fillHeight: true - } Kirigami.Icon { Layout.alignment: Qt.AlignHCenter source: "systemsettings" @@ -40,10 +38,38 @@ } Kirigami.Label { Layout.alignment: Qt.AlignHCenter - text: i18n("Select an item from the list to see the available options") + text: i18n("System settings") } - Item { - Layout.fillHeight: true + } + Kirigami.Separator { + id: separator + anchors.centerIn: parent + width: parent.width * 0.8 + } + ColumnLayout { + anchors { + top: separator.bottom + topMargin: Kirigami.Units.largeSpacing + horizontalCenter: parent.horizontalCenter + } + width: parent.width * 0.8 + Kirigami.Heading { + Layout.alignment: Qt.AlignHCenter + level: 3 + text: i18n("Frequently used:") + } + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + spacing: Kirigami.Units.largeSpacing + + Repeater { + model: systemsettings.mostUsedModel + delegate: IntroIcon { + icon: model.decoration + text: model.display + } + } } } }