File indexing completed on 2024-11-17 05:01:37
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2013 Kevin Ottens <ervin+bluesystems@kde.org> 0003 SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0004 SPDX-FileCopyrightText: 2014 Lukáš Tinkl <ltinkl@redhat.com> 0005 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0008 */ 0009 0010 #include <config-platformtheme.h> 0011 0012 #include "kdeplatformfiledialoghelper.h" 0013 #include "kdeplatformsystemtrayicon.h" 0014 #include "kdeplatformtheme.h" 0015 #include "kfontsettingsdata.h" 0016 #include "khintssettings.h" 0017 #include "kwaylandintegration.h" 0018 #include "x11integration.h" 0019 0020 #include <QApplication> 0021 #include <QDBusConnection> 0022 #include <QDBusConnectionInterface> 0023 #include <QDebug> 0024 #include <QFont> 0025 #include <QPalette> 0026 #include <QString> 0027 #include <QVariant> 0028 #include <QtQuickControls2/QQuickStyle> 0029 0030 #include <KConfigGroup> 0031 #include <KIO/Global> 0032 #include <KIO/JobUiDelegate> 0033 #include <KIO/JobUiDelegateFactory> 0034 #include <KIO/OpenWithHandlerInterface> 0035 #include <KJobWidgets> 0036 #include <KLocalizedString> 0037 #include <KStandardGuiItem> 0038 #include <KWayland/Client/connection_thread.h> 0039 #include <KWayland/Client/registry.h> 0040 #include <KWayland/Client/xdgforeign.h> 0041 #include <KWindowSystem> 0042 #include <kiconengine.h> 0043 #include <kiconloader.h> 0044 #include <kstandardshortcut.h> 0045 0046 #include "qdbusmenubar_p.h" 0047 #include "qxdgdesktopportalfiledialog_p.h" 0048 0049 static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); 0050 static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); 0051 0052 static bool checkDBusGlobalMenuAvailable() 0053 { 0054 if (qEnvironmentVariableIsSet("KDE_NO_GLOBAL_MENU")) { 0055 return false; 0056 } 0057 0058 QDBusConnection connection = QDBusConnection::sessionBus(); 0059 QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar"); 0060 return connection.interface()->isServiceRegistered(registrarService); 0061 } 0062 0063 static bool isDBusGlobalMenuAvailable() 0064 { 0065 static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable(); 0066 return dbusGlobalMenuAvailable; 0067 } 0068 0069 static QString desktopPortalService() 0070 { 0071 return QStringLiteral("org.freedesktop.impl.portal.desktop.kde"); 0072 } 0073 0074 static QString desktopPortalPath() 0075 { 0076 return QStringLiteral("/org/freedesktop/portal/desktop"); 0077 } 0078 0079 class XdgWindowExporter : public QObject 0080 { 0081 Q_OBJECT 0082 public: 0083 using QObject::QObject; 0084 virtual void run(QWidget *widget) = 0; 0085 Q_SIGNALS: 0086 void exported(const QString &id); 0087 }; 0088 0089 class XdgWindowExporterWayland : public XdgWindowExporter 0090 { 0091 public: 0092 using XdgWindowExporter::XdgWindowExporter; 0093 0094 void run(QWidget *widget) override 0095 { 0096 Q_ASSERT(widget); 0097 0098 auto connection = KWayland::Client::ConnectionThread::fromApplication(QGuiApplication::instance()); 0099 if (!connection) { 0100 Q_EMIT exported({}); 0101 return; 0102 } 0103 0104 auto registry = new KWayland::Client::Registry(this); 0105 QPointer<QWidget> maybeWidget(widget); 0106 connect(registry, &KWayland::Client::Registry::exporterUnstableV2Announced, this, [this, registry, maybeWidget](quint32 name, quint32 version) { 0107 auto exporter = registry->createXdgExporter(name, std::min(version, quint32(1)), this); 0108 if (!maybeWidget) { 0109 qWarning() << "widget was invalid, not exporting any window as transient parent"; 0110 Q_EMIT exported({}); 0111 return; 0112 } 0113 auto surface = KWayland::Client::Surface::fromWindow(maybeWidget->windowHandle()); 0114 if (!surface) { 0115 qWarning() << "wayland surface was unexpectedly null, not exporting any window as transient parent"; 0116 Q_EMIT exported({}); 0117 return; 0118 } 0119 auto xdgExported = exporter->exportTopLevel(surface, this); 0120 0121 connect(xdgExported, &KWayland::Client::XdgExported::done, this, [this, xdgExported] { 0122 Q_EMIT exported(QLatin1String("wayland:") + xdgExported->handle()); 0123 }); 0124 }); 0125 0126 registry->create(connection); 0127 registry->setup(); 0128 } 0129 }; 0130 0131 class XdgWindowExporterX11 : public XdgWindowExporter 0132 { 0133 public: 0134 using XdgWindowExporter::XdgWindowExporter; 0135 0136 void run(QWidget *widget) override 0137 { 0138 Q_ASSERT(widget); 0139 Q_EMIT exported(QLatin1String("x11:") + QString::number(widget->winId(), 16)); 0140 } 0141 }; 0142 0143 class KIOOpenWith : public KIO::OpenWithHandlerInterface 0144 { 0145 Q_OBJECT 0146 public: 0147 explicit KIOOpenWith(QWidget *parentWidget, QObject *parent = nullptr) 0148 : KIO::OpenWithHandlerInterface(parent) 0149 , m_parentWidget(parentWidget) 0150 { 0151 } 0152 0153 void promptUserForApplication(KJob *job, const QList<QUrl> &urls, const QString &mimeType) override 0154 { 0155 QWidget *widget = nullptr; 0156 if (job) { 0157 widget = KJobWidgets::window(job); 0158 } 0159 0160 if (!widget) { 0161 widget = m_parentWidget; 0162 } 0163 0164 if (!widget) { 0165 promptInternal({}, urls, mimeType); 0166 return; 0167 } 0168 0169 widget->winId(); // ensure we have a handle so we can export a window (without this windowHandle() may be null) 0170 0171 XdgWindowExporter *exporter = nullptr; 0172 switch (KWindowSystem::platform()) { 0173 case KWindowSystem::Platform::X11: 0174 exporter = new XdgWindowExporterX11(this); 0175 break; 0176 case KWindowSystem::Platform::Wayland: 0177 exporter = new XdgWindowExporterWayland(this); 0178 break; 0179 case KWindowSystem::Platform::Unknown: 0180 break; 0181 } 0182 if (exporter) { 0183 connect(exporter, &XdgWindowExporter::exported, this, [this, urls, exporter, mimeType](const QString &windowId) { 0184 exporter->deleteLater(); 0185 promptInternal(windowId, urls, mimeType); 0186 }); 0187 exporter->run(widget); 0188 } else { 0189 promptInternal({}, urls, mimeType); 0190 } 0191 } 0192 0193 void promptInternal(const QString &windowId, const QList<QUrl> &urls, const QString &mimeType) 0194 { 0195 QDBusMessage message = QDBusMessage::createMethodCall(desktopPortalService(), 0196 desktopPortalPath(), 0197 QStringLiteral("org.freedesktop.impl.portal.AppChooser"), 0198 QStringLiteral("ChooseApplicationPrivate")); 0199 0200 QStringList urlStrings; 0201 for (const auto &url : urls) { 0202 urlStrings << url.toString(); 0203 } 0204 0205 QString lastChoice; 0206 KSharedConfig::Ptr xdgConfig; 0207 if (!mimeType.isEmpty()) { 0208 xdgConfig = KSharedConfig::openConfig(QStringLiteral("xdg-desktop-portal-kderc"), KConfig::NoGlobals); 0209 KConfigGroup appChooserGroup = xdgConfig->group("org.freedesktop.impl.portal.AppChooser"); 0210 if (appChooserGroup.isValid()) { 0211 KConfigGroup lastUsedGroup = appChooserGroup.group("LastChoice"); 0212 if (lastUsedGroup.isValid()) { 0213 lastChoice = lastUsedGroup.readEntry(mimeType); 0214 } 0215 } 0216 } 0217 0218 message << windowId << urlStrings 0219 << QVariantMap{ 0220 {QStringLiteral("ask"), true}, 0221 {QStringLiteral("last_choice"), lastChoice}, 0222 }; 0223 0224 QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message, std::numeric_limits<int>::max()); 0225 auto watcher = new QDBusPendingCallWatcher(pendingCall, this); 0226 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, mimeType](QDBusPendingCallWatcher *watcher) { 0227 watcher->deleteLater(); 0228 0229 QDBusPendingReply<uint, QVariantMap> reply = *watcher; 0230 if (reply.isError()) { 0231 qWarning() << "Couldn't get reply"; 0232 qWarning() << "Error: " << reply.error().message(); 0233 Q_EMIT canceled(); 0234 } else { 0235 if (reply.argumentAt<0>() == 0) { 0236 const QString choice = reply.argumentAt<1>().value(QStringLiteral("choice")).toString(); 0237 Q_EMIT serviceSelected(KService::serviceByDesktopName(choice)); 0238 0239 if (!mimeType.isEmpty()) { 0240 KSharedConfig::Ptr xdgConfig = KSharedConfig::openConfig(QStringLiteral("xdg-desktop-portal-kderc"), KConfig::NoGlobals); 0241 KConfigGroup appChooserGroup = xdgConfig->group("org.freedesktop.impl.portal.AppChooser"); 0242 KConfigGroup lastUsedGroup = appChooserGroup.group("LastChoice"); 0243 lastUsedGroup.writeEntry(mimeType, choice); 0244 } 0245 } else { 0246 Q_EMIT canceled(); 0247 } 0248 } 0249 }); 0250 } 0251 0252 private: 0253 QWidget *const m_parentWidget; 0254 }; 0255 0256 class KIOUiDelegate : public KIO::JobUiDelegate 0257 { 0258 public: 0259 explicit KIOUiDelegate(KJobUiDelegate::Flags flags = AutoHandlingDisabled, QWidget *window = nullptr) 0260 : KIO::JobUiDelegate(KIO::JobUiDelegate::Version::V2, flags, window, {new KIOOpenWith(window, nullptr)}) 0261 { 0262 } 0263 }; 0264 0265 class KIOUiFactory : public KIO::JobUiDelegateFactoryV2 0266 { 0267 public: 0268 KIOUiFactory() = default; 0269 0270 KJobUiDelegate *createDelegate() const override 0271 { 0272 return new KIOUiDelegate; 0273 } 0274 0275 KJobUiDelegate *createDelegate(KJobUiDelegate::Flags flags, QWidget *window) const override 0276 { 0277 return new KIOUiDelegate(flags, window); 0278 } 0279 }; 0280 0281 KdePlatformTheme::KdePlatformTheme() 0282 { 0283 loadSettings(); 0284 0285 // explicitly not KWindowSystem::isPlatformWayland to not include the kwin process 0286 if (QGuiApplication::platformName() == QLatin1String("wayland")) { 0287 m_kwaylandIntegration.reset(new KWaylandIntegration(this)); 0288 } 0289 0290 #if HAVE_X11 0291 if (KWindowSystem::isPlatformX11()) { 0292 m_x11Integration.reset(new X11Integration(this)); 0293 m_x11Integration->init(); 0294 } 0295 #endif 0296 0297 // Don't show the titlebar "What's This?" help button for dialogs that 0298 // have any UI elements with help text, unless the window specifically 0299 // requests it. This is because KDE apps with help text will use the nice 0300 // contextual tooltips instead; we only want to ever see the titlebar button 0301 // to invoke the "What's This?" feature in 3rd-party Qt apps that have set 0302 // "What's This" help text and requested the button 0303 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton, true); 0304 0305 QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false); 0306 setQtQuickControlsTheme(); 0307 0308 static KIOUiFactory factory; 0309 KIO::setDefaultJobUiDelegateFactoryV2(&factory); 0310 0311 static KIOUiDelegate delegateExtension; 0312 KIO::setDefaultJobUiDelegateExtension(&delegateExtension); 0313 } 0314 0315 KdePlatformTheme::~KdePlatformTheme() 0316 { 0317 delete m_fontsData; 0318 delete m_hints; 0319 } 0320 0321 QVariant KdePlatformTheme::themeHint(QPlatformTheme::ThemeHint hintType) const 0322 { 0323 QVariant hint = m_hints->hint(hintType); 0324 if (hint.isValid()) { 0325 return hint; 0326 } else { 0327 return QPlatformTheme::themeHint(hintType); 0328 } 0329 } 0330 0331 QIcon KdePlatformTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const 0332 { 0333 if (iconOptions.testFlag(DontUseCustomDirectoryIcons) && fileInfo.isDir()) { 0334 return QIcon::fromTheme(QLatin1String("inode-directory")); 0335 } 0336 0337 return QIcon::fromTheme(KIO::iconNameForUrl(QUrl::fromLocalFile(fileInfo.absoluteFilePath()))); 0338 } 0339 0340 const QPalette *KdePlatformTheme::palette(Palette type) const 0341 { 0342 QPalette *palette = m_hints->palette(type); 0343 if (palette) { 0344 return palette; 0345 } else { 0346 return QPlatformTheme::palette(type); 0347 } 0348 } 0349 0350 const QFont *KdePlatformTheme::font(Font type) const 0351 { 0352 KFontSettingsData::FontTypes fdtype; 0353 switch (type) { 0354 case SystemFont: 0355 fdtype = KFontSettingsData::GeneralFont; 0356 break; 0357 case MenuFont: 0358 case MenuBarFont: 0359 case MenuItemFont: 0360 fdtype = KFontSettingsData::MenuFont; 0361 break; 0362 case MessageBoxFont: 0363 case LabelFont: 0364 case TipLabelFont: 0365 case StatusBarFont: 0366 case PushButtonFont: 0367 case ItemViewFont: 0368 case ListViewFont: 0369 case HeaderViewFont: 0370 case ListBoxFont: 0371 case ComboMenuItemFont: 0372 case ComboLineEditFont: 0373 fdtype = KFontSettingsData::GeneralFont; 0374 break; 0375 case TitleBarFont: 0376 case MdiSubWindowTitleFont: 0377 case DockWidgetTitleFont: 0378 fdtype = KFontSettingsData::WindowTitleFont; 0379 break; 0380 case SmallFont: 0381 case MiniFont: 0382 fdtype = KFontSettingsData::SmallestReadableFont; 0383 break; 0384 case FixedFont: 0385 fdtype = KFontSettingsData::FixedFont; 0386 break; 0387 case ToolButtonFont: 0388 fdtype = KFontSettingsData::ToolbarFont; 0389 break; 0390 default: 0391 fdtype = KFontSettingsData::GeneralFont; 0392 break; 0393 } 0394 0395 return m_fontsData->font(fdtype); 0396 } 0397 0398 QIconEngine *KdePlatformTheme::createIconEngine(const QString &iconName) const 0399 { 0400 return new KIconEngine(iconName, KIconLoader::global()); 0401 } 0402 0403 void KdePlatformTheme::loadSettings() 0404 { 0405 m_fontsData = new KFontSettingsData; 0406 m_hints = new KHintsSettings; 0407 } 0408 0409 QList<QKeySequence> KdePlatformTheme::keyBindings(QKeySequence::StandardKey key) const 0410 { 0411 switch (key) { 0412 case QKeySequence::HelpContents: 0413 return KStandardShortcut::shortcut(KStandardShortcut::Help); 0414 case QKeySequence::WhatsThis: 0415 return KStandardShortcut::shortcut(KStandardShortcut::WhatsThis); 0416 case QKeySequence::Open: 0417 return KStandardShortcut::shortcut(KStandardShortcut::Open); 0418 case QKeySequence::Close: 0419 return KStandardShortcut::shortcut(KStandardShortcut::Close); 0420 case QKeySequence::Save: 0421 return KStandardShortcut::shortcut(KStandardShortcut::Save); 0422 case QKeySequence::New: 0423 return KStandardShortcut::shortcut(KStandardShortcut::New); 0424 case QKeySequence::Cut: 0425 return KStandardShortcut::shortcut(KStandardShortcut::Cut); 0426 case QKeySequence::Copy: 0427 return KStandardShortcut::shortcut(KStandardShortcut::Copy); 0428 case QKeySequence::Paste: 0429 return KStandardShortcut::shortcut(KStandardShortcut::Paste); 0430 case QKeySequence::Undo: 0431 return KStandardShortcut::shortcut(KStandardShortcut::Undo); 0432 case QKeySequence::Redo: 0433 return KStandardShortcut::shortcut(KStandardShortcut::Redo); 0434 case QKeySequence::Back: 0435 return KStandardShortcut::shortcut(KStandardShortcut::Back); 0436 case QKeySequence::Forward: 0437 return KStandardShortcut::shortcut(KStandardShortcut::Forward); 0438 case QKeySequence::Refresh: 0439 return KStandardShortcut::shortcut(KStandardShortcut::Reload); 0440 case QKeySequence::ZoomIn: 0441 return KStandardShortcut::shortcut(KStandardShortcut::ZoomIn); 0442 case QKeySequence::ZoomOut: 0443 return KStandardShortcut::shortcut(KStandardShortcut::ZoomOut); 0444 case QKeySequence::Print: 0445 return KStandardShortcut::shortcut(KStandardShortcut::Print); 0446 case QKeySequence::Find: 0447 return KStandardShortcut::shortcut(KStandardShortcut::Find); 0448 case QKeySequence::FindNext: 0449 return KStandardShortcut::shortcut(KStandardShortcut::FindNext); 0450 case QKeySequence::FindPrevious: 0451 return KStandardShortcut::shortcut(KStandardShortcut::FindPrev); 0452 case QKeySequence::Replace: 0453 return KStandardShortcut::shortcut(KStandardShortcut::Replace); 0454 case QKeySequence::SelectAll: 0455 return KStandardShortcut::shortcut(KStandardShortcut::SelectAll); 0456 case QKeySequence::MoveToNextWord: 0457 return KStandardShortcut::shortcut(KStandardShortcut::ForwardWord); 0458 case QKeySequence::MoveToPreviousWord: 0459 return KStandardShortcut::shortcut(KStandardShortcut::BackwardWord); 0460 case QKeySequence::MoveToNextPage: 0461 return KStandardShortcut::shortcut(KStandardShortcut::Next); 0462 case QKeySequence::MoveToPreviousPage: 0463 return KStandardShortcut::shortcut(KStandardShortcut::Prior); 0464 case QKeySequence::MoveToStartOfLine: 0465 return KStandardShortcut::shortcut(KStandardShortcut::BeginningOfLine); 0466 case QKeySequence::MoveToEndOfLine: 0467 return KStandardShortcut::shortcut(KStandardShortcut::EndOfLine); 0468 case QKeySequence::MoveToStartOfDocument: 0469 return KStandardShortcut::shortcut(KStandardShortcut::Begin); 0470 case QKeySequence::MoveToEndOfDocument: 0471 return KStandardShortcut::shortcut(KStandardShortcut::End); 0472 case QKeySequence::SaveAs: 0473 return KStandardShortcut::shortcut(KStandardShortcut::SaveAs); 0474 case QKeySequence::Preferences: 0475 return KStandardShortcut::shortcut(KStandardShortcut::Preferences); 0476 case QKeySequence::Quit: 0477 return KStandardShortcut::shortcut(KStandardShortcut::Quit); 0478 case QKeySequence::FullScreen: 0479 return KStandardShortcut::shortcut(KStandardShortcut::FullScreen); 0480 case QKeySequence::Deselect: 0481 return KStandardShortcut::shortcut(KStandardShortcut::Deselect); 0482 case QKeySequence::DeleteStartOfWord: 0483 return KStandardShortcut::shortcut(KStandardShortcut::DeleteWordBack); 0484 case QKeySequence::DeleteEndOfWord: 0485 return KStandardShortcut::shortcut(KStandardShortcut::DeleteWordForward); 0486 case QKeySequence::NextChild: 0487 return KStandardShortcut::shortcut(KStandardShortcut::TabNext); 0488 case QKeySequence::PreviousChild: 0489 return KStandardShortcut::shortcut(KStandardShortcut::TabPrev); 0490 case QKeySequence::Delete: 0491 return KStandardShortcut::shortcut(KStandardShortcut::MoveToTrash); 0492 default: 0493 return QPlatformTheme::keyBindings(key); 0494 } 0495 } 0496 0497 bool KdePlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const 0498 { 0499 return type == QPlatformTheme::FileDialog && qobject_cast<QApplication *>(QCoreApplication::instance()); 0500 } 0501 0502 QString KdePlatformTheme::standardButtonText(int button) const 0503 { 0504 switch (static_cast<QPlatformDialogHelper::StandardButton>(button)) { 0505 case QPlatformDialogHelper::NoButton: 0506 qWarning() << Q_FUNC_INFO << "Unsupported standard button:" << button; 0507 return QString(); 0508 case QPlatformDialogHelper::Ok: 0509 return KStandardGuiItem::ok().text(); 0510 case QPlatformDialogHelper::Save: 0511 return KStandardGuiItem::save().text(); 0512 case QPlatformDialogHelper::SaveAll: 0513 return i18nc("@action:button", "Save All"); 0514 case QPlatformDialogHelper::Open: 0515 return KStandardGuiItem::open().text(); 0516 case QPlatformDialogHelper::Yes: 0517 return i18nc("@action:button", "&Yes"); 0518 case QPlatformDialogHelper::YesToAll: 0519 return i18nc("@action:button", "Yes to All"); 0520 case QPlatformDialogHelper::No: 0521 return i18nc("@action:button", "&No"); 0522 case QPlatformDialogHelper::NoToAll: 0523 return i18nc("@action:button", "No to All"); 0524 case QPlatformDialogHelper::Abort: 0525 // FIXME KStandardGuiItem::stop() doesn't seem right here 0526 return i18nc("@action:button", "Abort"); 0527 case QPlatformDialogHelper::Retry: 0528 return i18nc("@action:button", "Retry"); 0529 case QPlatformDialogHelper::Ignore: 0530 return i18nc("@action:button", "Ignore"); 0531 case QPlatformDialogHelper::Close: 0532 return KStandardGuiItem::close().text(); 0533 case QPlatformDialogHelper::Cancel: 0534 return KStandardGuiItem::cancel().text(); 0535 case QPlatformDialogHelper::Discard: 0536 return KStandardGuiItem::discard().text(); 0537 case QPlatformDialogHelper::Help: 0538 return KStandardGuiItem::help().text(); 0539 case QPlatformDialogHelper::Apply: 0540 return KStandardGuiItem::apply().text(); 0541 case QPlatformDialogHelper::Reset: 0542 return KStandardGuiItem::reset().text(); 0543 case QPlatformDialogHelper::RestoreDefaults: 0544 return KStandardGuiItem::defaults().text(); 0545 default: 0546 return QPlatformTheme::defaultStandardButtonText(button); 0547 } 0548 } 0549 0550 QPlatformDialogHelper *KdePlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const 0551 { 0552 switch (type) { 0553 case QPlatformTheme::FileDialog: 0554 if (useXdgDesktopPortal()) { 0555 return new QXdgDesktopPortalFileDialog; 0556 } 0557 return new KDEPlatformFileDialogHelper; 0558 case QPlatformTheme::FontDialog: 0559 case QPlatformTheme::ColorDialog: 0560 case QPlatformTheme::MessageDialog: 0561 default: 0562 return nullptr; 0563 } 0564 } 0565 0566 QPlatformSystemTrayIcon *KdePlatformTheme::createPlatformSystemTrayIcon() const 0567 { 0568 return new KDEPlatformSystemTrayIcon; 0569 } 0570 0571 QPlatformMenuBar *KdePlatformTheme::createPlatformMenuBar() const 0572 { 0573 if (isDBusGlobalMenuAvailable()) { 0574 #ifndef KF6_TODO_DBUS_MENUBAR 0575 auto *menu = new QDBusMenuBar(const_cast<KdePlatformTheme *>(this)); 0576 0577 QObject::connect(menu, &QDBusMenuBar::windowChanged, menu, [this, menu](QWindow *newWindow, QWindow *oldWindow) { 0578 const QString &serviceName = QDBusConnection::sessionBus().baseService(); 0579 const QString &objectPath = menu->objectPath(); 0580 0581 setMenuBarForWindow(oldWindow, {}, {}); 0582 setMenuBarForWindow(newWindow, serviceName, objectPath); 0583 }); 0584 0585 return menu; 0586 #endif 0587 } 0588 0589 return nullptr; 0590 } 0591 0592 // Force QtQuickControls to use org.kde.desktop theme for QApplication, 0593 // and org.kde.breeze for QGuiApplication unless it's otherwise set. 0594 void KdePlatformTheme::setQtQuickControlsTheme() 0595 { 0596 // if the user is running only a QGuiApplication, explicitly unset the QQC1 desktop style and use 0597 // org.kde.breeze style if it's installed. 0598 if (!qobject_cast<QApplication *>(qApp)) { 0599 if (qgetenv("QT_QUICK_CONTROLS_1_STYLE").right(7) == "Desktop") { 0600 qunsetenv("QT_QUICK_CONTROLS_1_STYLE"); 0601 } 0602 QQuickStyle::setStyle(QLatin1String("org.kde.breeze")); 0603 return; 0604 } 0605 // if the user has explicitly set something else, don't meddle 0606 // Also ignore the default Fusion style 0607 if (!QQuickStyle::name().isEmpty() && QQuickStyle::name() != QLatin1String("Fusion")) { 0608 return; 0609 } 0610 QQuickStyle::setStyle(QLatin1String("org.kde.desktop")); 0611 } 0612 0613 bool KdePlatformTheme::useXdgDesktopPortal() 0614 { 0615 static bool usePortal = qEnvironmentVariableIntValue("PLASMA_INTEGRATION_USE_PORTAL") == 1; 0616 return usePortal; 0617 } 0618 0619 inline bool windowRelevantForGlobalMenu(QWindow *window) 0620 { 0621 return !(window->type() & Qt::WindowType::Popup); 0622 } 0623 0624 void KdePlatformTheme::globalMenuBarExistsNow() 0625 { 0626 #ifndef KF6_TODO_DBUS_MENUBAR 0627 const QString &serviceName = QDBusConnection::sessionBus().baseService(); 0628 const QString &objectPath = QDBusMenuBar::globalMenuBar()->objectPath(); 0629 0630 for (auto *window : qApp->topLevelWindows()) { 0631 if (QDBusMenuBar::menuBarForWindow(window)) 0632 continue; 0633 if (!windowRelevantForGlobalMenu(window)) 0634 return; 0635 0636 setMenuBarForWindow(window, serviceName, objectPath); 0637 } 0638 #endif 0639 } 0640 0641 void KdePlatformTheme::windowCreated(QWindow *window) 0642 { 0643 #ifndef KF6_TODO_DBUS_MENUBAR 0644 if (!QDBusMenuBar::globalMenuBar()) 0645 return; 0646 0647 if (QDBusMenuBar::menuBarForWindow(window)) 0648 return; 0649 0650 const QString &serviceName = QDBusConnection::sessionBus().baseService(); 0651 const QString &objectPath = QDBusMenuBar::globalMenuBar()->objectPath(); 0652 0653 setMenuBarForWindow(window, serviceName, objectPath); 0654 #endif 0655 } 0656 0657 void KdePlatformTheme::globalMenuBarNoLongerExists() 0658 { 0659 #ifndef KF6_TODO_DBUS_MENUBAR 0660 for (auto *window : qApp->topLevelWindows()) { 0661 if (QDBusMenuBar::menuBarForWindow(window)) 0662 continue; 0663 if (!windowRelevantForGlobalMenu(window)) 0664 return; 0665 0666 setMenuBarForWindow(window, {}, {}); 0667 } 0668 #endif 0669 } 0670 0671 void KdePlatformTheme::setMenuBarForWindow(QWindow *window, const QString &serviceName, const QString &objectPath) const 0672 { 0673 if (!window) 0674 return; 0675 0676 if (m_x11Integration) { 0677 m_x11Integration->setWindowProperty(window, s_x11AppMenuServiceNamePropertyName, serviceName.toUtf8()); 0678 m_x11Integration->setWindowProperty(window, s_x11AppMenuObjectPathPropertyName, objectPath.toUtf8()); 0679 } 0680 0681 if (m_kwaylandIntegration) { 0682 m_kwaylandIntegration->setAppMenu(window, serviceName, objectPath); 0683 } 0684 } 0685 #include "kdeplatformtheme.moc"