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"