diff --git a/app/SettingsBase.cpp b/app/SettingsBase.cpp index d9aac4fb..a835a53f 100644 --- a/app/SettingsBase.cpp +++ b/app/SettingsBase.cpp @@ -1,423 +1,423 @@ /*************************************************************************** * 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 "SettingsBase.h" #include "BaseConfig.h" #include "systemsettings_app_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BaseData.h" #include "ModuleView.h" SettingsBase::SettingsBase( QWidget * parent ) : KXmlGuiWindow(parent) { // Ensure delayed loading doesn't cause a crash activeView = nullptr; aboutDialog = nullptr; configDialog = nullptr; lostFound = nullptr; // Prepare the view area stackedWidget = new QStackedWidget( this ); setWindowTitle(i18n("System Settings")); setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-system"))); setCentralWidget(stackedWidget); setWindowFlags( windowFlags() | Qt::WindowContextHelpButtonHint ); // Initialise search searchText = new KLineEdit( this ); searchText->setClearButtonShown( true ); searchText->setPlaceholderText( i18nc( "Search through a list of control modules", "Search" ) ); searchText->setCompletionMode( KCompletion::CompletionPopup ); spacerWidget = new QWidget( this ); spacerWidget->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Maximum ); - // Initalise the window so we don't flicker + // Initialise the window so we don't flicker initToolBar(); // We can now launch the delayed loading safely QTimer::singleShot(0, this, &SettingsBase::initApplication); } SettingsBase::~SettingsBase() { delete rootModule; } QSize SettingsBase::sizeHint() const { // on smaller or portrait-rotated screens, do not max out height and/or width const QSize screenSize = (QGuiApplication::primaryScreen()->availableSize()*0.9); const QSize targetSize = QSize(1024, 700); return targetSize.boundedTo(screenSize); } void SettingsBase::initApplication() { // Prepare the menu of all modules categories = KServiceTypeTrader::self()->query(QStringLiteral("SystemSettingsCategory")); modules = KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QStringLiteral("[X-KDE-System-Settings-Parent-Category] != ''")); modules += KServiceTypeTrader::self()->query(QStringLiteral("SystemSettingsExternalApp")); rootModule = new MenuItem( true, nullptr ); initMenuList(rootModule); // Handle lost+found modules... if (lostFound) { for (int i = 0; i < modules.size(); ++i) { const KService::Ptr entry = modules.at(i); MenuItem * infoItem = new MenuItem(false, lostFound); infoItem->setService( entry ); qCDebug(SYSTEMSETTINGS_APP_LOG) << "Added " << entry->name(); } } // Prepare the Base Data BaseData::instance()->setMenuItem( rootModule ); // Load all possible views const KService::List pluginObjects = KServiceTypeTrader::self()->query( QStringLiteral("SystemSettingsView") ); const int nbPlugins = pluginObjects.count(); for( int pluginsDone = 0; pluginsDone < nbPlugins ; ++pluginsDone ) { KService::Ptr activeService = pluginObjects.at( pluginsDone ); QString error; BaseMode * controller = activeService->createInstance(this, QVariantList(), &error); if( error.isEmpty() ) { possibleViews.insert( activeService->library(), controller ); controller->init( activeService ); connect(controller, &BaseMode::changeToolBarItems, this, &SettingsBase::changeToolBar); connect(controller, &BaseMode::actionsChanged, this, &SettingsBase::updateViewActions); connect(searchText, &KLineEdit::textChanged, controller, &BaseMode::searchChanged); connect(controller, &BaseMode::viewChanged, this, &SettingsBase::viewChange); } else { qCWarning(SYSTEMSETTINGS_APP_LOG) << QStringLiteral("View load error: ") + error; } } searchText->completionObject()->setIgnoreCase( true ); searchText->completionObject()->setItems( BaseData::instance()->menuItem()->keywords() ); changePlugin(); // enforce minimum window size setMinimumSize(SettingsBase::sizeHint()); activateWindow(); } void SettingsBase::initToolBar() { // Fill the toolbar with default actions // Exit is the very last action quitAction = actionCollection()->addAction( KStandardAction::Quit, QStringLiteral("quit_action"), this, SLOT(close()) ); // Configure goes at the end configureAction = actionCollection()->addAction( KStandardAction::Preferences, QStringLiteral("configure"), this, SLOT(configShow()) ); actionCollection()->setDefaultShortcut(configureAction, QKeySequence(Qt::CTRL + Qt::Key_M)); configureAction->setText( i18n("Configure") ); // Help after it initHelpMenu(); configureAction->setIcon(QIcon::fromTheme(QStringLiteral("settings-configure"))); // Then a spacer so the search line-edit is kept separate spacerAction = new QWidgetAction( this ); spacerAction->setDefaultWidget(spacerWidget); actionCollection()->addAction( QStringLiteral("spacer"), spacerAction ); // Finally the search line-edit searchAction = new QWidgetAction( this ); searchAction->setDefaultWidget(searchText); actionCollection()->setDefaultShortcut(searchAction, QKeySequence(Qt::CTRL + Qt::Key_F)); connect( searchAction, SIGNAL(triggered(bool)), searchText, SLOT(setFocus())); actionCollection()->addAction( QStringLiteral("searchText"), searchAction ); // Initialise the Window setupGUI(Save|Create,QString()); menuBar()->hide(); // Toolbar & Configuration helpActionMenu->setMenu( dynamic_cast( factory()->container(QStringLiteral("help"), this) ) ); toolBar()->setMovable(false); // We don't allow any changes changeToolBar( BaseMode::Search | BaseMode::Configure ); } void SettingsBase::initHelpMenu() { helpActionMenu = new KActionMenu( QIcon::fromTheme(QStringLiteral("help-contents")), i18n("Help"), this ); helpActionMenu->setDelayed( false ); actionCollection()->addAction( QStringLiteral("help_toolbar_menu"), helpActionMenu ); // Add the custom actions aboutModuleAction = actionCollection()->addAction( KStandardAction::AboutApp, QStringLiteral("help_about_module"), this, SLOT(about()) ); changeAboutMenu( nullptr, aboutModuleAction, i18n("About Active Module") ); aboutViewAction = actionCollection()->addAction( KStandardAction::AboutApp, QStringLiteral("help_about_view"), this, SLOT(about()) ); } void SettingsBase::initConfig() { // Prepare dialog first configDialog = new KConfigDialog( this, QStringLiteral("systemsettingsconfig"), BaseConfig::self() ); configDialog->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); // Add our page QWidget * configPage = new QWidget( configDialog ); configWidget.setupUi(configPage); QString iconName = KAboutData::applicationData().programIconName(); configDialog->addPage( configPage, i18nc("General config for System Settings", "General"), iconName ); QVBoxLayout * configLayout = new QVBoxLayout; // Get the list of modules foreach( BaseMode * mode, possibleViews ) { mode->addConfiguration( configDialog ); QRadioButton * radioButton = new QRadioButton( mode->service()->name(), configWidget.GbViewStyle ); radioButton->setIcon( QIcon::fromTheme(mode->service()->icon()) ); configLayout->addWidget( radioButton ); viewSelection.addButton( radioButton, possibleViews.values().indexOf(mode) ); } configWidget.GbViewStyle->setLayout( configLayout ); configWidget.GbViewStyle->setVisible( possibleViews.count() > 1 ); KWindowConfig::restoreWindowSize(configDialog->windowHandle(), KSharedConfig::openConfig()->group("ConfigDialog")); connect(configDialog, &KConfigDialog::accepted, this, &SettingsBase::configUpdated); } void SettingsBase::initMenuList(MenuItem * parent) { // look for any categories inside this level, and recurse into them for (int i = 0; i < categories.size(); ++i) { const KService::Ptr entry = categories.at(i); const QString parentCategory = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category")).toString(); const QString parentCategory2 = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category-V2")).toString(); if ( parentCategory == parent->category() || // V2 entries must not be empty if they want to become a proper category. ( !parentCategory2.isEmpty() && parentCategory2 == parent->category() ) ) { MenuItem * menuItem = new MenuItem(true, parent); menuItem->setService( entry ); if( menuItem->category() == QLatin1String("lost-and-found") ) { lostFound = menuItem; continue; } initMenuList( menuItem ); } } KService::List removeList; // scan for any modules at this level and add them for (int i = 0; i < modules.size(); ++i) { const KService::Ptr entry = modules.at(i); const QString category = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category")).toString(); const QString category2 = entry->property(QStringLiteral("X-KDE-System-Settings-Parent-Category-V2")).toString(); if( !parent->category().isEmpty() && (category == parent->category() || category2 == parent->category()) ) { if (!entry->noDisplay() ) { // Add the module info to the menu MenuItem * infoItem = new MenuItem(false, parent); infoItem->setService( entry ); } removeList.append( modules.at(i) ); } } for (int i = 0; i < removeList.size(); ++i) { modules.removeOne( removeList.at(i) ); } parent->sortChildrenByWeight(); } void SettingsBase::configUpdated() { KConfigGroup dialogConfig = KSharedConfig::openConfig()->group("ConfigDialog"); KWindowConfig::saveWindowSize(configDialog->windowHandle(), dialogConfig); BaseConfig::setActiveView( possibleViews.keys().at(viewSelection.checkedId()) ); BaseConfig::setShowToolTips( configWidget.ChTooltips->isChecked() ); activeView->setShowToolTips( configWidget.ChTooltips->isChecked() ); activeView->saveConfiguration(); changePlugin(); } void SettingsBase::configShow() { // Initialise the configuration dialog if it hasn't already if( !configDialog ) { initConfig(); } if( activeView && activeView->moduleView() && !activeView->moduleView()->resolveChanges() ) { return; // It shouldn't be triggering anyway, since the action is disabled } activeView->loadConfiguration(); const QStringList pluginList = possibleViews.keys(); const int configIndex = pluginList.indexOf( BaseConfig::activeView() ); if( configIndex != -1 ) { viewSelection.button( configIndex )->setChecked(true); } configWidget.ChTooltips->setChecked( BaseConfig::showToolTips() ); if( pluginList.isEmpty() ) { KMessageBox::error(this, i18n("System Settings was unable to find any views, and hence nothing is available to configure."), i18n("No views found")); } else { configDialog->show(); } } bool SettingsBase::queryClose() { bool changes = true; if( activeView ) { activeView->saveState(); changes = activeView->moduleView()->resolveChanges(); } BaseConfig::self()->save(); return changes; } void SettingsBase::about() { delete aboutDialog; aboutDialog = nullptr; const KAboutData * about = nullptr; if( sender() == aboutViewAction ) { about = activeView->aboutData(); } else if( sender() == aboutModuleAction && activeView->moduleView() ) { about = activeView->moduleView()->aboutData(); } if( about ) { aboutDialog = new KAboutApplicationDialog(*about, nullptr); aboutDialog->show(); } } void SettingsBase::changePlugin() { if( possibleViews.count() == 0 ) { // We should ensure we have a plugin available to choose KMessageBox::error(this, i18n("System Settings was unable to find any views, and hence has nothing to display."), i18n("No views found")); close(); return; // Halt now! } if( activeView ) { activeView->saveState(); activeView->leaveModuleView(); } const QString viewToUse = BaseConfig::activeView(); if( possibleViews.keys().contains(viewToUse) ) { // First the configuration entry activeView = possibleViews.value(viewToUse); } else { // Otherwise we activate the failsafe activeView = possibleViews.begin().value(); } if( stackedWidget->indexOf(activeView->mainWidget()) == -1 ) { stackedWidget->addWidget(activeView->mainWidget()); } // Handle the tooltips qDeleteAll( tooltipManagers ); tooltipManagers.clear(); if ( BaseConfig::showToolTips() ) { QList theViews = activeView->views(); foreach ( QAbstractItemView* view, theViews ) { tooltipManagers << new ToolTipManager( view ); } } activeView->setShowToolTips( BaseConfig::showToolTips() ); changeAboutMenu( activeView->aboutData(), aboutViewAction, i18n("About Active View") ); viewChange(false); stackedWidget->setCurrentWidget(activeView->mainWidget()); updateViewActions(); activeView->giveFocus(); } void SettingsBase::viewChange(bool state) { KCModuleInfo * moduleInfo = activeView->moduleView()->activeModule(); configureAction->setDisabled(state); if( moduleInfo ) { setCaption( moduleInfo->moduleName(), state ); } else { setCaption( QString(), state ); } changeAboutMenu( activeView->moduleView()->aboutData(), aboutModuleAction, i18n("About Active Module") ); } void SettingsBase::updateViewActions() { guiFactory()->unplugActionList( this, QStringLiteral("viewActions") ); guiFactory()->plugActionList( this, QStringLiteral("viewActions"), activeView->actionsList() ); } void SettingsBase::changeToolBar( BaseMode::ToolBarItems toolbar ) { if( sender() != activeView ) { return; } guiFactory()->unplugActionList( this, QStringLiteral("configure") ); guiFactory()->unplugActionList( this, QStringLiteral("search") ); guiFactory()->unplugActionList( this, QStringLiteral("quit") ); if ( BaseMode::Search & toolbar ) { QList searchBarActions; searchBarActions << spacerAction << searchAction; guiFactory()->plugActionList( this, QStringLiteral("search"), searchBarActions ); } if ( BaseMode::Configure & toolbar ) { QList configureBarActions; configureBarActions << configureAction; guiFactory()->plugActionList( this, QStringLiteral("configure"), configureBarActions ); } if ( BaseMode::Quit & toolbar ) { QList quitBarActions; quitBarActions << quitAction; guiFactory()->plugActionList( this, QStringLiteral("quit"), quitBarActions ); } toolBar()->setVisible(toolbar != BaseMode::NoItems || (activeView && activeView->actionsList().count() > 0)); } void SettingsBase::changeAboutMenu( const KAboutData * menuAbout, QAction * menuItem, QString fallback ) { if( !menuItem ) { return; } if( menuAbout ) { menuItem->setText( i18n( "About %1", menuAbout->displayName() ) ); menuItem->setIcon( QIcon::fromTheme( menuAbout->programIconName() ) ); menuItem->setEnabled(true); } else { menuItem->setText( fallback ); menuItem->setIcon( QIcon::fromTheme( KAboutData::applicationData().programIconName() ) ); menuItem->setEnabled(false); } } diff --git a/core/BaseMode.h b/core/BaseMode.h index 7f04c691..c85552f4 100644 --- a/core/BaseMode.h +++ b/core/BaseMode.h @@ -1,257 +1,257 @@ /***************************************************************************** * Copyright (C) 2009 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 BASEMODE_H #define BASEMODE_H #include #include "systemsettingsview_export.h" #include class QAction; class MenuItem; class ModuleView; class KAboutData; class KConfigDialog; class KConfigGroup; class QAbstractItemView; template class QList; /** * @brief Provides a interface for System Settings views * * BaseMode is a standard interface for all plugins to System Settings to allow them to provide * their own interface to KDE control modules.\n * * The developer need only ensure that they perform all initialization of their plugin in * initEvent() to ensure that the plugin is displayed, and initial actions are loaded. * * @author Ben Cooksley * @author Mathias Soeken */ class SYSTEMSETTINGSVIEW_EXPORT BaseMode : public QObject { Q_OBJECT /** - * System Settings main application is allowed privilaged access to handle tooltips + * System Settings main application is allowed priviledged access to handle tooltips */ friend class SettingsBase; public: /** * Constructs a BaseMode for use in System Settings.\n * Plugin developers should perform all initialisation in initEvent() not here. * * @param parent The parent of this BaseMode. */ explicit BaseMode( QObject * parent ); /** * Normal destructor. Plugin developers only need destroy what they created * not what is provided by BaseMode itself. */ ~BaseMode() override; /** * These flags are used to control the presence of the Search and Configure actions on the toolbar */ enum ToolBarItemsFlags { NoItems = 0x1, /**< The Toolbar will not have any items added by System Settings */ Search = 0x2, /**< The Toolbar will have the search bar added by System Settings */ Configure = 0x4, /**< The Toolbar will have configure added by System Settings */ Quit = 0x8 /**< The toolbar will have exit added by System Settings */ }; Q_DECLARE_FLAGS(ToolBarItems, ToolBarItemsFlags) /** * Performs internal setup.\n * Plugin developers should perform initialisation in initEvent() not here. * * @param modeService Plugins service object, used for providing extra information to System Settings. */ void init( const KService::Ptr modeService ); /** * Prepares the BaseMode for use.\n * Plugin developers should perform initialisation here, creating the Models. They should perform widget * initialisation the first time mainWidget() is called, not here. */ virtual void initEvent(); /** * Returns the widget to be displayed in the center of System Settings.\n * The widget should be created the first time this function is called. * * @warning This function is called multiple times, ensure the widget is only created once. * @returns The main widget of the plugin. */ virtual QWidget * mainWidget(); /** * Provides information about the plugin, which is used in the About dialog of System Settings.\n * This does not need to be implemented, and need only be implemented if the author * wants information about the view displayed in the About dialog. * * @returns The about data of the plugin. */ virtual KAboutData * aboutData(); /** * The state of the plugin ( position of the splitter for instance ) should be saved * to the configuration object when this is called. */ virtual void saveState(); /** * Causes the view to unload all modules in the module view, and return to their module selection state * * @warning Failure to reimplement will cause modules to not be unloaded when changing views. * This must be implemented. */ virtual void leaveModuleView(); /** * Used to give focus to the plugin. Plugin should call setFocus() on the appropriate widget * - * @note Failure to reimplement will cause keyboard accessibiltity and widget focusing problems + * @note Failure to reimplement will cause keyboard accessibility and widget focusing problems */ virtual void giveFocus(); /** * Provides access to the ModuleView the application uses to display control modules.\n * * @warning Failure to reimplement will cause modules not to be checked for configuration * changes, and for the module to not be displayed in the About dialog. It must be implemented. * @returns The ModuleView used by the plugin for handling modules. */ virtual ModuleView * moduleView() const; /** * Provides the list of actions the plugin wants System Settings to display in the toolbar when * it is loaded. This function does not need to be implemented if adding actions to the toolbar * is not required. * * @returns The list of actions the plugin provides. */ virtual QList& actionsList() const; /** * Provides the service object, which is used to retrieve information for the configuration dialog. * * @returns the service object of the plugin. */ const KService::Ptr& service() const; /** - * tells the config view wether to make use of tooltips or not + * tells the config view whether to make use of tooltips or not */ void setShowToolTips( bool show); /** * @returns true if the view should use tooltips */ bool showToolTips() const; public Q_SLOTS: /** * Called when the text in the search box changes allowing the display to be filtered. * * @warning Search will not work in the view if this function is not implemented. */ virtual void searchChanged( const QString& text ); /** * Allows views to add custom configuration pages to the System Settings configure dialog * * @warning Deleting the config object will cause System Settings to crash */ virtual void addConfiguration( KConfigDialog * config ); /** * Allows views to load their configuration before the configuration dialog is shown * Views should revert any changes that have not been saved */ virtual void loadConfiguration(); /** - * Should be implmented to ensure that views settings are saved when the user confirms their changes + * Should be implemented to ensure that views settings are saved when the user confirms their changes * Views should also apply the configuration at the same time */ virtual void saveConfiguration(); Q_SIGNALS: /** * Triggers a reload of the views actions by the host application. * * @warning Actions previously contained in the list must not be destroyed before this has been emitted. */ void actionsChanged(); /** * Should be emitted when the type ( list of modules / display of module ) * of displayed view is changed. * * @param state Determines whether changes have been made in the view. * @warning Failure to emit this will result in inconsistent application headers and change state. */ void viewChanged( bool state ); /** * Causes System Settings to hide / show the toolbar items specified. * This is used to control the display of the Configure and Search actions * * @param items The items that are wanted in the toolbar */ void changeToolBarItems( BaseMode::ToolBarItems items ); protected: /** * Returns the root item of the list of categorised modules. * This is usually passed to the constructor of MenuModel. * * @warning This is shared between all views, and should not be deleted manually. * @returns The root menu item as provided by System Settings. */ MenuItem * rootItem() const; /** * Provides access to the configuration for the plugin. * * @returns The configuration group for the plugin. */ KConfigGroup& config() const; /** * Provides access to item views used by the plugin. * This is currently used to show the enhanced tooltips. * * @returns A list of pointers to item views used by the mode. * The list can be empty. */ virtual QList views() const; private: class Private; Private *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(BaseMode::ToolBarItems) #endif diff --git a/core/Mainpage.dox b/core/Mainpage.dox index b4b52482..909f6a68 100644 --- a/core/Mainpage.dox +++ b/core/Mainpage.dox @@ -1,17 +1,17 @@ /** @mainpage System Settings view interface -This provides a standardised interface for views to System Settings, allowing you to provide your own view on the list of modules and categories. In order to simplify this task, a number of convienence classes are available. +This provides a standardised interface for views to System Settings, allowing you to provide your own view on the list of modules and categories. In order to simplify this task, a number of convenience classes are available. These classes include: - BaseMode the base view plugin interface. - BaseData providing access to data shared between all plugins. - MenuItem the categories and modules information. - MenuModel uses MenuItem to provide a tree structure of information about the modules. - MenuProxyModel ensures that only System Settings modules are displayed, and provides filtering capabilities - ModuleView provides an easy interface to load and display control modules, as well as handle saving of configuration correctly - ToolTipManager provides large informative tooltips, containing detailed information. This is handled automatically for you in most cases */ // DOXYGEN_SET_PROJECT_NAME = SystemSettingsView // DOXYGEN_ENABLE = YES // DOXYGEN_SET_EXCLUDE_PATTERNS += */ToolTips/* diff --git a/core/MenuItem.h b/core/MenuItem.h index 01e70382..9f98ff0f 100644 --- a/core/MenuItem.h +++ b/core/MenuItem.h @@ -1,165 +1,165 @@ /*************************************************************************** * This file is part of the KDE project * * Copyright 2007 Will Stephenson * * Copyright (C) 2009 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 MENUITEM_H #define MENUITEM_H #include "systemsettingsview_export.h" #include class QString; class KCModuleInfo; template class QList; /** * @brief Provides a specific item in the list of modules or categories * - * This provides convienent access to the list of modules, providing information about them + * This provides convenient access to the list of modules, providing information about them * such as name, module information and its service object.\n * This is created automatically by System Settings, and is shared among all plugins and so should not * be modified under any circumstances.\n * * System Settings creates it in a tree like manner, with categories containing subcategories and modules, * and subcategories repeating this.\n * * The service object must be set, unless it is the top level item, otherwise using applications * will crash when attempting to sort the children by weight * * @author Ben Cooksley * @author Will Stephenson */ class SYSTEMSETTINGSVIEW_EXPORT MenuItem { public: /** * Creates a MenuItem. * @note Will not provide keywords, name, or a module item until a service has been set. * * @param isMenu Specifies if it is a category or not. * @param parent The item it is parented to. Provide 0 for a top level item. */ MenuItem( bool isMenu, MenuItem * parent ); /** * Destroys a MenuItem, including all children, the service object and the module information. * * @warning Destroys the KService and KCModuleInfo objects provided by service() and item(). */ ~MenuItem(); /** * Sorts the children depending on the value of "X-KDE-Weight" in the desktop files of the * category or module. */ void sortChildrenByWeight(); /** * Provides the MenuItem for the child at the specified index. * * @param index The index of the child. * @returns The MenuItem object of the specified child. */ MenuItem * child( int index ); /** * Returns the list of keywords, which is used for searching the list of categories and modules. * * @note The parent items share all the keywords of their children. * @returns The list of keywords the item has. */ QStringList keywords(); /** * Returns the parent of this item. * * @returns The MenuItem object of this items parent. */ MenuItem *parent() const; /** * Provides a list of all the children of this item. * * @returns The list of children this has. */ QList& children() const; /** * Returns the service object of this item, which contains useful information about it. * * @returns The service object of this item if it has been set. */ KService::Ptr& service() const; /** * Provides the KDE control module information item, which can be used to load control modules * by the ModuleView. * * @returns The control module information object of the item, if the service object has been set. */ KCModuleInfo& item() const; /** - * Convienence function which provides the name of the current item. + * Convenience function which provides the name of the current item. * * @returns The name of the item, if the service object has been set. */ QString& name() const; /** - * Convienence function which provides the System Settings category of the current item. + * Convenience function which provides the System Settings category of the current item. * * @returns The category of the item, if the service object has been set. */ QString& category() const; /** * Provides the weight of the current item, as determined by its service. * If the service does not specify a weight, it is 100 * * @returns The weight of the service */ int weight(); /** * Provides information on which type the current item is. * * @returns true if it is a category. * @returns false if it is not a category. */ bool menu() const; /** * Sets the service object, which is used to provide the module information, name and keywords * Applications will crash if it is not set, unless it is the top level item. * * @param service The service object to store. */ void setService( const KService::Ptr& service ); private: class Private; Private *const d; }; Q_DECLARE_METATYPE( MenuItem * ) #endif diff --git a/core/MenuProxyModel.h b/core/MenuProxyModel.h index b03a7daa..8a3f414a 100644 --- a/core/MenuProxyModel.h +++ b/core/MenuProxyModel.h @@ -1,125 +1,125 @@ /************************************************************************** * Copyright (C) 2009 Ben Cooksley * * Copyright (C) 2007 Will Stephenson * * * * 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 MENUPROXYMODEL_H #define MENUPROXYMODEL_H #include #include "systemsettingsview_export.h" /** * @brief Provides a filter model for MenuModel * * Provides a standardised model to be used with views to filter a MenuModel.\n * It automatically sorts the items appropriately depending on if it is categorised * or not. * Call setFilterRegExp(QString) with the desired text to filter to perform searching. * Items that do not match the search parameters will be disabled, not hidden. * * @author Will Stephenson * @author Ben Cooksley */ class SYSTEMSETTINGSVIEW_EXPORT MenuProxyModel : public KCategorizedSortFilterProxyModel { Q_OBJECT Q_PROPERTY(QString filterRegExp READ filterRegExp WRITE setFilterRegExp NOTIFY filterRegExpChanged) public: /** * Constructs a MenuProxyModel with the specified parent. * * @param parent The QObject to use as a parent. */ MenuProxyModel( QObject *parent = nullptr ); QHash roleNames() const override; /** * Please see the Qt QSortFilterProxyModel documentation for further information.\n * Provides information on whether or not the QModelIndex specified by left is below right. * * @param left the QModelIndex that is being used for comparing. - * @param right the QModelIndex to compare aganist. + * @param right the QModelIndex to compare against. * @returns true if the left is below the right. */ bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override; /** - * Please see the KDE KCategorizedSortFilterProxyModel documentation for futher information.\n + * Please see the KDE KCategorizedSortFilterProxyModel documentation for further information.\n * Provides information on whether or not the QModelIndex specified by left is below right. * * @param left the QModelIndex that is being used for comparing. - * @param right the QModelIndex to compare aganist. + * @param right the QModelIndex to compare against. * @returns true if the left is below the right. */ bool subSortLessThan( const QModelIndex &left, const QModelIndex &right ) const override; /** * Please see the Qt QSortFilterProxyModel documentation for futher information.\n * Provides additional filtering of the MenuModel to only show categories which contain modules. * * @param source_column Please see QSortFilterProxyModel documentation. * @param source_parent Please see QSortFilterProxyModel documentation. * @returns true if the row should be displayed, false if it should not. */ bool filterAcceptsRow( int source_column, const QModelIndex &source_parent ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides the status flags for the QModelIndex specified. * The item will be selectable and enabled for its status unless invalid or filtered by search terms. * * @returns The flags for the QModelIndex provided. */ Qt::ItemFlags flags( const QModelIndex &index ) const override; /** * Please see Qt QAbstractItemModel documentation for more details.\n * Reimplemented for internal reasons. */ void setFilterRegExp ( const QRegExp & regExp ); /** * Please see Qt QAbstractItemModel documentation for more details.\n * Reimplemented for internal reasons. */ void setFilterRegExp ( const QString & pattern ); QString filterRegExp() const; /** * makes the filter highlight matching entries instead of hiding them */ void setFilterHighlightsEntries (bool highlight ); /** * @returns the filter highlight matching entries instead of hiding them, default true */ bool filterHighlightsEntries() const; Q_SIGNALS: void filterRegExpChanged(); private: bool m_filterHighlightsEntries : 1; }; #endif diff --git a/core/ModuleView.cpp b/core/ModuleView.cpp index a27da690..2a9d1e0b 100644 --- a/core/ModuleView.cpp +++ b/core/ModuleView.cpp @@ -1,443 +1,443 @@ /***************************************************************************** * 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 #include "MenuItem.h" class ModuleView::Private { public: Private() {} QMap mPages; QMap mModules; KPageWidget* mPageWidget = nullptr; QVBoxLayout* mLayout = nullptr; QDialogButtonBox* mButtons = nullptr; KAuth::ObjectDecorator* mApplyAuthorize = nullptr; QPushButton* mApply = nullptr; QPushButton* mReset = nullptr; QPushButton* mDefault = nullptr; QPushButton* mHelp = nullptr; 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 = nullptr; if( activeModule ) { aboutData = const_cast( activeModule->aboutData() ); } if ( aboutData ) { aboutData->setProgramIconName( activeModule->moduleInfo().service()->icon() ); return aboutData; } return nullptr; } void ModuleView::loadModule( const 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 ( const QModelIndex &module, indexes ) { MenuItem *newMenuItem = module.data( Qt::UserRole ).value(); addModule( &newMenuItem->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(QStringLiteral("SystemSettingsExternalApp")) || // Is it an external app? - module->service()->substituteUid() ) { // ...or does it require UID substituion? + module->service()->substituteUid() ) { // ...or does it require UID substitution? 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() ) ); //HACK: not much other ways to detect is a qml kcm if ( moduleProxy && moduleProxy->realModule()->inherits("KCModuleQml") ) { page->setHeaderVisible(false); } if( light ) { return; } if( moduleProxy && moduleProxy->realModule()->useRootOnlyMessage() ) { page->setHeader( moduleInfo->comment() + QStringLiteral("
") + moduleProxy->realModule()->rootOnlyMessage() + QStringLiteral("") ); page->setIcon( KDE::icon( moduleInfo->icon(), QStringList() << QStringLiteral("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( QStringLiteral("help:/")+docPath ); QProcess::startDetached(QStringLiteral("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(QStringLiteral("kcm:") + activeModule->moduleInfo().service()->storageId()), QStringLiteral("org.kde.systemsettings")); if (activeModule->realModule() && activeModule->realModule()->inherits("KCModuleQml")) { d->mButtons->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin), style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); d->mLayout->setContentsMargins(0, 0, 0, 0); } else { d->mButtons->setContentsMargins(0, 0, 0, 0); d->mLayout->setContentsMargins( style()->pixelMetric(QStyle::PM_LayoutLeftMargin), style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); } } } 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/core/ModuleView.h b/core/ModuleView.h index b2900653..b7880f8b 100644 --- a/core/ModuleView.h +++ b/core/ModuleView.h @@ -1,160 +1,160 @@ /***************************************************************************** * 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 * *****************************************************************************/ #ifndef MODULE_VIEW_H #define MODULE_VIEW_H #include #include #include #include "systemsettingsview_export.h" class KAboutData; class KCModuleInfo; class KCModuleProxy; class KPageWidgetItem; /** - * @brief Provides a convienent way to display modules + * @brief Provides a convenient way to display modules * * Provides a standardised interface for displaying multiple modules simultaneously * and provides facilities to access the current module, and to load its help, restore * default settings, save new settings and revert changes to settings * * It also provides checking for when a module has changed its configuration, and will prompt * if the user tries to change module or view if BaseMode is reimplemented correctly * * It also provides signals for active module changes, configuration changes and for when it has * been requested to close by button press * * @author Mathias Soeken * @author Ben Cooksley */ class SYSTEMSETTINGSVIEW_EXPORT ModuleView : public QWidget { Q_OBJECT public: /** * Constructs a ModuleView, with the parent specified. */ explicit ModuleView(QWidget * parent = nullptr); /** * Destroys the module view, along with all modules loaded, and any changes present in them. * * @warning The user will not be prompted to save changes if any exist. */ ~ModuleView() override; /** * Provides the module information, which is used to set the caption of the window when either the * active module or configuration changes. */ KCModuleInfo * activeModule() const; /** * Provides the about data of the active module, used for the about dialog. */ const KAboutData * aboutData() const; /** * Resolves any changes in the currently active module by prompting the user if they exist. * * @returns true if the user saved or discarded changes, or there were no changes at all. * @returns false if the user canceled the module change. */ bool resolveChanges(); /** * Closes all active modules, after checking there are no active changes. * * @warning This forces all modules to be destroyed regardless of if changes exist or not * If possible, always check with resolveChanges() first. */ void closeModules(); void setFaceType(KPageView::FaceType type); KPageView::FaceType faceType() const; public Q_SLOTS: /** * Loads the module specified by menuItem.\n * If the module has children, they will all be loaded instead of the module. * * @param menuItem the QModelIndex that you want to load. Must be sourced from either MenuModel or MenuProxyModel */ void loadModule(const QModelIndex &menuItem ); /** * Will open KHelpCenter, and load the help for the active module. */ void moduleHelp(); /** * Causes the active module to reload its configuration, reverting all changes. */ void moduleLoad(); /** * Causes the active module to save its configuration, applying all changes. */ bool moduleSave(); /** * Causes the active module to revert all changes to the configuration, and return to defaults. */ void moduleDefaults(); /** * Reimplemented for internal reasons.\n */ void keyPressEvent( QKeyEvent * event ) override; private: bool resolveChanges( KCModuleProxy *currentProxy ); void addModule( KCModuleInfo *module ); bool moduleSave( KCModuleProxy *module ); void updatePageIconHeader( KPageWidgetItem * page, bool light = false ); private Q_SLOTS: void activeModuleChanged( KPageWidgetItem* current, KPageWidgetItem* previous); void updateButtons(); void stateChanged(); Q_SIGNALS: /** * Emitted when the currently active module is changed. This occurs whenever the active module or * its configuration changes. This causes the window caption to update. */ void moduleChanged( bool state ); /** * Emitted when the ModuleView is asked to close.\n */ void closeRequest(); private: class Private; Private *const d; }; #endif diff --git a/sidebar/package/contents/ui/CategoriesPage.qml b/sidebar/package/contents/ui/CategoriesPage.qml index 7f300dec..c378477e 100644 --- a/sidebar/package/contents/ui/CategoriesPage.qml +++ b/sidebar/package/contents/ui/CategoriesPage.qml @@ -1,181 +1,181 @@ /* 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.3 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.0 as QtControls import QtQuick.Controls 2.0 as QtControls2 import org.kde.kirigami 2.1 as Kirigami Kirigami.ScrollablePage { id: mainColumn Component.onCompleted: searchField.forceActiveFocus() header: Rectangle { color: Kirigami.Theme.backgroundColor width: mainColumn.width height: Math.round(Kirigami.Units.gridUnit * 2.5) RowLayout { id: searchLayout spacing: Kirigami.Units.smallSpacing anchors { fill: parent margins: Kirigami.Units.smallSpacing } QtControls.ToolButton { id: menuButton iconName: "application-menu" Layout.maximumWidth: Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.smallSpacing * 2 Layout.maximumHeight: width Keys.onBacktabPressed: { root.focusPreviousRequest() } menu: ActionMenu { actions: ["configure", "help_contents", "help_about_app", "help_about_kde"] } } QtControls2.TextField { id: searchField focus: true Layout.minimumHeight: Layout.maximumHeight Layout.maximumHeight: Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.smallSpacing * 2 Layout.fillWidth: true placeholderText: i18n("Search...") onTextChanged: { systemsettings.categoryModel.filterRegExp = text; } MouseArea { anchors { right: parent.right verticalCenter: parent.verticalCenter rightMargin: y } opacity: searchField.text.length > 0 ? 1 : 0 width: Kirigami.Units.iconSizes.small height: width onClicked: searchField.text = "" Kirigami.Icon { anchors.fill: parent source: LayoutMirroring.enabled ? "edit-clear-rtl" : "edit-clear" } Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } } } Kirigami.Separator { anchors { left: parent.left right: parent.right top: parent.bottom } } } background: Rectangle { color: Kirigami.Theme.viewBackgroundColor } Kirigami.Heading { anchors.centerIn: parent width: parent.width * 0.7 wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter - text: i18nc("A search yelded no results", "No items matching your search") + text: i18nc("A search yielded no results", "No items matching your search") opacity: categoryView.count == 0 ? 0.3 : 0 Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } ListView { id: categoryView anchors.fill: parent model: systemsettings.categoryModel currentIndex: systemsettings.activeCategory onContentYChanged: systemsettings.hideToolTip(); activeFocusOnTab: true keyNavigationWraps: true Accessible.role: Accessible.List Keys.onTabPressed: { if (applicationWindow().wideScreen) { subCategoryColumn.focus = true; } else { root.focusNextRequest(); } } section { property: "categoryDisplayRole" delegate: Kirigami.AbstractListItem { separatorVisible: false supportsMouseEvents: false RowLayout { anchors { left: parent.left right: parent.right leftMargin: Kirigami.Units.smallSpacing } QtControls2.Label { id: sectionLabel text: section Layout.minimumHeight: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium) elide: Text.ElideRight font.weight: Font.Bold } } } } delegate: Kirigami.BasicListItem { id: delegate icon: model.decoration label: model.display separatorVisible: false Accessible.role: Accessible.ListItem Accessible.name: model.display onClicked: { if (systemsettings.activeCategory == index) { root.pageStack.currentIndex = 1; } else { systemsettings.activeCategory = index; subCategoryColumn.title = model.display; } } onHoveredChanged: { if (hovered) { systemsettings.requestToolTip(index, delegate.mapToItem(root, 0, 0, width, height)); } else { systemsettings.hideToolTip(); } } onFocusChanged: { if (focus) { onCurrentIndexChanged: categoryView.positionViewAtIndex(index, ListView.Contain); } } highlighted: systemsettings.activeCategory == index Keys.onEnterPressed: clicked(); Keys.onReturnPressed: clicked(); } } }