dde-dock/frame/window/tray/widgets/snitrayitemwidget.cpp
Yutao Meng ec5c447264 feat: DockPopupWindow改用DBlurEffectWidget实现
DockPopupWindow改为使用DBlurEffectWidget来实现新的设计,以及摆脱原来DArrowRectangle出现的侧边任务栏PopupWindow圆角显示不对称的问题.

Log: DockPopupWindow改用DBlurEffectWidget实现

Signed-off-by: Yutao Meng <mengyutao@uniontech.com>
2023-04-11 08:40:17 +00:00

796 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "snitrayitemwidget.h"
#include "themeappicon.h"
#include "tipswidget.h"
#include "utils.h"
#include <dbusmenu-qt5/dbusmenuimporter.h>
#include <DGuiApplicationHelper>
#include <QPainter>
#include <QApplication>
#include <QDBusPendingCall>
#include <QtConcurrent>
#include <QFuture>
#include <QMouseEvent>
#include <xcb/xproto.h>
DGUI_USE_NAMESPACE
#define IconSize 20
const QStringList ItemCategoryList {"ApplicationStatus", "Communications", "SystemServices", "Hardware"};
const QStringList ItemStatusList {"Passive", "Active", "NeedsAttention"};
const QStringList LeftClickInvalidIdList {"sogou-qimpanel",};
QPointer<DockPopupWindow> SNITrayItemWidget::PopupWindow = nullptr;
Dock::Position SNITrayItemWidget::DockPosition = Dock::Position::Bottom;
using namespace Dock;
SNITrayItemWidget::SNITrayItemWidget(const QString &sniServicePath, QWidget *parent)
: BaseTrayWidget(parent)
, m_dbusMenuImporter(nullptr)
, m_menu(nullptr)
, m_updateIconTimer(new QTimer(this))
, m_updateOverlayIconTimer(new QTimer(this))
, m_updateAttentionIconTimer(new QTimer(this))
, m_sniServicePath(sniServicePath)
, m_popupTipsDelayTimer(new QTimer(this))
, m_handleMouseReleaseTimer(new QTimer(this))
, m_tipsLabel(new TipsWidget)
, m_popupShown(false)
{
m_popupTipsDelayTimer->setInterval(500);
m_popupTipsDelayTimer->setSingleShot(true);
m_handleMouseReleaseTimer->setSingleShot(true);
m_handleMouseReleaseTimer->setInterval(100);
connect(m_handleMouseReleaseTimer, &QTimer::timeout, this, &SNITrayItemWidget::handleMouseRelease);
connect(m_popupTipsDelayTimer, &QTimer::timeout, this, &SNITrayItemWidget::showHoverTips);
if (PopupWindow.isNull()) {
DockPopupWindow *arrowRectangle = new DockPopupWindow(nullptr);
arrowRectangle->setRadius(6);
arrowRectangle->setObjectName("snitraypopup");
PopupWindow = arrowRectangle;
if (Utils::IS_WAYLAND_DISPLAY)
PopupWindow->setWindowFlags(PopupWindow->windowFlags() | Qt::FramelessWindowHint);
connect(qApp, &QApplication::aboutToQuit, PopupWindow, &DockPopupWindow::deleteLater);
}
if (m_sniServicePath.startsWith("/") || !m_sniServicePath.contains("/")) {
qDebug() << "SNI service path invalid";
return;
}
QPair<QString, QString> pair = serviceAndPath(m_sniServicePath);
m_dbusService = pair.first;
m_dbusPath = pair.second;
QDBusConnection conn = QDBusConnection::sessionBus();
setOwnerPID(conn.interface()->servicePid(m_dbusService));
m_sniInter = new StatusNotifierItem(m_dbusService, m_dbusPath, QDBusConnection::sessionBus(), this);
m_sniInter->setSync(false);
if (!m_sniInter->isValid()) {
qDebug() << "SNI dbus interface is invalid!" << m_dbusService << m_dbusPath << m_sniInter->lastError();
return;
}
m_updateIconTimer->setInterval(100);
m_updateIconTimer->setSingleShot(true);
m_updateOverlayIconTimer->setInterval(500);
m_updateOverlayIconTimer->setSingleShot(true);
m_updateAttentionIconTimer->setInterval(1000);
m_updateAttentionIconTimer->setSingleShot(true);
connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &SNITrayItemWidget::refreshIcon);
connect(m_updateIconTimer, &QTimer::timeout, this, &SNITrayItemWidget::refreshIcon);
connect(m_updateOverlayIconTimer, &QTimer::timeout, this, &SNITrayItemWidget::refreshOverlayIcon);
connect(m_updateAttentionIconTimer, &QTimer::timeout, this, &SNITrayItemWidget::refreshAttentionIcon);
// SNI property change
// thses signals of properties may not be emit automatically!!
// since the SniInter in on async mode we can not call property's getter function to obtain property directly
// the way to refresh properties(emit the following signals) is call property's getter function and wait these signals
connect(m_sniInter, &StatusNotifierItem::AttentionIconNameChanged, this, &SNITrayItemWidget::onSNIAttentionIconNameChanged);
connect(m_sniInter, &StatusNotifierItem::AttentionIconPixmapChanged, this, &SNITrayItemWidget::onSNIAttentionIconPixmapChanged);
connect(m_sniInter, &StatusNotifierItem::AttentionMovieNameChanged, this, &SNITrayItemWidget::onSNIAttentionMovieNameChanged);
connect(m_sniInter, &StatusNotifierItem::CategoryChanged, this, &SNITrayItemWidget::onSNICategoryChanged);
connect(m_sniInter, &StatusNotifierItem::IconNameChanged, this, &SNITrayItemWidget::onSNIIconNameChanged);
connect(m_sniInter, &StatusNotifierItem::IconPixmapChanged, this, &SNITrayItemWidget::onSNIIconPixmapChanged);
connect(m_sniInter, &StatusNotifierItem::IconThemePathChanged, this, &SNITrayItemWidget::onSNIIconThemePathChanged);
connect(m_sniInter, &StatusNotifierItem::IdChanged, this, &SNITrayItemWidget::onSNIIdChanged);
connect(m_sniInter, &StatusNotifierItem::MenuChanged, this, &SNITrayItemWidget::onSNIMenuChanged);
connect(m_sniInter, &StatusNotifierItem::OverlayIconNameChanged, this, &SNITrayItemWidget::onSNIOverlayIconNameChanged);
connect(m_sniInter, &StatusNotifierItem::OverlayIconPixmapChanged, this, &SNITrayItemWidget::onSNIOverlayIconPixmapChanged);
connect(m_sniInter, &StatusNotifierItem::StatusChanged, this, &SNITrayItemWidget::onSNIStatusChanged);
// the following signals can be emit automatically
// need refresh cached properties in these slots
connect(m_sniInter, &StatusNotifierItem::NewIcon, [ = ] {
m_sniIconName = m_sniInter->iconName();
m_sniIconPixmap = m_sniInter->iconPixmap();
m_sniIconThemePath = m_sniInter->iconThemePath();
m_updateIconTimer->start();
});
connect(m_sniInter, &StatusNotifierItem::NewOverlayIcon, [ = ] {
m_sniOverlayIconName = m_sniInter->overlayIconName();
m_sniOverlayIconPixmap = m_sniInter->overlayIconPixmap();
m_sniIconThemePath = m_sniInter->iconThemePath();
m_updateOverlayIconTimer->start();
});
connect(m_sniInter, &StatusNotifierItem::NewAttentionIcon, [ = ] {
m_sniAttentionIconName = m_sniInter->attentionIconName();
m_sniAttentionIconPixmap = m_sniInter->attentionIconPixmap();
m_sniIconThemePath = m_sniInter->iconThemePath();
m_updateAttentionIconTimer->start();
});
connect(m_sniInter, &StatusNotifierItem::NewStatus, [ = ] {
onSNIStatusChanged(m_sniInter->status());
});
QMetaObject::invokeMethod(this, &SNITrayItemWidget::initMember, Qt::QueuedConnection);
}
SNITrayItemWidget::~SNITrayItemWidget()
{
m_tipsLabel->deleteLater();
}
QString SNITrayItemWidget::itemKeyForConfig()
{
return QString("sni:%1").arg(m_sniId.isEmpty() ? m_sniServicePath : m_sniId);
}
void SNITrayItemWidget::updateIcon()
{
m_updateIconTimer->start();
}
void SNITrayItemWidget::sendClick(uint8_t mouseButton, int x, int y)
{
switch (mouseButton) {
case XCB_BUTTON_INDEX_1: {
QFuture<void> future = QtConcurrent::run([ = ] {
StatusNotifierItem inter(m_dbusService, m_dbusPath, QDBusConnection::sessionBus());
QDBusPendingReply<> reply = inter.Activate(x, y);
// try to invoke context menu while calling activate get a error.
// primarily work for apps using libappindicator.
reply.waitForFinished();
if (reply.isError()) {
showContextMenu(x,y);
}
});
}
break;
case XCB_BUTTON_INDEX_2:
m_sniInter->SecondaryActivate(x, y);
break;
case XCB_BUTTON_INDEX_3:
showContextMenu(x, y);
break;
default:
qDebug() << "unknown mouse button key";
break;
}
}
bool SNITrayItemWidget::isValid()
{
return m_sniInter->isValid();
}
SNITrayItemWidget::ItemStatus SNITrayItemWidget::status()
{
if (!ItemStatusList.contains(m_sniStatus)) {
m_sniStatus = "Active";
return ItemStatus::Active;
}
return static_cast<ItemStatus>(ItemStatusList.indexOf(m_sniStatus));
}
SNITrayItemWidget::ItemCategory SNITrayItemWidget::category()
{
if (!ItemCategoryList.contains(m_sniCategory)) {
return UnknownCategory;
}
return static_cast<ItemCategory>(ItemCategoryList.indexOf(m_sniCategory));
}
QString SNITrayItemWidget::toSNIKey(const QString &sniServicePath)
{
return QString("sni:%1").arg(sniServicePath);
}
bool SNITrayItemWidget::isSNIKey(const QString &itemKey)
{
return itemKey.startsWith("sni:");
}
QPair<QString, QString> SNITrayItemWidget::serviceAndPath(const QString &servicePath)
{
QStringList list = servicePath.split("/");
QPair<QString, QString> pair;
pair.first = list.takeFirst();
for (auto i : list) {
pair.second.append("/");
pair.second.append(i);
}
return pair;
}
uint SNITrayItemWidget::servicePID(const QString &servicePath)
{
QString serviceName = serviceAndPath(servicePath).first;
QDBusConnection conn = QDBusConnection::sessionBus();
return conn.interface()->servicePid(serviceName);
}
void SNITrayItemWidget::initMenu()
{
const QString &sniMenuPath = m_sniMenuPath.path();
if (sniMenuPath.isEmpty()) {
qDebug() << "Error: current sni menu path is empty of dbus service:" << m_dbusService << "id:" << m_sniId;
return;
}
qDebug() << "using sni service path:" << m_dbusService << "menu path:" << sniMenuPath;
m_dbusMenuImporter = new DBusMenuImporter(m_dbusService, sniMenuPath, ASYNCHRONOUS, this);
qDebug() << "generate the sni menu object";
m_menu = m_dbusMenuImporter->menu();
qDebug() << "the sni menu obect is:" << m_menu;
}
void SNITrayItemWidget::refreshIcon()
{
QPixmap pix = newIconPixmap(Icon);
if (pix.isNull()) {
return;
}
m_pixmap = pix;
update();
Q_EMIT iconChanged();
if (!isVisible()) {
Q_EMIT needAttention();
}
}
void SNITrayItemWidget::refreshOverlayIcon()
{
QPixmap pix = newIconPixmap(OverlayIcon);
if (pix.isNull()) {
return;
}
m_overlayPixmap = pix;
update();
Q_EMIT iconChanged();
if (!isVisible()) {
Q_EMIT needAttention();
}
}
void SNITrayItemWidget::refreshAttentionIcon()
{
/* TODO: A new approach may be needed to deal with attentionIcon */
QPixmap pix = newIconPixmap(AttentionIcon);
if (pix.isNull()) {
return;
}
m_pixmap = pix;
update();
Q_EMIT iconChanged();
if (!isVisible()) {
Q_EMIT needAttention();
}
}
void SNITrayItemWidget::showContextMenu(int x, int y)
{
// 这里的PopupWindow属性是置顶的,如果不隐藏,会导致菜单显示不出来
hidePopup();
// ContextMenu does not work
if (m_sniMenuPath.path().startsWith("/NO_DBUSMENU")) {
m_sniInter->ContextMenu(x, y);
} else {
if (!m_menu) {
qDebug() << "context menu has not be ready, init menu";
initMenu();
}
if (m_menu)
m_menu->popup(QPoint(x, y));
}
}
void SNITrayItemWidget::onSNIAttentionIconNameChanged(const QString &value)
{
m_sniAttentionIconName = value;
m_updateAttentionIconTimer->start();
}
void SNITrayItemWidget::onSNIAttentionIconPixmapChanged(DBusImageList value)
{
m_sniAttentionIconPixmap = value;
m_updateAttentionIconTimer->start();
}
void SNITrayItemWidget::onSNIAttentionMovieNameChanged(const QString &value)
{
m_sniAttentionMovieName = value;
m_updateAttentionIconTimer->start();
}
void SNITrayItemWidget::onSNICategoryChanged(const QString &value)
{
m_sniCategory = value;
}
void SNITrayItemWidget::onSNIIconNameChanged(const QString &value)
{
m_sniIconName = value;
m_updateIconTimer->start();
}
void SNITrayItemWidget::onSNIIconPixmapChanged(DBusImageList value)
{
m_sniIconPixmap = value;
m_updateIconTimer->start();
}
void SNITrayItemWidget::onSNIIconThemePathChanged(const QString &value)
{
m_sniIconThemePath = value;
m_updateIconTimer->start();
}
void SNITrayItemWidget::onSNIIdChanged(const QString &value)
{
m_sniId = value;
}
void SNITrayItemWidget::onSNIMenuChanged(const QDBusObjectPath &value)
{
m_sniMenuPath = value;
}
void SNITrayItemWidget::onSNIOverlayIconNameChanged(const QString &value)
{
m_sniOverlayIconName = value;
m_updateOverlayIconTimer->start();
}
void SNITrayItemWidget::onSNIOverlayIconPixmapChanged(DBusImageList value)
{
m_sniOverlayIconPixmap = value;
m_updateOverlayIconTimer->start();
}
void SNITrayItemWidget::onSNIStatusChanged(const QString &status)
{
if (!ItemStatusList.contains(status) || m_sniStatus == status) {
return;
}
m_sniStatus = status;
Q_EMIT statusChanged(static_cast<SNITrayItemWidget::ItemStatus>(ItemStatusList.indexOf(status)));
}
void SNITrayItemWidget::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
if (!needShow()) {
return;
}
if (m_pixmap.isNull())
return;
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
//#ifdef QT_DEBUG
// painter.fillRect(rect(), Qt::green);
//#endif
const QRectF &rf = QRect(rect());
const QRectF &rfp = QRect(m_pixmap.rect());
const QPointF &p = rf.center() - rfp.center() / m_pixmap.devicePixelRatioF();
painter.drawPixmap(p, m_pixmap);
if (!m_overlayPixmap.isNull()) {
painter.drawPixmap(p, m_overlayPixmap);
}
painter.end();
}
QPixmap SNITrayItemWidget::newIconPixmap(IconType iconType)
{
QPixmap pixmap;
if (iconType == UnknownIconType) {
return pixmap;
}
QString iconName;
DBusImageList dbusImageList;
QString iconThemePath = m_sniIconThemePath;
switch (iconType) {
case Icon:
iconName = m_sniIconName;
dbusImageList = m_sniIconPixmap;
break;
case OverlayIcon:
iconName = m_sniOverlayIconName;
dbusImageList = m_sniOverlayIconPixmap;
break;
case AttentionIcon:
iconName = m_sniAttentionIconName;
dbusImageList = m_sniAttentionIconPixmap;
break;
case AttentionMovieIcon:
iconName = m_sniAttentionMovieName;
break;
default:
break;
}
const auto ratio = devicePixelRatioF();
const int iconSizeScaled = IconSize * ratio;
do {
// load icon from sni dbus
if (!dbusImageList.isEmpty() && !dbusImageList.first().pixels.isEmpty()) {
for (DBusImage dbusImage : dbusImageList) {
char *image_data = dbusImage.pixels.data();
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
for (int i = 0; i < dbusImage.pixels.size(); i += 4) {
*(qint32 *)(image_data + i) = qFromBigEndian(*(qint32 *)(image_data + i));
}
}
QImage image((const uchar *)dbusImage.pixels.constData(), dbusImage.width, dbusImage.height, QImage::Format_ARGB32);
pixmap = QPixmap::fromImage(image.scaled(iconSizeScaled, iconSizeScaled, Qt::KeepAspectRatio, Qt::SmoothTransformation));
pixmap.setDevicePixelRatio(ratio);
if (!pixmap.isNull()) {
break;
}
}
}
// load icon from specified file
if (!iconThemePath.isEmpty() && !iconName.isEmpty()) {
QDirIterator it(iconThemePath, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
if (it.fileName().startsWith(iconName, Qt::CaseInsensitive)) {
QImage image(it.filePath());
pixmap = QPixmap::fromImage(image.scaled(iconSizeScaled, iconSizeScaled, Qt::KeepAspectRatio, Qt::SmoothTransformation));
pixmap.setDevicePixelRatio(ratio);
if (!pixmap.isNull()) {
break;
}
}
}
if (!pixmap.isNull()) {
break;
}
}
// load icon from theme
// Note: this will ensure return a None-Null pixmap
// so, it should be the last fallback
if (!iconName.isEmpty()) {
// ThemeAppIcon::getIcon 会处理高分屏缩放问题
ThemeAppIcon::getIcon(pixmap, iconName, IconSize);
if (!pixmap.isNull()) {
break;
}
}
if (pixmap.isNull()) {
qDebug() << "get icon faild!" << iconType;
}
} while (false);
// QLabel *l = new QLabel;
// l->setPixmap(pixmap);
// l->setFixedSize(100, 100);
// l->show();
return pixmap;
}
void SNITrayItemWidget::enterEvent(QEvent *event)
{
// 触屏不显示hover效果
if (!qApp->property(IS_TOUCH_STATE).toBool()) {
m_popupTipsDelayTimer->start();
}
BaseTrayWidget::enterEvent(event);
}
void SNITrayItemWidget::leaveEvent(QEvent *event)
{
m_popupTipsDelayTimer->stop();
if (m_popupShown && !PopupWindow->model())
hidePopup();
update();
BaseTrayWidget::leaveEvent(event);
}
void SNITrayItemWidget::mousePressEvent(QMouseEvent *event)
{
// call QWidget::mousePressEvent means to show dock-context-menu
// when right button of mouse is pressed immediately in fashion mode
// here we hide the right button press event when it is click in the special area
m_popupTipsDelayTimer->stop();
if (event->button() == Qt::RightButton && perfectIconRect().contains(event->pos(), true)) {
event->accept();
setMouseData(event);
return;
}
QWidget::mousePressEvent(event);
}
void SNITrayItemWidget::mouseReleaseEvent(QMouseEvent *e)
{
//e->accept();
// 由于 XWindowTrayWidget 中对 发送鼠标事件到X窗口的函数, 如 sendClick/sendHoverEvent 中
// 使用了 setX11PassMouseEvent, 而每次调用 setX11PassMouseEvent 时都会导致产生 mousePress 和 mouseRelease 事件
// 因此如果直接在这里处理事件会导致一些问题, 所以使用 Timer 来延迟处理 100 毫秒内的最后一个事件
setMouseData(e);
QWidget::mouseReleaseEvent(e);
}
void SNITrayItemWidget::handleMouseRelease()
{
Q_ASSERT(sender() == m_handleMouseReleaseTimer);
// do not dealwith all mouse event of SystemTray, class SystemTrayItem will handle it
if (trayType() == SystemTray)
return;
const QPoint point(m_lastMouseReleaseData.first - rect().center());
if (point.manhattanLength() > 24)
return;
QPoint globalPos = QCursor::pos();
uint8_t buttonIndex = XCB_BUTTON_INDEX_1;
switch (m_lastMouseReleaseData.second) {
case Qt:: MiddleButton:
buttonIndex = XCB_BUTTON_INDEX_2;
break;
case Qt::RightButton:
buttonIndex = XCB_BUTTON_INDEX_3;
break;
default:
break;
}
sendClick(buttonIndex, globalPos.x(), globalPos.y());
// left mouse button clicked
if (buttonIndex == XCB_BUTTON_INDEX_1) {
Q_EMIT clicked();
}
}
void SNITrayItemWidget::initMember()
{
onSNIAttentionIconNameChanged(m_sniInter->attentionIconName());
onSNIAttentionIconPixmapChanged(m_sniInter->attentionIconPixmap());
onSNIAttentionMovieNameChanged(m_sniInter->attentionMovieName());
onSNICategoryChanged(m_sniInter->category());
onSNIIconNameChanged(m_sniInter->iconName());
onSNIIconPixmapChanged(m_sniInter->iconPixmap());
onSNIIconThemePathChanged(m_sniInter->iconThemePath());
onSNIIdChanged(m_sniInter->id());
onSNIMenuChanged(m_sniInter->menu());
onSNIOverlayIconNameChanged(m_sniInter->overlayIconName());
onSNIOverlayIconPixmapChanged(m_sniInter->overlayIconPixmap());
onSNIStatusChanged(m_sniInter->status());
m_updateIconTimer->start();
m_updateOverlayIconTimer->start();
m_updateAttentionIconTimer->start();
}
void SNITrayItemWidget::showHoverTips()
{
if (PopupWindow->model())
return;
QProcess p;
p.start("qdbus", {m_dbusService});
if (!p.waitForFinished(1000)) {
qDebug() << "sni dbus service error : " << m_dbusService;
return;
}
QDBusInterface infc(m_dbusService, m_dbusPath);
QDBusMessage msg = infc.call("Get", "org.kde.StatusNotifierItem", "ToolTip");
if (msg.type() == QDBusMessage::ReplyMessage) {
QDBusArgument arg = msg.arguments().at(0).value<QDBusVariant>().variant().value<QDBusArgument>();
DBusToolTip tooltip = qdbus_cast<DBusToolTip>(arg);
if (tooltip.title.isEmpty())
return;
// 当提示信息中有换行符时需要使用setTextList
if (tooltip.title.contains('\n'))
m_tipsLabel->setTextList(tooltip.title.split('\n'));
else
m_tipsLabel->setText(tooltip.title);
m_tipsLabel->setAccessibleName(itemKeyForConfig().replace("sni:",""));
showPopupWindow(m_tipsLabel);
}
}
void SNITrayItemWidget::hideNonModel()
{
// auto hide if popup is not model window
if (m_popupShown && !PopupWindow->model())
hidePopup();
}
void SNITrayItemWidget::popupWindowAccept()
{
if (!PopupWindow->isVisible())
return;
hidePopup();
}
void SNITrayItemWidget::hidePopup()
{
m_popupTipsDelayTimer->stop();
m_popupShown = false;
PopupWindow->hide();
emit PopupWindow->accept();
emit requestWindowAutoHide(true);
}
// 获取在最外层的窗口(MainWindow)中的位置
const QPoint SNITrayItemWidget::topleftPoint() const
{
QPoint p;
const QWidget *w = this;
do {
p += w->pos();
w = qobject_cast<QWidget *>(w->parent());
} while (w);
return p;
}
const QPoint SNITrayItemWidget::popupMarkPoint() const
{
QPoint p(topleftPoint());
const QRect r = rect();
const QRect wr = window()->rect();
switch (DockPosition) {
case Dock::Position::Top:
p += QPoint(r.width() / 2, r.height() + (wr.height() - r.height()) / 2);
break;
case Dock::Position::Bottom:
p += QPoint(r.width() / 2, 0 - (wr.height() - r.height()) / 2);
break;
case Dock::Position::Left:
p += QPoint(r.width() + (wr.width() - r.width()) / 2, r.height() / 2);
break;
case Dock::Position::Right:
p += QPoint(0 - (wr.width() - r.width()) / 2, r.height() / 2);
break;
}
return p;
}
QPixmap SNITrayItemWidget::icon()
{
return m_pixmap;
}
void SNITrayItemWidget::showPopupWindow(QWidget *const content, const bool model)
{
m_popupShown = true;
if (model)
emit requestWindowAutoHide(false);
DockPopupWindow *popup = PopupWindow.data();
QWidget *lastContent = popup->getContent();
if (lastContent)
lastContent->setVisible(false);
popup->setPosition(DockPosition);
popup->resize(content->sizeHint());
popup->setContent(content);
QPoint p = popupMarkPoint();
if (!popup->isVisible())
QMetaObject::invokeMethod(popup, "show", Qt::QueuedConnection, Q_ARG(QPoint, p), Q_ARG(bool, model));
else
popup->show(p, model);
}
void SNITrayItemWidget::setMouseData(QMouseEvent *e)
{
m_lastMouseReleaseData.first = e->pos();
m_lastMouseReleaseData.second = e->button();
m_handleMouseReleaseTimer->start();
}
bool SNITrayItemWidget::containsPoint(const QPoint &pos) {
QPoint ptGlobal = mapToGlobal(QPoint(0, 0));
QRect rectGlobal(ptGlobal, this->size());
if (rectGlobal.contains(pos)) return true;
if (!m_menu) {
if (m_dbusMenuImporter) {
qInfo() << "importer exists: " << m_dbusMenuImporter;
m_menu = m_dbusMenuImporter->menu();
} else {
qInfo() << "importer not exists.";
initMenu();
}
}
// 如果菜单列表隐藏,则认为不在区域内
if (!m_menu->isVisible()) return false;
// 判断鼠标是否在菜单区域
return m_menu->geometry().contains(pos);
}