405 lines
12 KiB
C++
405 lines
12 KiB
C++
/*
|
|
* 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 <FramelessHelper/Core/micamaterial.h>
|
|
#include <FramelessHelper/Core/framelessmanager.h>
|
|
#include <FramelessHelper/Core/private/micamaterial_p.h>
|
|
#include <QtCore/qloggingcategory.h>
|
|
#include <QtGui/qpainter.h>
|
|
#include <QtQuick/qquickwindow.h>
|
|
#include <QtQuick/qsgsimpletexturenode.h>
|
|
#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE
|
|
# include <QtQuick/private/qquickitem_p.h>
|
|
# include <QtQuick/private/qquickrectangle_p.h>
|
|
# include <QtQuick/private/qquickanchors_p.h>
|
|
#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:
|
|
QPointer<QuickMicaMaterial> m_item = nullptr;
|
|
QSGSimpleTextureNode *m_node = nullptr;
|
|
std::unique_ptr<QSGTexture> m_texture = nullptr;
|
|
QPointer<MicaMaterial> m_mica{ nullptr };
|
|
QPointer<MicaMaterialPrivate> m_micaPriv{ nullptr };
|
|
QPointer<QQuickWindow> m_window{ nullptr };
|
|
};
|
|
|
|
WallpaperImageNode::WallpaperImageNode(QuickMicaMaterial *item)
|
|
{
|
|
Q_ASSERT(item);
|
|
if (!item) {
|
|
return;
|
|
}
|
|
m_item = item;
|
|
initialize();
|
|
}
|
|
|
|
WallpaperImageNode::~WallpaperImageNode()
|
|
{
|
|
QuickMicaMaterialPrivate::get(m_item)->removeNode(this);
|
|
}
|
|
|
|
void WallpaperImageNode::initialize()
|
|
{
|
|
m_window = m_item->window();
|
|
m_mica = QuickMicaMaterialPrivate::get(m_item)->m_micaMaterial;
|
|
m_micaPriv = MicaMaterialPrivate::get(m_mica);
|
|
|
|
// QtQuick's render engine will free it when appropriate.
|
|
m_node = new QSGSimpleTextureNode;
|
|
m_node->setFiltering(QSGTexture::Linear);
|
|
|
|
maybeGenerateWallpaperImageCache();
|
|
maybeUpdateWallpaperImageClipRect();
|
|
|
|
appendChildNode(m_node);
|
|
|
|
connect(m_window, &QQuickWindow::beforeRendering, this,
|
|
&WallpaperImageNode::maybeUpdateWallpaperImageClipRect, Qt::DirectConnection);
|
|
|
|
QuickMicaMaterialPrivate::get(m_item)->appendNode(this);
|
|
}
|
|
|
|
void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force)
|
|
{
|
|
if (m_texture && !force) {
|
|
return;
|
|
}
|
|
static constexpr const auto originPoint = QPoint{ 0, 0 };
|
|
const QSize imageSize = MicaMaterialPrivate::wallpaperSize();
|
|
auto pixmap = QPixmap(imageSize);
|
|
pixmap.fill(kDefaultTransparentColor);
|
|
QPainter painter(&pixmap);
|
|
// We need the real wallpaper image here, so always use "active" state.
|
|
m_mica->paint(&painter, QRect{ originPoint, imageSize }, true);
|
|
m_texture.reset(m_window->createTextureFromImage(pixmap.toImage()));
|
|
m_node->setTexture(m_texture.get());
|
|
}
|
|
|
|
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));
|
|
const auto rect = QRectF(m_item->mapToGlobal(QPointF(0.0, 0.0)), itemSize);
|
|
m_node->setSourceRect(m_micaPriv->mapToWallpaper(rect.toRect()));
|
|
}
|
|
|
|
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::removeNode(WallpaperImageNode *node)
|
|
{
|
|
Q_ASSERT(node);
|
|
if (!node) {
|
|
return;
|
|
}
|
|
if (!m_nodes.contains(node)) {
|
|
return;
|
|
}
|
|
m_nodes.removeAll(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<WallpaperImageNode *>(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"
|