scripting: Add qml effect bindings
This allows creating third party qtquick scene effects without linking to libkwineffects and thus rebuilding the effect every kwin release.
This commit is contained in:
parent
793a0e72bf
commit
264ebe6377
17 changed files with 473 additions and 17 deletions
1
examples/quick-effect/.gitignore
vendored
Normal file
1
examples/quick-effect/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
build
|
14
examples/quick-effect/CMakeLists.txt
Normal file
14
examples/quick-effect/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
# SPDX-FileCopyrightText: None
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(quick-effect)
|
||||
|
||||
find_package(ECM 5.240 REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
|
||||
|
||||
find_package(KF6 5.240 REQUIRED COMPONENTS
|
||||
Package
|
||||
)
|
||||
|
||||
kpackage_install_package(package quick-effect effects kwin)
|
12
examples/quick-effect/package/contents/config/main.xml
Normal file
12
examples/quick-effect/package/contents/config/main.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<kcfgfile name=""/>
|
||||
<group name="">
|
||||
<entry name="BackgroundColor" type="Color">
|
||||
<default>#ff00ff</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: None
|
||||
SPDX-License-Identifier: CC0-1.0
|
39
examples/quick-effect/package/contents/ui/config.ui
Normal file
39
examples/quick-effect/package/contents/ui/config.ui
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QuickEffectConfig</class>
|
||||
<widget class="QWidget" name="QuickEffectConfig">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>455</width>
|
||||
<height>177</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Background color:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="KColorButton" name="kcfg_BackgroundColor">
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>KColorButton</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>kcolorbutton.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: None
|
||||
SPDX-License-Identifier: CC0-1.0
|
42
examples/quick-effect/package/contents/ui/main.qml
Normal file
42
examples/quick-effect/package/contents/ui/main.qml
Normal file
|
@ -0,0 +1,42 @@
|
|||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kwin
|
||||
|
||||
SceneEffect {
|
||||
id: effect
|
||||
|
||||
delegate: Rectangle {
|
||||
color: effect.configuration.BackgroundColor
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: SceneView.screen.name
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: effect.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
ScreenEdgeHandler {
|
||||
enabled: true
|
||||
edge: ScreenEdgeHandler.TopEdge
|
||||
onActivated: effect.visible = !effect.visible
|
||||
}
|
||||
|
||||
ShortcutHandler {
|
||||
name: "Toggle Quick Effect"
|
||||
text: "Toggle Quick Effect"
|
||||
sequence: "Meta+Ctrl+Q"
|
||||
onActivated: effect.visible = !effect.visible
|
||||
}
|
||||
|
||||
PinchGestureHandler {
|
||||
direction: PinchGestureHandler.Direction.Contracting
|
||||
fingerCount: 3
|
||||
onActivated: effect.visible = !effect.visible
|
||||
}
|
||||
}
|
20
examples/quick-effect/package/metadata.json
Normal file
20
examples/quick-effect/package/metadata.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"KPackageStructure": "KWin/Effect",
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Email": "user@example.com",
|
||||
"Name": "Real Name"
|
||||
}
|
||||
],
|
||||
"Category": "Appearance",
|
||||
"Description": "Quick Effect",
|
||||
"EnabledByDefault": true,
|
||||
"Id": "quick-effect",
|
||||
"License": "GPL",
|
||||
"Name": "Quick Effect"
|
||||
},
|
||||
"X-KDE-Ordering": 60,
|
||||
"X-KDE-PluginKeyword": "quick-effect",
|
||||
"X-Plasma-API": "declarativescript"
|
||||
}
|
2
examples/quick-effect/package/metadata.json.license
Normal file
2
examples/quick-effect/package/metadata.json.license
Normal file
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: None
|
||||
SPDX-License-Identifier: CC0-1.0
|
|
@ -156,6 +156,7 @@ target_sources(kwin PRIVATE
|
|||
scripting/gesturehandler.cpp
|
||||
scripting/screenedgehandler.cpp
|
||||
scripting/scriptedeffect.cpp
|
||||
scripting/scriptedquicksceneeffect.cpp
|
||||
scripting/scripting.cpp
|
||||
scripting/scripting_logging.cpp
|
||||
scripting/scriptingutils.cpp
|
||||
|
@ -212,6 +213,7 @@ target_link_libraries(kwin
|
|||
PRIVATE
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigQml
|
||||
KF6::ConfigWidgets
|
||||
KF6::CoreAddons
|
||||
KF6::Crash
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "libkwineffects/kwineffects.h"
|
||||
#include "plugin.h"
|
||||
#include "scripting/scriptedeffect.h"
|
||||
#include "scripting/scriptedquicksceneeffect.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "utils/common.h"
|
||||
// KDE
|
||||
#include <KConfigGroup>
|
||||
|
@ -23,6 +25,8 @@
|
|||
#include <QDebug>
|
||||
#include <QFutureWatcher>
|
||||
#include <QPluginLoader>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <QStaticPlugin>
|
||||
#include <QStringList>
|
||||
#include <QtConcurrentRun>
|
||||
|
@ -116,11 +120,28 @@ bool ScriptedEffectLoader::loadEffect(const KPluginMetaData &effect, LoadEffectF
|
|||
qCDebug(KWIN_CORE) << "Loading flags disable effect: " << name;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_loadedEffects.contains(name)) {
|
||||
qCDebug(KWIN_CORE) << name << "already loaded";
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString api = effect.value(QStringLiteral("X-Plasma-API"));
|
||||
if (api == QLatin1String("javascript")) {
|
||||
return loadJavascriptEffect(effect);
|
||||
} else if (api == QLatin1String("declarativescript")) {
|
||||
return loadDeclarativeEffect(effect);
|
||||
} else {
|
||||
qCWarning(KWIN_CORE, "Failed to load %s effect: invalid X-Plasma-API field: %s. "
|
||||
"Available options are javascript, and declarativescript", qPrintable(name), qPrintable(api));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptedEffectLoader::loadJavascriptEffect(const KPluginMetaData &effect)
|
||||
{
|
||||
const QString name = effect.pluginId();
|
||||
if (!ScriptedEffect::supported()) {
|
||||
qCDebug(KWIN_CORE) << "Effect is not supported: " << name;
|
||||
return false;
|
||||
|
@ -141,6 +162,44 @@ bool ScriptedEffectLoader::loadEffect(const KPluginMetaData &effect, LoadEffectF
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ScriptedEffectLoader::loadDeclarativeEffect(const KPluginMetaData &metadata)
|
||||
{
|
||||
const QString name = metadata.pluginId();
|
||||
const QString scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
|
||||
QLatin1String("kwin/effects/") + name + QLatin1String("/contents/ui/main.qml"));
|
||||
if (scriptFile.isNull()) {
|
||||
qCWarning(KWIN_CORE) << "Could not locate the effect script";
|
||||
return false;
|
||||
}
|
||||
|
||||
QQmlEngine *engine = Scripting::self()->qmlEngine();
|
||||
QQmlComponent component(engine);
|
||||
component.loadUrl(QUrl::fromLocalFile(scriptFile));
|
||||
if (component.isError()) {
|
||||
qCWarning(KWIN_CORE).nospace() << "Failed to load " << scriptFile << ": " << component.errors();
|
||||
return false;
|
||||
}
|
||||
|
||||
QObject *object = component.beginCreate(engine->rootContext());
|
||||
auto effect = qobject_cast<ScriptedQuickSceneEffect *>(object);
|
||||
if (!effect) {
|
||||
qCDebug(KWIN_CORE) << "Could not initialize scripted effect: " << name;
|
||||
delete object;
|
||||
return false;
|
||||
}
|
||||
effect->setMetaData(metadata);
|
||||
component.completeCreate();
|
||||
|
||||
connect(effect, &Effect::destroyed, this, [this, name]() {
|
||||
m_loadedEffects.removeAll(name);
|
||||
});
|
||||
|
||||
qCDebug(KWIN_CORE) << "Successfully loaded scripted effect: " << name;
|
||||
Q_EMIT effectLoaded(effect, name);
|
||||
m_loadedEffects << name;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptedEffectLoader::queryAndLoadAll()
|
||||
{
|
||||
if (m_queryConnection) {
|
||||
|
|
|
@ -290,6 +290,9 @@ public:
|
|||
private:
|
||||
QList<KPluginMetaData> findAllEffects() const;
|
||||
KPluginMetaData findEffect(const QString &name) const;
|
||||
bool loadJavascriptEffect(const KPluginMetaData &effect);
|
||||
bool loadDeclarativeEffect(const KPluginMetaData &effect);
|
||||
|
||||
QStringList m_loadedEffects;
|
||||
EffectLoadQueue<ScriptedEffectLoader, KPluginMetaData> *m_queue;
|
||||
QMetaObject::Connection m_queryConnection;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "logging_p.h"
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlIncubator>
|
||||
#include <QQuickItem>
|
||||
|
@ -62,8 +63,9 @@ public:
|
|||
}
|
||||
bool isItemOnScreen(QQuickItem *item, EffectScreen *screen) const;
|
||||
|
||||
std::unique_ptr<QQmlComponent> qmlComponent;
|
||||
std::unique_ptr<QQmlComponent> delegate;
|
||||
QUrl source;
|
||||
std::map<EffectScreen *, std::unique_ptr<QQmlContext>> contexts;
|
||||
std::map<EffectScreen *, std::unique_ptr<QQmlIncubator>> incubators;
|
||||
std::map<EffectScreen *, std::unique_ptr<QuickSceneView>> views;
|
||||
QPointer<QuickSceneView> mouseImplicitGrab;
|
||||
|
@ -244,7 +246,25 @@ void QuickSceneEffect::setSource(const QUrl &url)
|
|||
}
|
||||
if (d->source != url) {
|
||||
d->source = url;
|
||||
d->qmlComponent.reset();
|
||||
d->delegate.reset();
|
||||
}
|
||||
}
|
||||
|
||||
QQmlComponent *QuickSceneEffect::delegate() const
|
||||
{
|
||||
return d->delegate.get();
|
||||
}
|
||||
|
||||
void QuickSceneEffect::setDelegate(QQmlComponent *delegate)
|
||||
{
|
||||
if (isRunning()) {
|
||||
qWarning() << "Cannot change QuickSceneEffect.source while running";
|
||||
return;
|
||||
}
|
||||
if (d->delegate.get() != delegate) {
|
||||
d->source = QUrl();
|
||||
d->delegate.reset(delegate);
|
||||
Q_EMIT delegateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,6 +413,7 @@ void QuickSceneEffect::handleScreenRemoved(EffectScreen *screen)
|
|||
{
|
||||
d->views.erase(screen);
|
||||
d->incubators.erase(screen);
|
||||
d->contexts.erase(screen);
|
||||
}
|
||||
|
||||
void QuickSceneEffect::addScreen(EffectScreen *screen)
|
||||
|
@ -415,14 +436,18 @@ void QuickSceneEffect::addScreen(EffectScreen *screen)
|
|||
view->scheduleRepaint();
|
||||
d->views[screen] = std::move(view);
|
||||
} else if (incubator->isError()) {
|
||||
qCWarning(LIBKWINEFFECTS) << "Could not create a view for QML file" << d->qmlComponent->url();
|
||||
qCWarning(LIBKWINEFFECTS) << "Could not create a view for QML file" << d->delegate->url();
|
||||
qCWarning(LIBKWINEFFECTS) << incubator->errors();
|
||||
}
|
||||
});
|
||||
|
||||
incubator->setInitialProperties(properties);
|
||||
|
||||
QQmlContext *creationContext = d->delegate->creationContext();
|
||||
QQmlContext *context = new QQmlContext(creationContext ? creationContext : qmlContext(this));
|
||||
|
||||
d->contexts[screen].reset(context);
|
||||
d->incubators[screen].reset(incubator);
|
||||
d->qmlComponent->create(*incubator);
|
||||
d->delegate->create(*incubator, context);
|
||||
}
|
||||
|
||||
void QuickSceneEffect::startInternal()
|
||||
|
@ -431,19 +456,20 @@ void QuickSceneEffect::startInternal()
|
|||
return;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(d->source.isEmpty())) {
|
||||
qWarning() << "QuickSceneEffect.source is empty. Did you forget to call setSource()?";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->qmlComponent) {
|
||||
d->qmlComponent = std::make_unique<QQmlComponent>(effects->qmlEngine());
|
||||
d->qmlComponent->loadUrl(d->source);
|
||||
if (d->qmlComponent->isError()) {
|
||||
qWarning().nospace() << "Failed to load " << d->source << ": " << d->qmlComponent->errors();
|
||||
d->qmlComponent.reset();
|
||||
if (!d->delegate) {
|
||||
if (Q_UNLIKELY(d->source.isEmpty())) {
|
||||
qWarning() << "QuickSceneEffect.source is empty. Did you forget to call setSource()?";
|
||||
return;
|
||||
}
|
||||
|
||||
d->delegate = std::make_unique<QQmlComponent>(effects->qmlEngine());
|
||||
d->delegate->loadUrl(d->source);
|
||||
if (d->delegate->isError()) {
|
||||
qWarning().nospace() << "Failed to load " << d->source << ": " << d->delegate->errors();
|
||||
d->delegate.reset();
|
||||
return;
|
||||
}
|
||||
Q_EMIT delegateChanged();
|
||||
}
|
||||
|
||||
effects->setActiveFullScreenEffect(this);
|
||||
|
@ -474,6 +500,7 @@ void QuickSceneEffect::stopInternal()
|
|||
|
||||
d->incubators.clear();
|
||||
d->views.clear();
|
||||
d->contexts.clear();
|
||||
d->running = false;
|
||||
qApp->removeEventFilter(this);
|
||||
effects->ungrabKeyboard();
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "libkwineffects/kwineffects.h"
|
||||
#include "libkwineffects/kwinoffscreenquickview.h"
|
||||
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlComponent>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -74,6 +74,7 @@ class KWINEFFECTS_EXPORT QuickSceneEffect : public Effect
|
|||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QuickSceneView *activeView READ activeView NOTIFY activeViewChanged)
|
||||
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
|
||||
|
||||
public:
|
||||
explicit QuickSceneEffect(QObject *parent = nullptr);
|
||||
|
@ -112,6 +113,12 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void activateView(QuickSceneView *view);
|
||||
|
||||
/**
|
||||
* The delegate provides a template defining the contents of each instantiated screen view.
|
||||
*/
|
||||
QQmlComponent *delegate() const;
|
||||
void setDelegate(QQmlComponent *delegate);
|
||||
|
||||
/**
|
||||
* Returns the source URL.
|
||||
*/
|
||||
|
@ -150,6 +157,7 @@ Q_SIGNALS:
|
|||
void itemDraggedOutOfScreen(QQuickItem *item, QList<EffectScreen *> screens);
|
||||
void itemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item, EffectScreen *screen);
|
||||
void activeViewChanged(KWin::QuickSceneView *view);
|
||||
void delegateChanged();
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
|
126
src/scripting/scriptedquicksceneeffect.cpp
Normal file
126
src/scripting/scriptedquicksceneeffect.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "scripting/scriptedquicksceneeffect.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KConfigLoader>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
ScriptedQuickSceneEffect::ScriptedQuickSceneEffect()
|
||||
{
|
||||
m_visibleTimer.setSingleShot(true);
|
||||
connect(&m_visibleTimer, &QTimer::timeout, this, [this]() {
|
||||
setRunning(false);
|
||||
});
|
||||
}
|
||||
|
||||
ScriptedQuickSceneEffect::~ScriptedQuickSceneEffect()
|
||||
{
|
||||
}
|
||||
|
||||
int ScriptedQuickSceneEffect::requestedEffectChainPosition() const
|
||||
{
|
||||
return m_requestedEffectChainPosition;
|
||||
}
|
||||
|
||||
void ScriptedQuickSceneEffect::setMetaData(const KPluginMetaData &metaData)
|
||||
{
|
||||
m_requestedEffectChainPosition = metaData.value(QStringLiteral("X-KDE-Ordering"), 50);
|
||||
|
||||
KConfigGroup cg = kwinApp()->config()->group(QStringLiteral("Effect-%1").arg(metaData.pluginId()));
|
||||
const QString configFilePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kwin/effects/") + metaData.pluginId() + QLatin1String("/contents/config/main.xml"));
|
||||
if (configFilePath.isNull()) {
|
||||
m_configLoader = new KConfigLoader(cg, nullptr, this);
|
||||
} else {
|
||||
QFile xmlFile(configFilePath);
|
||||
m_configLoader = new KConfigLoader(cg, &xmlFile, this);
|
||||
m_configLoader->load();
|
||||
}
|
||||
|
||||
m_configuration = new KConfigPropertyMap(m_configLoader, this);
|
||||
connect(m_configLoader, &KConfigLoader::configChanged, this, &ScriptedQuickSceneEffect::configurationChanged);
|
||||
}
|
||||
|
||||
bool ScriptedQuickSceneEffect::isVisible() const
|
||||
{
|
||||
return m_isVisible;
|
||||
}
|
||||
|
||||
void ScriptedQuickSceneEffect::setVisible(bool visible)
|
||||
{
|
||||
if (m_isVisible == visible) {
|
||||
return;
|
||||
}
|
||||
m_isVisible = visible;
|
||||
|
||||
if (m_isVisible) {
|
||||
m_visibleTimer.stop();
|
||||
setRunning(true);
|
||||
} else {
|
||||
// Delay setRunning(false) to avoid destroying views while still executing JS code.
|
||||
m_visibleTimer.start();
|
||||
}
|
||||
|
||||
Q_EMIT visibleChanged();
|
||||
}
|
||||
|
||||
KConfigPropertyMap *ScriptedQuickSceneEffect::configuration() const
|
||||
{
|
||||
return m_configuration;
|
||||
}
|
||||
|
||||
QQmlListProperty<QObject> ScriptedQuickSceneEffect::data()
|
||||
{
|
||||
return QQmlListProperty<QObject>(this, nullptr,
|
||||
data_append,
|
||||
data_count,
|
||||
data_at,
|
||||
data_clear);
|
||||
}
|
||||
|
||||
void ScriptedQuickSceneEffect::data_append(QQmlListProperty<QObject> *objects, QObject *object)
|
||||
{
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptedQuickSceneEffect *effect = static_cast<ScriptedQuickSceneEffect *>(objects->object);
|
||||
if (!effect->m_children.contains(object)) {
|
||||
object->setParent(effect);
|
||||
effect->m_children.append(object);
|
||||
}
|
||||
}
|
||||
|
||||
qsizetype ScriptedQuickSceneEffect::data_count(QQmlListProperty<QObject> *objects)
|
||||
{
|
||||
ScriptedQuickSceneEffect *effect = static_cast<ScriptedQuickSceneEffect *>(objects->object);
|
||||
return effect->m_children.count();
|
||||
}
|
||||
|
||||
QObject *ScriptedQuickSceneEffect::data_at(QQmlListProperty<QObject> *objects, qsizetype index)
|
||||
{
|
||||
ScriptedQuickSceneEffect *effect = static_cast<ScriptedQuickSceneEffect *>(objects->object);
|
||||
return effect->m_children.value(index);
|
||||
}
|
||||
|
||||
void ScriptedQuickSceneEffect::data_clear(QQmlListProperty<QObject> *objects)
|
||||
{
|
||||
ScriptedQuickSceneEffect *effect = static_cast<ScriptedQuickSceneEffect *>(objects->object);
|
||||
while (!effect->m_children.isEmpty()) {
|
||||
QObject *child = effect->m_children.takeLast();
|
||||
child->setParent(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_scriptedquicksceneeffect.cpp"
|
93
src/scripting/scriptedquicksceneeffect.h
Normal file
93
src/scripting/scriptedquicksceneeffect.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "libkwineffects/kwinquickeffect.h"
|
||||
|
||||
#include <KConfigPropertyMap>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
class KConfigLoader;
|
||||
class KConfigPropertyMap;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* The SceneEffect type provides a way to implement effects that replace the default scene with
|
||||
* a custom one.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* SceneEffect {
|
||||
* id: root
|
||||
*
|
||||
* delegate: Rectangle {
|
||||
* color: "blue"
|
||||
* }
|
||||
*
|
||||
* ShortcutHandler {
|
||||
* name: "Toggle Effect"
|
||||
* text: i18n("Toggle Effect")
|
||||
* sequence: "Meta+E"
|
||||
* onActivated: root.visible = !root.visible;
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class ScriptedQuickSceneEffect : public QuickSceneEffect
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQmlListProperty<QObject> data READ data)
|
||||
Q_CLASSINFO("DefaultProperty", "data")
|
||||
|
||||
/**
|
||||
* The key-value store with the effect settings.
|
||||
*/
|
||||
Q_PROPERTY(KConfigPropertyMap *configuration READ configuration NOTIFY configurationChanged)
|
||||
|
||||
/**
|
||||
* Whether the effect is shown. Setting this property to @c true activates the effect; setting
|
||||
* this property to @c false will deactivate the effect and the screen views will be unloaded at
|
||||
* the next available time.
|
||||
*/
|
||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
|
||||
|
||||
public:
|
||||
explicit ScriptedQuickSceneEffect();
|
||||
~ScriptedQuickSceneEffect() override;
|
||||
|
||||
void setMetaData(const KPluginMetaData &metaData);
|
||||
|
||||
int requestedEffectChainPosition() const override;
|
||||
|
||||
bool isVisible() const;
|
||||
void setVisible(bool visible);
|
||||
|
||||
QQmlListProperty<QObject> data();
|
||||
KConfigPropertyMap *configuration() const;
|
||||
|
||||
static void data_append(QQmlListProperty<QObject> *objects, QObject *object);
|
||||
static qsizetype data_count(QQmlListProperty<QObject> *objects);
|
||||
static QObject *data_at(QQmlListProperty<QObject> *objects, qsizetype index);
|
||||
static void data_clear(QQmlListProperty<QObject> *objects);
|
||||
|
||||
Q_SIGNALS:
|
||||
void visibleChanged();
|
||||
void configurationChanged();
|
||||
|
||||
private:
|
||||
KConfigLoader *m_configLoader = nullptr;
|
||||
KConfigPropertyMap *m_configuration = nullptr;
|
||||
QObjectList m_children;
|
||||
QTimer m_visibleTimer;
|
||||
bool m_isVisible = false;
|
||||
int m_requestedEffectChainPosition = 0;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
|
@ -16,6 +16,7 @@
|
|||
#include "gesturehandler.h"
|
||||
#include "libkwineffects/kwinquickeffect.h"
|
||||
#include "screenedgehandler.h"
|
||||
#include "scriptedquicksceneeffect.h"
|
||||
#include "scripting_logging.h"
|
||||
#include "scriptingutils.h"
|
||||
#include "shortcuthandler.h"
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include "workspace.h"
|
||||
// KDE
|
||||
#include <KConfigGroup>
|
||||
#include <KConfigPropertyMap>
|
||||
#include <KGlobalAccel>
|
||||
#include <KLocalizedContext>
|
||||
#include <KPackage/PackageLoader>
|
||||
|
@ -652,12 +654,14 @@ void KWin::Scripting::init()
|
|||
qmlRegisterType<WindowFilterModel>("org.kde.kwin", 3, 0, "WindowFilterModel");
|
||||
qmlRegisterType<VirtualDesktopModel>("org.kde.kwin", 3, 0, "VirtualDesktopModel");
|
||||
qmlRegisterUncreatableType<KWin::QuickSceneView>("org.kde.kwin", 3, 0, "SceneView", QStringLiteral("Can't instantiate an object of type SceneView"));
|
||||
qmlRegisterType<ScriptedQuickSceneEffect>("org.kde.kwin", 3, 0, "SceneEffect");
|
||||
|
||||
qmlRegisterSingletonType<DeclarativeScriptWorkspaceWrapper>("org.kde.kwin", 3, 0, "Workspace", [](QQmlEngine *qmlEngine, QJSEngine *jsEngine) {
|
||||
return new DeclarativeScriptWorkspaceWrapper();
|
||||
});
|
||||
qmlRegisterSingletonInstance("org.kde.kwin", 3, 0, "Options", options);
|
||||
|
||||
qmlRegisterAnonymousType<KConfigPropertyMap>("org.kde.kwin", 3);
|
||||
qmlRegisterAnonymousType<KWin::Output>("org.kde.kwin", 3);
|
||||
qmlRegisterAnonymousType<KWin::Window>("org.kde.kwin", 3);
|
||||
qmlRegisterAnonymousType<KWin::VirtualDesktop>("org.kde.kwin", 3);
|
||||
|
|
Loading…
Reference in a new issue