#include "NotifyManager.h" #include "NotifyWidget.h" // NotifyWidget.h没有放到NotifyManager.h文件,是因为NotifyWidget.cpp也include了NotifyManager.h,为了能互相include才这么做 NotifyManager::NotifyManager(QWidget *widget, QObject *parent) : m_parentWidget(widget), QObject(parent) { m_maxCount = 5; // 可见的最大通知数目设置为5 m_displayTime = 3000; // 显示时间默认设置为3秒,通知显示出来过了10秒之后隐藏并自动销毁 m_animateTime = 300; // 动画时间设置为300ms m_spacing = 10; // 通知框之间的间距设置为10 m_notifyWndSize = QSize(300, 40); // 通知框大小 m_defaultIcon = ":/message1.png"; // 默认图标 initStyleSheet(); m_isShowQueueCount = true; // 默认显示队列的通知数目 // 传入边距以设置最下面的通知的右下角坐标 this->setCornerMargins(10, 10); // 初始化队列的剩余通知数目界面 m_notifyCount = new NotifyCountWidget(this, m_parentWidget); } // 弹出通知框 void NotifyManager::notify(const QString &title, const QString &body, int type, int displayTime, const QVariantMap &data) { // 将标题栏和内容数据添加到队列中 QVariantMap tmp = data; tmp.insert("title", title); tmp.insert("body", body); // tmp.insert("type", type); switch (type) { case NotificationType::SUCCESS: m_defaultIcon = ":/success.png"; tmp.insert("theme", "success"); break; case NotificationType::WARNING: m_defaultIcon = ":/warning.png"; tmp.insert("theme", "warn"); break; case NotificationType::ERROR: m_defaultIcon = ":/error.png"; tmp.insert("theme", "error"); break; default: break; } setDisplayTime(displayTime); m_dataQueue.enqueue(tmp); // 显示下一条通知 showNext(); } // 设置通知框的最大数目 void NotifyManager::setMaxCount(int count) { m_maxCount = count; } // 获取通知框显示时间 int NotifyManager::displayTime() const { return m_displayTime; } // 设置通知框显示时间 void NotifyManager::setDisplayTime(int displayTime) { m_displayTime = displayTime; } // 获取动画时间 int NotifyManager::animateTime() const { return m_animateTime; } // 设置动画时间 void NotifyManager::setAnimateTime(int animateTime) { m_animateTime = animateTime; } // 获取通知栏之间的间距 int NotifyManager::spacing() const { return m_spacing; } // 设置通知栏之间的间距 void NotifyManager::setSpacing(int spacing) { m_spacing = spacing; } // 获取最下面的通知的右下角坐标 QPoint NotifyManager::cornerPos() { QScreen *screen = QGuiApplication::primaryScreen(); QRect screenGeometry = screen->availableGeometry(); qreal dpiScale = screen->devicePixelRatio(); QRect widgetGeometry = m_parentWidget->geometry(); QPoint pt = widgetGeometry.topLeft(); m_cornerPos.setX((pt.x() + m_parentWidget->width() / 2)); m_cornerPos.setY(widgetGeometry.top() - m_notifyWndSize.height()); return m_cornerPos; } // 传入边距以设置最下面的通知的右下角坐标 void NotifyManager::setCornerMargins(int right, int bottom) { // QRect desktopRect = QApplication::primaryScreen()->availableGeometry(); // QPoint bottomRignt = desktopRect.bottomRight(); // m_cornerPos = QPoint(bottomRignt.x() - right, bottomRignt.y() - bottom); // 获取当前屏幕的DPI和缩放比例 QScreen *screen = QGuiApplication::primaryScreen(); QRect screenGeometry = screen->availableGeometry(); qreal dpiScale = screen->devicePixelRatio(); QPoint bottomRignt; QPoint pt; QRect widgetGeometry = m_parentWidget->geometry(); int y = widgetGeometry.top(); QPoint lpt = widgetGeometry.topLeft(); pt = m_parentWidget->mapToGlobal(lpt); bottomRignt.setX((screenGeometry.width() - m_parentWidget->width()) / 2); bottomRignt.setY((screenGeometry.height()) / 2); m_cornerPos = QPoint(bottomRignt.x() * dpiScale, bottomRignt.y() * dpiScale); } // 获取通知框的尺寸 QSize NotifyManager::notifyWndSize() const { return m_notifyWndSize; } // 设置通知框的尺寸 void NotifyManager::setNotifyWndSize(int width, int height) { m_notifyWndSize = QSize(width, height); } // 获取默认图标的路径 QString NotifyManager::defaultIcon() const { return m_defaultIcon; } // 设置默认图标 void NotifyManager::setDefaultIcon(const QString &defaultIcon) { m_defaultIcon = defaultIcon; } // 获取指定theme的样式 QString NotifyManager::styleSheet(const QString &theme) const { if (!m_styleSheets.contains(theme)) return m_styleSheets.value("default"); return m_styleSheets.value(theme); } // 设置指定theme的样式 void NotifyManager::setStyleSheet(const QString &styleSheet, const QString &theme) { m_styleSheets[theme] = styleSheet; } // 设置是否显示队列的通知数目 void NotifyManager::setShowQueueCount(bool isShowQueueCount) { m_isShowQueueCount = isShowQueueCount; if (!m_isShowQueueCount) m_notifyCount->showArranged(0); } // 显示下一条通知 void NotifyManager::showNext() { // 如果通知数目超出限制,则显示"通知当前数目界面" if (m_notifyList.size() >= m_maxCount || m_dataQueue.isEmpty()) { showQueueCount(); return; } // 创建并显示新的通知框 NotifyWidget *notifyWidget = new NotifyWidget(this); // 将管理员自身传给notifyWidget的m_manager m_notifyList.append(notifyWidget); // 添加到通知框列表 notifyWidget->showArranged(m_notifyList.size()); // 设置新通知的显示位置 notifyWidget->setData( m_dataQueue .dequeue()); // 设置数据队列的第一个数据(dequeue,删除队列第一个元素,并返回这个元素) showQueueCount(); // 显示队列的剩余通知数目 // 通知过了displayTime时间之后隐藏之后销毁,然后触发下面槽函数 connect(notifyWidget, &QObject::destroyed, this, [notifyWidget, this]() { // 找到被销毁的通知在队列中的索引,然后移除该通知 int index = m_notifyList.indexOf(notifyWidget); m_notifyList.removeAt(index); // 旧消息被移出后,就要显示通知队列中的下一个新消息,并排序 for (; index < m_notifyList.size(); index++) m_notifyList[index]->showArranged(index + 1); // 这里是为了实现周期提示功能,一般不用到,可以注释 QTimer::singleShot(m_animateTime, this, [this]() { showNext(); }); }); } // 显示队列的剩余通知数目 void NotifyManager::showQueueCount() { // 判断是否允许显示队列的剩余通知数目 if (!m_isShowQueueCount) return; // 数据队列大于0,说明还有未显示的剩余通知,则显示数目;否则隐藏"剩余通知数目" if (!m_dataQueue.isEmpty()) { m_notifyCount->showArranged(m_maxCount + 1); m_notifyCount->setCount(m_dataQueue.size()); } else { m_notifyCount->showArranged(0); } } void NotifyManager::initStyleSheet() { m_styleSheets["default"] = "#notify-background {" "background: white;" "border-radius: 6px;" "}" "#notify-title{" "font-weight: bold;" "font-size: 14px;" "color: #333333;" "}" "#notify-body{" "font-size: 12px;" "color: #444444;" "}" "#notify-close-btn{ " "border: 0;" "color: #999999;" "}" "#notify-close-btn:hover{ " "background: #cccccc;" "}"; m_styleSheets["success"] = "#notify-background {" "background: rgba(236, 253, 245, 0.95);" "border-radius: 6px;" "border: 1px solid #A7F3D0;" "}" "#notify-title{" "font-weight: bold;" "font-size: 14px;" "color: #059669;" "}" "#notify-body{" "font-size: 12px;" "color: #059669;" "}" "#notify-close-btn{ " "border: 0;" "color: #059669;" "}" "#notify-close-btn:hover{ " "background: #D1FAE5;" "}"; m_styleSheets["warn"] = "#notify-background {" "background: rgba(254, 252, 232, 0.95);" "border-radius: 6px;" "border: 1px solid #FEF08A;" "}" "#notify-title{" "font-weight: bold;" "font-size: 14px;" "color: #D97706;" "}" "#notify-body{" "font-size: 12px;" "color: #D97706;" "}" "#notify-close-btn{ " "border: 0;" "color: #D97706;" "}" "#notify-close-btn:hover{ " "background: #FEF9C3;" "}"; m_styleSheets["error"] = "#notify-background {" "background: rgba(254, 242, 242, 0.95);" "border-radius: 6px;" "border: 1px solid #FECACA;" "}" "#notify-title{" "font-weight: bold;" "font-size: 14px;" "color: #DC2626;" "}" "#notify-body{" "font-size: 12px;" "color: #DC2626;" "}" "#notify-close-btn{ " "border: 0;" "color: #DC2626;" "}" "#notify-close-btn:hover{ " "background: #FEE2E2;" "}"; }