diff --git a/kcms/style/previewitem.cpp b/kcms/style/previewitem.cpp index a58a697cc..22997218e 100644 --- a/kcms/style/previewitem.cpp +++ b/kcms/style/previewitem.cpp @@ -1,304 +1,304 @@ /* * Copyright © 2003-2007 Fredrik Höglund * Copyright 2019 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "previewitem.h" #include #include #include #include #include #include #include #include #include PreviewItem::PreviewItem(QQuickItem *parent) : QQuickPaintedItem(parent) { setAcceptHoverEvents(true); // HACK QtCurve deadlocks on application teardown when the Q_GLOBAL_STATIC QFactoryLoader // in QStyleFactory is destroyed which destroys all loaded styles prompting QtCurve // to disconnect from DBus stalling the application. // This also happens before any of the KCM objects are destroyed, so our only chance // is cleaning up in response to aboutToQuit connect(qApp, &QApplication::aboutToQuit, this, [this] { m_style.reset(); }); } PreviewItem::~PreviewItem() = default; void PreviewItem::componentComplete() { QQuickPaintedItem::componentComplete(); reload(); } bool PreviewItem::eventFilter(QObject *watched, QEvent *event) { if (watched == m_widget.data()) { switch (event->type()) { case QEvent::Show: case QEvent::UpdateRequest: update(); break; default: break; } } return QQuickPaintedItem::eventFilter(watched, event); } QString PreviewItem::styleName() const { return m_styleName; } void PreviewItem::setStyleName(const QString &styleName) { if (m_styleName == styleName) { return; } m_styleName = styleName; reload(); emit styleNameChanged(); } bool PreviewItem::isValid() const { return m_style && m_widget; } void setStyleRecursively(QWidget *widget, QStyle *style, const QPalette &palette) { // Don't let styles kill the palette for other styles being previewed. widget->setPalette(QPalette()); widget->setPalette(palette); widget->setStyle(style); const auto children = widget->children(); for (QObject *child : children) { if (child->isWidgetType()) { setStyleRecursively(static_cast(child), style, palette); } } } void PreviewItem::reload() { if (!isComponentComplete()) { return; } const bool oldValid = isValid(); m_style.reset(QStyleFactory::create(m_styleName)); if (!m_style) { qWarning() << "Failed to load style" << m_styleName; if (oldValid != isValid()) { emit validChanged(); } return; } m_widget.reset(new QWidget); // Don't actually show the widget as a separate window when calling show() m_widget->setAttribute(Qt::WA_DontShowOnScreen); // Do not wait for this widget to close before the app closes m_widget->setAttribute(Qt::WA_QuitOnClose, false); m_ui.setupUi(m_widget.data()); // Prevent Qt from wrongly caching radio button images QPixmapCache::clear(); QPalette palette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig())); m_style->polish(palette); // HACK Needed so the previews look like their window is active // The previews don't have a parent (we're in QML, after all, there is no QWidget* to parent it to) // so QWidget::isActiveWindow() always returns false making the widget look dull // You still won't get hover effects in some themes (those that don't do that for inactive windows) // but at least at a glance it looks fine... for (int i = 0; i < QPalette::NColorRoles; ++i) { const auto role = static_cast(i); palette.setColor(QPalette::Inactive, role, palette.color(QPalette::Active, role)); } setStyleRecursively(m_widget.data(), m_style.data(), palette); m_widget->ensurePolished(); m_widget->resize(qRound(width()), qRound(height())); m_widget->installEventFilter(this); m_widget->show(); const auto sizeHint = m_widget->sizeHint(); setImplicitSize(sizeHint.width(), sizeHint.height()); if (oldValid != isValid()) { emit validChanged(); } } void PreviewItem::paint(QPainter *painter) { if (m_widget && m_widget->isVisible()) { m_widget->render(painter); } } void PreviewItem::hoverMoveEvent(QHoverEvent *event) { sendHoverEvent(event); } void PreviewItem::hoverLeaveEvent(QHoverEvent *event) { if (m_lastWidgetUnderMouse) { dispatchEnterLeave(nullptr, m_lastWidgetUnderMouse, mapToGlobal(event->pos())); m_lastWidgetUnderMouse = nullptr; } } void PreviewItem::sendHoverEvent(QHoverEvent *event) { if (!m_widget || !m_widget->isVisible()) { return; } QPointF pos = event->pos(); QWidget *child = m_widget->childAt(pos.toPoint()); QWidget *receiver = child ? child : m_widget.data(); dispatchEnterLeave(receiver, m_lastWidgetUnderMouse, mapToGlobal(event->pos())); m_lastWidgetUnderMouse = receiver; pos = receiver->mapFrom(m_widget.data(), pos.toPoint()); QMouseEvent mouseEvent(QEvent::MouseMove, pos, receiver->mapTo(receiver->topLevelWidget(), pos.toPoint()), receiver->mapToGlobal(pos.toPoint()), Qt::NoButton, {} /*buttons*/, event->modifiers()); qApp->sendEvent(receiver, &mouseEvent); event->setAccepted(mouseEvent.isAccepted()); } // Simplified copy of QApplicationPrivate::dispatchEnterLeave void PreviewItem::dispatchEnterLeave(QWidget *enter, QWidget *leave, const QPointF &globalPosF) { if ((!enter && !leave) || (enter == leave)) { return; } QWidgetList leaveList; QWidgetList enterList; bool sameWindow = leave && enter && leave->window() == enter->window(); if (leave && !sameWindow) { auto *w = leave; do { leaveList.append(w); } while (!w->isWindow() && (w = w->parentWidget())); } if (enter && !sameWindow) { auto *w = enter; do { enterList.append(w); } while (!w->isWindow() && (w = w->parentWidget())); } if (sameWindow) { int enterDepth = 0; int leaveDepth = 0; auto *e = enter; while (!e->isWindow() && (e = e->parentWidget())) enterDepth++; auto *l = leave; while (!l->isWindow() && (l = l->parentWidget())) leaveDepth++; QWidget* wenter = enter; QWidget* wleave = leave; while (enterDepth > leaveDepth) { wenter = wenter->parentWidget(); enterDepth--; } while (leaveDepth > enterDepth) { wleave = wleave->parentWidget(); leaveDepth--; } while (!wenter->isWindow() && wenter != wleave) { wenter = wenter->parentWidget(); wleave = wleave->parentWidget(); } for (auto *w = leave; w != wleave; w = w->parentWidget()) leaveList.append(w); for (auto *w = enter; w != wenter; w = w->parentWidget()) enterList.append(w); } const QPoint globalPos = globalPosF.toPoint(); QEvent leaveEvent(QEvent::Leave); for (int i = 0; i < leaveList.size(); ++i) { auto *w = leaveList.at(i); QApplication::sendEvent(w, &leaveEvent); if (w->testAttribute(Qt::WA_Hover)) { QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), w->mapFromGlobal(globalPos), QApplication::keyboardModifiers()); QApplication::sendEvent(w, &he); } } if (!enterList.isEmpty()) { const QPoint windowPos = qAsConst(enterList).back()->window()->mapFromGlobal(globalPos); for (auto it = enterList.crbegin(), end = enterList.crend(); it != end; ++it) { auto *w = *it; const QPointF localPos = w->mapFromGlobal(globalPos); QEnterEvent enterEvent(localPos, windowPos, globalPosF); QApplication::sendEvent(w, &enterEvent); if (w->testAttribute(Qt::WA_Hover)) { QHoverEvent he(QEvent::HoverEnter, localPos, QPoint(-1, -1), QApplication::keyboardModifiers()); QApplication::sendEvent(w, &he); } } } } void PreviewItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (!m_widget || newGeometry == oldGeometry) { - return; + if (m_widget && newGeometry != oldGeometry) { + m_widget->resize(qRound(newGeometry.width()), qRound(newGeometry.height())); } - m_widget->resize(qRound(newGeometry.width()), qRound(newGeometry.height())); + QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry); }