File indexing completed on 2026-08-09 14:49:15
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 #include "previewbridge.h" 0007 #include "previewclient.h" 0008 #include "previewitem.h" 0009 #include "previewsettings.h" 0010 0011 #include <KDecoration2/DecoratedClient> 0012 #include <KDecoration2/Decoration> 0013 0014 #include <KCModule> 0015 #include <KPluginFactory> 0016 #include <KPluginMetaData> 0017 0018 #include <QDBusConnection> 0019 #include <QDBusMessage> 0020 #include <QDebug> 0021 #include <QDialog> 0022 #include <QDialogButtonBox> 0023 #include <QPushButton> 0024 #include <QQuickItem> 0025 #include <QQuickRenderControl> 0026 #include <QQuickWindow> 0027 #include <QVBoxLayout> 0028 #include <QWindow> 0029 0030 namespace KDecoration2 0031 { 0032 namespace Preview 0033 { 0034 0035 static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); 0036 0037 PreviewBridge::PreviewBridge(QObject *parent) 0038 : DecorationBridge(parent) 0039 , m_lastCreatedClient(nullptr) 0040 , m_lastCreatedSettings(nullptr) 0041 , m_valid(false) 0042 { 0043 connect(this, &PreviewBridge::pluginChanged, this, &PreviewBridge::createFactory); 0044 } 0045 0046 PreviewBridge::~PreviewBridge() = default; 0047 0048 std::unique_ptr<DecoratedClientPrivate> PreviewBridge::createClient(DecoratedClient *client, Decoration *decoration) 0049 { 0050 auto ptr = std::unique_ptr<PreviewClient>(new PreviewClient(client, decoration)); 0051 m_lastCreatedClient = ptr.get(); 0052 return ptr; 0053 } 0054 0055 std::unique_ptr<DecorationSettingsPrivate> PreviewBridge::settings(DecorationSettings *parent) 0056 { 0057 auto ptr = std::unique_ptr<PreviewSettings>(new PreviewSettings(parent)); 0058 m_lastCreatedSettings = ptr.get(); 0059 return ptr; 0060 } 0061 0062 void PreviewBridge::registerPreviewItem(PreviewItem *item) 0063 { 0064 m_previewItems.append(item); 0065 } 0066 0067 void PreviewBridge::unregisterPreviewItem(PreviewItem *item) 0068 { 0069 m_previewItems.removeAll(item); 0070 } 0071 0072 void PreviewBridge::setPlugin(const QString &plugin) 0073 { 0074 if (m_plugin == plugin) { 0075 return; 0076 } 0077 m_plugin = plugin; 0078 Q_EMIT pluginChanged(); 0079 } 0080 0081 QString PreviewBridge::theme() const 0082 { 0083 return m_theme; 0084 } 0085 0086 void PreviewBridge::setTheme(const QString &theme) 0087 { 0088 if (m_theme == theme) { 0089 return; 0090 } 0091 m_theme = theme; 0092 Q_EMIT themeChanged(); 0093 } 0094 0095 QString PreviewBridge::plugin() const 0096 { 0097 return m_plugin; 0098 } 0099 0100 void PreviewBridge::createFactory() 0101 { 0102 m_factory.clear(); 0103 0104 if (m_plugin.isNull()) { 0105 setValid(false); 0106 qWarning() << "Plugin not set"; 0107 return; 0108 } 0109 0110 const auto offers = KPluginMetaData::findPlugins(s_pluginName); 0111 auto item = std::find_if(offers.constBegin(), offers.constEnd(), [this](const auto &plugin) { 0112 return plugin.pluginId() == m_plugin; 0113 }); 0114 if (item != offers.constEnd()) { 0115 m_factory = KPluginFactory::loadFactory(*item).plugin; 0116 } 0117 0118 setValid(!m_factory.isNull()); 0119 } 0120 0121 bool PreviewBridge::isValid() const 0122 { 0123 return m_valid; 0124 } 0125 0126 void PreviewBridge::setValid(bool valid) 0127 { 0128 if (m_valid == valid) { 0129 return; 0130 } 0131 m_valid = valid; 0132 Q_EMIT validChanged(); 0133 } 0134 0135 Decoration *PreviewBridge::createDecoration(QObject *parent) 0136 { 0137 if (!m_valid) { 0138 return nullptr; 0139 } 0140 QVariantMap args({{QStringLiteral("bridge"), QVariant::fromValue(this)}}); 0141 if (!m_theme.isNull()) { 0142 args.insert(QStringLiteral("theme"), m_theme); 0143 } 0144 return m_factory->create<KDecoration2::Decoration>(parent, QVariantList({args})); 0145 } 0146 0147 DecorationButton *PreviewBridge::createButton(KDecoration2::Decoration *decoration, KDecoration2::DecorationButtonType type, QObject *parent) 0148 { 0149 if (!m_valid) { 0150 return nullptr; 0151 } 0152 return m_factory->create<KDecoration2::DecorationButton>(parent, QVariantList({QVariant::fromValue(type), QVariant::fromValue(decoration)})); 0153 } 0154 0155 void PreviewBridge::configure(QQuickItem *ctx) 0156 { 0157 if (!m_valid) { 0158 return; 0159 } 0160 // setup the UI 0161 QDialog *dialog = new QDialog(); 0162 dialog->setAttribute(Qt::WA_DeleteOnClose); 0163 if (m_lastCreatedClient) { 0164 dialog->setWindowTitle(m_lastCreatedClient->caption()); 0165 } 0166 0167 // create the KCModule through the plugintrader 0168 QVariantMap args; 0169 if (!m_theme.isNull()) { 0170 args.insert(QStringLiteral("theme"), m_theme); 0171 } 0172 0173 KCModule *kcm = m_factory->create<KCModule>(dialog, QVariantList({args})); 0174 0175 if (!kcm) { 0176 return; 0177 } 0178 0179 auto save = [this, kcm] { 0180 kcm->save(); 0181 if (m_lastCreatedSettings) { 0182 Q_EMIT m_lastCreatedSettings->decorationSettings()->reconfigured(); 0183 } 0184 // Send signal to all kwin instances 0185 QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), 0186 QStringLiteral("org.kde.KWin"), 0187 QStringLiteral("reloadConfig")); 0188 QDBusConnection::sessionBus().send(message); 0189 }; 0190 connect(dialog, &QDialog::accepted, this, save); 0191 0192 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Reset, 0193 dialog); 0194 0195 QPushButton *reset = buttons->button(QDialogButtonBox::Reset); 0196 reset->setEnabled(false); 0197 // Here we connect our buttons with the dialog 0198 connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept); 0199 connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject); 0200 connect(reset, &QPushButton::clicked, kcm, &KCModule::load); 0201 auto changedSignal = static_cast<void (KCModule::*)(bool)>(&KCModule::changed); 0202 connect(kcm, changedSignal, reset, &QPushButton::setEnabled); 0203 connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, kcm, &KCModule::defaults); 0204 0205 QVBoxLayout *layout = new QVBoxLayout(dialog); 0206 layout->addWidget(kcm); 0207 layout->addWidget(buttons); 0208 0209 if (ctx->window()) { 0210 dialog->winId(); // so it creates windowHandle 0211 dialog->windowHandle()->setTransientParent(QQuickRenderControl::renderWindowFor(ctx->window())); 0212 dialog->setModal(true); 0213 } 0214 0215 dialog->show(); 0216 } 0217 0218 BridgeItem::BridgeItem(QObject *parent) 0219 : QObject(parent) 0220 , m_bridge(new PreviewBridge()) 0221 { 0222 connect(m_bridge, &PreviewBridge::themeChanged, this, &BridgeItem::themeChanged); 0223 connect(m_bridge, &PreviewBridge::pluginChanged, this, &BridgeItem::pluginChanged); 0224 connect(m_bridge, &PreviewBridge::validChanged, this, &BridgeItem::validChanged); 0225 } 0226 0227 BridgeItem::~BridgeItem() 0228 { 0229 m_bridge->deleteLater(); 0230 } 0231 0232 } 0233 }