diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Crash ItemViews + ItemModels KCMUtils I18n KIO diff --git a/core/MenuModel.h b/core/MenuModel.h --- a/core/MenuModel.h +++ b/core/MenuModel.h @@ -41,6 +41,24 @@ Q_OBJECT public: + enum Roles { + MenuItemRole = Qt::UserRole, + + /** + * Role used to request the keywords to filter the items when searching. + */ + UserFilterRole, + + /** + * Role used to request the weight of a module, used to sort the items. + */ + UserSortRole, + + DepthRole, + + IsCategoryRole + }; + /** * Creates a MenuModel using the MenuItem specified. The MenuItem must always be valid * throughout the life of the MenuModel, otherwise it will cause crashes. @@ -55,6 +73,8 @@ */ ~MenuModel() override; + QHash roleNames() const override; + /** * Please see Qt QAbstractItemModel documentation for more details.\n * Provides the name, tooltip, icon, category, keywords and the internal MenuItem to views. @@ -130,16 +150,6 @@ */ void removeException( MenuItem * exception ); - /** - * Role used to request the keywords to filter the items when searching. - */ - static const int UserFilterRole; - - /** - * Role used to request the weight of a module, used to sort the items. - */ - static const int UserSortRole; - protected: /** * Provides the MenuItem which is used internally to provide information. diff --git a/core/MenuModel.cpp b/core/MenuModel.cpp --- a/core/MenuModel.cpp +++ b/core/MenuModel.cpp @@ -22,11 +22,8 @@ #include #include - #include "MenuItem.h" -const int MenuModel::UserFilterRole = 0x015D1AE6; -const int MenuModel::UserSortRole = 0x03A8CC00; class MenuModel::Private { public: @@ -49,6 +46,14 @@ delete d; } +QHash MenuModel::roleNames() const +{ + QHash names = QAbstractItemModel::roleNames(); + names[DepthRole] = "DepthRole"; + names[IsCategoryRole] = "IsCategoryRole"; + return names; +} + int MenuModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED( parent ); @@ -91,20 +96,42 @@ theData.setValue( QStringLiteral("%1%2").arg( QString::number(mi->parent()->weight()), 5, QLatin1Char('0') ).arg( mi->parent()->name() ) ); } break; - case KCategorizedSortFilterProxyModel::CategoryDisplayRole: - if ( mi->parent() ) { - theData.setValue( mi->parent()->name() ); + case KCategorizedSortFilterProxyModel::CategoryDisplayRole: { + MenuItem *candidate = mi->parent(); + // The model has an invisible single root item. + // So to get the "root category" we don't go up all the way + // To the actual root, but to the list of the first childs. + // That's why we check for candidate->parent()->parent() + while ( candidate && candidate->parent() && candidate->parent()->parent() ) { + candidate = candidate->parent(); + } + if (candidate) { + theData.setValue( candidate->name() ); } break; - case Qt::UserRole: + } + case MenuModel::MenuItemRole: theData.setValue( mi ); break; case MenuModel::UserFilterRole: theData.setValue( mi->keywords().join( QString() ) ); break; case MenuModel::UserSortRole: theData.setValue( QStringLiteral("%1").arg( QString::number(mi->weight()), 5, QLatin1Char('0') ) ); break; + case MenuModel::DepthRole: { + MenuItem *candidate = mi; + int depth = 0; + while ( candidate && candidate->parent() ) { + candidate = candidate->parent(); + ++depth; + } + theData.setValue( depth ); + break; + } + case MenuModel::IsCategoryRole: + theData.setValue( mi->menu() ); + break; default: break; } diff --git a/core/MenuProxyModel.cpp b/core/MenuProxyModel.cpp --- a/core/MenuProxyModel.cpp +++ b/core/MenuProxyModel.cpp @@ -78,6 +78,12 @@ bool MenuProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const { if (!m_filterHighlightsEntries) { + // Don't show empty categories + QModelIndex index = sourceModel()->index( source_row, 0, source_parent ); + MenuItem * mItem = index.data( Qt::UserRole ).value(); + if ( mItem->menu() && mItem->children().isEmpty() ) { + return false; + } return KCategorizedSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } diff --git a/sidebar/CMakeLists.txt b/sidebar/CMakeLists.txt --- a/sidebar/CMakeLists.txt +++ b/sidebar/CMakeLists.txt @@ -7,6 +7,7 @@ target_link_libraries(systemsettings_sidebar_mode systemsettingsview KF5::ItemViews + KF5::ItemModels KF5::KCMUtils KF5::I18n KF5::KIOWidgets diff --git a/sidebar/SidebarMode.h b/sidebar/SidebarMode.h --- a/sidebar/SidebarMode.h +++ b/sidebar/SidebarMode.h @@ -22,6 +22,7 @@ #include "BaseMode.h" #include +#include class ModuleView; class KAboutData; @@ -41,15 +42,37 @@ void focusPrevious(); }; +class SubcategoryModel : public QStandardItemModel +{ + Q_OBJECT + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + +public: + explicit SubcategoryModel(QAbstractItemModel *parentModel, QObject *parent = nullptr); + + QString title() const; + + void setParentIndex(const QModelIndex &activeModule); + +Q_SIGNALS: + void titleChanged(); + +private: + QAbstractItemModel *m_parentModel; + QString m_title; +}; + class SidebarMode : public BaseMode { Q_OBJECT Q_PROPERTY(QAbstractItemModel *categoryModel READ categoryModel CONSTANT) + Q_PROPERTY(QAbstractItemModel *searchModel READ searchModel 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 activeCategoryRow READ activeCategoryRow NOTIFY activeCategoryRowChanged) + Q_PROPERTY(int activeSearchRow READ activeSearchRow NOTIFY activeSearchRowChanged) + Q_PROPERTY(int activeSubCategoryRow READ activeSubCategoryRow NOTIFY activeSubCategoryRowChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(bool actionMenuVisible READ actionMenuVisible NOTIFY actionMenuVisibleChanged) Q_PROPERTY(bool introPageVisible READ introPageVisible WRITE setIntroPageVisible NOTIFY introPageVisibleChanged) @@ -63,14 +86,13 @@ KAboutData * aboutData() override; ModuleView * moduleView() const override; QAbstractItemModel *categoryModel() const; + QAbstractItemModel *searchModel() const; QAbstractItemModel *subCategoryModel() const; QAbstractItemModel *mostUsedModel() const; - int activeCategory() const; - void setActiveCategory(int cat); - - int activeSubCategory() const; - void setActiveSubCategory(int cat); + int activeCategoryRow() const; + int activeSubCategoryRow() const; + int activeSearchRow() const; int width() const; @@ -82,27 +104,26 @@ Q_INVOKABLE QAction *action(const QString &name) const; // QML doesn't understand QIcon, otherwise we could get it from the QAction itself Q_INVOKABLE QString actionIconName(const QString &name) const; - Q_INVOKABLE void requestToolTip(int index, const QRectF &rect); - Q_INVOKABLE void requestSubCategoryToolTip(int index, const QRectF &rect); + Q_INVOKABLE void requestToolTip(const QModelIndex &index, const QRectF &rect); Q_INVOKABLE void requestMostUsedToolTip(int index, const QRectF &rect); Q_INVOKABLE void hideToolTip(); - Q_INVOKABLE void hideSubCategoryToolTip(); Q_INVOKABLE void hideMostUsedToolTip(); - Q_INVOKABLE void loadMostUsed(int index); Q_INVOKABLE void showActionMenu(const QPoint &position); + Q_INVOKABLE void loadModule(const QModelIndex& activeModule); + protected: QList views() const override; bool eventFilter(QObject* watched, QEvent* event) override; private Q_SLOTS: - void changeModule( const QModelIndex& activeModule ); void moduleLoaded(); void initWidget(); Q_SIGNALS: - void activeCategoryChanged(); - void activeSubCategoryChanged(); + void activeCategoryRowChanged(); + void activeSubCategoryRowChanged(); + void activeSearchRowChanged(); void widthChanged(); void actionMenuVisibleChanged(); void introPageVisibleChanged(); diff --git a/sidebar/SidebarMode.cpp b/sidebar/SidebarMode.cpp --- a/sidebar/SidebarMode.cpp +++ b/sidebar/SidebarMode.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -77,38 +78,39 @@ focusNextPrevChild(false); } -class SubcategoryModel : public QStandardItemModel +SubcategoryModel::SubcategoryModel(QAbstractItemModel *parentModel, QObject *parent) + : QStandardItemModel(parent), + m_parentModel(parentModel) +{} + +QString SubcategoryModel::title() const { -public: - explicit SubcategoryModel(QAbstractItemModel *parentModel, QObject *parent = nullptr) - : QStandardItemModel(parent), - m_parentModel(parentModel) - {} + return m_title; +} - 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); - } +void SubcategoryModel::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 = activeModule.isValid() ? m_parentModel->rowCount(activeModule) : 0; + 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(); } + blockSignals(false); + beginResetModel(); + endResetModel(); + m_title = activeModule.data(Qt::DisplayRole).toString(); + emit titleChanged(); +} -private: - QAbstractItemModel *m_parentModel; -}; class MostUsedModel : public QSortFilterProxyModel { @@ -221,7 +223,9 @@ private: QHash m_menuItems; + // Model when there is nothing from kactivities-stat QStandardItemModel *m_defaultModel; + // Model fed by kactivities-stats ResultModel *m_resultModel; }; @@ -231,16 +235,15 @@ : quickWidget( nullptr ), moduleView( nullptr ), collection( nullptr ), - activeCategory( -1 ), - activeSubCategory( -1 ) + activeCategoryRow( -1 ), + activeSubCategoryRow( -1 ) {} virtual ~Private() { delete aboutIcon; } ToolTipManager *toolTipManager = nullptr; - ToolTipManager *subCategoryToolTipManager = nullptr; ToolTipManager *mostUsedToolTipManager = nullptr; QQuickWidget * quickWidget = nullptr; KPackage::Package package; @@ -250,14 +253,17 @@ QQuickWidget * placeHolderWidget = nullptr; QHBoxLayout * mainLayout = nullptr; KDeclarative::KDeclarative kdeclarative; + MenuModel * model = nullptr; MenuProxyModel * categorizedModel = nullptr; MenuProxyModel * searchModel = nullptr; + KDescendantsProxyModel * flatModel = nullptr; KAboutData * aboutIcon = nullptr; ModuleView * moduleView = nullptr; KActionCollection *collection = nullptr; QPersistentModelIndex activeCategoryIndex; - int activeCategory; - int activeSubCategory; + int activeCategoryRow = -1; + int activeSubCategoryRow = -1; + int activeSearchRow = -1; bool m_actionMenuVisible = false; void setActionMenuVisible(SidebarMode* sidebarMode, const bool &actionMenuVisible) { @@ -310,6 +316,11 @@ } QAbstractItemModel * SidebarMode::categoryModel() const +{ + return d->categorizedModel; +} + +QAbstractItemModel * SidebarMode::searchModel() const { return d->searchModel; } @@ -333,30 +344,28 @@ void SidebarMode::initEvent() { - MenuModel * model = new MenuModel( rootItem(), this ); + d->model = new MenuModel( rootItem(), this ); foreach( MenuItem * child, rootItem()->children() ) { - model->addException( child ); + d->model->addException( child ); } d->categorizedModel = new MenuProxyModel( this ); d->categorizedModel->setCategorizedModel( true ); - d->categorizedModel->setSourceModel( model ); + d->categorizedModel->setSourceModel( d->model ); d->categorizedModel->sort( 0 ); d->categorizedModel->setFilterHighlightsEntries( false ); + d->flatModel = new KDescendantsProxyModel( this ); + d->flatModel->setSourceModel( d->model ); + d->searchModel = new MenuProxyModel( this ); + d->searchModel->setCategorizedModel( true ); d->searchModel->setFilterHighlightsEntries( false ); - d->searchModel->setSourceModel( d->categorizedModel ); - connect( d->searchModel, &MenuProxyModel::filterRegExpChanged, this, [this] () { - if (d->activeCategoryIndex.isValid() && d->activeCategoryIndex.row() >= 0) { - d->subCategoryModel->setParentIndex( d->activeCategoryIndex ); - emit activeCategoryChanged(); - } - }); + d->searchModel->setSourceModel( d->flatModel ); d->mostUsedModel = new MostUsedModel( this ); - d->subCategoryModel = new SubcategoryModel( d->searchModel, this ); + d->subCategoryModel = new SubcategoryModel( d->categorizedModel, this ); d->mainWidget = new FocusHackWidget(); d->mainWidget->installEventFilter(this); d->mainLayout = new QHBoxLayout(d->mainWidget); @@ -385,17 +394,11 @@ return QString(); } -void SidebarMode::requestToolTip(int index, const QRectF &rect) +void SidebarMode::requestToolTip(const QModelIndex &index, const QRectF &rect) { - if (showToolTips()) { - d->toolTipManager->requestToolTip(d->searchModel->index(index, 0), rect.toRect()); - } -} - -void SidebarMode::requestSubCategoryToolTip(int index, const QRectF &rect) -{ - if (showToolTips()) { - d->subCategoryToolTipManager->requestToolTip(d->subCategoryModel->index(index, 0), rect.toRect()); + if (showToolTips() && index.model()) { + d->toolTipManager->setModel(index.model()); + d->toolTipManager->requestToolTip(index, rect.toRect()); } } @@ -411,24 +414,11 @@ d->toolTipManager->hideToolTip(); } -void SidebarMode::hideSubCategoryToolTip() -{ - d->subCategoryToolTipManager->hideToolTip(); -} - void SidebarMode::hideMostUsedToolTip() { d->mostUsedToolTipManager->hideToolTip(); } -void SidebarMode::loadMostUsed(int index) -{ - const QModelIndex idx = d->mostUsedModel->index(index, 0); - d->moduleView->closeModules(); - d->moduleView->loadModule( idx ); - setIntroPageVisible(false); -} - void SidebarMode::showActionMenu(const QPoint &position) { QMenu *menu = new QMenu(); @@ -444,77 +434,139 @@ d->setActionMenuVisible(this, true); } -void SidebarMode::changeModule( const QModelIndex& activeModule ) +void SidebarMode::loadModule( const QModelIndex& activeModule ) { + if (!activeModule.isValid()) { + return; + } + d->moduleView->closeModules(); - if (!activeModule.isValid()) { + MenuItem *mi = activeModule.data(MenuModel::MenuItemRole).value(); + + if (!mi) { return; } - const int subRows = d->searchModel->rowCount(activeModule); - if ( subRows < 2) { + setIntroPageVisible(false); + if ( mi->children().length() < 1) { d->moduleView->loadModule( activeModule ); } else { - d->moduleView->loadModule( d->searchModel->index(0, 0, activeModule) ); + d->moduleView->loadModule( activeModule.model()->index(0, 0, activeModule) ); } - d->subCategoryModel->setParentIndex( activeModule ); + if (activeModule.model() == d->categorizedModel) { + const int newCategoryRow = activeModule.row(); + + if (d->activeCategoryRow == newCategoryRow) { + return; + } + if( !d->moduleView->resolveChanges() ) { + return; + } + + d->activeCategoryIndex = activeModule; + d->activeCategoryRow = newCategoryRow; + + d->activeSubCategoryRow = 0; + + d->subCategoryModel->setParentIndex( activeModule ); + + if (d->activeSearchRow > -1) { + d->activeSearchRow = -1; + emit activeSearchRowChanged(); + } + + emit activeCategoryRowChanged(); + emit activeSubCategoryRowChanged(); + + } else if (activeModule.model() == d->subCategoryModel) { + if (d->activeSearchRow > -1) { + d->activeSearchRow = -1; + emit activeSearchRowChanged(); + } + d->activeSubCategoryRow = activeModule.row(); + emit activeSubCategoryRowChanged(); + + } else if (activeModule.model() == d->searchModel) { + QModelIndex originalIndex = d->categorizedModel->mapFromSource( + d->flatModel->mapToSource( + d->searchModel->mapToSource(activeModule))); + + if (originalIndex.isValid()) { + //are we in a subcategory of the top categories? + if (originalIndex.parent().isValid() && mi->parent()->menu()) { + d->activeCategoryRow = originalIndex.parent().row(); + d->activeSubCategoryRow = originalIndex.row(); + + // Is this kcm directly at the top level without a top category? + } else { + d->activeCategoryRow = originalIndex.row(); + d->activeSubCategoryRow = -1; + } + + + d->subCategoryModel->setParentIndex( originalIndex.parent() ); + emit activeCategoryRowChanged(); + emit activeSubCategoryRowChanged(); + } + + d->activeSearchRow = activeModule.row(); + emit activeSearchRowChanged(); + + } else if (activeModule.model() == d->mostUsedModel) { + if (d->activeSearchRow > -1) { + d->activeSearchRow = -1; + emit activeSearchRowChanged(); + } + + QModelIndex flatIndex; + + // search the corresponding item on the main model + for (int i = 0; i < d->flatModel->rowCount(); ++i) { + QModelIndex idx = d->flatModel->index(i, 0); + MenuItem *otherMi = idx.data(MenuModel::MenuItemRole).value(); + + if (otherMi->item() == mi->item()) { + flatIndex = idx; + break; + } + } + + if (flatIndex.isValid()) { + QModelIndex idx = d->categorizedModel->mapFromSource(d->flatModel->mapToSource(flatIndex)); + + MenuItem *parentMi = idx.parent().data(MenuModel::MenuItemRole).value(); + if (idx.isValid()) { + if (parentMi->menu()) { + d->subCategoryModel->setParentIndex( idx.parent() ); + d->activeCategoryRow = idx.parent().row(); + d->activeSubCategoryRow = idx.row(); + } else { + d->activeCategoryRow = idx.row(); + d->activeSubCategoryRow = -1; + } + emit activeCategoryRowChanged(); + emit activeSubCategoryRowChanged(); + } + } + } } void SidebarMode::moduleLoaded() { d->placeHolderWidget->hide(); d->moduleView->show(); } -int SidebarMode::activeCategory() const -{ - return d->searchModel->mapFromSource(d->searchModel->sourceModel()->index(d->activeCategory, 0)).row(); -} - -void SidebarMode::setActiveCategory(int cat) +int SidebarMode::activeSearchRow() const { - const QModelIndex idx = d->searchModel->index(cat, 0); - int newCategoryRow; - if (cat != -1) { - setIntroPageVisible(false); - newCategoryRow = d->searchModel->mapToSource(idx).row(); - } else { - newCategoryRow = cat; - } - - if (d->activeCategory == newCategoryRow) { - return; - } - if( !d->moduleView->resolveChanges() ) { - return; - } - - d->activeCategoryIndex = idx; - d->activeCategory = newCategoryRow; - - changeModule(idx); - d->activeSubCategory = 0; - emit activeCategoryChanged(); - emit activeSubCategoryChanged(); + return d->activeSearchRow; } -void SidebarMode::setActiveSubCategory(int cat) +int SidebarMode::activeCategoryRow() const { - if (d->activeSubCategory == cat) { - return; - } - - if( !d->moduleView->resolveChanges() ) { - return; - } - - d->activeSubCategory = cat; - d->moduleView->closeModules(); - d->moduleView->loadModule( d->subCategoryModel->index(cat, 0) ); - setIntroPageVisible(cat < 0); - emit activeSubCategoryChanged(); + return d->activeCategoryRow; } void SidebarMode::setIntroPageVisible(const bool &introPageVisible) @@ -524,8 +576,10 @@ } if (introPageVisible) { - setActiveCategory(-1); - setActiveSubCategory(-1); + d->activeCategoryRow = -1; + emit activeCategoryRowChanged(); + d->activeSubCategoryRow = -1; + emit activeSubCategoryRowChanged(); d->placeHolderWidget->show(); d->moduleView->hide(); } else { @@ -547,9 +601,9 @@ return d->m_actionMenuVisible; } -int SidebarMode::activeSubCategory() const +int SidebarMode::activeSubCategoryRow() const { - return d->activeSubCategory; + return d->activeSubCategoryRow; } bool SidebarMode::introPageVisible() const @@ -624,8 +678,7 @@ d->mainLayout->addWidget( d->placeHolderWidget ); emit changeToolBarItems(BaseMode::NoItems); - d->toolTipManager = new ToolTipManager(d->searchModel, d->quickWidget, ToolTipManager::ToolTipPosition::Right); - d->subCategoryToolTipManager = new ToolTipManager(d->subCategoryModel, d->quickWidget, ToolTipManager::ToolTipPosition::Right); + d->toolTipManager = new ToolTipManager(d->categorizedModel, d->quickWidget, ToolTipManager::ToolTipPosition::Right); d->mostUsedToolTipManager = new ToolTipManager(d->mostUsedModel, d->placeHolderWidget, ToolTipManager::ToolTipPosition::BottomCenter); d->mostUsedModel->setResultModel(new ResultModel( AllResources | Agent(QStringLiteral("org.kde.systemsettings")) | HighScoredFirst | Limit(5), this)); diff --git a/sidebar/ToolTips/tooltipmanager.h b/sidebar/ToolTips/tooltipmanager.h --- a/sidebar/ToolTips/tooltipmanager.h +++ b/sidebar/ToolTips/tooltipmanager.h @@ -48,9 +48,12 @@ * @param parent The view which will have the tooltips displayed for. * @param toolTipPosition The position of the tooltip. */ - explicit ToolTipManager(QAbstractItemModel *model, QWidget* parent, ToolTipManager::ToolTipPosition toolTipPosition); + explicit ToolTipManager(const QAbstractItemModel *model, QWidget* parent, ToolTipManager::ToolTipPosition toolTipPosition); ~ToolTipManager() override; + void setModel(const QAbstractItemModel *model); + const QAbstractItemModel *model() const; + public Q_SLOTS: /** * Hides the currently shown tooltip. Invoking this method is diff --git a/sidebar/ToolTips/tooltipmanager.cpp b/sidebar/ToolTips/tooltipmanager.cpp --- a/sidebar/ToolTips/tooltipmanager.cpp +++ b/sidebar/ToolTips/tooltipmanager.cpp @@ -57,15 +57,15 @@ KToolTipWidget *tooltip; QWidget* view; - QAbstractItemModel *model; + const QAbstractItemModel *model; QTimer* timer; QPersistentModelIndex item; QRect itemRect; int delay; ToolTipPosition toolTipPosition; }; -ToolTipManager::ToolTipManager(QAbstractItemModel *model, QWidget* parent, ToolTipPosition toolTipPosition) +ToolTipManager::ToolTipManager(const QAbstractItemModel *model, QWidget* parent, ToolTipPosition toolTipPosition) : QObject(parent) , d(new ToolTipManager::Private) { @@ -88,6 +88,16 @@ delete d; } +void ToolTipManager::setModel(const QAbstractItemModel *model) +{ + d->model = model; +} + +const QAbstractItemModel *ToolTipManager::model() const +{ + return d->model; +} + bool ToolTipManager::eventFilter(QObject* watched, QEvent* event) { if (watched == d->view) { diff --git a/sidebar/package/contents/ui/CategoriesPage.qml b/sidebar/package/contents/ui/CategoriesPage.qml --- a/sidebar/package/contents/ui/CategoriesPage.qml +++ b/sidebar/package/contents/ui/CategoriesPage.qml @@ -24,6 +24,7 @@ Kirigami.ScrollablePage { id: mainColumn Component.onCompleted: searchField.forceActiveFocus() + readonly property bool searchMode: searchField.text.length > 0 header: Rectangle { Kirigami.Theme.colorSet: Kirigami.Theme.Window @@ -63,7 +64,7 @@ Layout.maximumHeight: Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.smallSpacing * 2 Layout.fillWidth: true onTextChanged: { - systemsettings.categoryModel.filterRegExp = text; + systemsettings.searchModel.filterRegExp = text; } KeyNavigation.tab: categoryView } @@ -110,11 +111,19 @@ } } } + + Binding { + target: categoryView + property: "currentIndex" + value: mainColumn.searchMode + ? systemsettings.activeSearchRow + : systemsettings.activeCategoryRow + } ListView { id: categoryView anchors.fill: parent - model: systemsettings.categoryModel - currentIndex: systemsettings.activeCategory + model: mainColumn.searchMode ? systemsettings.searchModel : systemsettings.categoryModel + onContentYChanged: systemsettings.hideToolTip(); activeFocusOnTab: true keyNavigationWraps: true @@ -134,36 +143,63 @@ } } - delegate: Kirigami.BasicListItem { + delegate: Kirigami.AbstractListItem { id: delegate - icon: model.decoration - label: model.display + separatorVisible: false Accessible.role: Accessible.ListItem Accessible.name: model.display + supportsMouseEvents: !model.IsCategoryRole || !mainColumn.searchMode + enabled: !model.IsCategoryRole || !mainColumn.searchMode onClicked: { - if (systemsettings.activeCategory == index) { + if (model.IsCategoryRole && mainColumn.searchMode) { + return; + } + + if (mainColumn.searchMode || systemsettings.activeCategoryRow !== index) { + systemsettings.loadModule(categoryView.model.index(index, 0)); + } else if (!mainColumn.searchMode) { root.pageStack.currentIndex = 1; - } else { - systemsettings.activeCategory = index; - subCategoryColumn.title = model.display; } } onHoveredChanged: { + if (model.IsCategoryRole && mainColumn.searchMode) { + return; + } if (hovered) { - systemsettings.requestToolTip(index, delegate.mapToItem(root, 0, 0, width, height)); + systemsettings.requestToolTip(categoryView.model.index(index, 0), delegate.mapToItem(root, 0, 0, width, height)); } else { systemsettings.hideToolTip(); } } onFocusChanged: { + if (model.IsCategoryRole && mainColumn.searchMode) { + return; + } if (focus) { - onCurrentIndexChanged: categoryView.positionViewAtIndex(index, ListView.Contain); + categoryView.positionViewAtIndex(index, ListView.Contain); } } - highlighted: systemsettings.activeCategory == index + highlighted: categoryView.currentIndex == index Keys.onEnterPressed: clicked(); Keys.onReturnPressed: clicked(); + contentItem: RowLayout { + id: layout + spacing: Kirigami.Settings.tabletMode ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing + Kirigami.Icon { + id: icon + source: model.decoration + Layout.preferredHeight: Layout.preferredWidth + Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium + Layout.leftMargin: (model.DepthRole-2) * (icon.width + layout.spacing) + } + QQC2.Label { + Layout.fillWidth: true + text: model.display + color: (delegate.highlighted || delegate.checked || (delegate.pressed && delegate.supportsMouseEvents)) ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor + elide: Text.ElideRight + } + } } } } diff --git a/sidebar/package/contents/ui/IntroIcon.qml b/sidebar/package/contents/ui/IntroIcon.qml --- a/sidebar/package/contents/ui/IntroIcon.qml +++ b/sidebar/package/contents/ui/IntroIcon.qml @@ -36,7 +36,7 @@ activeFocusOnTab: true hoverEnabled: true - onClicked: systemsettings.loadMostUsed(index); + onClicked: systemsettings.loadModule(systemsettings.mostUsedModel.index(index, 0)); onEntered: systemsettings.requestMostUsedToolTip(index, item.mapToItem(root, 0, Kirigami.Units.largeSpacing, width, height)); onExited: systemsettings.hideMostUsedToolTip(); diff --git a/sidebar/package/contents/ui/SubCategoryPage.qml b/sidebar/package/contents/ui/SubCategoryPage.qml --- a/sidebar/package/contents/ui/SubCategoryPage.qml +++ b/sidebar/package/contents/ui/SubCategoryPage.qml @@ -24,6 +24,8 @@ Kirigami.ScrollablePage { id: subCategoryColumn + title: systemsettings.subCategoryModel.title + header: Rectangle { id: headerRect Kirigami.Theme.colorSet: Kirigami.Theme.Window @@ -107,57 +109,74 @@ id: subCategoryView anchors.fill: parent model: systemsettings.subCategoryModel - currentIndex: systemsettings.activeSubCategory - onContentYChanged: systemsettings.hideSubCategoryToolTip(); + currentIndex: systemsettings.activeSubCategoryRow + onContentYChanged: systemsettings.hideToolTip(); activeFocusOnTab: true keyNavigationWraps: true Accessible.role: Accessible.List Keys.onTabPressed: root.focusNextRequest(); Keys.onBacktabPressed: { mainColumn.focus = true; } onCountChanged: { - if (count > 1) { + if (count > 1 && !root.searchMode) { if (root.pageStack.depth < 2) { root.pageStack.push(subCategoryColumn); } } else { root.pageStack.pop(mainColumn) } } + Connections { + target: root + onSearchModeChanged: { + if (root.searchMode) { + root.pageStack.pop(mainColumn); + } else if (subCategoryView.count > 1) { + root.pageStack.push(subCategoryColumn); + } + } + } Connections { target: systemsettings onActiveSubCategoryChanged: { - subCategoryView.currentIndex = systemsettings.activeSubCategory; - if (systemsettings.activeSubCategory < 0) { + subCategoryView.currentIndex = systemsettings.activeSubCategoryRow; + if (systemsettings.activeSubCategoryRow < 0) { root.pageStack.pop(mainColumn) } else { root.pageStack.currentIndex = 1; subCategoryView.forceActiveFocus(); } } + onIntroPageVisibleChanged: { + if (systemsettings.introPageVisible) { + root.pageStack.pop(mainColumn) + } + } } delegate: Kirigami.BasicListItem { id: delegate icon: model.decoration label: model.display separatorVisible: false - onClicked: systemsettings.activeSubCategory = index + onClicked: { + systemsettings.loadModule(subCategoryView.model.index(index, 0)); + } onHoveredChanged: { if (hovered) { - systemsettings.requestSubCategoryToolTip(index, delegate.mapToItem(root, 0, 0, width, height)); + systemsettings.requestToolTip(subCategoryView.model.index(index, 0), delegate.mapToItem(root, 0, 0, width, height)); } else { - systemsettings.hideSubCategoryToolTip(); + systemsettings.hideToolTip(); } } onFocusChanged: { if (focus) { onCurrentIndexChanged: subCategoryView.positionViewAtIndex(index, ListView.Contain); } } - highlighted: systemsettings.activeSubCategory == index + highlighted: systemsettings.activeSubCategoryRow == index Keys.onEnterPressed: clicked(); Keys.onReturnPressed: clicked(); } diff --git a/sidebar/package/contents/ui/main.qml b/sidebar/package/contents/ui/main.qml --- a/sidebar/package/contents/ui/main.qml +++ b/sidebar/package/contents/ui/main.qml @@ -29,6 +29,8 @@ LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true + property alias searchMode: mainColumn.searchMode + signal focusNextRequest() signal focusPreviousRequest()