Skip to content

Commit

Permalink
chore: 增加应用内通知动画 (linuxdeepin#592)
Browse files Browse the repository at this point in the history
增加应用内通知动画

Log:
  • Loading branch information
Whale107 committed Sep 23, 2024
1 parent c7f6d4c commit fb921f9
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 24 deletions.
3 changes: 2 additions & 1 deletion include/widgets/dfloatingmessage.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019 - 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2019 - 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

Expand Down Expand Up @@ -38,6 +38,7 @@ class DFloatingMessage : public DFloatingWidget

Q_SIGNALS:
void closeButtonClicked();
void messageClosed();

protected:
using DFloatingWidget::setWidget;
Expand Down
8 changes: 5 additions & 3 deletions include/widgets/dmessagemanager.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifndef DMESSAGEMANAGER_H
#define DMESSAGEMANAGER_H

#include <QObject>
#include <DObject>
#include <QWidget>
#include <dtkwidget_global.h>
#include <dtkgui_global.h>
Expand All @@ -16,9 +16,11 @@ DGUI_END_NAMESPACE

DWIDGET_BEGIN_NAMESPACE
class DFloatingMessage;
class DMessageManager: public QObject
class DMessageManagerPrivate;
class DMessageManager: public QObject, public DCORE_NAMESPACE::DObject
{
Q_OBJECT
D_DECLARE_PRIVATE(DMessageManager)

private:
DMessageManager(); //构造函数是私有的
Expand Down
21 changes: 18 additions & 3 deletions src/widgets/dfloatingmessage.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019 - 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2019 - 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

Expand All @@ -11,6 +11,7 @@
#include <QTimer>
#include <QLabel>
#include <QDebug>
#include <QGraphicsDropShadowEffect>

class MessageLabel : public QLabel
{
Expand Down Expand Up @@ -54,13 +55,17 @@ void DFloatingMessagePrivate::init()
iconButton->setIconSize(DSizeModeHelper::element(QSize(20, 20), QSize(30, 30)));

hBoxLayout->addWidget(iconButton);
hBoxLayout->addSpacing(10);
hBoxLayout->addWidget(labMessage);

if (notifyType == DFloatingMessage::MessageType::TransientType) { //临时消息
timer = new QTimer(q);
timer->setInterval(4000);
timer->setSingleShot(true);
q->connect(timer, &QTimer::timeout, q, &DFloatingMessage::close);
q->connect(timer, &QTimer::timeout, q, [q]() {
q->close();
Q_EMIT q->messageClosed();
});
} else { //常驻消息
content = nullptr;
closeButton = new DDialogCloseButton(q);
Expand All @@ -69,8 +74,18 @@ void DFloatingMessagePrivate::init()

hBoxLayout->addWidget(closeButton);
q->connect(closeButton, &DIconButton::clicked, q, &DFloatingMessage::closeButtonClicked);
q->connect(closeButton, &DIconButton::clicked, q, &DFloatingMessage::close);
q->connect(closeButton, &DIconButton::clicked, q, [q]() {
q->close();
Q_EMIT q->messageClosed();
});
}

auto effect = new QGraphicsDropShadowEffect(q);
effect->setColor(QColor(0, 0, 0, 0.1 * 255));
effect->setBlurRadius(20);
effect->setXOffset(0);
effect->setYOffset(2);
q->setGraphicsEffect(effect);
}

/*!
Expand Down
127 changes: 111 additions & 16 deletions src/widgets/dmessagemanager.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,48 @@
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "dmessagemanager.h"
#include "private/dmessagemanager_p.h"

#include <DFloatingMessage>
#include <DDciIcon>

#include <QVBoxLayout>
#include <QEvent>
#include <QPainter>

#define D_MESSAGE_MANAGER_CONTENT "_d_message_manager_content"
const int MARGIN = 20;
const int MESSGAE_HEIGHT = 50;
const int ANIMATION_DURATION = 400;

Q_DECLARE_METATYPE(QMargins)

class ImageLabel : public QLabel {
Q_OBJECT
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity)
public:
explicit ImageLabel(QWidget *parent=nullptr)
: QLabel (parent)
, m_opacity(0)
{
};
void setOpacity(qreal opac) { m_opacity = opac; }
qreal opacity() { return m_opacity ;}

protected:
void paintEvent(QPaintEvent *e) override
{
Q_UNUSED(e)
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setOpacity(m_opacity);
p.drawPixmap(rect().marginsRemoved(contentsMargins()), *pixmap());
};
private:
qreal m_opacity;
};

// 仅仅为了接口兼容, 符号不会减少, 如果使用了这个接口,实际调用走的有namespace的
class Q_DECL_EXPORT DMessageManager: public QObject
{
Expand Down Expand Up @@ -91,7 +120,30 @@ static void sendMessage_helper(DMessageManager *manager, QWidget *par, IconType
manager->sendMessage(par, floMsg);
}

DMessageManagerPrivate::DMessageManagerPrivate(DMessageManager *qq)
: DObjectPrivate(qq)
, m_aniGeometry(new QPropertyAnimation(qq))
, m_aniOpacity(new QPropertyAnimation(qq))
, m_aniGroup(new QParallelAnimationGroup(qq))
, m_label(new ImageLabel)
{
m_aniGeometry->setPropertyName("geometry");
m_aniGeometry->setDuration(ANIMATION_DURATION);
m_aniGeometry->setEasingCurve(QEasingCurve::OutCubic);

m_aniOpacity->setPropertyName("opacity");
m_aniOpacity->setDuration(ANIMATION_DURATION);
m_aniOpacity->setEasingCurve(QEasingCurve::OutCubic);
m_aniOpacity->setTargetObject(m_label);
m_aniOpacity->setStartValue(0);
m_aniOpacity->setEndValue(1);

m_aniGroup->addAnimation(m_aniGeometry);
m_aniGroup->addAnimation(m_aniOpacity);
}

DMessageManager::DMessageManager() //私有静态构造函数
: DObject(*new DMessageManagerPrivate(this))
{
}

Expand All @@ -114,6 +166,7 @@ DMessageManager *DMessageManager::instance() //公有静态函数
*/
void DMessageManager::sendMessage(QWidget *par, DFloatingMessage *floMsg)
{
D_D(DMessageManager);
QWidget *content = par->findChild<QWidget *>(D_MESSAGE_MANAGER_CONTENT, Qt::FindDirectChildrenOnly);

if (!content) {
Expand All @@ -125,18 +178,68 @@ void DMessageManager::sendMessage(QWidget *par, DFloatingMessage *floMsg)
if (par->property("_d_margins").isValid())
content->setContentsMargins(magins);
else
content->setContentsMargins(QMargins(20, 0, 20, 0));
content->setContentsMargins(QMargins(MARGIN, 0, MARGIN, 0));

content->installEventFilter(this);
par->installEventFilter(this);
QVBoxLayout *layout = new QVBoxLayout(content);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->setDirection(QBoxLayout::BottomToTop);
content->show();
}

if (content->layout()->count() >= 1) {
content->layout()->itemAt(content->layout()->count() - 1)->widget()->hide();
delete content->layout()->takeAt(content->layout()->count() - 1);
}

static_cast<QBoxLayout*>(content->layout())->addWidget(floMsg, 0, Qt::AlignHCenter);

// 限制通知消息的最大宽度
for (DFloatingMessage *message : content->findChildren<DFloatingMessage*>(QString(), Qt::FindDirectChildrenOnly)) {
message->setMaximumWidth(par->rect().marginsRemoved(content->contentsMargins()).width());
message->setMinimumHeight(message->sizeHint().height());
}

QRect geometry(QPoint(0, 0), floMsg->sizeHint() + QSize(MARGIN * 2, 0));
geometry.moveCenter(par->rect().center());
geometry.moveBottom(par->rect().bottom() - MESSGAE_HEIGHT);

content->setGeometry(geometry);
content->hide();

if (d->m_aniGeometry->state() == QPropertyAnimation::State::Running)
return;

d->m_label->setParent(par);
d->m_label->setAlignment(Qt::AlignCenter);
d->m_label->setContentsMargins(MARGIN, 0, MARGIN, 0);
d->m_label->setPixmap(floMsg->grab());
d->m_label->setScaledContents(true);
d->m_label->show();
d->m_aniGeometry->setTargetObject(d->m_label);
d->m_aniOpacity->setTargetObject(d->m_label);
d->m_aniGeometry->setStartValue(QRect(par->rect().center().x(), par->rect().bottom(), 0, 0));
d->m_aniGeometry->setEndValue(content->geometry());
d->m_aniGroup->start();
connect(d->m_aniGroup, &QPropertyAnimation::finished, this, [d, content]() {
if (d->m_aniGroup->direction() == QAbstractAnimation::Backward) {
d->m_aniGroup->setDirection(QAbstractAnimation::Forward);
} else {
content->show();
}
d->m_label->hide();
});

connect(floMsg, &DFloatingMessage::messageClosed, [=, this]() {
d->m_aniGeometry->setStartValue(QRect(par->rect().center().x(), par->rect().bottom(), 0, 0));
d->m_aniGeometry->setEndValue(content->geometry());
d->m_label->setPixmap(floMsg->grab());

d->m_aniGroup->setDirection(QAbstractAnimation::Backward);
d->m_label->show();
d->m_aniGroup->start();
});
}

/*!
Expand Down Expand Up @@ -184,27 +287,19 @@ bool DMessageManager::setContentMargens(QWidget *par, const QMargins &margins)
*/
bool DMessageManager::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::LayoutRequest || event->type() == QEvent::Resize) {
if (QWidget *widget = qobject_cast<QWidget *>(watched)) {
QWidget *content;

if (widget->objectName() == D_MESSAGE_MANAGER_CONTENT) {
content = widget;
} else {
content = widget->findChild<QWidget*>(D_MESSAGE_MANAGER_CONTENT, Qt::FindDirectChildrenOnly);
}
if (event->type() == QEvent::Resize) {
if (auto content = watched->findChild<QWidget *>(D_MESSAGE_MANAGER_CONTENT, Qt::FindDirectChildrenOnly)) {

QWidget *par = content->parentWidget();
auto par = qobject_cast<QWidget *>(watched);

// 限制通知消息的最大宽度
for (DFloatingMessage *message : content->findChildren<DFloatingMessage*>(QString(), Qt::FindDirectChildrenOnly)) {
message->setMaximumWidth(par->rect().marginsRemoved(content->contentsMargins()).width());
message->setMinimumHeight(message->sizeHint().height());
}

QRect geometry(QPoint(0, 0), content->sizeHint());
geometry.moveCenter(par->rect().center());
geometry.moveBottom(par->rect().bottom());
geometry.moveBottom(par->rect().bottom() - MESSGAE_HEIGHT);
content->setGeometry(geometry);
}
} else if (event->type() == QEvent::ChildRemoved) {
Expand Down
11 changes: 10 additions & 1 deletion src/widgets/dstyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1339,9 +1339,18 @@ void DStyle::drawPrimitive(const QStyle *style, DStyle::PrimitiveElement pe, con
//先绘画阴影
DDrawUtils::drawShadow(p, btn->rect + shadow_margin, frameRadius, frameRadius, QColor(0, 0, 0, 0.25 * 255), shadowRadius, QPoint(offsetX, offsetY));
//再绘画上面的待显示区域
p->setPen(QPen(btn->dpalette.frameShadowBorder(), 1));
p->setPen(Qt::NoPen);
p->setBrush(btn->noBackground ? Qt::NoBrush : p->background());
p->drawRoundedRect(opt->rect, frameRadius, frameRadius);

p->setBrush(Qt::NoBrush);
QPen pen;
pen.setWidth(1);
pen.setColor(DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::DarkType
? QColor(255, 255, 255, int(0.1 * 255))
: QColor(0, 0, 0, int(0.12 * 255)));
p->setPen(pen);
p->drawRoundedRect(opt->rect, frameRadius, frameRadius);
}
break;
}
Expand Down
33 changes: 33 additions & 0 deletions src/widgets/private/dmessagemanager_p.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifndef DABOUTDIALOG_P_H
#define DABOUTDIALOG_P_H

#include "dmessagemanager.h"

#include <DObjectPrivate>

#include <QLabel>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>

DWIDGET_BEGIN_NAMESPACE

class DMessageManagerPrivate : public DCORE_NAMESPACE::DObjectPrivate
{
public:
DMessageManagerPrivate(DMessageManager *qq);

QPropertyAnimation *m_aniGeometry;
QPropertyAnimation *m_aniOpacity;;
QParallelAnimationGroup *m_aniGroup;
QLabel *m_label;

D_DECLARE_PUBLIC(DMessageManager);
};

DWIDGET_END_NAMESPACE

#endif // DABOUTDIALOG_P_H

0 comments on commit fb921f9

Please sign in to comment.