mirror of
https://github.com/linuxdeepin/dde-dock.git
synced 2025-05-30 22:01:41 +00:00

V23接口改造适配 Log: V23接口改造适配 Influence: 无 Task: https://pms.uniontech.com/task-view-207483.html Change-Id: Ide530c023ea41f86fad2e8001ec67f1afaa897ab
865 lines
25 KiB
C++
865 lines
25 KiB
C++
/*
|
||
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
|
||
*
|
||
* Author: sbw <sbw@sbw.so>
|
||
*
|
||
* Maintainer: sbw <sbw@sbw.so>
|
||
* listenerri <listenerri@gmail.com>
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "appitem.h"
|
||
#include "themeappicon.h"
|
||
#include "xcb_misc.h"
|
||
#include "appswingeffectbuilder.h"
|
||
#include "utils.h"
|
||
#include "screenspliter.h"
|
||
|
||
#include <X11/X.h>
|
||
#include <X11/Xlib.h>
|
||
|
||
#include <QPainter>
|
||
#include <QDrag>
|
||
#include <QMouseEvent>
|
||
#include <QApplication>
|
||
#include <QHBoxLayout>
|
||
#include <QGraphicsScene>
|
||
#include <QTimeLine>
|
||
#include <QX11Info>
|
||
#include <QGSettings>
|
||
|
||
#include <DGuiApplicationHelper>
|
||
#include <DConfig>
|
||
|
||
DGUI_USE_NAMESPACE
|
||
DCORE_USE_NAMESPACE
|
||
|
||
#define APP_DRAG_THRESHOLD 20
|
||
|
||
QPoint AppItem::MousePressPos;
|
||
|
||
AppItem::AppItem(DockInter *dockInter, const QGSettings *appSettings, const QGSettings *activeAppSettings, const QGSettings *dockedAppSettings, const QDBusObjectPath &entry, QWidget *parent)
|
||
: DockItem(parent)
|
||
, m_appSettings(appSettings)
|
||
, m_activeAppSettings(activeAppSettings)
|
||
, m_dockedAppSettings(dockedAppSettings)
|
||
, m_appPreviewTips(nullptr)
|
||
, m_itemEntryInter(new DockEntryInter(dockServiceName(), entry.path(), QDBusConnection::sessionBus(), this))
|
||
, m_swingEffectView(nullptr)
|
||
, m_itemAnimation(nullptr)
|
||
, m_wmHelper(DWindowManagerHelper::instance())
|
||
, m_drag(nullptr)
|
||
, m_retryTimes(0)
|
||
, m_iconValid(true)
|
||
, m_lastclickTimes(0)
|
||
, m_appIcon(QPixmap())
|
||
, m_updateIconGeometryTimer(new QTimer(this))
|
||
, m_retryObtainIconTimer(new QTimer(this))
|
||
, m_refershIconTimer(new QTimer(this))
|
||
, m_themeType(DGuiApplicationHelper::instance()->themeType())
|
||
, m_createMSecs(QDateTime::currentMSecsSinceEpoch())
|
||
, m_screenSpliter(ScreenSpliterFactory::createScreenSpliter(this, m_itemEntryInter))
|
||
, m_dockInter(dockInter)
|
||
{
|
||
QHBoxLayout *centralLayout = new QHBoxLayout;
|
||
centralLayout->setMargin(0);
|
||
centralLayout->setSpacing(0);
|
||
|
||
setObjectName(m_itemEntryInter->name());
|
||
setAcceptDrops(true);
|
||
setLayout(centralLayout);
|
||
|
||
m_id = m_itemEntryInter->id();
|
||
m_active = m_itemEntryInter->isActive();
|
||
|
||
m_updateIconGeometryTimer->setInterval(500);
|
||
m_updateIconGeometryTimer->setSingleShot(true);
|
||
|
||
m_retryObtainIconTimer->setInterval(3000);
|
||
m_retryObtainIconTimer->setSingleShot(true);
|
||
|
||
m_refershIconTimer->setInterval(1000);
|
||
m_refershIconTimer->setSingleShot(false);
|
||
|
||
connect(m_itemEntryInter, &DockEntryInter::IsActiveChanged, this, &AppItem::activeChanged);
|
||
connect(m_itemEntryInter, &DockEntryInter::IsActiveChanged, this, static_cast<void (AppItem::*)()>(&AppItem::update));
|
||
connect(m_itemEntryInter, &DockEntryInter::WindowInfosChanged, this, &AppItem::updateWindowInfos, Qt::QueuedConnection);
|
||
connect(m_itemEntryInter, &DockEntryInter::IconChanged, this, &AppItem::refreshIcon);
|
||
connect(m_itemEntryInter, &DockEntryInter::ModeChanged, this, &AppItem::modeChanged);
|
||
connect(m_updateIconGeometryTimer, &QTimer::timeout, this, &AppItem::updateWindowIconGeometries, Qt::QueuedConnection);
|
||
connect(m_retryObtainIconTimer, &QTimer::timeout, this, &AppItem::refreshIcon, Qt::QueuedConnection);
|
||
|
||
connect(this, &AppItem::requestUpdateEntryGeometries, this, &AppItem::updateWindowIconGeometries);
|
||
|
||
updateWindowInfos(m_itemEntryInter->windowInfos());
|
||
|
||
if (m_appSettings)
|
||
connect(m_appSettings, &QGSettings::changed, this, &AppItem::onGSettingsChanged);
|
||
if (m_dockedAppSettings)
|
||
connect(m_dockedAppSettings, &QGSettings::changed, this, &AppItem::onGSettingsChanged);
|
||
if (m_activeAppSettings)
|
||
connect(m_activeAppSettings, &QGSettings::changed, this, &AppItem::onGSettingsChanged);
|
||
|
||
connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &AppItem::onThemeTypeChanged);
|
||
|
||
/** 日历 1S定时判断是否刷新icon的处理 */
|
||
connect(m_refershIconTimer, &QTimer::timeout, this, &AppItem::onRefreshIcon);
|
||
}
|
||
|
||
/**将属于同一个应用的窗口合并到同一个应用图标
|
||
* @brief AppItem::checkEntry
|
||
*/
|
||
void AppItem::checkEntry()
|
||
{
|
||
m_itemEntryInter->Check();
|
||
}
|
||
|
||
const QString AppItem::appId() const
|
||
{
|
||
return m_id;
|
||
}
|
||
|
||
QString AppItem::name() const
|
||
{
|
||
return m_itemEntryInter->name();
|
||
}
|
||
|
||
bool AppItem::isValid() const
|
||
{
|
||
return m_itemEntryInter->isValid() && !m_itemEntryInter->id().isEmpty();
|
||
}
|
||
|
||
// Update _NET_WM_ICON_GEOMETRY property for windows that every item
|
||
// that manages, so that WM can do proper animations for specific
|
||
// window behaviors like minimization.
|
||
void AppItem::updateWindowIconGeometries()
|
||
{
|
||
// wayland没做处理
|
||
if (Utils::IS_WAYLAND_DISPLAY)
|
||
return;
|
||
|
||
const QRect r(mapToGlobal(QPoint(0, 0)),
|
||
mapToGlobal(QPoint(width(), height())));
|
||
if (!QX11Info::connection()) {
|
||
qWarning() << "QX11Info::connection() is 0x0";
|
||
return;
|
||
}
|
||
|
||
auto *xcb_misc = XcbMisc::instance();
|
||
|
||
for (auto it(m_windowInfos.cbegin()); it != m_windowInfos.cend(); ++it)
|
||
xcb_misc->set_window_icon_geometry(it.key(), r);
|
||
}
|
||
|
||
/**取消驻留在dock上的应用
|
||
* @brief AppItem::undock
|
||
*/
|
||
void AppItem::undock()
|
||
{
|
||
m_itemEntryInter->RequestUndock();
|
||
}
|
||
|
||
QWidget *AppItem::appDragWidget()
|
||
{
|
||
if (m_drag) {
|
||
return m_drag->appDragWidget();
|
||
}
|
||
|
||
return nullptr;
|
||
}
|
||
|
||
void AppItem::setDockInfo(Dock::Position dockPosition, const QRect &dockGeometry)
|
||
{
|
||
if (m_drag) {
|
||
m_drag->appDragWidget()->setDockInfo(dockPosition, dockGeometry);
|
||
}
|
||
}
|
||
|
||
void AppItem::setDraging(bool drag)
|
||
{
|
||
if (drag == isDragging())
|
||
return;
|
||
|
||
DockItem::setDraging(drag);
|
||
if (!drag)
|
||
m_screenSpliter->releaseSplit();
|
||
}
|
||
|
||
void AppItem::startSplit(const QRect &rect)
|
||
{
|
||
m_screenSpliter->startSplit(rect);
|
||
}
|
||
|
||
bool AppItem::supportSplitWindow()
|
||
{
|
||
return m_screenSpliter->suportSplitScreen();
|
||
}
|
||
|
||
bool AppItem::splitWindowOnScreen(ScreenSpliter::SplitDirection direction)
|
||
{
|
||
return m_screenSpliter->split(direction);
|
||
}
|
||
|
||
int AppItem::mode() const
|
||
{
|
||
return m_itemEntryInter->mode();
|
||
}
|
||
|
||
|
||
DockEntryInter *AppItem::itemEntryInter() const
|
||
{
|
||
return m_itemEntryInter;
|
||
}
|
||
|
||
QString AppItem::accessibleName()
|
||
{
|
||
return m_itemEntryInter->name();
|
||
}
|
||
|
||
void AppItem::requestDock()
|
||
{
|
||
m_itemEntryInter->RequestDock();
|
||
}
|
||
|
||
bool AppItem::isDocked() const
|
||
{
|
||
return m_itemEntryInter->isDocked();
|
||
}
|
||
|
||
qint64 AppItem::appOpenMSecs() const
|
||
{
|
||
return m_createMSecs;
|
||
}
|
||
|
||
void AppItem::updateMSecs()
|
||
{
|
||
m_createMSecs = QDateTime::currentMSecsSinceEpoch();
|
||
}
|
||
|
||
const WindowInfoMap &AppItem::windowsMap() const
|
||
{
|
||
return m_windowInfos;
|
||
}
|
||
|
||
void AppItem::moveEvent(QMoveEvent *e)
|
||
{
|
||
DockItem::moveEvent(e);
|
||
|
||
if (m_drag) {
|
||
m_drag->appDragWidget()->setOriginPos(mapToGlobal(appIconPosition()));
|
||
}
|
||
|
||
m_updateIconGeometryTimer->start();
|
||
}
|
||
|
||
void AppItem::paintEvent(QPaintEvent *e)
|
||
{
|
||
DockItem::paintEvent(e);
|
||
|
||
if (isDragging() || (m_swingEffectView != nullptr && DockDisplayMode != Fashion))
|
||
return;
|
||
|
||
QPainter painter(this);
|
||
if (!painter.isActive())
|
||
return;
|
||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||
|
||
const QRectF itemRect = rect();
|
||
|
||
if (DockDisplayMode == Efficient) {
|
||
// draw background
|
||
qreal min = qMin(itemRect.width(), itemRect.height());
|
||
QRectF backgroundRect = QRectF(itemRect.x(), itemRect.y(), min, min);
|
||
backgroundRect = backgroundRect.marginsRemoved(QMargins(2, 2, 2, 2));
|
||
backgroundRect.moveCenter(itemRect.center());
|
||
|
||
QPainterPath path;
|
||
path.addRoundedRect(backgroundRect, 8, 8);
|
||
|
||
// 在没有开启窗口多开的情况下,显示背景色
|
||
if (!m_dockInter->showMultiWindow()) {
|
||
if (m_active) {
|
||
QColor color = Qt::black;
|
||
color.setAlpha(255 * 0.8);
|
||
painter.fillPath(path, color);
|
||
} else if (!m_windowInfos.isEmpty()) {
|
||
if (hasAttention()) {
|
||
painter.fillPath(path, QColor(241, 138, 46, 255 * .8));
|
||
} else {
|
||
QColor color = Qt::black;
|
||
color.setAlpha(255 * 0.3);
|
||
painter.fillPath(path, color);
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (!m_windowInfos.isEmpty()) {
|
||
QPoint p;
|
||
QPixmap pixmap;
|
||
QPixmap activePixmap;
|
||
if (DGuiApplicationHelper::DarkType == m_themeType) {
|
||
m_horizontalIndicator = QPixmap(":/indicator/resources/indicator_dark.svg");
|
||
m_verticalIndicator = QPixmap(":/indicator/resources/indicator_dark_ver.svg");
|
||
} else {
|
||
m_horizontalIndicator = QPixmap(":/indicator/resources/indicator.svg");
|
||
m_verticalIndicator = QPixmap(":/indicator/resources/indicator_ver.svg");
|
||
}
|
||
m_activeHorizontalIndicator = QPixmap(":/indicator/resources/indicator_active.svg");
|
||
m_activeVerticalIndicator = QPixmap(":/indicator/resources/indicator_active_ver.svg");
|
||
switch (DockPosition) {
|
||
case Top:
|
||
pixmap = m_horizontalIndicator;
|
||
activePixmap = m_activeHorizontalIndicator;
|
||
p.setX((itemRect.width() - pixmap.width()) / 2);
|
||
p.setY(1);
|
||
break;
|
||
case Bottom:
|
||
pixmap = m_horizontalIndicator;
|
||
activePixmap = m_activeHorizontalIndicator;
|
||
p.setX((itemRect.width() - pixmap.width()) / 2);
|
||
p.setY(itemRect.height() - pixmap.height() - 1);
|
||
break;
|
||
case Left:
|
||
pixmap = m_verticalIndicator;
|
||
activePixmap = m_activeVerticalIndicator;
|
||
p.setX(1);
|
||
p.setY((itemRect.height() - pixmap.height()) / 2);
|
||
break;
|
||
case Right:
|
||
pixmap = m_verticalIndicator;
|
||
activePixmap = m_activeVerticalIndicator;
|
||
p.setX(itemRect.width() - pixmap.width() - 1);
|
||
p.setY((itemRect.height() - pixmap.height()) / 2);
|
||
break;
|
||
}
|
||
|
||
if (m_active)
|
||
painter.drawPixmap(p, activePixmap);
|
||
else
|
||
painter.drawPixmap(p, pixmap);
|
||
}
|
||
}
|
||
|
||
if (m_swingEffectView != nullptr)
|
||
return;
|
||
|
||
// icon
|
||
if (m_appIcon.isNull())
|
||
return;
|
||
|
||
painter.drawPixmap(appIconPosition(), m_appIcon);
|
||
}
|
||
|
||
void AppItem::mouseReleaseEvent(QMouseEvent *e)
|
||
{
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
|
||
// 获取时间戳qint64转quint64,是不存在任何问题的
|
||
quint64 curTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
|
||
if ((curTimestamp - m_lastclickTimes) < 300)
|
||
return;
|
||
|
||
m_lastclickTimes = curTimestamp;
|
||
|
||
// 鼠标在图标外边松开时,没必要响应点击操作
|
||
const QRect rect { QPoint(0, 0), size()};
|
||
if (!rect.contains(mapFromGlobal(QCursor::pos())))
|
||
return;
|
||
|
||
if (e->button() == Qt::MiddleButton) {
|
||
m_itemEntryInter->NewInstance(QX11Info::getTimestamp());
|
||
|
||
// play launch effect
|
||
if (m_windowInfos.isEmpty())
|
||
playSwingEffect();
|
||
|
||
} else if (e->button() == Qt::LeftButton) {
|
||
if (checkAndResetTapHoldGestureState() && e->source() == Qt::MouseEventSynthesizedByQt) {
|
||
qDebug() << "tap and hold gesture detected, ignore the synthesized mouse release event";
|
||
return;
|
||
}
|
||
|
||
qDebug() << "app item clicked, name:" << m_itemEntryInter->name()
|
||
<< "id:" << m_itemEntryInter->id() << "my-id:" << m_id << "icon:" << m_itemEntryInter->icon();
|
||
|
||
if (m_dockInter->showMultiWindow()) {
|
||
// 如果开启了多窗口显示,则直接新建一个窗口
|
||
m_itemEntryInter->NewInstance(QX11Info::getTimestamp());
|
||
} else {
|
||
// 如果没有开启新窗口显示,则
|
||
m_itemEntryInter->Activate(QX11Info::getTimestamp());
|
||
// play launch effect
|
||
if (m_windowInfos.isEmpty() && DGuiApplicationHelper::isSpecialEffectsEnvironment())
|
||
playSwingEffect();
|
||
}
|
||
}
|
||
}
|
||
|
||
void AppItem::mousePressEvent(QMouseEvent *e)
|
||
{
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
m_updateIconGeometryTimer->stop();
|
||
hidePopup();
|
||
|
||
if (e->button() == Qt::LeftButton)
|
||
MousePressPos = e->pos();
|
||
|
||
// context menu will handle in DockItem
|
||
DockItem::mousePressEvent(e);
|
||
}
|
||
|
||
void AppItem::mouseMoveEvent(QMouseEvent *e)
|
||
{
|
||
e->accept();
|
||
|
||
// handle preview
|
||
// if (e->buttons() == Qt::NoButton)
|
||
// return showPreview();
|
||
|
||
// handle drag
|
||
if (e->buttons() != Qt::LeftButton)
|
||
return;
|
||
|
||
const QPoint pos = e->pos();
|
||
if (!rect().contains(pos))
|
||
return;
|
||
|
||
const QPoint distance = pos - MousePressPos;
|
||
if (distance.manhattanLength() > APP_DRAG_THRESHOLD)
|
||
return startDrag();
|
||
}
|
||
|
||
void AppItem::wheelEvent(QWheelEvent *e)
|
||
{
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
|
||
QWidget::wheelEvent(e);
|
||
|
||
if (qAbs(e->angleDelta().y()) > 20) {
|
||
m_itemEntryInter->PresentWindows();
|
||
}
|
||
}
|
||
|
||
void AppItem::resizeEvent(QResizeEvent *e)
|
||
{
|
||
DockItem::resizeEvent(e);
|
||
|
||
refreshIcon();
|
||
}
|
||
|
||
void AppItem::dragEnterEvent(QDragEnterEvent *e)
|
||
{
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
|
||
// ignore drag from panel
|
||
if (e->source()) {
|
||
return e->ignore();
|
||
}
|
||
|
||
// ignore request dock event
|
||
QString draggingMimeKey = e->mimeData()->formats().contains("RequestDock") ? "RequestDock" : "text/plain";
|
||
if (QMimeDatabase().mimeTypeForFile(e->mimeData()->data(draggingMimeKey)).name() == "application/x-desktop") {
|
||
return e->ignore();
|
||
}
|
||
|
||
e->accept();
|
||
}
|
||
|
||
void AppItem::dragMoveEvent(QDragMoveEvent *e)
|
||
{
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
|
||
DockItem::dragMoveEvent(e);
|
||
|
||
if (m_windowInfos.isEmpty())
|
||
return;
|
||
|
||
if (!PopupWindow->isVisible() || !m_appPreviewTips)
|
||
showPreview();
|
||
}
|
||
|
||
void AppItem::dropEvent(QDropEvent *e)
|
||
{
|
||
QStringList uriList;
|
||
for (auto uri : e->mimeData()->urls()) {
|
||
uriList << uri.toEncoded();
|
||
}
|
||
|
||
qDebug() << "accept drop event with URIs: " << uriList;
|
||
m_itemEntryInter->HandleDragDrop(QX11Info::getTimestamp(), uriList);
|
||
}
|
||
|
||
void AppItem::leaveEvent(QEvent *e)
|
||
{
|
||
DockItem::leaveEvent(e);
|
||
|
||
if (m_appPreviewTips) {
|
||
if (m_appPreviewTips->isVisible()) {
|
||
m_appPreviewTips->prepareHide();
|
||
}
|
||
}
|
||
}
|
||
|
||
void AppItem::showHoverTips()
|
||
{
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
|
||
if (!m_windowInfos.isEmpty())
|
||
return showPreview();
|
||
|
||
DockItem::showHoverTips();
|
||
}
|
||
|
||
void AppItem::invokedMenuItem(const QString &itemId, const bool checked)
|
||
{
|
||
Q_UNUSED(checked);
|
||
|
||
m_itemEntryInter->HandleMenuItem(QX11Info::getTimestamp(), itemId);
|
||
}
|
||
|
||
const QString AppItem::contextMenu() const
|
||
{
|
||
return m_itemEntryInter->menu();
|
||
}
|
||
|
||
QWidget *AppItem::popupTips()
|
||
{
|
||
if (checkGSettingsControl())
|
||
return nullptr;
|
||
|
||
if (isDragging())
|
||
return nullptr;
|
||
|
||
static TipsWidget appNameTips(topLevelWidget());
|
||
appNameTips.setAccessibleName("tip");
|
||
appNameTips.setObjectName(m_itemEntryInter->name());
|
||
|
||
if (!m_windowInfos.isEmpty()) {
|
||
const quint32 currentWindow = m_itemEntryInter->currentWindow();
|
||
Q_ASSERT(m_windowInfos.contains(currentWindow));
|
||
appNameTips.setText(m_windowInfos[currentWindow].title.simplified());
|
||
} else {
|
||
appNameTips.setText(m_itemEntryInter->name().simplified());
|
||
}
|
||
|
||
return &appNameTips;
|
||
}
|
||
|
||
void AppItem::startDrag()
|
||
{
|
||
// 拖拽实现放到mainpanelcontrol
|
||
|
||
/*
|
||
if (!acceptDrops())
|
||
return;
|
||
|
||
if (checkGSettingsControl()) {
|
||
return;
|
||
}
|
||
|
||
m_dragging = true;
|
||
update();
|
||
|
||
const QPixmap &dragPix = m_appIcon;
|
||
|
||
m_drag = new AppDrag(this);
|
||
m_drag->setMimeData(new QMimeData);
|
||
|
||
// handle drag finished here
|
||
connect(m_drag->appDragWidget(), &AppDragWidget::destroyed, this, [ = ] {
|
||
m_dragging = false;
|
||
m_drag.clear();
|
||
setVisible(true);
|
||
update();
|
||
});
|
||
|
||
if (m_wmHelper->hasComposite()) {
|
||
m_drag->setPixmap(dragPix);
|
||
m_drag->appDragWidget()->setOriginPos(mapToGlobal(appIconPosition()));
|
||
emit dragStarted();
|
||
m_drag->exec(Qt::MoveAction);
|
||
} else {
|
||
m_drag->QDrag::setPixmap(dragPix);
|
||
m_drag->setHotSpot(dragPix.rect().center() / dragPix.devicePixelRatioF());
|
||
emit dragStarted();
|
||
m_drag->QDrag::exec(Qt::MoveAction);
|
||
}
|
||
|
||
// MainPanel will put this item to Item-Container when received this signal(MainPanel::itemDropped)
|
||
//emit itemDropped(m_drag->target());
|
||
|
||
if (!m_wmHelper->hasComposite()) {
|
||
if (!m_drag->target()) {
|
||
m_itemEntryInter->RequestUndock();
|
||
}
|
||
}
|
||
*/
|
||
}
|
||
|
||
bool AppItem::hasAttention() const
|
||
{
|
||
auto it = std::find_if(m_windowInfos.constBegin(), m_windowInfos.constEnd(), [ = ] (const auto &info) {
|
||
return info.attention;
|
||
});
|
||
|
||
return (it != m_windowInfos.end());
|
||
}
|
||
|
||
QPoint AppItem::appIconPosition() const
|
||
{
|
||
const auto ratio = devicePixelRatioF();
|
||
const QRectF itemRect = rect();
|
||
const QRectF iconRect = m_appIcon.rect();
|
||
const qreal iconX = itemRect.center().x() - iconRect.center().x() / ratio;
|
||
const qreal iconY = itemRect.center().y() - iconRect.center().y() / ratio;
|
||
|
||
return QPoint(iconX, iconY);
|
||
}
|
||
|
||
void AppItem::updateWindowInfos(const WindowInfoMap &info)
|
||
{
|
||
// 如果是打开第一个窗口,则更新窗口时间
|
||
if (m_windowInfos.isEmpty() && !info.isEmpty())
|
||
updateMSecs();
|
||
|
||
m_windowInfos = info;
|
||
if (m_appPreviewTips)
|
||
m_appPreviewTips->setWindowInfos(m_windowInfos, m_itemEntryInter->GetAllowedCloseWindows().value());
|
||
m_updateIconGeometryTimer->start();
|
||
|
||
// process attention effect
|
||
if (hasAttention()) {
|
||
if (DockDisplayMode == DisplayMode::Fashion)
|
||
playSwingEffect();
|
||
} else {
|
||
stopSwingEffect();
|
||
}
|
||
|
||
update();
|
||
|
||
// 通知外面窗体数量发生变化,需要更新多开窗口的信息
|
||
Q_EMIT windowCountChanged();
|
||
}
|
||
|
||
void AppItem::refreshIcon()
|
||
{
|
||
if (!isVisible())
|
||
return;
|
||
|
||
const QString icon = m_itemEntryInter->icon();
|
||
const int iconSize = qMin(width(), height());
|
||
|
||
if (DockDisplayMode == Efficient)
|
||
m_iconValid = ThemeAppIcon::getIcon(m_appIcon, icon, iconSize * 0.7, !m_iconValid);
|
||
else
|
||
m_iconValid = ThemeAppIcon::getIcon(m_appIcon, icon, iconSize * 0.8, !m_iconValid);
|
||
|
||
if (!m_refershIconTimer->isActive() && m_itemEntryInter->icon() == "dde-calendar") {
|
||
m_refershIconTimer->start();
|
||
}
|
||
|
||
if (!m_iconValid) {
|
||
if (m_retryTimes < 10) {
|
||
m_retryTimes++;
|
||
qDebug() << m_itemEntryInter->name() << "obtain app icon(" << icon << ")failed, retry times:" << m_retryTimes;
|
||
// Maybe the icon was installed after we loaded the caches.
|
||
// QIcon::setThemeSearchPaths will force Qt to re-check the gtk cache validity.
|
||
QIcon::setThemeSearchPaths(QIcon::themeSearchPaths());
|
||
|
||
m_retryObtainIconTimer->start();
|
||
} else {
|
||
// 如果图标获取失败,一分钟后再自动刷新一次(如果还是显示异常,基本需要应用自身看下为什么了)
|
||
if (!m_iconValid)
|
||
QTimer::singleShot(60 * 1000, this, &AppItem::refreshIcon);
|
||
}
|
||
|
||
update();
|
||
|
||
return;
|
||
}
|
||
|
||
if (m_retryTimes > 0) {
|
||
// reset times
|
||
m_retryTimes = 0;
|
||
}
|
||
|
||
update();
|
||
|
||
m_updateIconGeometryTimer->start();
|
||
}
|
||
|
||
void AppItem::onRefreshIcon()
|
||
{
|
||
if (QDate::currentDate() == m_curDate)
|
||
return;
|
||
|
||
m_curDate = QDate::currentDate();
|
||
refreshIcon();
|
||
}
|
||
|
||
void AppItem::onResetPreview()
|
||
{
|
||
if (m_appPreviewTips != nullptr) {
|
||
m_appPreviewTips->deleteLater();
|
||
m_appPreviewTips = nullptr;
|
||
}
|
||
}
|
||
|
||
void AppItem::activeChanged()
|
||
{
|
||
m_active = !m_active;
|
||
}
|
||
|
||
void AppItem::showPreview()
|
||
{
|
||
if (m_windowInfos.isEmpty())
|
||
return;
|
||
|
||
m_appPreviewTips = new PreviewContainer;
|
||
m_appPreviewTips->setWindowInfos(m_windowInfos, m_itemEntryInter->GetAllowedCloseWindows().value());
|
||
m_appPreviewTips->updateLayoutDirection(DockPosition);
|
||
|
||
connect(m_appPreviewTips, &PreviewContainer::requestActivateWindow, this, &AppItem::requestActivateWindow, Qt::QueuedConnection);
|
||
connect(m_appPreviewTips, &PreviewContainer::requestPreviewWindow, this, &AppItem::requestPreviewWindow, Qt::QueuedConnection);
|
||
connect(m_appPreviewTips, &PreviewContainer::requestCancelPreviewWindow, this, &AppItem::requestCancelPreview);
|
||
connect(m_appPreviewTips, &PreviewContainer::requestHidePopup, this, &AppItem::hidePopup);
|
||
connect(m_appPreviewTips, &PreviewContainer::requestCheckWindows, m_itemEntryInter, &DockEntryInter::Check);
|
||
|
||
connect(m_appPreviewTips, &PreviewContainer::requestActivateWindow, this, &AppItem::onResetPreview);
|
||
connect(m_appPreviewTips, &PreviewContainer::requestCancelPreviewWindow, this, &AppItem::onResetPreview);
|
||
connect(m_appPreviewTips, &PreviewContainer::requestHidePopup, this, &AppItem::onResetPreview);
|
||
|
||
// 预览标题显示方式的配置
|
||
DConfig config(QString("com.deepin.dde.dock.dconfig"), QString());
|
||
if (config.isValid() && config.keyList().contains("Dock_Show_Window_name"))
|
||
m_appPreviewTips->setTitleDisplayMode(config.value("Dock_Show_Window_name").toInt());
|
||
|
||
showPopupWindow(m_appPreviewTips, true);
|
||
}
|
||
|
||
void AppItem::playSwingEffect()
|
||
{
|
||
// NOTE(sbw): return if animation view already playing
|
||
if (m_swingEffectView != nullptr)
|
||
return;
|
||
|
||
if (rect().isEmpty())
|
||
return checkAttentionEffect();
|
||
|
||
stopSwingEffect();
|
||
|
||
QPair<QGraphicsView *, QGraphicsItemAnimation *> pair = SwingEffect(
|
||
this, m_appIcon, rect(), devicePixelRatioF());
|
||
|
||
m_swingEffectView = pair.first;
|
||
m_itemAnimation = pair.second;
|
||
|
||
QTimeLine *tl = m_itemAnimation->timeLine();
|
||
connect(tl, &QTimeLine::stateChanged, this, [ = ](QTimeLine::State newState) {
|
||
if (newState == QTimeLine::NotRunning) {
|
||
m_swingEffectView->hide();
|
||
layout()->removeWidget(m_swingEffectView);
|
||
m_swingEffectView = nullptr;
|
||
m_itemAnimation = nullptr;
|
||
checkAttentionEffect();
|
||
}
|
||
});
|
||
|
||
layout()->addWidget(m_swingEffectView);
|
||
tl->start();
|
||
}
|
||
|
||
void AppItem::stopSwingEffect()
|
||
{
|
||
if (m_swingEffectView == nullptr || m_itemAnimation == nullptr)
|
||
return;
|
||
|
||
// stop swing effect
|
||
m_swingEffectView->setVisible(false);
|
||
|
||
if (m_itemAnimation->timeLine() && m_itemAnimation->timeLine()->state() != QTimeLine::NotRunning)
|
||
m_itemAnimation->timeLine()->stop();
|
||
}
|
||
|
||
void AppItem::checkAttentionEffect()
|
||
{
|
||
QTimer::singleShot(1000, this, [ = ] {
|
||
if (DockDisplayMode == DisplayMode::Fashion && hasAttention())
|
||
playSwingEffect();
|
||
});
|
||
}
|
||
|
||
void AppItem::onGSettingsChanged(const QString &key)
|
||
{
|
||
if (key != "enable") {
|
||
return;
|
||
}
|
||
|
||
const QGSettings *setting = m_itemEntryInter->isDocked()
|
||
? m_dockedAppSettings
|
||
: m_activeAppSettings;
|
||
|
||
if (setting && setting->keys().contains("enable")) {
|
||
const bool isEnable = !m_appSettings || (m_appSettings->keys().contains("enable") && m_appSettings->get("enable").toBool());
|
||
setVisible(isEnable && setting->get("enable").toBool());
|
||
}
|
||
}
|
||
|
||
bool AppItem::checkGSettingsControl() const
|
||
{
|
||
const QGSettings *setting = m_itemEntryInter->isDocked()
|
||
? m_dockedAppSettings
|
||
: m_activeAppSettings;
|
||
|
||
return ((m_appSettings && m_appSettings->keys().contains("control") && m_appSettings->get("control").toBool())
|
||
|| (setting && setting->keys().contains("control") && setting->get("control").toBool()));
|
||
}
|
||
|
||
void AppItem::onThemeTypeChanged(DGuiApplicationHelper::ColorType themeType)
|
||
{
|
||
m_themeType = themeType;
|
||
update();
|
||
}
|
||
|
||
// 放到最下面是因为析构函数和匿名函数会影响lcov统计单元测试的覆盖率
|
||
AppItem::~AppItem()
|
||
{
|
||
stopSwingEffect();
|
||
}
|
||
|
||
void AppItem::showEvent(QShowEvent *e)
|
||
{
|
||
DockItem::showEvent(e);
|
||
|
||
QTimer::singleShot(0, this, [ = ] {
|
||
onGSettingsChanged("enable");
|
||
});
|
||
|
||
refreshIcon();
|
||
}
|