/* * MIT License * * Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "quickmicamaterial.h" #include "quickmicamaterial_p.h" #include #include #include #include #include #include #include #include #include #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE # include # include # include #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE FRAMELESSHELPER_BEGIN_NAMESPACE [[maybe_unused]] static Q_LOGGING_CATEGORY(lcQuickMicaMaterial, "wangwenx190.framelesshelper.quick.quickmicamaterial") #ifdef FRAMELESSHELPER_QUICK_NO_DEBUG_OUTPUT # define INFO QT_NO_QDEBUG_MACRO() # define DEBUG QT_NO_QDEBUG_MACRO() # define WARNING QT_NO_QDEBUG_MACRO() # define CRITICAL QT_NO_QDEBUG_MACRO() #else # define INFO qCInfo(lcQuickMicaMaterial) # define DEBUG qCDebug(lcQuickMicaMaterial) # define WARNING qCWarning(lcQuickMicaMaterial) # define CRITICAL qCCritical(lcQuickMicaMaterial) #endif using namespace Global; class WallpaperImageNode : public QObject, public QSGTransformNode { Q_OBJECT Q_DISABLE_COPY_MOVE(WallpaperImageNode) public: explicit WallpaperImageNode(QuickMicaMaterial *item); ~WallpaperImageNode() override; public Q_SLOTS: void maybeUpdateWallpaperImageClipRect(); void maybeGenerateWallpaperImageCache(const bool force = false); private: void initialize(); private: QSGTexture *m_texture = nullptr; QPointer m_item = nullptr; QSGSimpleTextureNode *m_node = nullptr; QPixmap m_pixmapCache = {}; }; WallpaperImageNode::WallpaperImageNode(QuickMicaMaterial *item) { Q_ASSERT(item); if (!item) { return; } m_item = item; initialize(); } WallpaperImageNode::~WallpaperImageNode(){ if (m_texture) { delete m_texture; m_texture = nullptr; } if (m_node) { delete m_node; m_node = nullptr; } }; void WallpaperImageNode::initialize() { QQuickWindow * const window = m_item->window(); m_node = new QSGSimpleTextureNode; m_node->setFiltering(QSGTexture::Linear); maybeGenerateWallpaperImageCache(); maybeUpdateWallpaperImageClipRect(); appendChildNode(m_node); connect(window, &QQuickWindow::beforeRendering, this, &WallpaperImageNode::maybeUpdateWallpaperImageClipRect, Qt::DirectConnection); QuickMicaMaterialPrivate::get(m_item)->appendNode(this); } void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force) { if (!m_pixmapCache.isNull() && !force) { return; } const QSize desktopSize = QGuiApplication::primaryScreen()->virtualSize(); static constexpr const QPoint originPoint = {0, 0}; m_pixmapCache = QPixmap(desktopSize); m_pixmapCache.fill(kDefaultTransparentColor); QPainter painter(&m_pixmapCache); MicaMaterial * const mica = QuickMicaMaterialPrivate::get(m_item)->m_micaMaterial; Q_ASSERT(mica); // We need the real wallpaper image here, so always use "active" state. mica->paint(&painter, desktopSize, originPoint, true); if (m_texture) { delete m_texture; m_texture = nullptr; } m_texture = m_item->window()->createTextureFromImage(m_pixmapCache.toImage()); m_node->setTexture(m_texture); } void WallpaperImageNode::maybeUpdateWallpaperImageClipRect() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) const QSizeF itemSize = m_item->size(); #else const QSizeF itemSize = {m_item->width(), m_item->height()}; #endif m_node->setRect(QRectF(QPointF(0.0, 0.0), itemSize)); m_node->setSourceRect(QRectF(m_item->mapToGlobal(QPointF(0.0, 0.0)), itemSize)); } QuickMicaMaterialPrivate::QuickMicaMaterialPrivate(QuickMicaMaterial *q) : QObject(q) { Q_ASSERT(q); if (!q) { return; } q_ptr = q; initialize(); } QuickMicaMaterialPrivate::~QuickMicaMaterialPrivate() = default; QuickMicaMaterialPrivate *QuickMicaMaterialPrivate::get(QuickMicaMaterial *q) { Q_ASSERT(q); if (!q) { return nullptr; } return q->d_func(); } const QuickMicaMaterialPrivate *QuickMicaMaterialPrivate::get(const QuickMicaMaterial *q) { Q_ASSERT(q); if (!q) { return nullptr; } return q->d_func(); } void QuickMicaMaterialPrivate::initialize() { Q_Q(QuickMicaMaterial); q->setFlag(QuickMicaMaterial::ItemHasContents); q->setSmooth(true); q->setAntialiasing(true); q->setClip(true); m_micaMaterial = new MicaMaterial(this); connect(m_micaMaterial, &MicaMaterial::tintColorChanged, q, &QuickMicaMaterial::tintColorChanged); connect(m_micaMaterial, &MicaMaterial::tintOpacityChanged, q, &QuickMicaMaterial::tintOpacityChanged); connect(m_micaMaterial, &MicaMaterial::fallbackColorChanged, q, &QuickMicaMaterial::fallbackColorChanged); connect(m_micaMaterial, &MicaMaterial::noiseOpacityChanged, q, &QuickMicaMaterial::noiseOpacityChanged); connect(m_micaMaterial, &MicaMaterial::fallbackEnabledChanged, q, &QuickMicaMaterial::fallbackEnabledChanged); connect(m_micaMaterial, &MicaMaterial::shouldRedraw, this, &QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache); #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE m_fallbackColorItem = new QQuickRectangle(q); QQuickItemPrivate::get(m_fallbackColorItem)->anchors()->setFill(q); QQuickPen * const border = m_fallbackColorItem->border(); border->setColor(kDefaultTransparentColor); border->setWidth(0); updateFallbackColor(); m_fallbackColorItem->setVisible(false); connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &QuickMicaMaterialPrivate::updateFallbackColor); #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE } void QuickMicaMaterialPrivate::rebindWindow() { Q_Q(QuickMicaMaterial); const QQuickWindow * const window = q->window(); if (!window) { return; } QQuickItem * const rootItem = window->contentItem(); q->setParent(rootItem); q->setParentItem(rootItem); #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE QQuickItemPrivate::get(q)->anchors()->setFill(rootItem); #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE q->setZ(-999); // Make sure we always stays on the bottom most place. if (m_rootWindowXChangedConnection) { disconnect(m_rootWindowXChangedConnection); m_rootWindowXChangedConnection = {}; } if (m_rootWindowYChangedConnection) { disconnect(m_rootWindowYChangedConnection); m_rootWindowYChangedConnection = {}; } m_rootWindowXChangedConnection = connect(window, &QQuickWindow::xChanged, q, [q](){ q->update(); }); m_rootWindowYChangedConnection = connect(window, &QQuickWindow::yChanged, q, [q](){ q->update(); }); #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE if (m_rootWindowActiveChangedConnection) { disconnect(m_rootWindowActiveChangedConnection); m_rootWindowActiveChangedConnection = {}; } m_rootWindowActiveChangedConnection = connect(window, &QQuickWindow::activeChanged, q, [this, window](){ if (m_micaMaterial->isFallbackEnabled()) { m_fallbackColorItem->setVisible(!window->isActive()); } else { m_fallbackColorItem->setVisible(false); } }); #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE } void QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache() { if (m_nodes.isEmpty()) { return; } for (auto &&node : std::as_const(m_nodes)) { if (node) { node->maybeGenerateWallpaperImageCache(true); } } } void QuickMicaMaterialPrivate::appendNode(WallpaperImageNode *node) { Q_ASSERT(node); if (!node) { return; } if (m_nodes.contains(node)) { return; } m_nodes.append(node); } void QuickMicaMaterialPrivate::updateFallbackColor() { #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE if (!m_fallbackColorItem || !m_micaMaterial) { return; } const QColor color = m_micaMaterial->fallbackColor(); if (color.isValid()) { m_fallbackColorItem->setColor(color); return; } m_fallbackColorItem->setColor(MicaMaterialPrivate::systemFallbackColor()); #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE } QuickMicaMaterial::QuickMicaMaterial(QQuickItem *parent) : QQuickItem(parent), d_ptr(new QuickMicaMaterialPrivate(this)) { } QuickMicaMaterial::~QuickMicaMaterial() = default; QColor QuickMicaMaterial::tintColor() const { Q_D(const QuickMicaMaterial); return d->m_micaMaterial->tintColor(); } void QuickMicaMaterial::setTintColor(const QColor &value) { Q_D(QuickMicaMaterial); d->m_micaMaterial->setTintColor(value); } qreal QuickMicaMaterial::tintOpacity() const { Q_D(const QuickMicaMaterial); return d->m_micaMaterial->tintOpacity(); } void QuickMicaMaterial::setTintOpacity(const qreal value) { Q_D(QuickMicaMaterial); d->m_micaMaterial->setTintOpacity(value); } QColor QuickMicaMaterial::fallbackColor() const { Q_D(const QuickMicaMaterial); return d->m_micaMaterial->fallbackColor(); } void QuickMicaMaterial::setFallbackColor(const QColor &value) { Q_D(QuickMicaMaterial); d->m_micaMaterial->setFallbackColor(value); } qreal QuickMicaMaterial::noiseOpacity() const { Q_D(const QuickMicaMaterial); return d->m_micaMaterial->noiseOpacity(); } void QuickMicaMaterial::setNoiseOpacity(const qreal value) { Q_D(QuickMicaMaterial); d->m_micaMaterial->setNoiseOpacity(value); } bool QuickMicaMaterial::isFallbackEnabled() const { Q_D(const QuickMicaMaterial); return d->m_micaMaterial->isFallbackEnabled(); } void QuickMicaMaterial::setFallbackEnabled(const bool value) { Q_D(QuickMicaMaterial); d->m_micaMaterial->setFallbackEnabled(value); } void QuickMicaMaterial::itemChange(const ItemChange change, const ItemChangeData &value) { QQuickItem::itemChange(change, value); Q_D(QuickMicaMaterial); switch (change) { case ItemDevicePixelRatioHasChanged: { d->forceRegenerateWallpaperImageCache(); update(); // Force re-paint immediately. } break; case ItemSceneChange: { if (value.window) { d->rebindWindow(); } } break; default: break; } } QSGNode *QuickMicaMaterial::updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) { Q_UNUSED(data); auto node = static_cast(old); if (!node) { node = new WallpaperImageNode(this); } return node; } void QuickMicaMaterial::classBegin() { QQuickItem::classBegin(); } void QuickMicaMaterial::componentComplete() { QQuickItem::componentComplete(); } FRAMELESSHELPER_END_NAMESPACE #include "quickmicamaterial.moc"