dde-dock/frame/window/tray/tray_gridview.cpp
donghualin c27b9788dc fix: 从任务栏移除托盘图标后放入托盘区
1、根据需求,从任务栏将图标拖出后,释放需要回到托盘区
2、修复从托盘区将图标移动到任务栏上图标消失的问题
3、修复向上拖动图标,托盘出现的位置歪了的问题

Log:
Influence: 将图标从任务栏移出,松手后图标自动移到托盘区
Bug: https://pms.uniontech.com/bug-view-171497.html
Bug: https://pms.uniontech.com/bug-view-171539.html
Change-Id: Icfcd63afd60f46fece0b4f5ac5e267b3cb977ff1
2022-11-23 01:09:16 +00:00

600 lines
19 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) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.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 "tray_gridview.h"
#include "settingconfig.h"
#include "expandiconwidget.h"
#include "tray_model.h"
#include "basetraywidget.h"
#include <QMouseEvent>
#include <QDragEnterEvent>
#include <QDragLeaveEvent>
#include <QDropEvent>
#include <QPropertyAnimation>
#include <QLabel>
#include <QDrag>
#include <QMimeData>
#include <QApplication>
#include <QDebug>
#include <QTimer>
TrayGridView::TrayGridView(QWidget *parent)
: DListView(parent)
, m_aniCurveType(QEasingCurve::Linear)
, m_aniDuringTime(250)
, m_dragDistance(15)
, m_aniStartTime(new QTimer(this))
, m_pressed(false)
, m_aniRunning(false)
, m_positon(Dock::Position::Bottom)
{
initUi();
}
void TrayGridView::setPosition(Dock::Position position)
{
m_positon = position;
}
Dock::Position TrayGridView::position() const
{
return m_positon;
}
QSize TrayGridView::suitableSize() const
{
return suitableSize(m_positon);
}
QSize TrayGridView::suitableSize(const Dock::Position &position) const
{
TrayModel *dataModel = qobject_cast<TrayModel *>(model());
if (!dataModel)
return QSize(-1, -1);
if (dataModel->isIconTray()) {
// 如果是托盘图标
int width = 2;
int height = 0;
int count = dataModel->rowCount();
if (count > 0) {
int columnCount = qMin(count, 3);
for (int i = 0; i < columnCount; i ++) {
QModelIndex index = dataModel->index(i, 0);
width += visualRect(index).width() + spacing() * 2; // 左右边距加上单元格宽度
}
int rowCount = count / 3;
if (count % 3 > 0)
rowCount++;
for (int i = 0; i < rowCount; i++) {
QModelIndex index = dataModel->index(i * 3);
height += visualRect(index).height() + spacing() * 2;
}
} else {
width = spacing() * 2 + 30;
height = spacing() * 2 + 30;
}
return QSize(width, height);
}
if (position == Dock::Position::Top || position == Dock::Position::Bottom) {
int length = spacing() + 2;
if (m_positon == Dock::Position::Top || m_positon == Dock::Position::Bottom) {
for (int i = 0; i < dataModel->rowCount(); i++) {
QModelIndex index = dataModel->index(i, 0);
QRect indexRect = visualRect(index);
length += indexRect.width() + spacing();
}
} else {
// 如果是从左右切换过来的,此时还未进入上下位置,则将当前位置的高度作为计算左右位置的宽度
for (int i = 0; i < dataModel->rowCount(); i++) {
QModelIndex index = dataModel->index(i, 0);
QRect indexRect = visualRect(index);
length += indexRect.height() + spacing();
}
}
return QSize(length, -1);
}
int height = spacing() + 2;
if (m_positon == Dock::Position::Left || m_positon == Dock::Position::Right) {
for (int i = 0; i < dataModel->rowCount(); i++) {
QModelIndex index = dataModel->index(i, 0);
QRect indexRect = visualRect(index);
height += indexRect.height() + spacing();
}
} else {
for (int i = 0; i < dataModel->rowCount(); i++) {
QModelIndex index = dataModel->index(i, 0);
QRect indexRect = visualRect(index);
height += indexRect.width() + spacing();
}
}
return QSize(-1, height);
}
void TrayGridView::setDragDistance(int pixel)
{
m_dragDistance = pixel;
}
void TrayGridView::setAnimationProperty(const QEasingCurve::Type easing, const int duringTime)
{
m_aniCurveType = easing;
m_aniDuringTime = duringTime;
}
void TrayGridView::moveAnimation()
{
if (m_aniRunning || m_aniStartTime->isActive())
return;
const QModelIndex dropModelIndex = indexAt(m_dropPos);
if (!dropModelIndex.isValid())
return;
const QModelIndex dragModelIndex = indexAt(m_dragPos);
if (dragModelIndex == dropModelIndex)
return;
if (!dragModelIndex.isValid()) {
m_dragPos = indexRect(dropModelIndex).center();
return;
}
TrayModel *listModel = qobject_cast<TrayModel *>(model());
if (!listModel)
return;
listModel->clearDragDropIndex();
listModel->setDragingIndex(dragModelIndex);
listModel->setDragDropIndex(dropModelIndex);
const int startPos = dragModelIndex.row();
const int endPos = dropModelIndex.row();
const bool next = startPos <= endPos;
const int start = next ? startPos : endPos;
const int end = !next ? startPos : endPos;
for (int i = start + next; i <= (end - !next); i++)
createAnimation(i, next, (i == (end - !next)));
m_dropPos = indexRect(dropModelIndex).center();
m_dragPos = indexRect(dropModelIndex).center();
}
const QModelIndex TrayGridView::modelIndex(const int index) const
{
return model()->index(index, 0, QModelIndex());
}
const QRect TrayGridView::indexRect(const QModelIndex &index) const
{
return rectForIndex(index);
}
void TrayGridView::dropSwap()
{
qDebug() << "drop end";
TrayModel *listModel = qobject_cast<TrayModel *>(model());
if (!listModel)
return;
QModelIndex index = indexAt(m_dropPos);
if (!index.isValid())
return;
listModel->dropSwap(index.row());
clearDragModelIndex();
m_aniRunning = false;
setState(NoState);
}
void TrayGridView::clearDragModelIndex()
{
TrayModel *listModel = static_cast<TrayModel *>(this->model());
if (!listModel)
return;
listModel->clearDragDropIndex();
}
void TrayGridView::createAnimation(const int pos, const bool moveNext, const bool isLastAni)
{
qDebug() << "create moveAnimation";
const QModelIndex index(modelIndex(pos));
if (!index.isValid())
return;
QLabel *floatLabel = new QLabel(this);
QPropertyAnimation *ani = new QPropertyAnimation(floatLabel, "pos", floatLabel);
qreal ratio = qApp->devicePixelRatio();
BaseTrayWidget *widget = qobject_cast<BaseTrayWidget *>(indexWidget(index));
if (!widget)
return;
QPixmap pixmap = widget->icon();
QString text = index.data(Qt::DisplayRole).toString();
pixmap.scaled(pixmap.size() * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
pixmap.setDevicePixelRatio(ratio);
floatLabel->setFixedSize(indexRect(index).size());
floatLabel->setPixmap(pixmap);
floatLabel->show();
ani->setStartValue(indexRect(index).center() - QPoint(0, floatLabel->height() /2));
ani->setEndValue(indexRect(modelIndex(moveNext ? pos - 1 : pos + 1)).center() - QPoint(0, floatLabel->height() /2));
ani->setEasingCurve(m_aniCurveType);
ani->setDuration(m_aniDuringTime);
connect(ani, &QPropertyAnimation::finished, floatLabel, &QLabel::deleteLater);
if (isLastAni) {
m_aniRunning = true;
TrayModel *model = qobject_cast<TrayModel *>(this->model());
if (!model)
return;
connect(ani, &QPropertyAnimation::finished, this, &TrayGridView::dropSwap);
connect(ani, &QPropertyAnimation::valueChanged, m_aniStartTime, &QTimer::stop);
} else {
}
ani->start(QPropertyAnimation::DeleteWhenStopped);
}
void TrayGridView::mousePressEvent(QMouseEvent *e)
{
if (e->buttons() == Qt::LeftButton && !m_aniRunning)
m_dragPos = e->pos();
m_pressed = true;
}
void TrayGridView::mouseMoveEvent(QMouseEvent *e)
{
if (!m_pressed)
return DListView::mouseMoveEvent(e);
setState(QAbstractItemView::NoState);
e->accept();
if (e->buttons() == Qt::RightButton)
return DListView::mouseMoveEvent(e);
QModelIndex index = indexAt(e->pos());
if (!index.isValid())
return DListView::mouseMoveEvent(e);
// 如果当前拖动的位置是托盘展开按钮,则不让其拖动
TrayIconType iconType = index.data(TrayModel::Role::TypeRole).value<TrayIconType>();
if (iconType == TrayIconType::ExpandIcon)
return DListView::mouseMoveEvent(e);
if ((qAbs(e->pos().x() - m_dragPos.x()) > m_dragDistance ||
qAbs(e->pos().y() - m_dragPos.y()) > m_dragDistance)) {
qDebug() << "start drag";
if (!beginDrag(Qt::CopyAction | Qt::MoveAction))
DListView::mouseMoveEvent(e);
}
}
void TrayGridView::mouseReleaseEvent(QMouseEvent *e)
{
Q_UNUSED(e);
m_pressed = false;
}
void TrayGridView::dragEnterEvent(QDragEnterEvent *e)
{
const QModelIndex index = indexAt(e->pos());
if (model()->canDropMimeData(e->mimeData(), e->dropAction(), index.row(),
index.column(), index))
e->accept();
else
e->ignore();
Q_EMIT dragEntered();
}
void TrayGridView::dragLeaveEvent(QDragLeaveEvent *e)
{
m_aniStartTime->stop();
e->accept();
dragLeaved();
}
void TrayGridView::dragMoveEvent(QDragMoveEvent *e)
{
m_aniStartTime->stop();
if (m_aniRunning)
return;
QModelIndex index = indexAt(e->pos());
if (!model()->canDropMimeData(e->mimeData(), e->dropAction(), index.row(),
index.column(), index))
return;
setState(QAbstractItemView::DraggingState);
if (index.isValid()) {
if (m_dropPos != indexRect(index).center()) {
qDebug() << "update drop position: " << index.row();
m_dropPos = indexRect(index).center();
}
}
if (m_pressed)
m_aniStartTime->start();
}
const QModelIndex TrayGridView::getIndexFromPos(QPoint currentPoint) const
{
QModelIndex index = indexAt(currentPoint);
if (index.isValid())
return index;
if (model()->rowCount() == 0)
return index;
// 如果在第一个之前,则认为拖到了第一个的位置
QRect indexRect0 = visualRect(model()->index(0, 0));
if (currentPoint.x() < indexRect0.x() || currentPoint.y() < indexRect0.y())
return model()->index(0, 0);
// 如果从指定的位置没有找到索引则依次从每个index中查找先横向查找
for (int i = 1; i < model()->rowCount(); i++) {
QModelIndex lastIndex = model()->index(i - 1, 0);
QModelIndex currentIndex = model()->index(i, 0);
QRect lastIndexRect = visualRect(lastIndex);
QRect indexRect = visualRect(currentIndex);
if (lastIndexRect.x() + lastIndexRect.width() <= currentPoint.x()
&& indexRect.x() >= currentPoint.x())
return currentIndex;
}
// 如果鼠标位置刚好在上下两个索引中间
for (int i = 0; i < model()->rowCount(); i++) {
QModelIndex currentIndex = model()->index(i, 0);
QRect indexRect = visualRect(currentIndex);
if (currentPoint.y() >= indexRect.y() - spacing() && currentPoint.y() < indexRect.y()
&& currentPoint.x() >= indexRect.x() - spacing() && currentPoint.x() < indexRect.x())
return currentIndex;
}
return QModelIndex();
}
void TrayGridView::handleDropEvent(QDropEvent *e)
{
setState(DListView::NoState);
clearDragModelIndex();
if (m_aniStartTime->isActive())
m_aniStartTime->stop();
if (e->mimeData()->formats().contains("type") && e->source() != this) {
e->setDropAction(Qt::CopyAction);
e->accept();
TrayModel *dataModel = qobject_cast<TrayModel *>(model());
if (dataModel) {
WinInfo info;
info.type = static_cast<TrayIconType>(e->mimeData()->data("type").toInt());
info.key = static_cast<QString>(e->mimeData()->data("key"));
info.winId = static_cast<quint32>(e->mimeData()->data("winId").toInt());
info.servicePath = static_cast<QString>(e->mimeData()->data("servicePath"));
info.itemKey = static_cast<QString>(e->mimeData()->data("itemKey"));
info.isTypeWriting = (static_cast<QString>(e->mimeData()->data("isTypeWritting")) == "1");
info.expand = (static_cast<QString>(e->mimeData()->data("expand")) == "1");
info.pluginInter = (PluginsItemInterface *)(e->mimeData()->imageData().value<qulonglong>());
QModelIndex targetIndex = getIndexFromPos(e->pos());
int index = -1;
if (targetIndex.isValid() && targetIndex.row() < dataModel->rowCount()) {
// 如果拖动的位置是合法的位置,则让其插入到当前的位置
index = targetIndex.row();
dataModel->insertRow(index, info);
} else {
// 在其他的情况下,让其插入到最后
dataModel->addRow(info);
}
dataModel->saveConfig(index, info);
}
} else {
e->ignore();
DListView::dropEvent(e);
}
}
void TrayGridView::onUpdateEditorView()
{
for (int i = 0; i < model()->rowCount(); i++) {
QModelIndex index = model()->index(i, 0);
closePersistentEditor(index);
}
// 在关闭QWidget后不要立即调用openPersistentEditor来打开
// 因为closePersistentEditor后异步删除QWidget在关闭后如果立即调用openPersistentEditor在删除的时候会把
// 通过openPersistentEditor新建的QWidget给删除引起bug因此在所有的都closePersistentEditor后异步来调用
// openPersistentEditor就不会出现这种问题
QMetaObject::invokeMethod(this, [ = ] {
for (int i = 0; i < model()->rowCount(); i++) {
QModelIndex index = model()->index(i, 0);
openPersistentEditor(index);
}
}, Qt::QueuedConnection);
}
bool TrayGridView::beginDrag(Qt::DropActions supportedActions)
{
QModelIndex modelIndex = indexAt(m_dragPos);
TrayIconType trayType = modelIndex.data(TrayModel::Role::TypeRole).value<TrayIconType>();
// 展开图标不能移动
if (trayType == TrayIconType::ExpandIcon)
return false;
m_dropPos = indexRect(modelIndex).center();
TrayModel *listModel = qobject_cast<TrayModel *>(model());
if (!listModel)
return false;
BaseTrayWidget *widget = qobject_cast<BaseTrayWidget *>(indexWidget(modelIndex));
if (!widget)
return false;
QMimeData *data = model()->mimeData(QModelIndexList() << modelIndex);
if (!data)
return false;
auto pixmap = widget->icon();
qreal ratio = qApp->devicePixelRatio();
// 创建拖拽释放时的应用图标
QLabel *pixLabel = new QLabel(this);
pixLabel->setPixmap(pixmap);
pixLabel->setFixedSize(indexRect(modelIndex).size() / ratio);
QDrag *drag = new QDrag(this);
pixmap.scaled(pixmap.size() * ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
pixmap.setDevicePixelRatio(ratio);
drag->setPixmap(pixmap);
drag->setHotSpot(pixmap.rect().center() / ratio);
data->setImageData(pixmap);
drag->setMimeData(data);
setState(DraggingState);
listModel->setDragKey(modelIndex.data(TrayModel::Role::KeyRole).toString());
listModel->setDragingIndex(modelIndex);
// 删除当前的图标
WinInfo winInfo = listModel->takeIndex(modelIndex);
Qt::DropAction dropAct = drag->exec(supportedActions);
// 拖拽完成结束动画
m_aniStartTime->stop();
m_pressed = false;
if (dropAct == Qt::IgnoreAction) {
if (listModel->isIconTray()) {
// 如果当前是从托盘区域释放,按照原来的流程走
QPropertyAnimation *posAni = new QPropertyAnimation(pixLabel, "pos", pixLabel);
connect(posAni, &QPropertyAnimation::finished, [ this, listModel, pixLabel, modelIndex, winInfo ] () {
pixLabel->hide();
pixLabel->deleteLater();
listModel->setDragKey(QString());
listModel->insertRow(modelIndex.row(), winInfo);
clearDragModelIndex();
listModel->setExpandVisible(!TrayModel::getIconModel()->isEmpty());
m_dropPos = QPoint();
m_dragPos = QPoint();
onUpdateEditorView();
Q_EMIT dragFinished();
});
posAni->setEasingCurve(QEasingCurve::Linear);
posAni->setDuration(m_aniDuringTime);
posAni->setStartValue((QCursor::pos() - QPoint(0, pixLabel->height() / 2)));
posAni->setEndValue(mapToGlobal(m_dropPos) - QPoint(0, pixLabel->height() / 2));
pixLabel->show();
posAni->start(QAbstractAnimation::DeleteWhenStopped);
Q_EMIT dragFinished();
} else {
// 如果当前是从任务栏区域释放,则将释放后的图标放到托盘
listModel->setDragKey(QString());
clearDragModelIndex();
TrayModel *trayModel = TrayModel::getIconModel();
trayModel->addRow(winInfo);
m_dragPos = QPoint();
m_dropPos = QPoint();
trayModel->saveConfig(-1, winInfo);
Q_EMIT dragFinished();
}
} else {
listModel->setDragKey(QString());
clearDragModelIndex();
if (listModel->isIconTray()) {
// 如果当前是从托盘移动到任务栏,则根据托盘内部是否有应用来决定是否显示展开图标
bool hasIcon = (listModel->rowCount() > 0);
TrayModel::getDockModel()->setExpandVisible(hasIcon, hasIcon);
// 如果没有图标,则隐藏托盘图标
if (!hasIcon)
ExpandIconWidget::popupTrayView()->hide();
}
m_dropPos = QPoint();
m_dragPos = QPoint();
Q_EMIT dragFinished();
}
return true;
}
void TrayGridView::initUi()
{
setAcceptDrops(true);
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragDrop);
setDropIndicatorShown(false);
setMouseTracking(false);
setUniformItemSizes(true);
setFocusPolicy(Qt::NoFocus);
setMovement(DListView::Free);
setOrientation(QListView::LeftToRight, true);
setLayoutMode(DListView::Batched);
setResizeMode(DListView::Adjust);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameStyle(QFrame::NoFrame);
setContentsMargins(0, 0, 0, 0);
setSpacing(0);
setItemSpacing(0);
setBackgroundType(DStyledItemDelegate::RoundedBackground);
setSelectionMode(QListView::SingleSelection);
setVerticalScrollMode(QListView::ScrollPerPixel);
viewport()->setAcceptDrops(true);
viewport()->setAutoFillBackground(false);
m_aniStartTime->setInterval(10);
m_aniStartTime->setSingleShot(true);
connect(m_aniStartTime, &QTimer::timeout, this, &TrayGridView::moveAnimation);
}
void TrayGridView::dropEvent(QDropEvent *e)
{
handleDropEvent(e);
}