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