diff --git a/kcmkwin/kwindecoration/decorationmodel.cpp b/kcmkwin/kwindecoration/decorationmodel.cpp index 55f76b05be..1064668cec 100644 --- a/kcmkwin/kwindecoration/decorationmodel.cpp +++ b/kcmkwin/kwindecoration/decorationmodel.cpp @@ -70,6 +70,7 @@ DecorationModel::DecorationModel(KSharedConfigPtr config, QObject* parent) roleNames[QmlMainScriptRole] = "mainScript"; roleNames[BorderSizeRole] = "borderSize"; roleNames[ButtonSizeRole] = "buttonSize"; + roleNames[LibraryNameRole] = "library"; setRoleNames(roleNames); m_config = KSharedConfig::openConfig("auroraerc"); findDecorations(); diff --git a/kcmkwin/kwindecoration/kwindecoration.cpp b/kcmkwin/kwindecoration/kwindecoration.cpp index 4c289daf55..f26f740cef 100644 --- a/kcmkwin/kwindecoration/kwindecoration.cpp +++ b/kcmkwin/kwindecoration/kwindecoration.cpp @@ -33,6 +33,7 @@ #include "configdialog.h" #include "decorationmodel.h" #include "auroraetheme.h" +#include "preview.h" // Qt #include #include @@ -83,6 +84,7 @@ KWinDecorationModule::KWinDecorationModule(QWidget* parent, const QVariantList & , m_listView(new QQuickView()) { qmlRegisterType("org.kde.kwin.aurorae", 0, 1, "AuroraeTheme"); + qmlRegisterType("org.kde.kwin.kcmdecoration", 0, 1, "PreviewItem"); m_ui = new KWinDecorationForm(this); m_ui->configureDecorationButton->setIcon(QIcon::fromTheme("configure")); m_ui->configureButtonsButton->setIcon(QIcon::fromTheme("configure")); diff --git a/kcmkwin/kwindecoration/preview.cpp b/kcmkwin/kwindecoration/preview.cpp index 887982266e..ea4645d9d5 100644 --- a/kcmkwin/kwindecoration/preview.cpp +++ b/kcmkwin/kwindecoration/preview.cpp @@ -1,6 +1,7 @@ /* * * Copyright (c) 2003 Lubos Lunak + * Copyright (c) 2013 Martin Gräßlin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +34,179 @@ #include +static const int SMALL_OFFSET = 10; +static const int LARGE_OFFSET = 40; + +PreviewItem::PreviewItem(QQuickItem *parent) + : QQuickPaintedItem(parent) + , m_plugins(new KDecorationPreviewPlugins(KSharedConfig::openConfig(QStringLiteral("kwinrc")))) + , m_activeBridge(new KDecorationPreviewBridge(this, true)) + , m_inactiveBridge(new KDecorationPreviewBridge(this, false)) + , m_activeDecoration(nullptr) + , m_inactiveDecoration(nullptr) + , m_activeEntered(nullptr) + , m_inactiveEntered(nullptr) +{ + setFlag(ItemHasContents, true); + setAcceptHoverEvents(true); + connect(this, &PreviewItem::libraryChanged, this, &PreviewItem::recreateDecorations); +} + +PreviewItem::~PreviewItem() +{ + delete m_activeDecoration; + delete m_inactiveDecoration; +} + +void PreviewItem::setLibraryName(const QString &library) +{ + if (library == m_libraryName || library.isEmpty()) { + return; + } + m_libraryName = library; + emit libraryChanged(); +} + +void PreviewItem::recreateDecorations() +{ + const bool loaded = m_plugins->loadPlugin(m_libraryName); + if (!loaded) { + return; + } + m_plugins->destroyPreviousPlugin(); + delete m_activeDecoration; + delete m_inactiveDecoration; + m_activeDecoration = m_plugins->createDecoration(m_activeBridge.data()); + m_inactiveDecoration = m_plugins->createDecoration(m_inactiveBridge.data()); + + if (m_activeDecoration) { + m_activeDecoration->init(); + if (m_activeDecoration->widget()) { + m_activeDecoration->widget()->installEventFilter(this); + } + } + if (m_inactiveDecoration) { + m_inactiveDecoration->init(); + if (m_inactiveDecoration->widget()) { + m_inactiveDecoration->widget()->installEventFilter(this); + } + } + updatePreview(); +} + +void PreviewItem::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) +{ + QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry); + updatePreview(); +} + +void PreviewItem::updatePreview() +{ + if (width() == 0 && height() == 0) { + return; + } + if (!m_activeDecoration && !m_inactiveDecoration) { + return; + } + const QSize size(width() - 50, height() - 50); + updateSize(size, m_activeDecoration, m_activeBuffer); + updateSize(size, m_inactiveDecoration, m_inactiveBuffer); + + render(&m_activeBuffer, m_activeDecoration); + render(&m_inactiveBuffer, m_inactiveDecoration); +} + +void PreviewItem::updateSize(const QSize &baseSize, KDecoration *decoration, QImage &buffer) +{ + if (!decoration) { + return; + } + int left, right, top, bottom; + left = right = top = bottom = 0; + decoration->padding(left, right, top, bottom); + const QSize size = baseSize + QSize(left + right, top + bottom); + if (decoration->geometry().size() != size) { + decoration->resize(size); + } + if (buffer.isNull() || buffer.size() != size) { + buffer = QImage(size, QImage::Format_ARGB32_Premultiplied); + } +} + +void PreviewItem::render(QImage* image, KDecoration* decoration) +{ + image->fill(Qt::transparent); + decoration->render(image, QRegion()); +} + +void PreviewItem::paint(QPainter *painter) +{ + int paddingLeft, paddingRigth, paddingTop, paddingBottom; + paddingLeft = paddingRigth = paddingTop = paddingBottom = 0; + if (m_inactiveDecoration) { + m_inactiveDecoration->padding(paddingLeft, paddingRigth, paddingTop, paddingBottom); + } + + painter->drawImage(LARGE_OFFSET - paddingLeft, SMALL_OFFSET - paddingTop, m_inactiveBuffer); + + paddingLeft = paddingRigth = paddingTop = paddingBottom = 0; + if (m_activeDecoration) { + m_activeDecoration->padding(paddingLeft, paddingRigth, paddingTop, paddingBottom); + } + painter->drawImage(SMALL_OFFSET - paddingLeft, LARGE_OFFSET - paddingTop, m_activeBuffer); +} + +void PreviewItem::hoverMoveEvent(QHoverEvent* event) +{ + QQuickItem::hoverMoveEvent(event); + const int w = width() - LARGE_OFFSET - SMALL_OFFSET; + const int h = height() - LARGE_OFFSET - SMALL_OFFSET; + forwardMoveEvent(event, QRect(SMALL_OFFSET, LARGE_OFFSET, w, h), m_activeDecoration, &m_activeEntered); + forwardMoveEvent(event, QRect(LARGE_OFFSET, SMALL_OFFSET, w, h), m_inactiveDecoration, &m_inactiveEntered); +} + +void PreviewItem::forwardMoveEvent(QHoverEvent *event, const QRect &geo, KDecoration *deco, QWidget **entered) +{ + auto leaveEvent = [](QWidget **widget) { + if (!(*widget)) { + return; + } + // send old one a leave event + QEvent leave(QEvent::Leave); + QApplication::sendEvent(*widget, &leave); + *widget = nullptr; + }; + if (geo.contains(event->pos()) && deco && deco->widget()) { + int paddingLeft, paddingRigth, paddingTop, paddingBottom; + paddingLeft = paddingRigth = paddingTop = paddingBottom = 0; + deco->padding(paddingLeft, paddingRigth, paddingTop, paddingBottom); + const QPoint widgetPos = event->pos() - geo.topLeft() + QPoint(paddingLeft, paddingTop); + if (QWidget *widget = deco->widget()->childAt(widgetPos)) { + if (widget != *entered) { + leaveEvent(entered); + // send enter event + *entered = widget; + QEnterEvent enter(widgetPos - widget->geometry().topLeft(), widgetPos, event->pos()); + QApplication::sendEvent(*entered, &enter); + } + } else { + leaveEvent(entered); + } + } else { + leaveEvent(entered); + } +} + +void PreviewItem::updateDecoration(KDecorationPreviewBridge *bridge) +{ + if (bridge == m_activeBridge.data()) { + render(&m_activeBuffer, m_activeDecoration); + } else if (bridge == m_inactiveBridge.data()) { + render(&m_inactiveBuffer, m_inactiveDecoration); + } + update(); +} + KDecorationPreview::KDecorationPreview(QWidget* parent) : QWidget(parent) { @@ -180,7 +355,14 @@ void KDecorationPreview::setMask(const QRegion ®ion, bool active) } KDecorationPreviewBridge::KDecorationPreviewBridge(KDecorationPreview* p, bool a) - : preview(p), active(a) + : preview(p), active(a), m_previewItem(nullptr) +{ +} + +KDecorationPreviewBridge::KDecorationPreviewBridge(PreviewItem *p, bool a) + : preview(nullptr) + , active(a) + , m_previewItem(p) { } @@ -311,8 +493,8 @@ void KDecorationPreviewBridge::performWindowOperation(WindowOperation) void KDecorationPreviewBridge::setMask(const QRegion& reg, int mode) { + Q_UNUSED(reg) Q_UNUSED(mode) - preview->setMask(reg, active); } bool KDecorationPreviewBridge::isPreview() const @@ -322,7 +504,10 @@ bool KDecorationPreviewBridge::isPreview() const QRect KDecorationPreviewBridge::geometry() const { - return preview->windowGeometry(active); + if (preview) { + return preview->windowGeometry(active); + } + return QRect(); } QRect KDecorationPreviewBridge::iconGeometry() const @@ -456,7 +641,10 @@ void KDecorationPreviewBridge::showWindowMenu(const QPoint &, long) void KDecorationPreviewBridge::update(const QRegion&) { - + if (m_previewItem) { + // call update + m_previewItem->updateDecoration(this); + } } KDecoration::WindowOperation KDecorationPreviewBridge::buttonToWindowOperation(Qt::MouseButtons) diff --git a/kcmkwin/kwindecoration/preview.h b/kcmkwin/kwindecoration/preview.h index d38895864f..74e9c4e01f 100644 --- a/kcmkwin/kwindecoration/preview.h +++ b/kcmkwin/kwindecoration/preview.h @@ -1,6 +1,7 @@ /* * * Copyright (c) 2003 Lubos Lunak + * Copyright (c) 2013 Martin Gräßlin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +22,7 @@ #define KWINDECORATION_PREVIEW_H #include +#include #include #include #include @@ -29,6 +31,48 @@ class KDecorationPreviewBridge; class KDecorationPreviewOptions; class QMouseEvent; +class PreviewItem : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(QString library READ libraryName WRITE setLibraryName NOTIFY libraryChanged) +public: + PreviewItem(QQuickItem *parent = nullptr); + virtual ~PreviewItem(); + virtual void paint(QPainter *painter); + + void setLibraryName(const QString &library); + const QString &libraryName() const { + return m_libraryName; + } + void updateDecoration(KDecorationPreviewBridge *bridge); + +Q_SIGNALS: + void libraryChanged(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void hoverMoveEvent(QHoverEvent *event); + +private Q_SLOTS: + void recreateDecorations(); + +private: + void updatePreview(); + void updateSize(const QSize &size, KDecoration *decoration, QImage &buffer); + void render(QImage *image, KDecoration *decoration); + void forwardMoveEvent(QHoverEvent *event, const QRect &geo, KDecoration *deco, QWidget **entered); + QScopedPointer m_plugins; + QScopedPointer m_activeBridge; + QScopedPointer m_inactiveBridge; + KDecoration *m_activeDecoration; + KDecoration *m_inactiveDecoration; + QWidget *m_activeEntered; + QWidget *m_inactiveEntered; + QString m_libraryName; + QImage m_activeBuffer; + QImage m_inactiveBuffer; +}; + class KDecorationPreview : public QWidget { @@ -64,6 +108,7 @@ class KDecorationPreviewBridge { public: KDecorationPreviewBridge(KDecorationPreview* preview, bool active); + KDecorationPreviewBridge(PreviewItem* preview, bool active); virtual bool isActive() const override; virtual bool isCloseable() const override; virtual bool isMaximizable() const override; @@ -131,6 +176,7 @@ public: private: KDecorationPreview* preview; + PreviewItem *m_previewItem; bool active; }; diff --git a/kcmkwin/kwindecoration/qml/main.qml b/kcmkwin/kwindecoration/qml/main.qml index 95c281ac1d..d209bdd79a 100644 --- a/kcmkwin/kwindecoration/qml/main.qml +++ b/kcmkwin/kwindecoration/qml/main.qml @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ import QtQuick 2.0 -import org.kde.qtextracomponents 2.0 as QtExtra +import org.kde.kwin.kcmdecoration 0.1 ListView { id: listView @@ -35,10 +35,15 @@ ListView { objectName: "decorationItem" width: listView.width height: 150 - QtExtra.QPixmapItem { - pixmap: preview + PreviewItem { + id: preview anchors.fill: parent visible: type == 0 + Component.onCompleted: { + if (type == 0) { + preview.library = model.library; + } + } } Loader { source: type == 1 ? "AuroraePreview.qml" : ""