diff --git a/src/declarativeimports/core/colorscope.cpp b/src/declarativeimports/core/colorscope.cpp index e471ec998..21670d58f 100644 --- a/src/declarativeimports/core/colorscope.cpp +++ b/src/declarativeimports/core/colorscope.cpp @@ -1,226 +1,226 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2011 Artur Duque de Souza * * Copyright 2013 Sebastian Kügler * * * * 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 "colorscope.h" #include #include #include QHash ColorScope::s_attachedScopes = QHash(); QWeakPointer ColorScope::s_theme; ColorScope::ColorScope(QQuickItem *parent, QObject *parentObject) : QQuickItem(parent), m_inherit(false), m_group(Plasma::Theme::NormalColorGroup), m_parent(parentObject), m_actualGroup(Plasma::Theme::NormalColorGroup) { m_theme = s_theme.toStrongRef(); if (!m_theme) { QSharedPointer themePtr(new Plasma::Theme); s_theme = themePtr; m_theme = s_theme.toStrongRef(); } - connect(s_theme.data(), &Plasma::Theme::themeChanged, this, &ColorScope::colorsChanged); + connect(m_theme.data(), &Plasma::Theme::themeChanged, this, &ColorScope::colorsChanged); connect(this, &ColorScope::colorGroupChanged, this, &ColorScope::colorsChanged); QQuickItem *parentItem = qobject_cast(parentObject); if (parentItem) { connect(parentItem, &QQuickItem::parentChanged, this, &ColorScope::checkColorGroupChanged); } else if (m_parent) { m_parent->installEventFilter(this); } } ColorScope::~ColorScope() { s_attachedScopes.remove(m_parent); } ColorScope *ColorScope::qmlAttachedProperties(QObject *object) { const auto cs = s_attachedScopes.value(object); if (cs) { return cs; } ColorScope *s = new ColorScope(nullptr, object); s_attachedScopes[object] = s; s->m_inherit = true; s->setParent(object); s->checkColorGroupChanged(); return s; } bool ColorScope::eventFilter(QObject* watched, QEvent* event) { Q_ASSERT(watched == m_parent && !qobject_cast(watched)); if (event->type() == QEvent::ParentChange) { checkColorGroupChanged(); } return QQuickItem::eventFilter(watched, event); } void ColorScope::setParentScope(ColorScope* parentScope) { if (parentScope == m_parentScope) return; if (m_parentScope) { disconnect(m_parentScope.data(), &ColorScope::colorGroupChanged, this, &ColorScope::checkColorGroupChanged); } m_parentScope = parentScope; if (parentScope) { connect(parentScope, &ColorScope::colorGroupChanged, this, &ColorScope::checkColorGroupChanged); } } ColorScope *ColorScope::findParentScope() { QObject *p = nullptr; if (m_parent) { QQuickItem *gp = qobject_cast(m_parent); if (gp) { p = gp->parentItem(); } else { p = m_parent->parent(); } } if (!p || !m_parent) { setParentScope(nullptr); return nullptr; } ColorScope *c = qobject_cast(p); if (!c) { c = qmlAttachedProperties(p); } setParentScope(c); return m_parentScope; } void ColorScope::setColorGroup(Plasma::Theme::ColorGroup group) { if (m_group == group) { return; } m_group = group; checkColorGroupChanged(); } Plasma::Theme::ColorGroup ColorScope::colorGroup() const { return m_actualGroup; } QColor ColorScope::textColor() const { return m_theme->color(Plasma::Theme::TextColor, colorGroup()); } QColor ColorScope::highlightColor() const { return m_theme->color(Plasma::Theme::HighlightColor, colorGroup()); } QColor ColorScope::highlightedTextColor() const { return m_theme->color(Plasma::Theme::HighlightedTextColor, colorGroup()); } QColor ColorScope::backgroundColor() const { return m_theme->color(Plasma::Theme::BackgroundColor, colorGroup()); } QColor ColorScope::positiveTextColor() const { return m_theme->color(Plasma::Theme::PositiveTextColor, colorGroup()); } QColor ColorScope::neutralTextColor() const { return m_theme->color(Plasma::Theme::NeutralTextColor, colorGroup()); } QColor ColorScope::negativeTextColor() const { return m_theme->color(Plasma::Theme::NegativeTextColor, colorGroup()); } bool ColorScope::inherit() const { return m_inherit; } void ColorScope::setInherit(bool inherit) { if (m_inherit == inherit) { return; } m_inherit = inherit; emit inheritChanged(); checkColorGroupChanged(); } void ColorScope::itemChange(ItemChange change, const ItemChangeData &value) { if (change == QQuickItem::ItemSceneChange) { //we have a window: create the representations if needed if (value.window) { checkColorGroupChanged(); } } QQuickItem::itemChange(change, value); } void ColorScope::checkColorGroupChanged() { const auto last = m_actualGroup; if (m_inherit) { findParentScope(); m_actualGroup = m_parentScope ? m_parentScope->colorGroup() : m_group; } else { m_actualGroup = m_group; } if (m_actualGroup != last) { Q_EMIT colorGroupChanged(); } } #include "moc_colorscope.cpp" diff --git a/src/plasma/containmentactions.cpp b/src/plasma/containmentactions.cpp index f5381be31..a80331cbe 100644 --- a/src/plasma/containmentactions.cpp +++ b/src/plasma/containmentactions.cpp @@ -1,163 +1,169 @@ /* * Copyright (c) 2009 Chani Armitage * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library 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 "containmentactions.h" #include "containment.h" #include "private/containmentactions_p.h" #include "private/containment_p.h" #include #include #include #include #include #include #include "version.h" namespace Plasma { ContainmentActions::ContainmentActions(QObject *parentObject) : d(new ContainmentActionsPrivate({}, this)) { setParent(parentObject); } ContainmentActions::ContainmentActions(QObject *parentObject, const QVariantList &args) : d(new ContainmentActionsPrivate(args.value(0), this)) { setParent(parentObject); // now remove first item since those are managed by Wallpaper and subclasses shouldn't // need to worry about them. yes, it violates the constness of this var, but it lets us add // or remove items later while applets can just pretend that their args always start at 0 QVariantList &mutableArgs = const_cast(args); if (!mutableArgs.isEmpty()) { mutableArgs.removeFirst(); } } ContainmentActions::~ContainmentActions() { delete d; } KPluginInfo ContainmentActions::pluginInfo() const { return KPluginInfo(d->containmentActionsDescription); } Containment *ContainmentActions::containment() { if (d->containment) { return d->containment; } return qobject_cast(parent()); } void ContainmentActions::restore(const KConfigGroup &config) { Q_UNUSED(config); } void ContainmentActions::save(KConfigGroup &config) { Q_UNUSED(config); } QWidget *ContainmentActions::createConfigurationInterface(QWidget *parent) { Q_UNUSED(parent); return nullptr; } void ContainmentActions::configurationAccepted() { //do nothing by default } void ContainmentActions::performNextAction() { //do nothing by default, implement in subclasses } void ContainmentActions::performPreviousAction() { //do nothing by default, implement in subclasses } QList ContainmentActions::contextualActions() { return QList(); } QString ContainmentActions::eventToString(QEvent *event) { QString trigger; Qt::KeyboardModifiers modifiers; switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: { QMouseEvent *e = static_cast(event); int m = QObject::staticQtMetaObject.indexOfEnumerator("MouseButtons"); QMetaEnum mouse = QObject::staticQtMetaObject.enumerator(m); trigger += QString::fromLatin1(mouse.valueToKey(e->button())); modifiers = e->modifiers(); break; } case QEvent::Wheel: { QWheelEvent *e = static_cast(event); int o = QObject::staticQtMetaObject.indexOfEnumerator("Orientations"); QMetaEnum orient = QObject::staticQtMetaObject.enumerator(o); trigger = QStringLiteral("wheel:"); +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) trigger += QString::fromLatin1(orient.valueToKey(e->orientation())); +#else + // ContainmentInterface::wheelEvent uses angleDelta.y() + // To support both, should we just remove this orientation string? + trigger += QStringLiteral("Vertical"); +#endif modifiers = e->modifiers(); break; } case QEvent::ContextMenu: { int m = QObject::staticQtMetaObject.indexOfEnumerator("MouseButtons"); QMetaEnum mouse = QObject::staticQtMetaObject.enumerator(m); trigger = QString::fromLatin1(mouse.valueToKey(Qt::RightButton)); modifiers = Qt::NoModifier; break; } default: return QString(); } int k = QObject::staticQtMetaObject.indexOfEnumerator("KeyboardModifiers"); QMetaEnum kbd = QObject::staticQtMetaObject.enumerator(k); trigger += QLatin1Char(';') + QString::fromLatin1(kbd.valueToKeys(modifiers)); return trigger; } void ContainmentActions::setContainment(Containment *newContainment) { d->containment = newContainment; } } // Plasma namespace #include "moc_containmentactions.cpp" diff --git a/src/plasma/framesvg.cpp b/src/plasma/framesvg.cpp index a0725007c..4022bb048 100644 --- a/src/plasma/framesvg.cpp +++ b/src/plasma/framesvg.cpp @@ -1,895 +1,895 @@ /* * Copyright 2008-2010 by Aaron Seigo * Copyright 2008-2010 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library 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 "framesvg.h" #include "private/framesvg_p.h" #include #include #include #include #include #include #include #include #include #include "theme.h" #include "private/svg_p.h" #include "private/framesvg_helpers.h" #include "debug_p.h" namespace Plasma { QHash> > FrameSvgPrivate::s_sharedFrames; // Any attempt to generate a frame whose width or height is larger than this // will be rejected static const int MAX_FRAME_SIZE = 100000; FrameData::~FrameData() { FrameSvgPrivate::s_sharedFrames[theme].remove(cacheId); } FrameSvg::FrameSvg(QObject *parent) : Svg(parent), d(new FrameSvgPrivate(this)) { connect(this, &FrameSvg::repaintNeeded, this, std::bind(&FrameSvgPrivate::updateNeeded, d)); } FrameSvg::~FrameSvg() { delete d; } void FrameSvg::setImagePath(const QString &path) { if (path == imagePath()) { return; } clearCache(); setContainsMultipleImages(true); Svg::d->setImagePath(path); if (!d->repaintBlocked) { d->updateFrameData(); } } void FrameSvg::setEnabledBorders(const EnabledBorders borders) { if (borders == d->enabledBorders) { return; } d->enabledBorders = borders; if (!d->repaintBlocked) { d->updateFrameData(); } } FrameSvg::EnabledBorders FrameSvg::enabledBorders() const { return d->enabledBorders; } void FrameSvg::setElementPrefix(Plasma::Types::Location location) { switch (location) { case Types::TopEdge: setElementPrefix(QStringLiteral("north")); break; case Types::BottomEdge: setElementPrefix(QStringLiteral("south")); break; case Types::LeftEdge: setElementPrefix(QStringLiteral("west")); break; case Types::RightEdge: setElementPrefix(QStringLiteral("east")); break; default: setElementPrefix(QString()); break; } d->location = location; } void FrameSvg::setElementPrefix(const QString &prefix) { if (!hasElement(prefix % QLatin1String("-center"))) { d->prefix.clear(); } else { d->prefix = prefix; if (!d->prefix.isEmpty()) { d->prefix += QLatin1Char('-'); } } d->requestedPrefix = prefix; d->location = Types::Floating; if (!d->repaintBlocked) { d->updateFrameData(); } } bool FrameSvg::hasElementPrefix(const QString &prefix) const { //for now it simply checks if a center element exists, //because it could make sense for certain themes to not have all the elements if (prefix.isEmpty()) { return hasElement(QStringLiteral("center")); } if (prefix.endsWith(QLatin1Char('-'))) { return hasElement(prefix % QLatin1String("center")); } return hasElement(prefix % QLatin1String("-center")); } bool FrameSvg::hasElementPrefix(Plasma::Types::Location location) const { switch (location) { case Types::TopEdge: return hasElementPrefix(QStringLiteral("north")); case Types::BottomEdge: return hasElementPrefix(QStringLiteral("south")); case Types::LeftEdge: return hasElementPrefix(QStringLiteral("west")); case Types::RightEdge: return hasElementPrefix(QStringLiteral("east")); default: return hasElementPrefix(QString()); } } QString FrameSvg::prefix() { return d->requestedPrefix; } void FrameSvg::resizeFrame(const QSizeF &size) { if (imagePath().isEmpty()) { return; } if (size.isEmpty()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Invalid size" << size; #endif return; } if (d->frame && size.toSize() == d->frame->frameSize) { return; } d->pendingFrameSize = size.toSize(); if (!d->repaintBlocked) { d->updateFrameData(FrameSvgPrivate::UpdateFrame); } } QSizeF FrameSvg::frameSize() const { if (!d->frame) { return QSize(-1, -1); } else { return d->frameSize(d->frame.data()); } } qreal FrameSvg::marginSize(const Plasma::Types::MarginEdge edge) const { if (!d->frame) { return .0; } if (d->frame->noBorderPadding) { return .0; } switch (edge) { case Plasma::Types::TopMargin: return d->frame->topMargin; case Plasma::Types::LeftMargin: return d->frame->leftMargin; case Plasma::Types::RightMargin: return d->frame->rightMargin; //Plasma::BottomMargin default: return d->frame->bottomMargin; } } qreal FrameSvg::fixedMarginSize(const Plasma::Types::MarginEdge edge) const { if (!d->frame) { return .0; } if (d->frame->noBorderPadding) { return .0; } switch (edge) { case Plasma::Types::TopMargin: return d->frame->fixedTopMargin; case Plasma::Types::LeftMargin: return d->frame->fixedLeftMargin; case Plasma::Types::RightMargin: return d->frame->fixedRightMargin; //Plasma::BottomMargin default: return d->frame->fixedBottomMargin; } } void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { if (!d->frame || d->frame->noBorderPadding) { left = top = right = bottom = 0; return; } top = d->frame->topMargin; left = d->frame->leftMargin; right = d->frame->rightMargin; bottom = d->frame->bottomMargin; } void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { if (!d->frame || d->frame->noBorderPadding) { left = top = right = bottom = 0; return; } top = d->frame->fixedTopMargin; left = d->frame->fixedLeftMargin; right = d->frame->fixedRightMargin; bottom = d->frame->fixedBottomMargin; } QRectF FrameSvg::contentsRect() const { if (d->frame) { QRectF rect(QPoint(0,0), d->frame->frameSize); return rect.adjusted(d->frame->leftMargin, d->frame->topMargin, -d->frame->rightMargin, -d->frame->bottomMargin); } else { return QRectF(); } } QPixmap FrameSvg::alphaMask() const { //FIXME: the distinction between overlay and return d->alphaMask(); } QRegion FrameSvg::mask() const { QRegion result; if (!d->frame) { return result; } QString id = d->cacheId(d->frame.data(), QString()); QRegion* obj = d->frame->cachedMasks.object(id); if (!obj) { obj = new QRegion(QBitmap(d->alphaMask().mask())); result = *obj; d->frame->cachedMasks.insert(id, obj); } else { result = *obj; } return result; } void FrameSvg::setCacheAllRenderedFrames(bool cache) { if (d->cacheAll && !cache) { clearCache(); } d->cacheAll = cache; } bool FrameSvg::cacheAllRenderedFrames() const { return d->cacheAll; } void FrameSvg::clearCache() { if (d->frame) { d->frame->cachedBackground = QPixmap(); d->frame->cachedMasks.clear(); } if (d->maskFrame) { d->maskFrame->cachedBackground = QPixmap(); d->maskFrame->cachedMasks.clear(); } } QPixmap FrameSvg::framePixmap() { if (d->frame->cachedBackground.isNull()) { d->generateBackground(d->frame); } return d->frame->cachedBackground; } void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source) { if (d->frame->cachedBackground.isNull()) { d->generateBackground(d->frame); if (d->frame->cachedBackground.isNull()) { return; } } painter->drawPixmap(target, d->frame->cachedBackground, source.isValid() ? source : target); } void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) { if (d->frame->cachedBackground.isNull()) { d->generateBackground(d->frame); if (d->frame->cachedBackground.isNull()) { return; } } painter->drawPixmap(pos, d->frame->cachedBackground); } //#define DEBUG_FRAMESVG_CACHE FrameSvgPrivate::~FrameSvgPrivate() = default; QPixmap FrameSvgPrivate::alphaMask() { QString maskPrefix; if (q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))) { maskPrefix = QStringLiteral("mask-"); } if (maskPrefix.isNull()) { if (frame->cachedBackground.isNull()) { generateBackground(frame); } return frame->cachedBackground; } // We are setting the prefix only temporary to generate // the needed mask image const QString maskRequestedPrefix = requestedPrefix.isEmpty() ? QStringLiteral("mask") : maskPrefix % requestedPrefix; maskPrefix = maskPrefix % prefix; if (!maskFrame) { maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix); if (!maskFrame->cachedBackground.isNull()) { return maskFrame->cachedBackground; } updateSizes(maskFrame); generateBackground(maskFrame); return maskFrame->cachedBackground; } const bool shouldUpdate = maskFrame->enabledBorders != frame->enabledBorders || maskFrame->frameSize != frameSize(frame.data()) || maskFrame->imagePath != frame->imagePath; if (shouldUpdate) { maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix); if (!maskFrame->cachedBackground.isNull()) { return maskFrame->cachedBackground; } updateSizes(maskFrame); } if (maskFrame->cachedBackground.isNull()) { generateBackground(maskFrame); } return maskFrame->cachedBackground; } QSharedPointer FrameSvgPrivate::lookupOrCreateMaskFrame(const QSharedPointer &frame, const QString &maskPrefix, const QString &maskRequestedPrefix) { const QString key = cacheId(frame.data(), maskPrefix); QSharedPointer mask = s_sharedFrames[q->theme()->d].value(key); // See if we can find a suitable candidate in the shared frames. // If there is one, use it. if (mask) { return mask; } mask.reset(new FrameData(*frame.data(), q)); mask->prefix = maskPrefix; mask->requestedPrefix = maskRequestedPrefix; mask->theme = q->theme()->d; mask->imagePath = frame->imagePath; mask->enabledBorders = frame->enabledBorders; mask->frameSize = frameSize(frame).toSize(); mask->cacheId = key; s_sharedFrames[q->theme()->d].insert(key, mask); return mask; } void FrameSvgPrivate::generateBackground(const QSharedPointer &frame) { if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(frame->prefix)) { return; } const QString id = cacheId(frame.data(), frame->prefix); bool frameCached = !frame->cachedBackground.isNull(); bool overlayCached = false; const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay")); QPixmap overlay; if (q->isUsingRenderingCache()) { frameCached = q->theme()->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull(); if (overlayAvailable) { overlayCached = q->theme()->findInCache(QLatin1String("overlay_") % id, overlay) && !overlay.isNull(); } } if (!frameCached) { generateFrameBackground(frame); } //Overlays QSize overlaySize; QPoint actualOverlayPos = QPoint(0, 0); if (overlayAvailable && !overlayCached) { overlaySize = q->elementSize(frame->prefix % QLatin1String("overlay")); if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-right"))) { actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width()); } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-bottom"))) { actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height()); //Stretched or Tiled? } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-stretch"))) { overlaySize = frameSize(frame).toSize(); } else { if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal"))) { overlaySize.setWidth(frameSize(frame).width()); } if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { overlaySize.setHeight(frameSize(frame).height()); } } overlay = alphaMask(); QPainter overlayPainter(&overlay); overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); //Tiling? if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal")) || q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { QSize s = q->size(); q->resize(q->elementSize(frame->prefix % QLatin1String("overlay"))); overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(frame->prefix % QLatin1String("overlay"))); q->resize(s); } else { q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), frame->prefix % QLatin1String("overlay")); } overlayPainter.end(); } if (!frameCached) { cacheFrame(frame->prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); } if (!overlay.isNull()) { QPainter p(&frame->cachedBackground); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize)); } } void FrameSvgPrivate::generateFrameBackground(const QSharedPointer &frame) { //qCDebug(LOG_PLASMA) << "generating background"; const QSize size = frameSize(frame).toSize() * q->devicePixelRatio(); if (!size.isValid()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Invalid frame size" << size; #endif return; } if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) { qCWarning(LOG_PLASMA) << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size; return; } frame->cachedBackground = QPixmap(size); frame->cachedBackground.fill(Qt::transparent); QPainter p(&frame->cachedBackground); p.setCompositionMode(QPainter::CompositionMode_Source); p.setRenderHint(QPainter::SmoothPixmapTransform); QRect contentRect = contentGeometry(frame, size); paintCenter(p, frame, contentRect, size); paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, contentRect); paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, contentRect); paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, contentRect); paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect); // Sides const int leftHeight = q->elementSize(frame->prefix % QLatin1String("left")).height(); paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight) * q->devicePixelRatio(), contentRect); const int rightHeight = q->elementSize(frame->prefix % QLatin1String("right")).height(); paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, rightHeight) * q->devicePixelRatio(), contentRect); const int topWidth = q->elementSize(frame->prefix % QLatin1String("top")).width(); paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight) * q->devicePixelRatio(), contentRect); const int bottomWidth = q->elementSize(frame->prefix % QLatin1String("bottom")).width(); paintBorder(p, frame, FrameSvg::BottomBorder, QSize(bottomWidth, frame->bottomHeight) * q->devicePixelRatio(), contentRect); p.end(); frame->cachedBackground.setDevicePixelRatio(q->devicePixelRatio()); } QRect FrameSvgPrivate::contentGeometry(const QSharedPointer &frame, const QSize& size) const { const QSize contentSize(size.width() - frame->leftWidth * q->devicePixelRatio() - frame->rightWidth * q->devicePixelRatio(), size.height() - frame->topHeight * q->devicePixelRatio() - frame->bottomHeight * q->devicePixelRatio()); QRect contentRect(QPoint(0,0), contentSize); if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(frame->prefix % QLatin1String("left"))) { contentRect.translate(frame->leftWidth * q->devicePixelRatio(), 0); } // Corners if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(frame->prefix % QLatin1String("top"))) { contentRect.translate(0, frame->topHeight * q->devicePixelRatio()); } return contentRect; } void FrameSvgPrivate::updateFrameData(UpdateType updateType) { auto fd = frame; QString newKey; if (fd) { const QString oldKey = fd->cacheId; const QString oldPath = fd->imagePath; const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders; const QSize currentSize = fd->frameSize; fd->enabledBorders = enabledBorders; fd->frameSize = pendingFrameSize; fd->imagePath = q->imagePath(); newKey = cacheId(fd.data(), prefix); //reset frame to old values fd->enabledBorders = oldBorders; fd->frameSize = currentSize; fd->imagePath = oldPath; //FIXME: something more efficient than string comparison? if (oldKey == newKey) { return; } //qCDebug(LOG_PLASMA) << "looking for" << newKey; auto newFd = FrameSvgPrivate::s_sharedFrames[q->theme()->d].value(newKey); if (newFd) { //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; // we've found a match, use that one - Q_ASSERT(newKey == newFd.data()->cacheId); + Q_ASSERT(newKey == newFd.lock()->cacheId); frame = newFd; return; } fd.reset(new FrameData(*fd, q)); } else { fd.reset(new FrameData(q, QString())); } frame = fd; fd->prefix = prefix; fd->requestedPrefix = requestedPrefix; //updateSizes(); fd->enabledBorders = enabledBorders; fd->frameSize = pendingFrameSize; fd->imagePath = q->imagePath(); //was fd just created empty now? if (newKey.isEmpty()) { newKey = cacheId(fd.data(), prefix); } // we know it isn't in s_sharedFrames due to the check above, so insert it now FrameSvgPrivate::s_sharedFrames[q->theme()->d].insert(newKey, fd); fd->cacheId = newKey; fd->theme = q->theme()->d; if (updateType == UpdateFrameAndMargins) { updateAndSignalSizes(); } else { updateSizes(frame); } } void FrameSvgPrivate::paintCenter(QPainter& p, const QSharedPointer &frame, const QRect& contentRect, const QSize& fullSize) { if (!contentRect.isEmpty()) { const QString centerElementId = frame->prefix % QLatin1String("center"); if (frame->tileCenter) { QSize centerTileSize = q->elementSize(centerElementId); QPixmap center(centerTileSize); center.fill(Qt::transparent); QPainter centerPainter(¢er); centerPainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(¢erPainter, QRect(QPoint(0, 0), centerTileSize),centerElementId); if (frame->composeOverBorder) { p.drawTiledPixmap(QRect(QPoint(0, 0), fullSize), center); } else { p.drawTiledPixmap(FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), center); } } else { if (frame->composeOverBorder) { q->paint(&p, QRect(QPoint(0, 0), fullSize), centerElementId); } else { q->paint(&p, FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), centerElementId); } } } if (frame->composeOverBorder) { p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.drawPixmap(QRect(QPoint(0, 0), fullSize), alphaMask()); p.setCompositionMode(QPainter::CompositionMode_SourceOver); } } void FrameSvgPrivate::paintBorder(QPainter& p, const QSharedPointer &frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const { QString side = frame->prefix % FrameSvgHelpers::borderToElementId(borders); if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) { if (frame->stretchBorders) { q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), side); } else { QPixmap px(size); px.fill(Qt::transparent); QPainter sidePainter(&px); sidePainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(&sidePainter, QRect(QPoint(0, 0), size), side); p.drawTiledPixmap(FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), px); } } } void FrameSvgPrivate::paintCorner(QPainter& p, const QSharedPointer &frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const { // Draw the corner only if both borders in both directions are enabled. if ((frame->enabledBorders & border) != border) { return; } const QString corner = frame->prefix % FrameSvgHelpers::borderToElementId(border); if (q->hasElement(corner)) { q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize * q->devicePixelRatio()), corner); } } QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const { const QSize size = frameSize(frame).toSize(); const QLatin1Char s('_'); return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % QString::number(q->scaleFactor()) % s % QString::number(q->devicePixelRatio()) % s % prefixToSave % s % frame->imagePath; } void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay) { if (!q->isUsingRenderingCache()) { return; } //insert background if (!frame) { return; } const QString id = cacheId(frame.data(), prefixToSave); //qCDebug(LOG_PLASMA)<<"Saving to cache frame"<theme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave); if (!overlay.isNull()) { //insert overlay q->theme()->insertIntoCache(QLatin1String("overlay_") % id, overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay")); } } void FrameSvgPrivate::updateSizes(FrameData *frame) const { //qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix; Q_ASSERT(frame); QSize s = q->size(); q->resize(); if (!frame->cachedBackground.isNull()) { frame->cachedBackground = QPixmap(); } //This has the same size regardless the border is enabled or not frame->fixedTopHeight = q->elementSize(frame->prefix % QLatin1String("top")).height(); if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) { frame->fixedTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height(); } else { frame->fixedTopMargin = frame->fixedTopHeight; } //The same, but its size depends from the margin being enabled if (frame->enabledBorders & FrameSvg::TopBorder) { frame->topMargin = frame->fixedTopMargin; frame->topHeight = frame->fixedTopHeight; } else { frame->topMargin = frame->topHeight = 0; } frame->fixedLeftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width(); if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) { frame->fixedLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width(); } else { frame->fixedLeftMargin = frame->fixedLeftWidth; } if (frame->enabledBorders & FrameSvg::LeftBorder) { frame->leftMargin = frame->fixedLeftMargin; frame->leftWidth = frame->fixedLeftWidth; } else { frame->leftMargin = frame->leftWidth = 0; } frame->fixedRightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width(); if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) { frame->fixedRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width(); } else { frame->fixedRightMargin = frame->fixedRightWidth; } if (frame->enabledBorders & FrameSvg::RightBorder) { frame->rightMargin = frame->fixedRightMargin; frame->rightWidth = frame->fixedRightWidth; } else { frame->rightMargin = frame->rightWidth = 0; } frame->fixedBottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height(); if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) { frame->fixedBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height(); } else { frame->fixedBottomMargin = frame->fixedBottomHeight; } if (frame->enabledBorders & FrameSvg::BottomBorder) { frame->bottomMargin = frame->fixedBottomMargin; frame->bottomHeight = frame->fixedBottomHeight; } else { frame->bottomMargin = frame->bottomHeight = 0; } frame->composeOverBorder = (q->hasElement(frame->prefix % QLatin1String("hint-compose-over-border")) && q->hasElement(QLatin1String("mask-") % frame->prefix % QLatin1String("center"))); //since it's rectangular, topWidth and bottomWidth must be the same //the ones that don't have a frame->prefix is for retrocompatibility frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(frame->prefix % QLatin1String("hint-tile-center"))); frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(frame->prefix % QLatin1String("hint-no-border-padding"))); frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(frame->prefix % QLatin1String("hint-stretch-borders"))); q->resize(s); } void FrameSvgPrivate::updateNeeded() { q->setElementPrefix(requestedPrefix); //frame not created yet? if (!frame) { return; } q->clearCache(); updateSizes(frame); } void FrameSvgPrivate::updateAndSignalSizes() { //frame not created yet? if (!frame) { return; } updateSizes(frame); emit q->repaintNeeded(); } QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const { if (!frame) { return QSizeF(); } if (!frame->frameSize.isValid()) { updateSizes(frame); frame->frameSize = q->size(); } return frame->frameSize; } QString FrameSvg::actualPrefix() const { return d->prefix; } bool FrameSvg::isRepaintBlocked() const { return d->repaintBlocked; } void FrameSvg::setRepaintBlocked(bool blocked) { d->repaintBlocked = blocked; if (!blocked) { d->updateFrameData(); } } } // Plasma namespace diff --git a/src/plasmaquick/dialog.cpp b/src/plasmaquick/dialog.cpp index 8c3ebe56e..47c05f2f4 100644 --- a/src/plasmaquick/dialog.cpp +++ b/src/plasmaquick/dialog.cpp @@ -1,1394 +1,1407 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2013 Sebastian Kügler * * Copyright 2014 Martin Gräßlin * * Copyright 2014 Vishesh Handa * * * * 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 "dialog.h" #include "config-plasma.h" #include "../declarativeimports/core/framesvgitem.h" #include "dialogshadows_p.h" #include "view.h" #include "configview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_KWAYLAND #include #include #endif #if HAVE_XCB_SHAPE #include #include #endif //Unfortunately QWINDOWSIZE_MAX is not exported #define DIALOGSIZE_MAX ((1<<24)-1) namespace PlasmaQuick { class DialogPrivate { public: DialogPrivate(Dialog *dialog) : q(dialog), location(Plasma::Types::BottomEdge), frameSvgItem(nullptr), hasMask(false), type(Dialog::Normal), hideOnWindowDeactivate(false), outputOnly(false), visible(false), componentComplete(dialog->parent() == nullptr), backgroundHints(Dialog::StandardBackground) { hintsCommitTimer.setSingleShot(true); hintsCommitTimer.setInterval(0); QObject::connect(&hintsCommitTimer, SIGNAL(timeout()), q, SLOT(updateLayoutParameters())); } void updateInputShape(); //SLOTS /** * Sync Borders updates the enabled borders of the frameSvgItem depending * on the geometry of the window. * * \param windowGeometry The window geometry which should be taken into * consideration when activating/deactivating certain borders */ void syncBorders(const QRect& windowGeometry); /** * This function sets the blurBehind, background contrast and shadows. It * does so wrt the frameSvgItem. So make sure the frameSvgItem is the * correct size before calling this function. */ void updateTheme(); void updateVisibility(bool visible); void updateMinimumWidth(); void updateMinimumHeight(); void updateMaximumWidth(); void updateMaximumHeight(); /** * Gets the maximum and minimum size hints for the window based on the contents. it doesn't actually resize anything */ void getSizeHints(QSize &min, QSize &max) const; /** * This function is an optimized version of updateMaximumHeight, * updateMaximumWidth,updateMinimumWidth and updateMinimumHeight. * It should be called when you need to call all 4 of these functions * AND you have called syncToMainItemSize before. */ void updateLayoutParameters(); QRect availableScreenGeometryForPosition(const QPoint& pos) const; /** * This function checks the current position of the dialog and repositions * it so that no part of it is not on the screen */ void repositionIfOffScreen(); void slotMainItemSizeChanged(); void slotWindowPositionChanged(); void syncToMainItemSize(); bool mainItemContainsPosition(const QPointF &point) const; QPointF positionAdjustedForMainItem(const QPointF &point) const; void setupWaylandIntegration(); Dialog *q; Plasma::Types::Location location; Plasma::FrameSvgItem *frameSvgItem; QPointer mainItem; QPointer visualParent; QTimer hintsCommitTimer; #if HAVE_KWAYLAND QPointer shellSurface; #endif QRect cachedGeometry; bool hasMask; Dialog::WindowType type; bool hideOnWindowDeactivate; bool outputOnly; bool visible; Plasma::Theme theme; bool componentComplete; Dialog::BackgroundHints backgroundHints; //Attached Layout property of mainItem, if any QPointer mainItemLayout; }; QRect DialogPrivate::availableScreenGeometryForPosition(const QPoint& pos) const { // FIXME: QWindow::screen() never ever changes if the window is moved across // virtual screens (normal two screens with X), this seems to be intentional // as it's explicitly mentioned in the docs. Until that's changed or some // more proper way of howto get the current QScreen for given QWindow is found, // we simply iterate over the virtual screens and pick the one our QWindow // says it's at. QRect avail; Q_FOREACH (QScreen *screen, QGuiApplication::screens()) { //we check geometry() but then take availableGeometry() //to reliably check in which screen a position is, we need the full //geometry, including areas for panels if (screen->geometry().contains(pos)) { avail = screen->availableGeometry(); break; } } /* * if the heuristic fails (because the topleft of the dialog is offscreen) * use at least our screen() * the screen should be correctly updated now on Qt 5.3+ so should be * more reliable anyways (could be tried to remove the whole Q_FOREACH * at this point) * * important: screen can be a nullptr... see bug 345173 */ if (avail.isEmpty() && q->screen()) { avail = q->screen()->availableGeometry(); } return avail; } void DialogPrivate::syncBorders(const QRect& geom) { QRect avail = availableScreenGeometryForPosition(geom.topLeft()); int borders = Plasma::FrameSvg::AllBorders; //Tooltips always have all the borders // floating windows have all borders if (!q->flags().testFlag(Qt::ToolTip) && location != Plasma::Types::Floating) { if (geom.x() <= avail.x() || location == Plasma::Types::LeftEdge) { borders = borders & ~Plasma::FrameSvg::LeftBorder; } if (geom.y() <= avail.y() || location == Plasma::Types::TopEdge) { borders = borders & ~Plasma::FrameSvg::TopBorder; } if (avail.right() <= geom.x() + geom.width() || location == Plasma::Types::RightEdge) { borders = borders & ~Plasma::FrameSvg::RightBorder; } if (avail.bottom() <= geom.y() + geom.height() || location == Plasma::Types::BottomEdge) { borders = borders & ~Plasma::FrameSvg::BottomBorder; } } if (frameSvgItem->enabledBorders() != (Plasma::FrameSvg::EnabledBorder)borders) { frameSvgItem->setEnabledBorders((Plasma::FrameSvg::EnabledBorder)borders); } } void DialogPrivate::updateTheme() { if (backgroundHints == Dialog::NoBackground) { frameSvgItem->setImagePath(QString()); KWindowEffects::enableBlurBehind(q->winId(), false); KWindowEffects::enableBackgroundContrast(q->winId(), false); q->setMask(QRegion()); DialogShadows::self()->removeWindow(q); } else { if (type == Dialog::Tooltip) { frameSvgItem->setImagePath(QStringLiteral("widgets/tooltip")); } else { frameSvgItem->setImagePath(QStringLiteral("dialogs/background")); } KWindowEffects::enableBlurBehind(q->winId(), theme.blurBehindEnabled(), frameSvgItem->mask()); KWindowEffects::enableBackgroundContrast(q->winId(), theme.backgroundContrastEnabled(), theme.backgroundContrast(), theme.backgroundIntensity(), theme.backgroundSaturation(), frameSvgItem->mask()); if (KWindowSystem::compositingActive()) { if (hasMask) { hasMask = false; q->setMask(QRegion()); } } else { hasMask = true; q->setMask(frameSvgItem->mask()); } if (q->isVisible()) { DialogShadows::self()->addWindow(q, frameSvgItem->enabledBorders()); } } updateInputShape(); } void DialogPrivate::updateVisibility(bool visible) { if (visible) { if (visualParent && visualParent->window()) { q->setTransientParent(visualParent->window()); } if (q->location() == Plasma::Types::FullScreen) { frameSvgItem->setEnabledBorders(Plasma::FrameSvg::NoBorder); // We cache the original size of the item, to retrieve it // when the dialog is switched back from fullscreen. if (q->geometry() != q->screen()->availableGeometry()) { cachedGeometry = q->geometry(); } q->setGeometry(q->screen()->availableGeometry()); } else { if (!cachedGeometry.isNull()) { q->resize(cachedGeometry.size()); slotWindowPositionChanged(); if (visualParent) { q->setPosition(q->popupPosition(visualParent, q->size())); } cachedGeometry = QRect(); } if (mainItem) { syncToMainItemSize(); } if (mainItemLayout) { updateLayoutParameters(); } #if HAVE_KWAYLAND //if is a wayland window that was hidden, we need //to set its position again as there won't be any move event to sync QWindow::position and shellsurface::position if (shellSurface && type != Dialog::OnScreenDisplay) { shellSurface->setPosition(q->position()); } #endif } } if (!q->flags().testFlag(Qt::ToolTip) && type != Dialog::Notification && type != Dialog::CriticalNotification) { KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge; switch (location) { case Plasma::Types::TopEdge: slideLocation = KWindowEffects::TopEdge; break; case Plasma::Types::LeftEdge: slideLocation = KWindowEffects::LeftEdge; break; case Plasma::Types::RightEdge: slideLocation = KWindowEffects::RightEdge; break; case Plasma::Types::BottomEdge: slideLocation = KWindowEffects::BottomEdge; break; //no edge, no slide default: break; } KWindowEffects::slideWindow(q->winId(), slideLocation, -1); } if (visible) { q->raise(); if (type != Dialog::Normal) { KWindowSystem::setType(q->winId(), (NET::WindowType)type); } else { q->setFlags(Qt::FramelessWindowHint | q->flags()); } if (type == Dialog::Dock || type == Dialog::Notification || type == Dialog::OnScreenDisplay || type == Dialog::CriticalNotification) { KWindowSystem::setOnAllDesktops(q->winId(), true); } else { KWindowSystem::setOnAllDesktops(q->winId(), false); } } } void DialogPrivate::updateMinimumWidth() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete) { return; } q->setMinimumWidth(0); //this is to try to get the internal item resized a tad before, but //the flicker almost always happen anyways, so is *probably* useless //this other kind of flicker is the view not being always focused exactly //on the scene auto margin = frameSvgItem->fixedMargins(); int minimumWidth = mainItemLayout->property("minimumWidth").toInt() + margin->left() + margin->right(); if (q->screen()) { minimumWidth = qMin(q->screen()->availableGeometry().width(), minimumWidth); } q->contentItem()->setWidth(qMax(q->width(), minimumWidth)); q->setWidth(qMax(q->width(), minimumWidth)); hintsCommitTimer.start(); } void DialogPrivate::updateMinimumHeight() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete) { return; } q->setMinimumHeight(0); //this is to try to get the internal item resized a tad before, but //the flicker almost always happen anyways, so is *probably* useless //this other kind of flicker is the view not being always focused exactly //on the scene auto margin = frameSvgItem->fixedMargins(); int minimumHeight = mainItemLayout->property("minimumHeight").toInt() + margin->top() + margin->bottom(); if (q->screen()) { minimumHeight = qMin(q->screen()->availableGeometry().height(), minimumHeight); } q->contentItem()->setHeight(qMax(q->height(), minimumHeight)); q->setHeight(qMax(q->height(), minimumHeight)); hintsCommitTimer.start(); } void DialogPrivate::updateMaximumWidth() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete) { return; } q->setMaximumWidth(DIALOGSIZE_MAX); auto margin = frameSvgItem->fixedMargins(); int maximumWidth = mainItemLayout->property("maximumWidth").toInt() + margin->left() + margin->right(); if (q->screen()) { maximumWidth = qMin(q->screen()->availableGeometry().width(), maximumWidth); } q->contentItem()->setWidth(qMax(q->width(), maximumWidth)); q->setWidth(qMax(q->width(), maximumWidth)); hintsCommitTimer.start(); } void DialogPrivate::updateMaximumHeight() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete) { return; } q->setMaximumHeight(DIALOGSIZE_MAX); auto margin = frameSvgItem->fixedMargins(); int maximumHeight = mainItemLayout->property("maximumHeight").toInt() + margin->top() + margin->bottom(); if (q->screen()) { maximumHeight = qMin(q->screen()->availableGeometry().height(), maximumHeight); } q->contentItem()->setHeight(qMax(q->height(), maximumHeight)); q->setHeight(qMin(q->height(), maximumHeight)); hintsCommitTimer.start(); } void DialogPrivate::getSizeHints(QSize &min, QSize &max) const { if (!componentComplete || !mainItem || !mainItemLayout) { return; } Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); auto margin = frameSvgItem->fixedMargins(); int minimumHeight = mainItemLayout->property("minimumHeight").toInt(); int maximumHeight = mainItemLayout->property("maximumHeight").toInt(); maximumHeight = maximumHeight ? maximumHeight : DIALOGSIZE_MAX; int minimumWidth = mainItemLayout->property("minimumWidth").toInt(); int maximumWidth = mainItemLayout->property("maximumWidth").toInt(); maximumWidth = maximumWidth ? maximumWidth : DIALOGSIZE_MAX; minimumHeight += margin->top() + margin->bottom(); maximumHeight += margin->top() + margin->bottom(); minimumWidth += margin->left() + margin->right(); maximumWidth += margin->left() + margin->right(); if (q->screen()) { minimumWidth = qMin(q->screen()->availableGeometry().width(), minimumWidth); minimumHeight = qMin(q->screen()->availableGeometry().height(), minimumHeight); maximumWidth = qMin(q->screen()->availableGeometry().width(), maximumWidth); maximumHeight = qMin(q->screen()->availableGeometry().height(), maximumHeight); } min = QSize(minimumWidth, minimumHeight); max = QSize(maximumWidth, maximumHeight); } void DialogPrivate::updateLayoutParameters() { if (!componentComplete || !mainItem || !mainItemLayout) { return; } mainItem->disconnect(q); auto margin = frameSvgItem->fixedMargins(); QSize min; QSize max(DIALOGSIZE_MAX, DIALOGSIZE_MAX); getSizeHints(min, max); const QSize finalSize(qBound(min.width(), q->width(), max.width()), qBound(min.height(), q->height(), max.height())); if (visualParent) { //it's important here that we're using re->size() as size, we don't want to do recursive resizeEvents const QRect geom(q->popupPosition(visualParent, finalSize), finalSize); q->adjustGeometry(geom); } else { q->resize(finalSize); } mainItem->setPosition(QPointF(margin->left(), margin->top())); mainItem->setSize(QSizeF(q->width() - margin->left() - margin->right(), q->height() - margin->top() - margin->bottom())); frameSvgItem->setSize(QSizeF(q->width(), q->height())); repositionIfOffScreen(); updateTheme(); //FIXME: this seems to interfere with the geometry change that //sometimes is still going on, causing flicker (this one is two repositions happening in quick succession). //it may have to be delayed further q->setMinimumSize(min); q->setMaximumSize(max); QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged())); QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged())); } void DialogPrivate::repositionIfOffScreen() { if (!componentComplete) { return; } const QRect avail = availableScreenGeometryForPosition(q->position()); int x = q->x(); int y = q->y(); if (x < avail.left()) { x = avail.left(); } else if (x + q->width() > avail.right()) { x = avail.right() - q->width() + 1; } if (y < avail.top()) { y = avail.top(); } else if (y + q->height() > avail.bottom()) { y = avail.bottom() - q->height() + 1; } q->setX(x); q->setY(y); } void DialogPrivate::updateInputShape() { if (!q->isVisible()) { return; } #if HAVE_XCB_SHAPE if (KWindowSystem::isPlatformX11()) { xcb_connection_t *c = QX11Info::connection(); static bool s_shapeExtensionChecked = false; static bool s_shapeAvailable = false; if (!s_shapeExtensionChecked) { xcb_prefetch_extension_data(c, &xcb_shape_id); const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, &xcb_shape_id); if (extension->present) { // query version auto cookie = xcb_shape_query_version(c); QScopedPointer version(xcb_shape_query_version_reply(c, cookie, nullptr)); if (!version.isNull()) { s_shapeAvailable = (version->major_version * 0x10 + version->minor_version) >= 0x11; } } s_shapeExtensionChecked = true; } if (!s_shapeAvailable) { return; } if (outputOnly) { // set input shape, so that it doesn't accept any input events xcb_shape_rectangles(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, q->winId(), 0, 0, 0, nullptr); } else { // delete the shape xcb_shape_mask(c, XCB_SHAPE_SO_INTERSECT, XCB_SHAPE_SK_INPUT, q->winId(), 0, 0, XCB_PIXMAP_NONE); } } #endif } void DialogPrivate::syncToMainItemSize() { Q_ASSERT(mainItem); if (!componentComplete) { return; } if (mainItem->width() <= 0 || mainItem->height() <= 0) { qWarning() << "trying to show an empty dialog"; } updateTheme(); if (visualParent) { // fixedMargins will get all the borders, no matter if they are enabled auto margins = frameSvgItem->fixedMargins(); const QSize fullSize = QSize(mainItem->width(), mainItem->height()) + QSize(margins->left() + margins->right(), margins->top() + margins->bottom()); // We get the popup position with the fullsize as we need the popup // position in order to determine our actual size, as the position // determines which borders will be shown. const QRect geom(q->popupPosition(visualParent, fullSize), fullSize); // We're then moving the window to where we think we would be with all // the borders. This way when syncBorders is called, it has a geometry // to work with. syncBorders(geom); } else { syncBorders(q->geometry()); } QSize s = QSize(mainItem->width(), mainItem->height()) + QSize(frameSvgItem->fixedMargins()->left() + frameSvgItem->fixedMargins()->right(), frameSvgItem->fixedMargins()->top() + frameSvgItem->fixedMargins()->bottom()); QSize min; QSize max(DIALOGSIZE_MAX, DIALOGSIZE_MAX); getSizeHints(min, max); s = QSize(qBound(min.width(), s.width(), max.width()), qBound(min.height(), s.height(), max.height())); q->contentItem()->setSize(s); frameSvgItem->setSize(s); if (visualParent) { const QRect geom(q->popupPosition(visualParent, s), s); if (geom == q->geometry()) { return; } q->adjustGeometry(geom); // The borders will instantly be updated but the geometry might take a // while as sub-classes can reimplement adjustGeometry and animate it. syncBorders(geom); } else { q->resize(s); } mainItem->setPosition(QPointF(frameSvgItem->fixedMargins()->left(), frameSvgItem->fixedMargins()->top())); updateTheme(); } void DialogPrivate::slotWindowPositionChanged() { // Tooltips always have all the borders // floating windows have all borders if (!q->isVisible() || q->flags().testFlag(Qt::ToolTip) || location == Plasma::Types::Floating) { return; } syncBorders(q->geometry()); updateTheme(); if (mainItem) { auto margin = frameSvgItem->fixedMargins(); mainItem->setPosition(QPoint(margin->left(), margin->top())); mainItem->setSize(QSize(q->width() - margin->left() - margin->right(), q->height() - margin->top() - margin->bottom())); } } bool DialogPrivate::mainItemContainsPosition(const QPointF &point) const { if (!mainItem) { return false; } return QRectF(mainItem->mapToScene(QPoint(0,0)), QSizeF(mainItem->width(), mainItem->height())).contains(point); } QPointF DialogPrivate::positionAdjustedForMainItem(const QPointF &point) const { if (!mainItem) { return point; } QRectF itemRect(mainItem->mapToScene(QPoint(0,0)), QSizeF(mainItem->width(), mainItem->height())); return QPointF(qBound(itemRect.left(), point.x(), itemRect.right()), qBound(itemRect.top(), point.y(), itemRect.bottom())); } void DialogPrivate::setupWaylandIntegration() { #if HAVE_KWAYLAND if (shellSurface) { // already setup return; } using namespace KWayland::Client; PlasmaShell *interface = DialogShadows::self()->waylandPlasmaShellInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(q); if (!s) { return; } shellSurface = interface->createSurface(s, q); #endif } Dialog::Dialog(QQuickItem *parent) : QQuickWindow(parent ? parent->window() : nullptr), d(new DialogPrivate(this)) { setClearBeforeRendering(true); setColor(QColor(Qt::transparent)); setFlags(Qt::FramelessWindowHint | Qt::Dialog); connect(this, &QWindow::xChanged, [=]() { d->slotWindowPositionChanged(); }); connect(this, &QWindow::yChanged, [=]() { d->slotWindowPositionChanged(); }); // Given dialogs are skip task bar and don't have a decoration // minimizing them using e.g. "minimize all" should just close them connect(this, &QWindow::windowStateChanged, this, [this](Qt::WindowState newState) { if (newState == Qt::WindowMinimized) { setVisible(false); } }); connect(this, &QWindow::visibleChanged, this, &Dialog::visibleChangedProxy); connect(this, SIGNAL(visibleChanged(bool)), this, SLOT(updateInputShape())); connect(this, SIGNAL(outputOnlyChanged()), this, SLOT(updateInputShape())); //HACK: this property is invoked due to the initialization that gets done to contentItem() in the getter property("data"); //Create the FrameSvg background. d->frameSvgItem = new Plasma::FrameSvgItem(contentItem()); //This is needed as a transition thing for KWayland setProperty("__plasma_frameSvg", QVariant::fromValue(d->frameSvgItem->frameSvg())); connect(&d->theme, SIGNAL(themeChanged()), this, SLOT(updateTheme())); } Dialog::~Dialog() { if (!QCoreApplication::instance()->closingDown()) { DialogShadows::self()->removeWindow(this); } } QQuickItem *Dialog::mainItem() const { return d->mainItem; } void Dialog::setMainItem(QQuickItem *mainItem) { if (d->mainItem != mainItem) { d->hintsCommitTimer.stop(); if (d->mainItem) { disconnect(d->mainItem, nullptr, this, nullptr); d->mainItem->setParentItem(nullptr); } if (d->mainItemLayout) { disconnect(d->mainItemLayout, nullptr, this, nullptr); } d->mainItem = mainItem; if (mainItem) { mainItem->setParentItem(contentItem()); connect(mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged())); connect(mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged())); d->slotMainItemSizeChanged(); //Extract the representation's Layout, if any QObject *layout = nullptr; setMinimumSize(QSize(0, 0)); setMaximumSize(QSize(DIALOGSIZE_MAX, DIALOGSIZE_MAX)); //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, mainItem->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; break; } } d->mainItemLayout = layout; if (layout) { //Why queued connections? //we need to be sure that the properties are //already *all* updated when we call the management code connect(layout, SIGNAL(minimumWidthChanged()), this, SLOT(updateMinimumWidth())); connect(layout, SIGNAL(minimumHeightChanged()), this, SLOT(updateMinimumHeight())); connect(layout, SIGNAL(maximumWidthChanged()), this, SLOT(updateMaximumWidth())); connect(layout, SIGNAL(maximumHeightChanged()), this, SLOT(updateMaximumHeight())); d->updateLayoutParameters(); } } //if this is called in Component.onCompleted we have to wait a loop the item is added to a scene emit mainItemChanged(); } } void DialogPrivate::slotMainItemSizeChanged() { syncToMainItemSize(); } QQuickItem *Dialog::visualParent() const { return d->visualParent; } void Dialog::setVisualParent(QQuickItem *visualParent) { if (d->visualParent == visualParent) { return; } d->visualParent = visualParent; emit visualParentChanged(); if (visualParent) { if (visualParent->window()) { setTransientParent(visualParent->window()); } if (d->mainItem) { d->syncToMainItemSize(); } } } QPoint Dialog::popupPosition(QQuickItem *item, const QSize &size) { if (!item) { //If no item was specified try to align at the center of the parent view QQuickItem *parentItem = qobject_cast(parent()); if (parentItem) { QScreen *screen = parentItem->window()->screen(); switch (d->location) { case Plasma::Types::TopEdge: return QPoint(screen->availableGeometry().center().x() - size.width() / 2, screen->availableGeometry().y()); case Plasma::Types::LeftEdge: return QPoint(screen->availableGeometry().x(), screen->availableGeometry().center().y() - size.height() / 2); case Plasma::Types::RightEdge: return QPoint(screen->availableGeometry().right() - size.width(), screen->availableGeometry().center().y() - size.height() / 2); case Plasma::Types::BottomEdge: return QPoint(screen->availableGeometry().center().x() - size.width() / 2, screen->availableGeometry().bottom() - size.height()); //Default center in the screen default: return screen->geometry().center() - QPoint(size.width() / 2, size.height() / 2); } } else { return QPoint(); } } QPointF pos = item->mapToScene(QPointF(0, 0)); if (item->window()) { pos = item->window()->mapToGlobal(pos.toPoint()); } else { return QPoint(); } //if the item is in a dock or in a window that ignores WM we want to position the popups outside of the dock const KWindowInfo winInfo(item->window()->winId(), NET::WMWindowType); const bool outsideParentWindow = ((winInfo.windowType(NET::AllTypesMask) == NET::Dock) || (item->window()->flags() & Qt::X11BypassWindowManagerHint)) && item->window()->mask().isNull(); QRect parentGeometryBounds; if (outsideParentWindow) { parentGeometryBounds = item->window()->geometry(); } else { parentGeometryBounds = item->mapRectToScene(item->boundingRect()).toRect(); if (item->window()) { parentGeometryBounds.moveTopLeft(item->window()->mapToGlobal(parentGeometryBounds.topLeft())); pos = parentGeometryBounds.topLeft(); } } const QPoint topPoint(pos.x() + (item->mapRectToScene(item->boundingRect()).width() - size.width()) / 2, parentGeometryBounds.top() - size.height()); const QPoint bottomPoint(pos.x() + (item->mapRectToScene(item->boundingRect()).width() - size.width()) / 2, parentGeometryBounds.bottom()); const QPoint leftPoint(parentGeometryBounds.left() - size.width(), pos.y() + (item->mapRectToScene(item->boundingRect()).height() - size.height()) / 2); const QPoint rightPoint(parentGeometryBounds.right(), pos.y() + (item->mapRectToScene(item->boundingRect()).height() - size.height()) / 2); QPoint dialogPos; if (d->location == Plasma::Types::TopEdge) { dialogPos = bottomPoint; } else if (d->location == Plasma::Types::LeftEdge) { dialogPos = rightPoint; } else if (d->location == Plasma::Types::RightEdge) { dialogPos = leftPoint; } else { // Types::BottomEdge dialogPos = topPoint; } //find the correct screen for the item //we do not rely on item->window()->screen() because //QWindow::screen() is always only the screen where the window gets first created //not actually the current window. See QWindow::screen() documentation QRect avail = item->window()->screen()->availableGeometry(); if (outsideParentWindow && d->frameSvgItem->enabledBorders() != Plasma::FrameSvg::AllBorders) { //make the panel look it's inside the panel, in order to not make it look cut switch (d->location) { case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: avail.setTop(qMax(avail.top(), parentGeometryBounds.top())); avail.setBottom(qMin(avail.bottom(), parentGeometryBounds.bottom())); break; default: avail.setLeft(qMax(avail.left(), parentGeometryBounds.left())); avail.setRight(qMin(avail.right(), parentGeometryBounds.right())); break; } } if (dialogPos.x() < avail.left()) { // popup hits lhs if (d->location != Plasma::Types::LeftEdge || d->location == Plasma::Types::RightEdge) { // move it dialogPos.setX(avail.left()); } else { // swap edge dialogPos.setX(rightPoint.x()); } } if (dialogPos.x() + size.width() > avail.right()) { // popup hits rhs if (d->location == Plasma::Types::TopEdge || d->location == Plasma::Types::BottomEdge) { dialogPos.setX(qMax(avail.left(), (avail.right() - size.width() + 1))); } else { dialogPos.setX(leftPoint.x()); } } if (dialogPos.y() < avail.top()) { // hitting top if (d->location == Plasma::Types::LeftEdge || d->location == Plasma::Types::RightEdge) { dialogPos.setY(avail.top()); } else { dialogPos.setY(bottomPoint.y()); } } if (dialogPos.y() + size.height() > avail.bottom()) { // hitting bottom if (d->location == Plasma::Types::TopEdge || d->location == Plasma::Types::BottomEdge) { dialogPos.setY(topPoint.y()); } else { dialogPos.setY(qMax(avail.top(), (avail.bottom() - size.height() + 1))); } } return dialogPos; } Plasma::Types::Location Dialog::location() const { return d->location; } void Dialog::setLocation(Plasma::Types::Location location) { if (d->location == location) { return; } d->location = location; emit locationChanged(); if (d->mainItem) { d->syncToMainItemSize(); } } QObject *Dialog::margins() const { return d->frameSvgItem->fixedMargins(); } void Dialog::setFramelessFlags(Qt::WindowFlags flags) { setFlags(Qt::FramelessWindowHint | flags); emit flagsChanged(); } void Dialog::adjustGeometry(const QRect &geom) { setGeometry(geom); } void Dialog::resizeEvent(QResizeEvent* re) { QQuickWindow::resizeEvent(re); //it's a spontaneous event generated in qguiapplication.cpp QGuiApplicationPrivate::processWindowScreenChangedEvent //QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window), QRect()); //This happens before the first show event when there is more than one screen, //right after the window has been created, the window is still 0x0, //but the resize event gets delivered with 0x0 again and executed with all the bad side effects //this seems to happen for every window when there are multiple screens, so something we have probably to watch out for in the future if (re->size().isEmpty() || re->size() == re->oldSize()) { return; } //A dialog can be resized even if no mainItem has ever been set if (!d->mainItem) { return; } d->mainItem->disconnect(this); d->frameSvgItem->setSize(QSizeF(re->size().width(), re->size().height())); auto margin = d->frameSvgItem->fixedMargins(); d->mainItem->setPosition(QPointF(margin->left(), margin->top())); d->mainItem->setSize(QSize(re->size().width() - margin->left() - margin->right(), re->size().height() - margin->top() - margin->bottom())); QObject::connect(d->mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged())); QObject::connect(d->mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged())); } void Dialog::setType(WindowType type) { if (type == d->type) { return; } d->type = type; if (d->type != Normal) { KWindowSystem::setType(winId(), (NET::WindowType)type); } else { setFlags(Qt::FramelessWindowHint | flags()); } //an OSD can't be a Dialog, as qt xcb would attempt to set a transient parent for it //see bug 370433 if (type == OnScreenDisplay) { setFlags((flags() & ~Qt::Dialog) | Qt::Window); } if (d->backgroundHints == Dialog::NoBackground) { d->frameSvgItem->setImagePath(QString()); } else { if (d->type == Tooltip) { d->frameSvgItem->setImagePath(QStringLiteral("widgets/tooltip")); } else { d->frameSvgItem->setImagePath(QStringLiteral("dialogs/background")); } } if (type == Dock || type == Notification || type == OnScreenDisplay || type == CriticalNotification) { KWindowSystem::setOnAllDesktops(winId(), true); } else { KWindowSystem::setOnAllDesktops(winId(), false); } emit typeChanged(); } Dialog::WindowType Dialog::type() const { return d->type; } void Dialog::focusInEvent(QFocusEvent *ev) { QQuickWindow::focusInEvent(ev); } void Dialog::focusOutEvent(QFocusEvent *ev) { if (d->hideOnWindowDeactivate) { bool parentHasFocus = false; QWindow *parentWindow = transientParent(); while (parentWindow) { if (parentWindow->isActive() && !(parentWindow->flags() & Qt::WindowDoesNotAcceptFocus)) { parentHasFocus = true; break; } parentWindow = parentWindow->transientParent(); } const QWindow *focusWindow = QGuiApplication::focusWindow(); bool childHasFocus = focusWindow && ((focusWindow->isActive() && isAncestorOf(focusWindow)) || focusWindow->type() & Qt::Popup); const bool viewClicked = qobject_cast(focusWindow) || qobject_cast(focusWindow) || qobject_cast(focusWindow); if (viewClicked || (!parentHasFocus && !childHasFocus)) { setVisible(false); emit windowDeactivated(); } } QQuickWindow::focusOutEvent(ev); } void Dialog::showEvent(QShowEvent *event) { if (d->backgroundHints != Dialog::NoBackground) { DialogShadows::self()->addWindow(this, d->frameSvgItem->enabledBorders()); } KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); QQuickWindow::showEvent(event); } bool Dialog::event(QEvent *event) { if (event->type() == QEvent::Expose) { auto ee = static_cast(event); if (!KWindowSystem::isPlatformWayland() || ee->region().isNull()) { return QQuickWindow::event(event); } /* * expose event is the only place where to correctly * register our wayland extensions, as showevent is a bit too * soon and the platform window isn't shown yet * create a shell surface every time the window gets exposed * (only the first expose event, guarded by shelldurface existence) * and tear it down when the window gets hidden * see https://phabricator.kde.org/T6064 */ #if HAVE_KWAYLAND //sometimes non null regions arrive even for non visible windows //for which surface creation would fail if (!d->shellSurface && isVisible()) { KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); d->setupWaylandIntegration(); d->updateVisibility(true); d->updateTheme(); } #endif } else if (event->type() == QEvent::PlatformSurface) { const QPlatformSurfaceEvent *pSEvent = static_cast(event); if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); } } else if (event->type() == QEvent::Show) { d->updateVisibility(true); } else if (event->type() == QEvent::Hide) { d->updateVisibility(false); #if HAVE_KWAYLAND delete d->shellSurface; d->shellSurface = nullptr; #endif } else if (event->type() == QEvent::Move) { #if HAVE_KWAYLAND if (d->shellSurface) { QMoveEvent *me = static_cast(event); d->shellSurface->setPosition(me->pos()); } #endif } /*Fitt's law: if the containment has margins, and the mouse cursor clicked * on the mouse edge, forward the click in the containment boundaries */ if (d->mainItem && !d->mainItem->size().isEmpty()) { switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast(event); //don't mess with position if the cursor is actually outside the view: //somebody is doing a click and drag that must not break when the cursor i outside if (geometry().contains(me->screenPos().toPoint()) && !d->mainItemContainsPosition(me->windowPos())) { QMouseEvent me2(me->type(), d->positionAdjustedForMainItem(me->windowPos()), d->positionAdjustedForMainItem(me->windowPos()), d->positionAdjustedForMainItem(me->windowPos()) + position(), me->button(), me->buttons(), me->modifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &me2); } return true; } break; } case QEvent::Wheel: { QWheelEvent *we = static_cast(event); - if (!d->mainItemContainsPosition(we->pos())) { - QWheelEvent we2(d->positionAdjustedForMainItem(we->pos()), - d->positionAdjustedForMainItem(we->pos()) + position(), +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + const QPoint pos = we->pos(); +#else + const QPoint pos = we->position().toPoint(); +#endif + + if (!d->mainItemContainsPosition(pos)) { +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + QWheelEvent we2(d->positionAdjustedForMainItem(pos), + d->positionAdjustedForMainItem(pos) + position(), we->pixelDelta(), we->angleDelta(), we->angleDelta().y(), we->orientation(), we->buttons(), we->modifiers(), we->phase()); +#else + QWheelEvent we2(d->positionAdjustedForMainItem(pos), + d->positionAdjustedForMainItem(pos) + position(), + we->pixelDelta(), we->angleDelta(), + we->buttons(), we->modifiers(), we->phase(), false /*inverted*/); +#endif if (isVisible()) { QCoreApplication::sendEvent(this, &we2); } return true; } break; } case QEvent::DragEnter: { QDragEnterEvent *de = static_cast(event); if (!d->mainItemContainsPosition(de->pos())) { QDragEnterEvent de2(d->positionAdjustedForMainItem(de->pos()).toPoint(), de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &de2); } return true; } break; } //DragLeave just works case QEvent::DragLeave: break; case QEvent::DragMove: { QDragMoveEvent *de = static_cast(event); if (!d->mainItemContainsPosition(de->pos())) { QDragMoveEvent de2(d->positionAdjustedForMainItem(de->pos()).toPoint(), de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &de2); } return true; } break; } case QEvent::Drop: { QDropEvent *de = static_cast(event); if (!d->mainItemContainsPosition(de->pos())) { QDropEvent de2(d->positionAdjustedForMainItem(de->pos()).toPoint(), de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &de2); } return true; } break; } default: break; } } return QQuickWindow::event(event); } void Dialog::hideEvent(QHideEvent *event) { QQuickWindow::hideEvent(event); } void Dialog::classBegin() { d->componentComplete = false; } void Dialog::componentComplete() { d->componentComplete = true; QQuickWindow::setVisible(d->visible); d->updateTheme(); } bool Dialog::hideOnWindowDeactivate() const { return d->hideOnWindowDeactivate; } void Dialog::setHideOnWindowDeactivate(bool hide) { if (d->hideOnWindowDeactivate == hide) { return; } d->hideOnWindowDeactivate = hide; emit hideOnWindowDeactivateChanged(); } bool Dialog::isOutputOnly() const { return d->outputOnly; } void Dialog::setOutputOnly(bool outputOnly) { if (d->outputOnly == outputOnly) { return; } d->outputOnly = outputOnly; emit outputOnlyChanged(); } void Dialog::setVisible(bool visible) { //only update real visibility when we have finished component completion //and all flags have been set d->visible = visible; if (d->componentComplete) { if (visible && d->visualParent) { setPosition(popupPosition(d->visualParent, size())); } // Bug 381242: Qt remembers minimize state and re-applies it when showing setWindowStates(windowStates() & ~Qt::WindowMinimized); QQuickWindow::setVisible(visible); //signal will be emitted and proxied from the QQuickWindow code } else { emit visibleChangedProxy(); } } bool Dialog::isVisible() const { if (d->componentComplete) { return QQuickWindow::isVisible(); } return d->visible; } Dialog::BackgroundHints Dialog::backgroundHints() const { return d->backgroundHints; } void Dialog::setBackgroundHints(Dialog::BackgroundHints hints) { if (d->backgroundHints == hints) { return; } d->backgroundHints = hints; d->updateTheme(); emit backgroundHintsChanged(); } } #include "moc_dialog.cpp"