File indexing completed on 2024-05-12 05:39:04

0001 /*
0002  * SPDX-FileCopyrightText: 2009 Ben Cooksley <bcooksley@kde.org>
0003  * SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "SidebarMode.h"
0009 
0010 #include "BaseData.h"
0011 #include "MenuItem.h"
0012 #include "MenuModel.h"
0013 #include "MenuProxyModel.h"
0014 #include "ModuleView.h"
0015 #include "kcmmetadatahelpers.h"
0016 
0017 #include <QGuiApplication>
0018 #include <QHBoxLayout>
0019 
0020 #include <KAboutData>
0021 #include <KActionCollection>
0022 #include <KCModuleLoader>
0023 #include <KConfigGroup>
0024 #include <KDescendantsProxyModel>
0025 #include <KLocalizedContext>
0026 #include <KLocalizedString>
0027 #include <KXmlGuiWindow>
0028 #include <QAction>
0029 #include <QGraphicsOpacityEffect>
0030 #include <QLabel>
0031 #include <QMenu>
0032 #include <QQmlContext>
0033 #include <QQmlEngine>
0034 #include <QQuickItem>
0035 #include <QQuickWidget>
0036 #include <QStandardPaths>
0037 
0038 FocusHackWidget::FocusHackWidget(QWidget *parent)
0039     : QWidget(parent)
0040 {
0041 }
0042 
0043 FocusHackWidget::~FocusHackWidget()
0044 {
0045 }
0046 
0047 void FocusHackWidget::focusNext()
0048 {
0049     focusNextChild();
0050 }
0051 
0052 void FocusHackWidget::focusPrevious()
0053 {
0054     focusPreviousChild();
0055 }
0056 
0057 SubcategoryModel::SubcategoryModel(QAbstractItemModel *parentModel, SidebarMode *parent)
0058     : KSelectionProxyModel(nullptr, parent)
0059     , m_parentModel(parentModel)
0060     , m_sidebarMode(parent)
0061 {
0062     setSourceModel(parentModel);
0063     setSelectionModel(new QItemSelectionModel(parentModel, this));
0064     setFilterBehavior(SubTreesWithoutRoots);
0065 }
0066 
0067 QString SubcategoryModel::title() const
0068 {
0069     auto const *mi = m_activeModuleIndex.data(MenuModel::MenuItemRole).value<MenuItem *>();
0070 
0071     if (!mi) {
0072         return {};
0073     }
0074 
0075     return mi->name();
0076 }
0077 
0078 QIcon SubcategoryModel::icon() const
0079 {
0080     return m_activeModuleIndex.data(Qt::DecorationRole).value<QIcon>();
0081 }
0082 
0083 bool SubcategoryModel::categoryOwnedByKCM() const
0084 {
0085     return m_activeModuleIndex.data(MenuModel::IsKCMRole).toBool();
0086 }
0087 
0088 void SubcategoryModel::setParentIndex(const QModelIndex &activeModule)
0089 {
0090     selectionModel()->select(activeModule, QItemSelectionModel::ClearAndSelect);
0091     m_activeModuleIndex = QPersistentModelIndex(activeModule);
0092     Q_EMIT titleChanged();
0093     Q_EMIT iconChanged();
0094     Q_EMIT categoryOwnedByKCMChanged();
0095 }
0096 
0097 void SubcategoryModel::loadParentCategoryModule()
0098 {
0099     auto menuItem = m_activeModuleIndex.data(MenuModel::MenuItemRole).value<MenuItem *>();
0100     if (menuItem->isLibrary()) {
0101         m_sidebarMode->loadModule(m_activeModuleIndex);
0102     }
0103 }
0104 
0105 class SidebarMode::Private
0106 {
0107 public:
0108     Private()
0109         : quickWidget(nullptr)
0110         , moduleView(nullptr)
0111         , collection(nullptr)
0112         , activeCategoryRow(-1)
0113         , activeSubCategoryRow(-1)
0114     {
0115     }
0116 
0117     QQuickWidget *quickWidget = nullptr;
0118     SubcategoryModel *subCategoryModel = nullptr;
0119     FocusHackWidget *mainWidget = nullptr;
0120     QQuickWidget *placeHolderWidget = nullptr;
0121     QHBoxLayout *mainLayout = nullptr;
0122     MenuModel *model = nullptr;
0123     MenuProxyModel *categorizedModel = nullptr;
0124     MenuProxyModel *searchModel = nullptr;
0125     KDescendantsProxyModel *flatModel = nullptr;
0126     ModuleView *moduleView = nullptr;
0127     KActionCollection *collection = nullptr;
0128     QPersistentModelIndex activeCategoryIndex;
0129     std::shared_ptr<QQmlEngine> engine;
0130     int activeCategoryRow = -1;
0131     int activeSubCategoryRow = -1;
0132     int activeSearchRow = -1;
0133     qreal headerHeight = 0;
0134     bool actionMenuVisible = false;
0135     bool m_introPageVisible = true;
0136     bool m_defaultsIndicatorsVisible = false;
0137 };
0138 
0139 SidebarMode::SidebarMode(QObject *parent, const QVariantList &args)
0140     : BaseMode(parent, args)
0141     , d(new Private())
0142 {
0143     qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
0144     qmlRegisterAnonymousType<QAction>("", 1);
0145     qmlRegisterAnonymousType<QAbstractItemModel>("", 1);
0146     init();
0147 }
0148 
0149 SidebarMode::~SidebarMode()
0150 {
0151     config().sync();
0152     delete d;
0153 }
0154 
0155 ModuleView *SidebarMode::moduleView() const
0156 {
0157     return d->moduleView;
0158 }
0159 
0160 QWidget *SidebarMode::mainWidget()
0161 {
0162     if (!d->quickWidget) {
0163         initWidget();
0164     }
0165     return d->mainWidget;
0166 }
0167 
0168 QAbstractItemModel *SidebarMode::categoryModel() const
0169 {
0170     return d->categorizedModel;
0171 }
0172 
0173 QAbstractItemModel *SidebarMode::searchModel() const
0174 {
0175     return d->searchModel;
0176 }
0177 
0178 QAbstractItemModel *SidebarMode::subCategoryModel() const
0179 {
0180     return d->subCategoryModel;
0181 }
0182 
0183 QList<QAbstractItemView *> SidebarMode::views() const
0184 {
0185     QList<QAbstractItemView *> list;
0186     // list.append( d->categoryView );
0187     return list;
0188 }
0189 
0190 void SidebarMode::initEvent()
0191 {
0192     d->model = new MenuModel(rootItem(), this);
0193     foreach (MenuItem *child, rootItem()->children()) {
0194         d->model->addException(child);
0195     }
0196 
0197     d->categorizedModel = new MenuProxyModel(this);
0198     d->categorizedModel->setCategorizedModel(true);
0199     d->categorizedModel->setSourceModel(d->model);
0200     d->categorizedModel->sort(0);
0201     d->categorizedModel->setFilterHighlightsEntries(false);
0202 
0203     d->flatModel = new KDescendantsProxyModel(this);
0204     d->flatModel->setSourceModel(d->model);
0205 
0206     d->searchModel = new MenuProxyModel(this);
0207     d->searchModel->setCategorizedModel(true);
0208     d->searchModel->setFilterHighlightsEntries(false);
0209     d->searchModel->setSourceModel(d->flatModel);
0210 
0211     d->subCategoryModel = new SubcategoryModel(d->categorizedModel, this);
0212     d->mainWidget = new FocusHackWidget();
0213     d->mainWidget->installEventFilter(this);
0214     d->mainLayout = new QHBoxLayout(d->mainWidget);
0215     d->mainLayout->setContentsMargins(0, 0, 0, 0);
0216     d->mainLayout->setSpacing(0);
0217     d->moduleView = new ModuleView(d->mainWidget);
0218     connect(d->moduleView, &ModuleView::moduleChanged, this, &SidebarMode::moduleLoaded);
0219     connect(d->moduleView, &ModuleView::moduleSaved, this, &SidebarMode::updateDefaults);
0220     d->quickWidget = nullptr;
0221     moduleView()->setFaceType(KPageView::Plain);
0222     if (applicationMode() == BaseMode::InfoCenter) {
0223         d->moduleView->setSaveStatistics(false);
0224         d->moduleView->setApplyVisible(false);
0225         d->moduleView->setDefaultsVisible(false);
0226     }
0227 
0228     if (config().readEntry("HighlightNonDefaultSettings", false)) {
0229         toggleDefaultsIndicatorsVisibility();
0230     }
0231 }
0232 
0233 QAction *SidebarMode::action(const QString &name) const
0234 {
0235     if (!d->collection) {
0236         return nullptr;
0237     }
0238 
0239     return d->collection->action(name);
0240 }
0241 
0242 QString SidebarMode::actionIconName(const QString &name) const
0243 {
0244     if (QAction *a = action(name)) {
0245         return a->icon().name();
0246     }
0247 
0248     return {};
0249 }
0250 
0251 void SidebarMode::showActionMenu(const QPoint &position)
0252 {
0253     auto menu = new QMenu();
0254     connect(menu, &QMenu::aboutToHide, this, [this]() {
0255         setActionMenuVisible(false);
0256     });
0257     menu->setAttribute(Qt::WA_DeleteOnClose);
0258     // Breeze and Oxygen have rounded corners on menus. They set this attribute in polish()
0259     // but at that time the underlying surface has already been created where setting this
0260     // flag makes no difference anymore (Bug 385311)
0261     menu->setAttribute(Qt::WA_TranslucentBackground);
0262 
0263     const QStringList actionList{QStringLiteral("switchto_iconview"),
0264                                  QStringLiteral("highlight_changes"),
0265                                  QStringLiteral("configure"),
0266                                  QStringLiteral("report_bug_in_current_module"),
0267                                  QStringLiteral("help_report_bug"),
0268                                  QStringLiteral("help_contents"),
0269                                  QStringLiteral("help_about_app"),
0270                                  QStringLiteral("help_about_kde")};
0271 
0272     for (const QString &actionName : actionList) {
0273         QAction *action = d->collection->action(actionName);
0274         if (action) {
0275             menu->addAction(action);
0276         }
0277     }
0278 
0279     menu->winId();
0280     menu->windowHandle()->setTransientParent(d->quickWidget->window()->windowHandle());
0281     menu->popup(position);
0282     setActionMenuVisible(true);
0283 }
0284 
0285 void SidebarMode::loadModule(const QModelIndex &activeModule, const QStringList &args)
0286 {
0287     if (!activeModule.isValid()) {
0288         return;
0289     }
0290 
0291     auto mi = activeModule.data(MenuModel::MenuItemRole).value<MenuItem *>();
0292 
0293     if (!mi) {
0294         return;
0295     }
0296 
0297     // If we are trying to load a module already open
0298     if (mi->name() == d->moduleView->activeModuleName()) {
0299         const QVariantList variantArgs(args.cbegin(), args.cend());
0300         d->moduleView->requestActivation(variantArgs);
0301         return;
0302     }
0303 
0304     if (!d->moduleView->resolveChanges()) {
0305         return;
0306     }
0307 
0308     d->moduleView->closeModules();
0309 
0310     if (homeItem()) {
0311         d->m_introPageVisible = activeModule == d->categorizedModel->mapFromSource(d->model->indexForItem(homeItem()));
0312         Q_EMIT introPageVisibleChanged();
0313     } else {
0314         setIntroPageVisible(false);
0315     }
0316 
0317     d->moduleView->loadModule(activeModule, args);
0318 
0319     if (activeModule.model() == d->categorizedModel) {
0320         const int newCategoryRow = activeModule.row();
0321 
0322         d->activeCategoryIndex = activeModule;
0323         d->activeCategoryRow = newCategoryRow;
0324 
0325         d->activeSubCategoryRow = 0;
0326 
0327         d->subCategoryModel->setParentIndex(activeModule);
0328 
0329         if (d->activeSearchRow > -1) {
0330             d->activeSearchRow = -1;
0331             Q_EMIT activeSearchRowChanged();
0332         }
0333         Q_EMIT activeCategoryRowChanged();
0334         Q_EMIT activeSubCategoryRowChanged();
0335 
0336     } else if (activeModule.model() == d->subCategoryModel) {
0337         if (d->activeSearchRow > -1) {
0338             d->activeSearchRow = -1;
0339             Q_EMIT activeSearchRowChanged();
0340         }
0341         d->activeSubCategoryRow = activeModule.row();
0342         Q_EMIT activeSubCategoryRowChanged();
0343 
0344     } else if (activeModule.model() == d->searchModel) {
0345         QModelIndex originalIndex = d->categorizedModel->mapFromSource(d->flatModel->mapToSource(d->searchModel->mapToSource(activeModule)));
0346 
0347         if (originalIndex.isValid()) {
0348             // are we in a  subcategory of the top categories?
0349             if (originalIndex.parent().isValid() && mi->parent()->menu()) {
0350                 d->activeCategoryRow = originalIndex.parent().row();
0351                 d->activeSubCategoryRow = originalIndex.row();
0352 
0353                 // Is this kcm directly at the top level without a top category?
0354             } else {
0355                 d->activeCategoryRow = originalIndex.row();
0356                 d->activeSubCategoryRow = -1;
0357             }
0358 
0359             d->subCategoryModel->setParentIndex(originalIndex.parent().isValid() ? originalIndex.parent() : originalIndex);
0360             Q_EMIT activeCategoryRowChanged();
0361             Q_EMIT activeSubCategoryRowChanged();
0362         }
0363 
0364         d->activeSearchRow = activeModule.row();
0365         Q_EMIT activeSearchRowChanged();
0366 
0367     } else {
0368         if (d->activeSearchRow > -1) {
0369             d->activeSearchRow = -1;
0370             Q_EMIT activeSearchRowChanged();
0371         }
0372 
0373         QModelIndex flatIndex;
0374 
0375         // search the corresponding item on the main model
0376         for (int i = 0; i < d->flatModel->rowCount(); ++i) {
0377             QModelIndex idx = d->flatModel->index(i, 0);
0378             auto otherMi = idx.data(MenuModel::MenuItemRole).value<MenuItem *>();
0379 
0380             if (mi->metaData().isValid() && otherMi->metaData() == mi->metaData()) {
0381                 flatIndex = idx;
0382                 break;
0383             }
0384         }
0385 
0386         if (flatIndex.isValid()) {
0387             QModelIndex idx = d->categorizedModel->mapFromSource(d->flatModel->mapToSource(flatIndex));
0388 
0389             auto parentMi = idx.parent().data(MenuModel::MenuItemRole).value<MenuItem *>();
0390 
0391             if (idx.isValid()) {
0392                 if (parentMi && parentMi->menu()) {
0393                     d->subCategoryModel->setParentIndex(idx.parent());
0394                     d->activeCategoryRow = idx.parent().row();
0395                     d->activeSubCategoryRow = idx.row();
0396                 } else {
0397                     if (d->categorizedModel->rowCount(idx) > 0) {
0398                         d->subCategoryModel->setParentIndex(idx);
0399                     }
0400                     d->activeCategoryRow = idx.row();
0401                     d->activeSubCategoryRow = -1;
0402                 }
0403                 Q_EMIT activeCategoryRowChanged();
0404                 Q_EMIT activeSubCategoryRowChanged();
0405             }
0406         }
0407     }
0408 }
0409 
0410 void SidebarMode::focusNext()
0411 {
0412     d->mainWidget->focusNext();
0413 }
0414 
0415 void SidebarMode::focusPrevious()
0416 {
0417     d->mainWidget->focusPrevious();
0418 }
0419 
0420 void SidebarMode::moduleLoaded()
0421 {
0422     if (d->placeHolderWidget) {
0423         d->placeHolderWidget->hide();
0424     }
0425     d->moduleView->show();
0426     if (applicationMode() == BaseMode::InfoCenter) {
0427         d->moduleView->setSaveStatistics(false);
0428         d->moduleView->setApplyVisible(false);
0429         d->moduleView->setDefaultsVisible(false);
0430     }
0431 }
0432 
0433 void SidebarMode::updateDefaults()
0434 {
0435     // When the landing page is loaded, we don't have activeCategoryRow and need to reload everything
0436     if (d->activeCategoryRow < 0) {
0437         refreshDefaults();
0438         return;
0439     }
0440     QModelIndex categoryIdx = d->categorizedModel->index(d->activeCategoryRow, 0);
0441     auto item = categoryIdx.data(Qt::UserRole).value<MenuItem *>();
0442     Q_ASSERT(item);
0443 
0444     // If subcategory exist update from subcategory, unless this category is owned by a kcm
0445     if (!item->children().isEmpty() && d->activeSubCategoryRow > -1) {
0446         auto subCateogryIdx = d->subCategoryModel->index(d->activeSubCategoryRow, 0);
0447         item = subCateogryIdx.data(Qt::UserRole).value<MenuItem *>();
0448     }
0449     // In case we are updating a parent like LnF we refresh all defaults
0450     if (item->isCategoryOwner() && item->parent() != rootItem()) {
0451         refreshDefaults();
0452         return;
0453     }
0454 
0455     KCModuleData *moduleData = loadModuleData(item->metaData());
0456     if (moduleData) {
0457         connect(moduleData, &KCModuleData::loaded, this, [this, item, moduleData, categoryIdx]() {
0458             item->setDefaultIndicator(!moduleData->isDefaults());
0459             updateCategoryModel(categoryIdx);
0460             moduleData->deleteLater();
0461         });
0462     }
0463 }
0464 
0465 void SidebarMode::updateCategoryModel(const QModelIndex &categoryIdx)
0466 {
0467     auto sourceIdx = d->categorizedModel->mapToSource(categoryIdx);
0468     Q_EMIT d->model->dataChanged(sourceIdx, sourceIdx);
0469 
0470     auto subCateogryIdx = d->subCategoryModel->index(d->activeSubCategoryRow, 0);
0471     auto subCategorySourceIdx = d->categorizedModel->mapToSource(d->subCategoryModel->mapToSource(subCateogryIdx));
0472     Q_EMIT d->model->dataChanged(subCategorySourceIdx, subCategorySourceIdx);
0473 }
0474 
0475 int SidebarMode::activeSearchRow() const
0476 {
0477     return d->activeSearchRow;
0478 }
0479 
0480 int SidebarMode::activeCategoryRow() const
0481 {
0482     return d->activeCategoryRow;
0483 }
0484 
0485 void SidebarMode::setIntroPageVisible(const bool &introPageVisible)
0486 {
0487     if (d->m_introPageVisible == introPageVisible) {
0488         return;
0489     }
0490 
0491     // TODO: Make the intro page of SystemSettings a KCM as well
0492     if (homeItem()) {
0493         if (d->placeHolderWidget) {
0494             d->placeHolderWidget->hide();
0495         }
0496         d->moduleView->show();
0497         if (introPageVisible) {
0498             QModelIndex index = d->categorizedModel->mapFromSource(d->model->indexForItem(homeItem()));
0499             if (index.isValid()) {
0500                 loadModule(index);
0501             } else {
0502                 d->moduleView->closeModules();
0503                 d->moduleView->loadModule(d->model->indexForItem(homeItem()), QStringList());
0504                 d->activeCategoryRow = -1;
0505                 d->activeSubCategoryRow = -1;
0506                 Q_EMIT activeCategoryRowChanged();
0507                 Q_EMIT activeSubCategoryRowChanged();
0508             }
0509         }
0510     } else {
0511         if (introPageVisible) {
0512             d->subCategoryModel->setParentIndex(QModelIndex());
0513             d->activeCategoryRow = -1;
0514             Q_EMIT activeCategoryRowChanged();
0515             d->activeSubCategoryRow = -1;
0516             Q_EMIT activeSubCategoryRowChanged();
0517             if (!d->placeHolderWidget) {
0518                 initPlaceHolderWidget();
0519             }
0520             d->placeHolderWidget->show();
0521             d->moduleView->hide();
0522         } else {
0523             if (d->placeHolderWidget) {
0524                 d->placeHolderWidget->hide();
0525             }
0526             d->moduleView->show();
0527         }
0528     }
0529 
0530     d->m_introPageVisible = introPageVisible;
0531     Q_EMIT introPageVisibleChanged();
0532 }
0533 
0534 void SidebarMode::setHeaderHeight(qreal height)
0535 {
0536     if (height == d->moduleView->headerHeight()) {
0537         return;
0538     }
0539 
0540     d->moduleView->setHeaderHeight(height);
0541     Q_EMIT headerHeightChanged();
0542 }
0543 
0544 qreal SidebarMode::headerHeight() const
0545 {
0546     return d->moduleView->headerHeight();
0547 }
0548 
0549 void SidebarMode::refreshDefaults()
0550 {
0551     if (d->m_defaultsIndicatorsVisible) {
0552         for (int i = 0; i < d->flatModel->rowCount(); ++i) {
0553             QModelIndex idx = d->flatModel->index(i, 0);
0554             auto item = idx.data(MenuModel::MenuItemRole).value<MenuItem *>();
0555             if (item->menu()) {
0556                 continue;
0557             }
0558 
0559             KCModuleData *moduleData = loadModuleData(item->metaData());
0560             if (moduleData) {
0561                 connect(moduleData, &KCModuleData::loaded, this, [this, item, moduleData]() {
0562                     item->setDefaultIndicator(!moduleData->isDefaults());
0563                     updateModelMenuItem(item);
0564                     moduleData->deleteLater();
0565                 });
0566             }
0567         }
0568     }
0569 }
0570 
0571 void SidebarMode::toggleDefaultsIndicatorsVisibility()
0572 {
0573     d->m_defaultsIndicatorsVisible = !d->m_defaultsIndicatorsVisible;
0574     d->moduleView->moduleShowDefaultsIndicators(d->m_defaultsIndicatorsVisible);
0575     refreshDefaults();
0576     config().writeEntry("HighlightNonDefaultSettings", d->m_defaultsIndicatorsVisible);
0577     Q_EMIT defaultsIndicatorsVisibleChanged();
0578 }
0579 
0580 void SidebarMode::updateModelMenuItem(MenuItem *item)
0581 {
0582     auto itemIdx = d->model->indexForItem(item);
0583     Q_EMIT d->model->dataChanged(itemIdx, itemIdx);
0584     MenuItem *parent = item->parent();
0585     while (parent) {
0586         auto parentIdx = d->model->indexForItem(parent);
0587         if (parentIdx.isValid()) {
0588             Q_EMIT d->model->dataChanged(parentIdx, parentIdx);
0589             parent = parent->parent();
0590         } else {
0591             parent = nullptr;
0592         }
0593     }
0594 }
0595 
0596 int SidebarMode::width() const
0597 {
0598     return d->mainWidget->width();
0599 }
0600 
0601 bool SidebarMode::actionMenuVisible() const
0602 {
0603     return d->actionMenuVisible;
0604 }
0605 
0606 void SidebarMode::setActionMenuVisible(bool visible)
0607 {
0608     if (d->actionMenuVisible == visible) {
0609         return;
0610     }
0611     d->actionMenuVisible = visible;
0612     Q_EMIT actionMenuVisibleChanged();
0613 }
0614 
0615 int SidebarMode::activeSubCategoryRow() const
0616 {
0617     return d->activeSubCategoryRow;
0618 }
0619 
0620 bool SidebarMode::introPageVisible() const
0621 {
0622     return (d->m_introPageVisible);
0623 }
0624 
0625 bool SidebarMode::defaultsIndicatorsVisible() const
0626 {
0627     return d->m_defaultsIndicatorsVisible;
0628 }
0629 
0630 void SidebarMode::initWidget()
0631 {
0632     // Create the widgets
0633 
0634     if (!KMainWindow::memberList().isEmpty()) {
0635         KXmlGuiWindow const *mainWindow = qobject_cast<KXmlGuiWindow *>(KMainWindow::memberList().constFirst());
0636         if (mainWindow) {
0637             d->collection = mainWindow->actionCollection();
0638         }
0639     }
0640     d->engine = BaseData::instance()->qmlEngine();
0641     d->quickWidget = new QQuickWidget(d->engine.get(), d->mainWidget);
0642     d->quickWidget->quickWindow()->setTitle(i18n("Sidebar"));
0643     d->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
0644 
0645     d->quickWidget->rootContext()->setContextProperty(QStringLiteral("systemsettings"), this);
0646 
0647     d->quickWidget->rootContext()->setContextObject(new KLocalizedContext(d->quickWidget));
0648 
0649     // Breaking change in Qt6: https://github.com/qt/qtdeclarative/commit/0d80dbd8c2cfc2a7d2a4d970b7acfc7fb5fb97a0
0650     // Use setContent to prevent the root item from being destroyed: https://github.com/qt/qtdeclarative/commit/69d61fecf2deee7510f5f2448614174683744d82
0651     QQmlComponent component(d->quickWidget->engine(),
0652                             QUrl(QStringLiteral("qrc:/qt/qml/org/kde/systemsettings/Main.qml")),
0653                             QQmlComponent::PreferSynchronous,
0654                             this);
0655     QQuickItem *item = qobject_cast<QQuickItem *>(component.create(d->quickWidget->rootContext()));
0656     if (!item) {
0657         for (const QList<QQmlError> errors = component.errors(); const auto &err : errors) {
0658             qWarning() << err.toString();
0659         }
0660         qFatal("Fatal error while loading the sidebar view qml component");
0661     }
0662     const int rootImplicitWidth = item->implicitWidth();
0663     if (rootImplicitWidth != 0) {
0664         d->quickWidget->setFixedWidth(rootImplicitWidth);
0665     } else {
0666         d->quickWidget->setFixedWidth(240);
0667     }
0668 
0669     setHeaderHeight(item->property("headerHeight").toReal());
0670 
0671     d->quickWidget->setContent(QUrl(), nullptr, item);
0672     connect(item, &QQuickItem::implicitWidthChanged, this, [this]() {
0673         const int rootImplicitWidth = d->quickWidget->rootObject()->property("implicitWidth").toInt();
0674         if (rootImplicitWidth != 0) {
0675             d->quickWidget->setFixedWidth(rootImplicitWidth);
0676         } else {
0677             d->quickWidget->setFixedWidth(240);
0678         }
0679     });
0680 
0681     d->quickWidget->installEventFilter(this);
0682 
0683     d->mainLayout->addWidget(d->quickWidget);
0684     d->moduleView->hide();
0685     d->mainLayout->addWidget(d->moduleView);
0686     Q_EMIT changeToolBarItems(BaseMode::NoItems);
0687 
0688     if (!startupModule().isEmpty()) {
0689         initPlaceHolderWidget();
0690         d->placeHolderWidget->show();
0691         MenuItem *item = rootItem()->descendantForModule(startupModule());
0692         if (item) {
0693             loadModule(d->model->indexForItem(item), startupModuleArgs());
0694         }
0695     } else if (homeItem()) {
0696         d->moduleView->show();
0697         QModelIndex index = d->categorizedModel->mapFromSource(d->model->indexForItem(homeItem()));
0698         if (index.isValid()) {
0699             loadModule(index);
0700         } else {
0701             d->moduleView->loadModule(d->model->indexForItem(homeItem()), QStringList());
0702         }
0703     } else {
0704         initPlaceHolderWidget();
0705         d->placeHolderWidget->show();
0706     }
0707 }
0708 
0709 void SidebarMode::initPlaceHolderWidget()
0710 {
0711     d->placeHolderWidget = new QQuickWidget(BaseData::instance()->qmlEngine().get(), d->mainWidget);
0712     d->placeHolderWidget->quickWindow()->setTitle(i18n("Most Used"));
0713     d->placeHolderWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
0714     d->placeHolderWidget->engine()->rootContext()->setContextObject(new KLocalizedContext(d->placeHolderWidget));
0715     d->placeHolderWidget->engine()->rootContext()->setContextProperty(QStringLiteral("systemsettings"), this);
0716     d->placeHolderWidget->setSource(QUrl(QStringLiteral("qrc:/qt/qml/org/kde/systemsettings/IntroPage.qml")));
0717     d->placeHolderWidget->installEventFilter(this);
0718 
0719     d->mainLayout->addWidget(d->placeHolderWidget);
0720 }
0721 
0722 void SidebarMode::reloadStartupModule()
0723 {
0724     if (!startupModule().isEmpty()) {
0725         MenuItem *item = rootItem()->descendantForModule(startupModule());
0726         if (item) {
0727             loadModule(d->model->indexForItem(item), startupModuleArgs());
0728         }
0729     }
0730 }
0731 
0732 bool SidebarMode::eventFilter(QObject *watched, QEvent *event)
0733 {
0734     if (watched != d->quickWidget && watched != d->mainWidget) {
0735         return BaseMode::eventFilter(watched, event);
0736     }
0737 
0738     // FIXME: those are all workarounds around the QQuickWidget brokenness
0739     switch (event->type()) {
0740     case QEvent::KeyPress: {
0741         // allow tab navigation inside the qquickwidget
0742         auto ke = static_cast<QKeyEvent *>(event);
0743         auto qqw = qobject_cast<QQuickWidget *>(watched);
0744         // Do nothing if qqw is nullptr, otherwise this would crash
0745         // This can happen if user has pressed ALT or other modifier key
0746         // to enable the quick shortcuts and there is a Widget on the same page.
0747         // BUG: 480006
0748         if (qqw == nullptr) {
0749             return false;
0750         }
0751         if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
0752             QCoreApplication::sendEvent(qqw->quickWindow(), event);
0753             return true;
0754         }
0755         break;
0756     }
0757 
0758     case QEvent::Leave: {
0759         if (watched == d->quickWidget) {
0760             QCoreApplication::sendEvent(d->quickWidget->quickWindow(), event);
0761         }
0762         break;
0763     }
0764 
0765     case QEvent::Resize: {
0766         if (watched == d->mainWidget) {
0767             Q_EMIT widthChanged();
0768         }
0769         break;
0770     }
0771 
0772     case QEvent::Show: {
0773         if (watched == d->mainWidget) {
0774             Q_EMIT changeToolBarItems(BaseMode::NoItems);
0775         }
0776         break;
0777     }
0778 
0779     default:
0780         break;
0781     }
0782 
0783     return BaseMode::eventFilter(watched, event);
0784 }
0785 
0786 void SidebarMode::giveFocus()
0787 {
0788     d->quickWidget->setFocus();
0789 }
0790 
0791 #include "moc_SidebarMode.cpp"