diff --git a/src/platformtheme/kdeplatformtheme.cpp b/src/platformtheme/kdeplatformtheme.cpp index 44a3146..451b2c5 100644 --- a/src/platformtheme/kdeplatformtheme.cpp +++ b/src/platformtheme/kdeplatformtheme.cpp @@ -1,413 +1,408 @@ /* This file is part of the KDE libraries * Copyright 2013 Kevin Ottens * Copyright 2013 Aleix Pol Gonzalez * Copyright 2014 Lukáš Tinkl * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include "kdeplatformtheme.h" #include "kfontsettingsdata.h" #include "khintssettings.h" #include "kdeplatformfiledialoghelper.h" #include "kdeplatformsystemtrayicon.h" #include "kwaylandintegration.h" #include "x11integration.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5,7,0) #include "qdbusmenubar_p.h" #endif static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); -static const QByteArray s_waylandAppMenuServiceNamePropertyName = QByteArrayLiteral("KDE_APPMENU_SERVICE_NAME"); -static const QByteArray s_waylandAppMenuObjectPathPropertyName = QByteArrayLiteral("KDE_APPMENU_OBJECT_PATH"); - static bool checkDBusGlobalMenuAvailable() { if (qEnvironmentVariableIsSet("KDE_NO_GLOBAL_MENU")) { return false; } QDBusConnection connection = QDBusConnection::sessionBus(); QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar"); return connection.interface()->isServiceRegistered(registrarService); } static bool isDBusGlobalMenuAvailable() { static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable(); return dbusGlobalMenuAvailable; } KdePlatformTheme::KdePlatformTheme() { loadSettings(); if (KWindowSystem::isPlatformWayland()) { m_kwaylandIntegration.reset(new KWaylandIntegration()); m_kwaylandIntegration->init(); } #if HAVE_X11 if (KWindowSystem::isPlatformX11()) { m_x11Integration.reset(new X11Integration()); m_x11Integration->init(); } #endif QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false); setQtQuickControlsTheme(); } KdePlatformTheme::~KdePlatformTheme() { delete m_fontsData; delete m_hints; } QVariant KdePlatformTheme::themeHint(QPlatformTheme::ThemeHint hintType) const { QVariant hint = m_hints->hint(hintType); if (hint.isValid()) { return hint; } else { return QPlatformTheme::themeHint(hintType); } } QIcon KdePlatformTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const { if (iconOptions.testFlag(DontUseCustomDirectoryIcons) && fileInfo.isDir()) { return QIcon::fromTheme(QLatin1String("inode-directory")); } return QIcon::fromTheme(KIO::iconNameForUrl(QUrl::fromLocalFile(fileInfo.absoluteFilePath()))); } #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) QPixmap KdePlatformTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size, QPlatformTheme::IconOptions iconOptions) const { return fileIcon(fileInfo, iconOptions).pixmap(size.toSize(), QIcon::Normal); } #endif const QPalette *KdePlatformTheme::palette(Palette type) const { QPalette *palette = m_hints->palette(type); if (palette) { return palette; } else { return QPlatformTheme::palette(type); } } const QFont *KdePlatformTheme::font(Font type) const { KFontSettingsData::FontTypes fdtype; switch (type) { case SystemFont: fdtype = KFontSettingsData::GeneralFont; break; case MenuFont: case MenuBarFont: case MenuItemFont: fdtype = KFontSettingsData::MenuFont; break; case MessageBoxFont: case LabelFont: case TipLabelFont: case StatusBarFont: case PushButtonFont: case ItemViewFont: case ListViewFont: case HeaderViewFont: case ListBoxFont: case ComboMenuItemFont: case ComboLineEditFont: fdtype = KFontSettingsData::GeneralFont; break; case TitleBarFont: case MdiSubWindowTitleFont: case DockWidgetTitleFont: fdtype = KFontSettingsData::WindowTitleFont; break; case SmallFont: case MiniFont: fdtype = KFontSettingsData::SmallestReadableFont; break; case FixedFont: fdtype = KFontSettingsData::FixedFont; break; case ToolButtonFont: fdtype = KFontSettingsData::ToolbarFont; break; default: fdtype = KFontSettingsData::GeneralFont; break; } return m_fontsData->font(fdtype); } QIconEngine *KdePlatformTheme::createIconEngine(const QString &iconName) const { return new KIconEngine(iconName, KIconLoader::global()); } void KdePlatformTheme::loadSettings() { m_fontsData = new KFontSettingsData; m_hints = new KHintsSettings; } QList KdePlatformTheme::keyBindings(QKeySequence::StandardKey key) const { switch (key) { case QKeySequence::HelpContents: return KStandardShortcut::shortcut(KStandardShortcut::Help); case QKeySequence::WhatsThis: return KStandardShortcut::shortcut(KStandardShortcut::WhatsThis); case QKeySequence::Open: return KStandardShortcut::shortcut(KStandardShortcut::Open); case QKeySequence::Close: return KStandardShortcut::shortcut(KStandardShortcut::Close); case QKeySequence::Save: return KStandardShortcut::shortcut(KStandardShortcut::Save); case QKeySequence::New: return KStandardShortcut::shortcut(KStandardShortcut::New); case QKeySequence::Cut: return KStandardShortcut::shortcut(KStandardShortcut::Cut); case QKeySequence::Copy: return KStandardShortcut::shortcut(KStandardShortcut::Copy); case QKeySequence::Paste: return KStandardShortcut::shortcut(KStandardShortcut::Paste); case QKeySequence::Undo: return KStandardShortcut::shortcut(KStandardShortcut::Undo); case QKeySequence::Redo: return KStandardShortcut::shortcut(KStandardShortcut::Redo); case QKeySequence::Back: return KStandardShortcut::shortcut(KStandardShortcut::Back); case QKeySequence::Forward: return KStandardShortcut::shortcut(KStandardShortcut::Forward); case QKeySequence::Refresh: return KStandardShortcut::shortcut(KStandardShortcut::Reload); case QKeySequence::ZoomIn: return KStandardShortcut::shortcut(KStandardShortcut::ZoomIn); case QKeySequence::ZoomOut: return KStandardShortcut::shortcut(KStandardShortcut::ZoomOut); case QKeySequence::Print: return KStandardShortcut::shortcut(KStandardShortcut::Print); case QKeySequence::Find: return KStandardShortcut::shortcut(KStandardShortcut::Find); case QKeySequence::FindNext: return KStandardShortcut::shortcut(KStandardShortcut::FindNext); case QKeySequence::FindPrevious: return KStandardShortcut::shortcut(KStandardShortcut::FindPrev); case QKeySequence::Replace: return KStandardShortcut::shortcut(KStandardShortcut::Replace); case QKeySequence::SelectAll: return KStandardShortcut::shortcut(KStandardShortcut::SelectAll); case QKeySequence::MoveToNextWord: return KStandardShortcut::shortcut(KStandardShortcut::ForwardWord); case QKeySequence::MoveToPreviousWord: return KStandardShortcut::shortcut(KStandardShortcut::BackwardWord); case QKeySequence::MoveToNextPage: return KStandardShortcut::shortcut(KStandardShortcut::Next); case QKeySequence::MoveToPreviousPage: return KStandardShortcut::shortcut(KStandardShortcut::Prior); case QKeySequence::MoveToStartOfLine: return KStandardShortcut::shortcut(KStandardShortcut::BeginningOfLine); case QKeySequence::MoveToEndOfLine: return KStandardShortcut::shortcut(KStandardShortcut::EndOfLine); case QKeySequence::MoveToStartOfDocument: return KStandardShortcut::shortcut(KStandardShortcut::Begin); case QKeySequence::MoveToEndOfDocument: return KStandardShortcut::shortcut(KStandardShortcut::End); case QKeySequence::SaveAs: return KStandardShortcut::shortcut(KStandardShortcut::SaveAs); case QKeySequence::Preferences: return KStandardShortcut::shortcut(KStandardShortcut::Preferences); case QKeySequence::Quit: return KStandardShortcut::shortcut(KStandardShortcut::Quit); case QKeySequence::FullScreen: return KStandardShortcut::shortcut(KStandardShortcut::FullScreen); case QKeySequence::Deselect: return KStandardShortcut::shortcut(KStandardShortcut::Deselect); case QKeySequence::DeleteStartOfWord: return KStandardShortcut::shortcut(KStandardShortcut::DeleteWordBack); case QKeySequence::DeleteEndOfWord: return KStandardShortcut::shortcut(KStandardShortcut::DeleteWordForward); case QKeySequence::NextChild: return KStandardShortcut::shortcut(KStandardShortcut::TabNext); case QKeySequence::PreviousChild: return KStandardShortcut::shortcut(KStandardShortcut::TabPrev); default: return QPlatformTheme::keyBindings(key); } } bool KdePlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const { return type == QPlatformTheme::FileDialog && qobject_cast(QCoreApplication::instance()); } QString KdePlatformTheme::standardButtonText(int button) const { switch (static_cast(button)) { case QPlatformDialogHelper::NoButton: qWarning() << Q_FUNC_INFO << "Unsupported standard button:" << button; return QString(); case QPlatformDialogHelper::Ok: return KStandardGuiItem::ok().text(); case QPlatformDialogHelper::Save: return KStandardGuiItem::save().text(); case QPlatformDialogHelper::SaveAll: return i18nc("@action:button", "Save All"); case QPlatformDialogHelper::Open: return KStandardGuiItem::open().text(); case QPlatformDialogHelper::Yes: return KStandardGuiItem::yes().text(); case QPlatformDialogHelper::YesToAll: return i18nc("@action:button", "Yes to All"); case QPlatformDialogHelper::No: return KStandardGuiItem::no().text(); case QPlatformDialogHelper::NoToAll: return i18nc("@action:button", "No to All"); case QPlatformDialogHelper::Abort: // FIXME KStandardGuiItem::stop() doesn't seem right here return i18nc("@action:button", "Abort"); case QPlatformDialogHelper::Retry: return i18nc("@action:button", "Retry"); case QPlatformDialogHelper::Ignore: return i18nc("@action:button", "Ignore"); case QPlatformDialogHelper::Close: return KStandardGuiItem::close().text(); case QPlatformDialogHelper::Cancel: return KStandardGuiItem::cancel().text(); case QPlatformDialogHelper::Discard: return KStandardGuiItem::discard().text(); case QPlatformDialogHelper::Help: return KStandardGuiItem::help().text(); case QPlatformDialogHelper::Apply: return KStandardGuiItem::apply().text(); case QPlatformDialogHelper::Reset: return KStandardGuiItem::reset().text(); case QPlatformDialogHelper::RestoreDefaults: return KStandardGuiItem::defaults().text(); default: return QPlatformTheme::defaultStandardButtonText(button); } } QPlatformDialogHelper *KdePlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const { switch (type) { case QPlatformTheme::FileDialog: return new KDEPlatformFileDialogHelper; case QPlatformTheme::FontDialog: case QPlatformTheme::ColorDialog: case QPlatformTheme::MessageDialog: default: return 0; } } QPlatformSystemTrayIcon *KdePlatformTheme::createPlatformSystemTrayIcon() const { return new KDEPlatformSystemTrayIcon; } #if QT_VERSION >= QT_VERSION_CHECK(5,7,0) QPlatformMenuBar *KdePlatformTheme::createPlatformMenuBar() const { if (isDBusGlobalMenuAvailable()) { auto *menu = new QDBusMenuBar(); QObject::connect(menu, &QDBusMenuBar::windowChanged, menu, [this, menu](QWindow *newWindow, QWindow *oldWindow) { const QString &serviceName = QDBusConnection::sessionBus().baseService(); const QString &objectPath = menu->objectPath(); if (m_x11Integration) { if (oldWindow) { m_x11Integration->setWindowProperty(oldWindow, s_x11AppMenuServiceNamePropertyName, {}); m_x11Integration->setWindowProperty(oldWindow, s_x11AppMenuObjectPathPropertyName, {}); } if (newWindow) { m_x11Integration->setWindowProperty(newWindow, s_x11AppMenuServiceNamePropertyName, serviceName.toUtf8()); m_x11Integration->setWindowProperty(newWindow, s_x11AppMenuObjectPathPropertyName, objectPath.toUtf8()); } } if (m_kwaylandIntegration) { if (oldWindow) { - m_kwaylandIntegration->setWindowProperty(oldWindow, s_waylandAppMenuServiceNamePropertyName, {}); - m_kwaylandIntegration->setWindowProperty(oldWindow, s_waylandAppMenuObjectPathPropertyName, {}); + m_kwaylandIntegration->setAppMenu(QString(), QString()); } if (newWindow) { - m_kwaylandIntegration->setWindowProperty(newWindow, s_waylandAppMenuServiceNamePropertyName, serviceName.toUtf8()); - m_kwaylandIntegration->setWindowProperty(newWindow, s_waylandAppMenuObjectPathPropertyName, objectPath.toUtf8()); + m_kwaylandIntegration->setAppMenu(serviceName, objectPath); } } }); return menu; } return nullptr; } #endif //force QtQuickControls2 to use the desktop theme as default void KdePlatformTheme::setQtQuickControlsTheme() { //if the user is running only a QGuiApplication, explicitely unset the QQC1 desktop style and abort //as this style is all about QWidgets and we know setting this will make it crash if (!qobject_cast(qApp)) { if (qgetenv("QT_QUICK_CONTROLS_1_STYLE").right(7) == "Desktop") { qunsetenv("QT_QUICK_CONTROLS_1_STYLE"); } return; } //if the user has explicitly set something else, don't meddle if (!QQuickStyle::name().isEmpty()) { return; } QQuickStyle::setStyle(QLatin1String("org.kde.desktop")); } #include "kdeplatformtheme.moc" diff --git a/src/platformtheme/kwaylandintegration.cpp b/src/platformtheme/kwaylandintegration.cpp index b86c310..88bbe4a 100644 --- a/src/platformtheme/kwaylandintegration.cpp +++ b/src/platformtheme/kwaylandintegration.cpp @@ -1,159 +1,184 @@ /* This file is part of the KDE libraries * Copyright 2015 Martin Gräßlin * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kwaylandintegration.h" #include #include #include #include #include #include #include #include +#include #include using namespace KWayland::Client; static const QByteArray s_schemePropertyName = QByteArrayLiteral("KDE_COLOR_SCHEME_PATH"); static const QByteArray s_blurBehindPropertyName = QByteArrayLiteral("ENABLE_BLUR_BEHIND_HINT"); KWaylandIntegration::KWaylandIntegration() : QObject() , m_decoration(Q_NULLPTR) { } KWaylandIntegration::~KWaylandIntegration() = default; void KWaylandIntegration::init() { auto connection = ConnectionThread::fromApplication(this); if (!connection) { return; } Registry *registry = new Registry(this); registry->create(connection); QObject::connect(registry, &Registry::interfacesAnnounced, this, [registry, this] { const auto interface = registry->interface(Registry::Interface::ServerSideDecorationManager); if (interface.name != 0) { m_decoration = registry->createServerSideDecorationManager(interface.name, interface.version, this); qputenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1"); QCoreApplication::instance()->installEventFilter(this); + + } + const auto menuInterface = registry->interface(Registry::Interface::AppMenu); + if (menuInterface.name != 0) { + m_appMenuManager = registry->createAppMenuManager(menuInterface.name, menuInterface.version, this); } } ); registry->setup(); connection->roundtrip(); } bool KWaylandIntegration::eventFilter(QObject *watched, QEvent *event) { if (!m_decoration) { return false; } else if (event->type() == QEvent::Expose) { auto ee = static_cast(event); if (ee->region().isNull()) { return false; } QWindow *w = qobject_cast(watched); if (!w || w->parent() || !w->isVisible()) { return false; } if(w->property("org.kde.plasma.integration.waylandserverdecoration").isNull()) { shellSurfaceCreated(w); } } else if (event->type() == QEvent::Hide) { QWindow *w = qobject_cast(watched); if (!w || w->parent()) { return false; } shellSurfaceDestroyed(w); } else if (event->type() == QEvent::ApplicationPaletteChange) { const auto topLevelWindows = QGuiApplication::topLevelWindows(); for (QWindow *w : topLevelWindows) { installColorScheme(w); } } return false; } void KWaylandIntegration::shellSurfaceCreated(QWindow *w) { // set colorscheme hint if (qApp->property(s_schemePropertyName.constData()).isValid()) { installColorScheme(w); } const auto blurBehindProperty = w->property(s_blurBehindPropertyName.constData()); if (blurBehindProperty.isValid()) { KWindowEffects::enableBlurBehind(w->winId(), blurBehindProperty.toBool()); } // create deco Surface *s = Surface::fromWindow(w); if (!s) { return; } auto deco = m_decoration->create(s, w); connect(deco, &ServerSideDecoration::modeChanged, w, [deco, w] { const auto flags = w->flags(); const auto ourMode = (flags.testFlag(Qt::FramelessWindowHint) || flags.testFlag(Qt::Popup) || flags.testFlag(Qt::ToolTip)) ? ServerSideDecoration::Mode::None : ServerSideDecoration::Mode::Server; if (deco->mode() != ourMode) { deco->requestMode(ourMode); } } ); const auto flags = w->flags(); const auto ourMode = (flags.testFlag(Qt::FramelessWindowHint) || flags.testFlag(Qt::Popup) || flags.testFlag(Qt::ToolTip)) ? ServerSideDecoration::Mode::None : ServerSideDecoration::Mode::Server; if (deco->defaultMode() != ourMode) { deco->requestMode(ourMode); } w->setProperty("org.kde.plasma.integration.waylandserverdecoration", QVariant::fromValue(deco)); + + if (m_appMenuManager) { + auto menu = m_appMenuManager->create(s, w); + w->setProperty("org.kde.plasma.integration.appmenu", QVariant::fromValue(menu)); + menu->setAddress(m_appMenuServiceName, m_appMenuObjectPath); + } } void KWaylandIntegration::shellSurfaceDestroyed(QWindow *w) { delete w->property("org.kde.plasma.integration.waylandserverdecoration").value(); w->setProperty("org.kde.plasma.integration.waylandserverdecoration", QVariant()); + + delete w->property("org.kde.plasma.integration.appmenu").value(); + w->setProperty("org.kde.plasma.integration.appmenu", QVariant()); } void KWaylandIntegration::installColorScheme(QWindow *w) { if (QPlatformNativeInterface *native = qApp->platformNativeInterface()) { if (QPlatformWindow *pw = w->handle()) { native->setWindowProperty(pw, QString::fromUtf8(s_schemePropertyName), qApp->property(s_schemePropertyName.constData())); } } } void KWaylandIntegration::setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value) { if (QPlatformNativeInterface *nativeInterface = qApp->platformNativeInterface()) { if (QPlatformWindow *platformWindow = window->handle()) { nativeInterface->setWindowProperty(platformWindow, QString::fromUtf8(name), QString::fromUtf8(value)); } } } +void KWaylandIntegration::setAppMenu(const QString &serviceName, const QString &objectPath) +{ + m_appMenuServiceName = serviceName; + m_appMenuObjectPath = objectPath; + auto menu = property("org.kde.plasma.integration.appmenu").value(); + if (menu) { + menu->setAddress(serviceName, objectPath); + } +} + #include "kwaylandintegration.moc" diff --git a/src/platformtheme/kwaylandintegration.h b/src/platformtheme/kwaylandintegration.h index 8c074c2..f811909 100644 --- a/src/platformtheme/kwaylandintegration.h +++ b/src/platformtheme/kwaylandintegration.h @@ -1,55 +1,61 @@ /* This file is part of the KDE libraries * Copyright 2015 Martin Gräßlin * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KWAYLANDINTEGRATION_H #define KWAYLANDINTEGRATION_H #include class QWindow; namespace KWayland { namespace Client { class ServerSideDecorationManager; +class AppMenuManager; } } class KWaylandIntegration : public QObject { Q_OBJECT public: explicit KWaylandIntegration(); virtual ~KWaylandIntegration(); void init(); void setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value); + void setAppMenu(const QString &serviceName, const QString &objectPath); bool eventFilter(QObject *watched, QEvent *event) override; private: void shellSurfaceCreated(QWindow *w); void shellSurfaceDestroyed(QWindow *w); void installColorScheme(QWindow *w); - KWayland::Client::ServerSideDecorationManager *m_decoration; + KWayland::Client::ServerSideDecorationManager *m_decoration = nullptr; + KWayland::Client::AppMenuManager *m_appMenuManager = nullptr; + + QString m_appMenuServiceName; + QString m_appMenuObjectPath; }; #endif