diff --git a/CMakeLists.txt b/CMakeLists.txt index a19d3385..d837f1aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,57 +1,59 @@ project(systemsettings) set(PROJECT_VERSION "5.10.90") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.7.0") set(KF5_MIN_VERSION "5.30") remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) find_package(ECM 1.2.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick Qml QuickWidgets ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS ItemViews KCMUtils I18n KIO Service IconThemes WidgetsAddons WindowSystem XmlGui DBusAddons Config DocTools Package Declarative + Activities + ActivitiesStats OPTIONAL_COMPONENTS KHtml QUIET ) find_package(KF5Kirigami2 2.1 REQUIRED) add_subdirectory(core) add_subdirectory(app) add_subdirectory(categories) add_subdirectory(icons) add_subdirectory(sidebar) add_subdirectory(doc) if(KF5KHtml_FOUND) add_subdirectory(classic) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 43a99719..c1f3a1ea 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,39 +1,40 @@ set(systemsettingsview_LIB_SRCS MenuItem.cpp MenuModel.cpp MenuProxyModel.cpp ModuleView.cpp BaseData.cpp BaseMode.cpp ExternalAppModule.cpp ) ki18n_wrap_ui( systemsettingsview_LIB_SRCS externalModule.ui ) set(systemsettingsview_LIB_HDRS ${CMAKE_CURRENT_BINARY_DIR}/systemsettingsview_export.h MenuItem.h MenuModel.h MenuProxyModel.h BaseData.h BaseMode.h ModuleView.h ) add_library( systemsettingsview ${systemsettingsview_LIB_SRCS} ) generate_export_header(systemsettingsview) target_link_libraries( systemsettingsview KF5::ItemViews KF5::KCMUtils KF5::I18n KF5::KIOWidgets KF5::Service KF5::IconThemes + KF5::Activities ) set_target_properties( systemsettingsview PROPERTIES SOVERSION 3 ) install( TARGETS systemsettingsview ${INSTALL_TARGETS_DEFAULT_ARGS} ) install( FILES ${systemsettingsview_LIB_HDRS} DESTINATION ${INCLUDE_INSTALL_DIR}/systemsettingsview COMPONENT Devel ) install( FILES systemsettingsview.desktop systemsettingsexternalapp.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) diff --git a/core/ModuleView.cpp b/core/ModuleView.cpp index 0236ff37..40e39c8d 100644 --- a/core/ModuleView.cpp +++ b/core/ModuleView.cpp @@ -1,415 +1,424 @@ /***************************************************************************** * Copyright (C) 2009 Ben Cooksley * * Copyright (C) 2009 by Mathias Soeken * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *****************************************************************************/ #include "ModuleView.h" #include "ExternalAppModule.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + #include "MenuItem.h" class ModuleView::Private { public: Private() {} QMap mPages; QMap mModules; KPageWidget* mPageWidget; QVBoxLayout* mLayout; QDialogButtonBox* mButtons; KAuth::ObjectDecorator* mApplyAuthorize; QPushButton* mApply; QPushButton* mReset; QPushButton* mDefault; QPushButton* mHelp; bool pageChangeSupressed; }; ModuleView::ModuleView( QWidget * parent ) : QWidget( parent ) , d( new Private() ) { // Configure a layout first d->mLayout = new QVBoxLayout(this); // Create the Page Widget d->mPageWidget = new KPageWidget(this); d->mPageWidget->layout()->setMargin(0); d->mLayout->addWidget(d->mPageWidget); // Create the dialog d->mButtons = new QDialogButtonBox( Qt::Horizontal, this ); d->mLayout->addWidget(d->mButtons); // Create the buttons in it d->mApply = d->mButtons->addButton( QDialogButtonBox::Apply ); KGuiItem::assign(d->mApply, KStandardGuiItem::apply()); d->mDefault = d->mButtons->addButton( QDialogButtonBox::RestoreDefaults ); KGuiItem::assign(d->mDefault, KStandardGuiItem::defaults()); d->mReset = d->mButtons->addButton( QDialogButtonBox::Reset ); KGuiItem::assign(d->mReset, KStandardGuiItem::reset()); d->mHelp = d->mButtons->addButton( QDialogButtonBox::Help ); KGuiItem::assign(d->mHelp, KStandardGuiItem::help()); // Set some more sensible tooltips d->mReset->setToolTip( i18n("Reset all current changes to previous values") ); // Set Auto-Default mode ( KDE Bug #211187 ) d->mApply->setAutoDefault(true); d->mDefault->setAutoDefault(true); d->mReset->setAutoDefault(true); d->mHelp->setAutoDefault(true); // Prevent the buttons from being used d->mApply->setEnabled(false); d->mDefault->setEnabled(false); d->mReset->setEnabled(false); d->mHelp->setEnabled(false); // Connect up the buttons connect( d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()) ); connect( d->mReset, &QAbstractButton::clicked, this, &ModuleView::moduleLoad ); connect( d->mHelp, &QAbstractButton::clicked, this, &ModuleView::moduleHelp ); connect( d->mDefault, &QAbstractButton::clicked, this, &ModuleView::moduleDefaults ); connect( d->mPageWidget, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(activeModuleChanged(KPageWidgetItem*,KPageWidgetItem*)) ); connect( this, &ModuleView::moduleChanged, this, &ModuleView::updateButtons ); d->mApplyAuthorize = new KAuth::ObjectDecorator(d->mApply); d->mApplyAuthorize->setAuthAction( KAuth::Action() ); } ModuleView::~ModuleView() { delete d; } KCModuleInfo * ModuleView::activeModule() const { return d->mModules.value( d->mPageWidget->currentPage() ); } const KAboutData * ModuleView::aboutData() const { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); KAboutData * aboutData = 0; if( activeModule ) { aboutData = const_cast( activeModule->aboutData() ); } if ( aboutData ) { aboutData->setProgramIconName( activeModule->moduleInfo().service()->icon() ); return aboutData; } return 0; } void ModuleView::loadModule( QModelIndex menuItem ) { if ( !menuItem.isValid() ) { return; } QList indexes; for ( int done = 0; menuItem.model()->rowCount( menuItem ) > done; done = 1 + done ) { indexes << menuItem.model()->index( done, 0, menuItem ); } if ( indexes.empty() ) { indexes << menuItem; } foreach ( QModelIndex module, indexes ) { MenuItem *menuItem = module.data( Qt::UserRole ).value(); addModule( &menuItem->item() ); } // changing state is not needed here as the adding / changing of pages does it } void ModuleView::addModule( KCModuleInfo *module ) { if( !module ) { return; } if( !module->service() ) { qWarning() << "ModuleInfo has no associated KService" ; return; } if ( !KAuthorized::authorizeControlModule( module->service()->menuId() ) ) { qWarning() << "Not authorised to load module" ; return; } if( module->service()->noDisplay() ) { return; } // Create the scroller QScrollArea * moduleScroll = new QScrollArea( this ); // Prepare the scroll area moduleScroll->setWidgetResizable( true ); moduleScroll->setFrameStyle( QFrame::NoFrame ); moduleScroll->viewport()->setAutoFillBackground( false ); // Create the page KPageWidgetItem *page = new KPageWidgetItem( moduleScroll, module->moduleName() ); // Provide information to the users if( module->service()->hasServiceType("SystemSettingsExternalApp") || // Is it an external app? module->service()->substituteUid() ) { // ...or does it require UID substituion? QWidget * externalWidget = new ExternalAppModule( this, module ); moduleScroll->setWidget( externalWidget ); } else { // It must be a normal module then KCModuleProxy * moduleProxy = new KCModuleProxy( *module, moduleScroll ); moduleScroll->setWidget( moduleProxy ); moduleProxy->setAutoFillBackground( false ); connect( moduleProxy, SIGNAL(changed(bool)), this, SLOT(stateChanged())); d->mPages.insert( page, moduleProxy ); } d->mModules.insert( page, module ); updatePageIconHeader( page, true ); // Add the new page d->mPageWidget->addPage( page ); } void ModuleView::updatePageIconHeader( KPageWidgetItem * page, bool light ) { if( !page ) { // Page is invalid. Probably means we have a race condition during closure of everyone so do nothing return; } KCModuleProxy * moduleProxy = d->mPages.value( page ); KCModuleInfo * moduleInfo = d->mModules.value( page ); if( !moduleInfo ) { // Seems like we have some form of a race condition going on here... return; } page->setHeader( moduleInfo->comment() ); page->setIcon( QIcon::fromTheme( moduleInfo->icon() ) ); if( light ) { return; } if( moduleProxy && moduleProxy->realModule()->useRootOnlyMessage() ) { page->setHeader( "" + moduleInfo->comment() + "
" + moduleProxy->realModule()->rootOnlyMessage() + "" ); page->setIcon( KDE::icon( moduleInfo->icon(), QStringList() << "dialog-warning" ) ); } } bool ModuleView::resolveChanges() { KCModuleProxy * currentProxy = d->mPages.value( d->mPageWidget->currentPage() ); return resolveChanges(currentProxy); } bool ModuleView::resolveChanges(KCModuleProxy * currentProxy) { if( !currentProxy || !currentProxy->changed() ) { return true; } // Let the user decide const int queryUser = KMessageBox::warningYesNoCancel( this, i18n("The settings of the current module have changed.\n" "Do you want to apply the changes or discard them?"), i18n("Apply Settings"), KStandardGuiItem::apply(), KStandardGuiItem::discard(), KStandardGuiItem::cancel() ); switch (queryUser) { case KMessageBox::Yes: return moduleSave(currentProxy); case KMessageBox::No: currentProxy->load(); return true; case KMessageBox::Cancel: return false; default: Q_ASSERT(false); return false; } } void ModuleView::closeModules() { d->pageChangeSupressed = true; d->mApplyAuthorize->setAuthAction( KAuth::Action() ); // Ensure KAuth knows that authentication is now pointless... QMap::iterator page = d->mModules.begin(); QMap::iterator pageEnd = d->mModules.end(); for ( ; page != pageEnd; ++page ) { d->mPageWidget->removePage( page.key() ); } d->mPages.clear(); d->mModules.clear(); d->pageChangeSupressed = false; } bool ModuleView::moduleSave() { KCModuleProxy * moduleProxy = d->mPages.value( d->mPageWidget->currentPage() ); return moduleSave( moduleProxy ); } bool ModuleView::moduleSave(KCModuleProxy *module) { if( !module ) { return false; } module->save(); return true; } void ModuleView::moduleLoad() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); if( activeModule ) { activeModule->load(); } } void ModuleView::moduleDefaults() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); if( activeModule ) { activeModule->defaults(); } } void ModuleView::moduleHelp() { KCModuleInfo * activeModule = d->mModules.value( d->mPageWidget->currentPage() ); if( !activeModule ) { return; } QString docPath = activeModule->docPath(); if( docPath.isEmpty() ) { return; } QUrl url( "help:/"+docPath ); QProcess::startDetached("khelpcenter", QStringList() << url.url()); } void ModuleView::activeModuleChanged(KPageWidgetItem * current, KPageWidgetItem * previous) { d->mPageWidget->blockSignals(true); d->mPageWidget->setCurrentPage(previous); KCModuleProxy * previousModule = d->mPages.value(previous); if( resolveChanges(previousModule) ) { d->mPageWidget->setCurrentPage(current); } d->mPageWidget->blockSignals(false); if( d->pageChangeSupressed ) { return; } // 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() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); KAuth::Action moduleAction; bool change = false; if( activeModule ) { change = activeModule->changed(); disconnect( d->mApplyAuthorize, SIGNAL(authorized(KAuth::Action)), this, SLOT(moduleSave()) ); disconnect( d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()) ); if( activeModule->realModule()->authAction().isValid() ) { connect( d->mApplyAuthorize, SIGNAL(authorized(KAuth::Action)), this, SLOT(moduleSave()) ); moduleAction = activeModule->realModule()->authAction(); } else { connect( d->mApply, SIGNAL(clicked()), this, SLOT(moduleSave()) ); } } updatePageIconHeader( d->mPageWidget->currentPage() ); d->mApplyAuthorize->setAuthAction( moduleAction ); d->mApply->setEnabled( change ); d->mReset->setEnabled( change ); emit moduleChanged( change ); } void ModuleView::keyPressEvent ( QKeyEvent * event ) { if ( event->key() == Qt::Key_F1 && d->mHelp->isVisible() && d->mHelp->isEnabled()) { d->mHelp->animateClick(); event->accept(); return; } else if ( event->key() == Qt::Key_Escape ) { event->accept(); emit closeRequest(); return; } else if ( event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier ) { QWhatsThis::enterWhatsThisMode(); event->accept(); return; } QWidget::keyPressEvent( event ); } void ModuleView::updateButtons() { KCModuleProxy * activeModule = d->mPages.value( d->mPageWidget->currentPage() ); if( !activeModule ) { return; } const int buttons = activeModule->buttons(); d->mApply->setVisible(buttons & KCModule::Apply ); d->mReset->setVisible(buttons & KCModule::Apply ); d->mHelp->setEnabled(buttons & KCModule::Help ); d->mDefault->setEnabled(buttons & KCModule::Default ); } void ModuleView::setFaceType(KPageView::FaceType type) { d->mPageWidget->setFaceType(type); } KPageView::FaceType ModuleView::faceType() const { return d->mPageWidget->faceType(); } diff --git a/sidebar/CMakeLists.txt b/sidebar/CMakeLists.txt index f3e5dcd1..b76b99d8 100644 --- a/sidebar/CMakeLists.txt +++ b/sidebar/CMakeLists.txt @@ -1,28 +1,29 @@ set( sidebar_mode_srcs SidebarMode.cpp CategoryDrawer.cpp CategorizedView.cpp SidebarDelegate.cpp ToolTips/tooltipmanager.cpp ) add_library(systemsettings_sidebar_mode MODULE ${sidebar_mode_srcs}) target_link_libraries(systemsettings_sidebar_mode systemsettingsview KF5::ItemViews KF5::KCMUtils KF5::I18n KF5::KIOWidgets KF5::Service KF5::XmlGui KF5::Package KF5::Declarative + KF5::ActivitiesStats Qt5::Qml Qt5::Quick Qt5::QuickWidgets ) install( TARGETS systemsettings_sidebar_mode DESTINATION ${PLUGIN_INSTALL_DIR} ) install( FILES settings-sidebar-view.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install(DIRECTORY package/ DESTINATION ${KDE_INSTALL_DATAROOTDIR}/kpackage/genericqml/org.kde.systemsettings.sidebar) diff --git a/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp index 78d77658..2ed346d9 100644 --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -1,360 +1,486 @@ /************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301, USA. * ***************************************************************************/ #include "SidebarMode.h" #include "CategoryDrawer.h" #include "CategorizedView.h" #include "MenuItem.h" #include "MenuModel.h" #include "ModuleView.h" #include "MenuProxyModel.h" #include "BaseData.h" #include "SidebarDelegate.h" #include "ToolTips/tooltipmanager.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 { public: SubcategoryModel(QAbstractItemModel *parentModel, QObject *parent = 0) : QStandardItemModel(parent), m_parentModel(parentModel) {} void setParentIndex(const QModelIndex &activeModule) { blockSignals(true); //make the view receive a single signal when the new subcategory is loaded, //never make the view believe there are zero items if this is not the final count //this avoids the brief flash it had clear(); const int subRows = m_parentModel->rowCount(activeModule); if ( subRows > 1) { for (int i = 0; i < subRows; ++i) { const QModelIndex& index = m_parentModel->index(i, 0, activeModule); QStandardItem *item = new QStandardItem(m_parentModel->data(index, Qt::DecorationRole).value(), m_parentModel->data(index, Qt::DisplayRole).toString()); item->setData(index.data(Qt::UserRole), Qt::UserRole); appendRow(item); } } blockSignals(false); beginResetModel(); endResetModel(); } private: 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 mi->service()->name(); + case Qt::DecorationRole: + return mi->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() : quickWidget( nullptr ), moduleView( nullptr ), collection( nullptr ), activeCategory( -1 ), activeSubCategory( -1 ) {} virtual ~Private() { delete aboutIcon; } ToolTipManager *toolTipManager; QQuickWidget * quickWidget; KPackage::Package package; SubcategoryModel * subCategoryModel; + MostUsedModel * mostUsedModel; QWidget * mainWidget; QQuickWidget * placeHolderWidget; QHBoxLayout * mainLayout; KDeclarative::KDeclarative kdeclarative; MenuProxyModel * proxyModel; KAboutData * aboutIcon; ModuleView * moduleView; KActionCollection *collection; int activeCategory; int activeSubCategory; }; SidebarMode::SidebarMode( QObject *parent, const QVariantList& ) : BaseMode( parent ) , d( new Private() ) { qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); d->aboutIcon = new KAboutData( "SidebarView", i18n( "Sidebar View" ), "1.0", i18n( "Provides a categorized sidebar for control modules." ), KAboutLicense::GPL, i18n( "(c) 2017, Marco Martin" ) ); d->aboutIcon->addAuthor( i18n( "Marco Martin" ), i18n( "Author" ), "mart@kde.org" ); d->aboutIcon->addAuthor( i18n( "Ben Cooksley" ), i18n( "Author" ), "bcooksley@kde.org" ); d->aboutIcon->addAuthor( i18n( "Mathias Soeken" ), i18n( "Developer" ), "msoeken@informatik.uni-bremen.de" ); d->aboutIcon->setProgramIconName( "view-sidetree" ); } SidebarMode::~SidebarMode() { delete d; } KAboutData * SidebarMode::aboutData() { return d->aboutIcon; } ModuleView * SidebarMode::moduleView() const { return d->moduleView; } QWidget * SidebarMode::mainWidget() { if( !d->quickWidget ) { initWidget(); } return d->mainWidget; } QAbstractItemModel * SidebarMode::categoryModel() const { return d->proxyModel; } QAbstractItemModel * SidebarMode::subCategoryModel() const { return d->subCategoryModel; } +QAbstractItemModel * SidebarMode::mostUsedModel() const +{ + return d->mostUsedModel; +} + QList SidebarMode::views() const { QList list; //list.append( d->categoryView ); return list; } void SidebarMode::initEvent() { MenuModel * model = new MenuModel( rootItem(), this ); foreach( MenuItem * child, rootItem()->children() ) { model->addException( child ); } d->proxyModel = new MenuProxyModel( this ); d->proxyModel->setCategorizedModel( true ); d->proxyModel->setSourceModel( model ); 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); d->mainLayout = new QHBoxLayout(d->mainWidget); d->mainLayout->setContentsMargins(0, 0, 0, 0); d->moduleView = new ModuleView( d->mainWidget ); connect( d->moduleView, &ModuleView::moduleChanged, this, &SidebarMode::moduleLoaded ); connect( d->moduleView, &ModuleView::closeRequest, this, &SidebarMode::leaveModuleView ); d->quickWidget = 0; moduleView()->setFaceType(KPageView::Plain); } void SidebarMode::triggerGlobalAction(const QString &name) { if (!d->collection) { return; } QAction *action = d->collection->action(name); if (action) { action->trigger(); } } void SidebarMode::requestToolTip(int index, const QRectF &rect) { if (showToolTips()) { d->toolTipManager->requestToolTip(d->proxyModel->index(index, 0), rect.toRect()); } } void SidebarMode::hideToolTip() { 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(); const int subRows = d->proxyModel->rowCount(activeModule); if ( subRows < 2) { d->moduleView->loadModule( activeModule ); } else { d->moduleView->loadModule( d->proxyModel->index(0, 0, activeModule) ); } d->subCategoryModel->setParentIndex( activeModule ); } void SidebarMode::moduleLoaded() { d->placeHolderWidget->hide(); d->moduleView->show(); emit changeToolBarItems(BaseMode::NoItems); } int SidebarMode::activeCategory() const { return d->proxyModel->mapFromSource(d->proxyModel->sourceModel()->index(d->activeCategory, 0)).row(); } void SidebarMode::setActiveCategory(int cat) { if (d->activeCategory == cat) { return; } const QModelIndex idx = d->proxyModel->index(cat, 0); d->activeCategory = d->proxyModel->mapToSource(idx).row(); changeModule(idx); d->activeSubCategory = 0; emit activeCategoryChanged(); emit activeSubCategoryChanged(); } int SidebarMode::activeSubCategory() const { return d->activeSubCategory; } void SidebarMode::setActiveSubCategory(int cat) { if (d->activeSubCategory == cat) { return; } d->activeSubCategory = cat; d->moduleView->closeModules(); d->moduleView->loadModule( d->subCategoryModel->index(cat, 0) ); emit activeSubCategoryChanged(); } int SidebarMode::width() const { return d->mainWidget->width(); } void SidebarMode::initWidget() { // Create the widgets if (!KMainWindow::memberList().isEmpty()) { KXmlGuiWindow *mainWindow = qobject_cast(KMainWindow::memberList().first()); if (mainWindow) { d->collection = mainWindow->actionCollection(); } } d->quickWidget = new QQuickWidget(d->mainWidget); d->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); d->quickWidget->engine()->rootContext()->setContextProperty("systemsettings", this); d->package = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML"); d->package.setPath(QStringLiteral("org.kde.systemsettings.sidebar")); d->kdeclarative.setDeclarativeEngine(d->quickWidget->engine()); d->kdeclarative.setupBindings(); d->quickWidget->setSource(d->package.filePath("mainscript")); const int rootImplicitWidth = d->quickWidget->rootObject()->property("implicitWidth").toInt(); if (rootImplicitWidth != 0) { d->quickWidget->setFixedWidth(rootImplicitWidth); } else { d->quickWidget->setFixedWidth(240); } connect(d->quickWidget->rootObject(), &QQuickItem::implicitWidthChanged, this, [this]() { const int rootImplicitWidth = d->quickWidget->rootObject()->property("implicitWidth").toInt(); if (rootImplicitWidth != 0) { d->quickWidget->setFixedWidth(rootImplicitWidth); } else { d->quickWidget->setFixedWidth(240); } }); d->quickWidget->installEventFilter(this); d->toolTipManager = new ToolTipManager(d->proxyModel, d->quickWidget); d->placeHolderWidget = new QQuickWidget(d->mainWidget); d->placeHolderWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); d->placeHolderWidget->engine()->rootContext()->setContextObject(new KLocalizedContext(d->placeHolderWidget)); d->placeHolderWidget->engine()->rootContext()->setContextProperty("systemsettings", this); d->placeHolderWidget->setSource(d->package.filePath("ui", "introPage.qml")); d->mainLayout->addWidget( d->quickWidget ); d->moduleView->hide(); 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) { //TODO: patch Qt if (watched == d->quickWidget && event->type() == QEvent::Leave) { QCoreApplication::sendEvent(d->quickWidget->quickWindow(), event); } else if (watched == d->mainWidget && event->type() == QEvent::Resize) { emit widthChanged(); } return BaseMode::eventFilter(watched, event); } void SidebarMode::leaveModuleView() { d->moduleView->closeModules(); // We have to force it here } void SidebarMode::giveFocus() { d->quickWidget->setFocus(); } #include "SidebarMode.moc" diff --git a/sidebar/SidebarMode.h b/sidebar/SidebarMode.h index 4422f577..bda0b424 100644 --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -1,84 +1,87 @@ /*************************************************************************** * Copyright (C) 2009 by Ben Cooksley * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * ***************************************************************************/ #ifndef SIDEBARMODE_H #define SIDEBARMODE_H #include "BaseMode.h" class ModuleView; class KAboutData; class QModelIndex; class QAbstractItemView; class QAbstractItemModel; class SidebarMode : public BaseMode { Q_OBJECT 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) public: SidebarMode(QObject * parent, const QVariantList& ); ~SidebarMode(); QWidget * mainWidget() Q_DECL_OVERRIDE; void initEvent() Q_DECL_OVERRIDE; void giveFocus() Q_DECL_OVERRIDE; void leaveModuleView() Q_DECL_OVERRIDE; KAboutData * aboutData() Q_DECL_OVERRIDE; ModuleView * moduleView() const Q_DECL_OVERRIDE; QAbstractItemModel *categoryModel() const; QAbstractItemModel *subCategoryModel() const; + QAbstractItemModel *mostUsedModel() const; int activeCategory() const; void setActiveCategory(int cat); int activeSubCategory() const; void setActiveSubCategory(int cat); int width() const; 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 Q_DECL_OVERRIDE; bool eventFilter(QObject* watched, QEvent* event) Q_DECL_OVERRIDE; private Q_SLOTS: void changeModule( const QModelIndex& activeModule ); void moduleLoaded(); void initWidget(); Q_SIGNALS: void activeCategoryChanged(); void activeSubCategoryChanged(); void widthChanged(); private: class Private; Private *const d; }; #endif diff --git a/sidebar/package/contents/ui/introPage.qml b/sidebar/package/contents/ui/IntroIcon.qml similarity index 67% copy from sidebar/package/contents/ui/introPage.qml copy to sidebar/package/contents/ui/IntroIcon.qml index 814b5ab1..3cb2f79f 100644 --- a/sidebar/package/contents/ui/introPage.qml +++ b/sidebar/package/contents/ui/IntroIcon.qml @@ -1,49 +1,49 @@ /* Copyright (c) 2017 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 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 index 814b5ab1..d10c7d6d 100644 --- a/sidebar/package/contents/ui/introPage.qml +++ b/sidebar/package/contents/ui/introPage.qml @@ -1,49 +1,75 @@ /* Copyright (c) 2017 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import QtQuick.Layouts 1.1 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" width: Kirigami.Units.iconSizes.enormous height: width opacity: 0.3 } 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 + } + } } } }