You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
15 KiB
630 lines
15 KiB
/* |
|
* |
|
* Copyright (c) 2003 Lubos Lunak <l.lunak@kde.org> |
|
* Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org> |
|
* |
|
* 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 |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program 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 General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
*/ |
|
|
|
#include "preview.h" |
|
|
|
#include <KLocalizedString> |
|
#include <kconfig.h> |
|
#include <QApplication> |
|
#include <QPainter> |
|
#include <QMouseEvent> |
|
|
|
#include <kdecorationfactory.h> |
|
|
|
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::loadDecorationPlugin); |
|
} |
|
|
|
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::loadDecorationPlugin() |
|
{ |
|
const bool loaded = m_plugins->loadPlugin(m_libraryName); |
|
if (!loaded) { |
|
return; |
|
} |
|
m_plugins->destroyPreviousPlugin(); |
|
connect(m_plugins->factory(), &KDecorationFactory::recreateDecorations, |
|
this, &PreviewItem::recreateDecorations, Qt::QueuedConnection); |
|
recreateDecorations(); |
|
} |
|
|
|
void PreviewItem::recreateDecorations() |
|
{ |
|
delete m_activeDecoration; |
|
delete m_inactiveDecoration; |
|
m_activeEntered = nullptr; |
|
m_inactiveEntered = nullptr; |
|
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); |
|
update(); |
|
} |
|
|
|
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) |
|
{ |
|
bridge[Active] = new KDecorationPreviewBridge(this, true); |
|
bridge[Inactive] = new KDecorationPreviewBridge(this, false); |
|
|
|
deco[Active] = deco[Inactive] = NULL; |
|
|
|
setMinimumSize(100, 100); |
|
} |
|
|
|
KDecorationPreview::~KDecorationPreview() |
|
{ |
|
for (int i = 0; i < NumWindows; i++) { |
|
delete deco[i]; |
|
delete bridge[i]; |
|
} |
|
} |
|
|
|
void KDecorationPreview::disablePreview() |
|
{ |
|
delete deco[Active]; |
|
delete deco[Inactive]; |
|
deco[Active] = deco[Inactive] = NULL; |
|
} |
|
|
|
KDecorationFactory *KDecorationPreview::factory() const |
|
{ |
|
return deco[Active] ? deco[Active]->factory() : 0; |
|
} |
|
|
|
QRect KDecorationPreview::windowGeometry(bool active) const |
|
{ |
|
QWidget *widget = active ? deco[Active]->widget() : deco[Inactive]->widget(); |
|
return widget->geometry(); |
|
} |
|
|
|
QRegion KDecorationPreview::unobscuredRegion(bool active, const QRegion& r) const |
|
{ |
|
Q_UNUSED(active) |
|
return r; |
|
} |
|
|
|
KDecorationPreviewBridge::KDecorationPreviewBridge(KDecorationPreview* p, bool a) |
|
: preview(p) |
|
, m_previewItem(nullptr) |
|
, active(a) |
|
{ |
|
} |
|
|
|
KDecorationPreviewBridge::KDecorationPreviewBridge(PreviewItem *p, bool a) |
|
: preview(nullptr) |
|
, m_previewItem(p) |
|
, active(a) |
|
{ |
|
} |
|
|
|
Qt::WindowFlags KDecorationPreviewBridge::initialWFlags() const |
|
{ |
|
return 0; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isActive() const |
|
{ |
|
return active; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isCloseable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isMaximizable() const |
|
{ |
|
return true; |
|
} |
|
|
|
KDecoration::MaximizeMode KDecorationPreviewBridge::maximizeMode() const |
|
{ |
|
return KDecoration::MaximizeRestore; |
|
} |
|
|
|
KDecoration::QuickTileMode KDecorationPreviewBridge::quickTileMode() const |
|
{ |
|
return KDecoration::QuickTileNone; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isMinimizable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool KDecorationPreviewBridge::providesContextHelp() const |
|
{ |
|
return true; |
|
} |
|
|
|
int KDecorationPreviewBridge::desktop() const |
|
{ |
|
return 1; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isOnAllDesktopsAvailable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isModal() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isShadeable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isShade() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isSetShade() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool KDecorationPreviewBridge::keepAbove() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool KDecorationPreviewBridge::keepBelow() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isMovable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool KDecorationPreviewBridge::isResizable() const |
|
{ |
|
return true; |
|
} |
|
|
|
NET::WindowType KDecorationPreviewBridge::windowType(unsigned long) const |
|
{ |
|
return NET::Normal; |
|
} |
|
|
|
QIcon KDecorationPreviewBridge::icon() const |
|
{ |
|
return QIcon::fromTheme(QStringLiteral("xorg")); |
|
} |
|
|
|
QString KDecorationPreviewBridge::caption() const |
|
{ |
|
return active ? i18n("Active Window") : i18n("Inactive Window"); |
|
} |
|
|
|
void KDecorationPreviewBridge::processMousePressEvent(QMouseEvent*) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::showWindowMenu(const QRect &) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::showWindowMenu(const QPoint &) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::showApplicationMenu(const QPoint &) |
|
{ |
|
} |
|
|
|
bool KDecorationPreviewBridge::menuAvailable() const |
|
{ |
|
return false; |
|
} |
|
|
|
void KDecorationPreviewBridge::performWindowOperation(WindowOperation) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::setMask(const QRegion& reg, int mode) |
|
{ |
|
Q_UNUSED(reg) |
|
Q_UNUSED(mode) |
|
} |
|
|
|
bool KDecorationPreviewBridge::isPreview() const |
|
{ |
|
return true; |
|
} |
|
|
|
QRect KDecorationPreviewBridge::geometry() const |
|
{ |
|
if (preview) { |
|
return preview->windowGeometry(active); |
|
} |
|
return QRect(); |
|
} |
|
|
|
QRect KDecorationPreviewBridge::iconGeometry() const |
|
{ |
|
return QRect(); |
|
} |
|
|
|
QRegion KDecorationPreviewBridge::unobscuredRegion(const QRegion& r) const |
|
{ |
|
return preview->unobscuredRegion(active, r); |
|
} |
|
|
|
WId KDecorationPreviewBridge::windowId() const |
|
{ |
|
return 0; // no decorated window |
|
} |
|
|
|
void KDecorationPreviewBridge::closeWindow() |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::maximize(MaximizeMode) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::minimize() |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::showContextHelp() |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::setDesktop(int) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::titlebarDblClickOperation() |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::titlebarMouseWheelOperation(int) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::setShade(bool) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::setKeepAbove(bool) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::setKeepBelow(bool) |
|
{ |
|
} |
|
|
|
int KDecorationPreviewBridge::currentDesktop() const |
|
{ |
|
return 1; |
|
} |
|
|
|
void KDecorationPreviewBridge::grabXServer(bool) |
|
{ |
|
} |
|
|
|
bool KDecorationPreviewBridge::compositingActive() const |
|
{ |
|
return true; |
|
} |
|
|
|
QRect KDecorationPreviewBridge::transparentRect() const |
|
{ |
|
return QRect(); |
|
} |
|
|
|
// Window tabbing |
|
|
|
int KDecorationPreviewBridge::tabCount() const |
|
{ |
|
return 1; |
|
} |
|
|
|
QString KDecorationPreviewBridge::caption(int) const |
|
{ |
|
return active ? "Active Window" : "Inactive Window"; |
|
} |
|
|
|
QIcon KDecorationPreviewBridge::icon(int) const |
|
{ |
|
return icon(); |
|
} |
|
|
|
long KDecorationPreviewBridge::tabId(int) const |
|
{ |
|
return 0; |
|
} |
|
|
|
long KDecorationPreviewBridge::currentTabId() const |
|
{ |
|
return 0; |
|
} |
|
|
|
void KDecorationPreviewBridge::setCurrentTab(long) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::tab_A_before_B(long, long) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::tab_A_behind_B(long, long) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::untab(long, const QRect&) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::closeTab(long) |
|
{ |
|
} |
|
|
|
void KDecorationPreviewBridge::closeTabGroup() |
|
{ |
|
} |
|
|
|
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) |
|
{ |
|
return KDecoration::NoOp; |
|
} |
|
|
|
QPalette KDecorationPreviewBridge::palette() const |
|
{ |
|
return QApplication::palette(); |
|
} |
|
|
|
KDecorationPreviewOptions::KDecorationPreviewOptions() |
|
{ |
|
customBorderSize = BordersCount; // invalid |
|
customButtonsChanged = false; // invalid |
|
customButtons = true; |
|
customTitleButtonsLeft.clear(); // invalid |
|
customTitleButtonsRight.clear(); // invalid |
|
updateSettings(); |
|
} |
|
|
|
KDecorationPreviewOptions::~KDecorationPreviewOptions() |
|
{ |
|
} |
|
|
|
void KDecorationPreviewOptions::updateSettings() |
|
{ |
|
KConfig cfg("kwinrc"); |
|
KDecorationOptions::updateSettings(&cfg); |
|
|
|
// set custom border size/buttons |
|
if (customBorderSize != BordersCount) |
|
setBorderSize(customBorderSize); |
|
if (customButtonsChanged) |
|
setCustomButtonPositions(customButtons); |
|
if (customButtons) { |
|
if (!customTitleButtonsLeft.isEmpty()) |
|
setTitleButtonsLeft(customTitleButtonsLeft); |
|
if (!customTitleButtonsRight.isEmpty()) |
|
setTitleButtonsRight(customTitleButtonsRight); |
|
} else { |
|
setTitleButtonsLeft(KDecorationOptions::defaultTitleButtonsLeft()); |
|
setTitleButtonsRight(KDecorationOptions::defaultTitleButtonsRight()); |
|
} |
|
} |
|
|
|
void KDecorationPreviewOptions::setCustomBorderSize(BorderSize size) |
|
{ |
|
customBorderSize = size; |
|
|
|
updateSettings(); |
|
} |
|
|
|
void KDecorationPreviewOptions::setCustomTitleButtonsEnabled(bool enabled) |
|
{ |
|
customButtonsChanged = true; |
|
customButtons = enabled; |
|
|
|
updateSettings(); |
|
} |
|
|
|
void KDecorationPreviewOptions::setCustomTitleButtons(const QList<DecorationButton> &left, const QList<DecorationButton> &right) |
|
{ |
|
customTitleButtonsLeft = left; |
|
customTitleButtonsRight = right; |
|
|
|
updateSettings(); |
|
} |
|
|
|
bool KDecorationPreviewPlugins::provides(Requirement) |
|
{ |
|
return false; |
|
} |
|
|
|
#include "preview.moc"
|
|
|