dde-dock/frame/window/traymanagerwindow.cpp
donghualin ae74a6133f fix: 实现高效模式下托盘和快捷插件拖动功能
高效模式下和时尚模式下托盘区域共用一个TrayModel,在拖动图标的时候,时尚模式和高效模式保持相同的状态

Log: 实现高效模式下图标的拖动功能
Influence: 高效模式,从托盘或快捷面板拖动图标到任务栏
Task: https://pms.uniontech.com/task-view-112073.html
Change-Id: I279b572231ea8efc9bd7f1ee0e628e9ee3eb064e
2022-11-02 06:58:03 +00:00

516 lines
20 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 "traymanagerwindow.h"
#include "quickpluginwindow.h"
#include "tray_gridview.h"
#include "tray_delegate.h"
#include "tray_model.h"
#include "constants.h"
#include "quicksettingcontainer.h"
#include "systempluginwindow.h"
#include "datetimedisplayer.h"
#include <DGuiApplicationHelper>
#include <DRegionMonitor>
#include <QDropEvent>
#include <QBoxLayout>
#include <QLabel>
#include <QMimeData>
#include <QDBusConnection>
#include <QPainter>
#include <QPainterPath>
#define MAXFIXEDSIZE 999999
#define CRITLCALHEIGHT 42
#define CONTENTSPACE 7
// 高度小于等于这个值的时候,间距最小值
#define MINHIGHT 46
// 最小值与最大值的差值
#define MINSPACE 4
// 当前高度与最小高度差值大于这个值的时候,间距使用最大值
#define MAXDIFF 3
TrayManagerWindow::TrayManagerWindow(QWidget *parent)
: QWidget(parent)
, m_appPluginDatetimeWidget(new QWidget(this))
, m_systemPluginWidget(new SystemPluginWindow(this))
, m_appPluginWidget(new QWidget(m_appPluginDatetimeWidget))
, m_quickIconWidget(new QuickPluginWindow(m_appPluginWidget))
, m_dateTimeWidget(new DateTimeDisplayer(false, m_appPluginDatetimeWidget))
, m_appPluginLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight, this))
, m_mainLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight, this))
, m_trayView(new TrayGridView(this))
, m_model(TrayModel::getDockModel())
, m_delegate(new TrayDelegate(m_trayView, m_trayView))
, m_position(Dock::Position::Bottom)
, m_displayMode(Dock::DisplayMode::Fashion)
, m_splitLine(new QLabel(m_appPluginDatetimeWidget))
, m_dockInter(new DockInter(dockServiceName(), dockServicePath(), QDBusConnection::sessionBus(), this))
, m_singleShow(false)
, m_borderRadius(0)
{
initUi();
initConnection();
setAcceptDrops(true);
setMouseTracking(true);
QMetaObject::invokeMethod(this, &TrayManagerWindow::updateLayout, Qt::QueuedConnection);
}
TrayManagerWindow::~TrayManagerWindow()
{
}
void TrayManagerWindow::updateBorderRadius(int borderRadius)
{
m_borderRadius = borderRadius;
update();
}
void TrayManagerWindow::updateLayout()
{
bool lastIsSingle = m_singleShow;
if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
m_singleShow = (height() <= CRITLCALHEIGHT);
else
m_singleShow = true;
if (m_singleShow)
resetSingleDirection();
else
resetMultiDirection();
resetChildWidgetSize();
// 当尺寸发生变化的时候,通知托盘区域刷新尺寸,让托盘图标始终保持居中显示
Q_EMIT m_delegate->sizeHintChanged(m_model->index(0, 0));
// 当插件区域从单行变成两行或者两行变成单行的时候,发送该信号,通知外部重新调整区域大小
if (lastIsSingle != m_singleShow)
Q_EMIT requestUpdate();
}
void TrayManagerWindow::setPositon(Dock::Position position)
{
if (m_position == position)
return;
m_position = position;
if (position == Dock::Position::Top || position == Dock::Position::Bottom)
m_trayView->setOrientation(QListView::Flow::LeftToRight, false);
else
m_trayView->setOrientation(QListView::Flow::TopToBottom, false);
TrayDelegate *delegate = static_cast<TrayDelegate *>(m_trayView->itemDelegate());
delegate->setPositon(position);
m_trayView->setPosition(position);
m_quickIconWidget->setPositon(position);
m_dateTimeWidget->setPositon(position);
m_systemPluginWidget->setPositon(position);
if (m_model->hasExpand()) {
// 切换位置的时候需要先关闭编辑器然后在model函数的flag方法中打开
m_trayView->closePersistentEditor(m_model->index(0, 0));
}
updateLayout();
}
void TrayManagerWindow::setDisplayMode(Dock::DisplayMode displayMode)
{
m_displayMode = displayMode;
// 从时尚模式切换到高效模式的时候,需要重新布局
onTrayCountChanged();
}
int TrayManagerWindow::appDatetimeSize(const Dock::Position &position) const
{
if (position == Dock::Position::Top || position == Dock::Position::Bottom) {
bool showSingle = m_singleShow;
// 正在从左右切换到上下(m_position当前显示的位置还未切换),此时根据托盘区域的尺寸来决定显示一行还是两行
if (m_position == Dock::Position::Left || m_position == Dock::Position::Right) {
showSingle = m_dockInter->windowSizeFashion() < CRITLCALHEIGHT;
}
// 如果是一行或者是在切换位置(从左右切换到上下)
if (showSingle) {
return m_trayView->suitableSize(position).width() + m_quickIconWidget->suitableSize(position).width()
+ m_dateTimeWidget->suitableSize(position).width() + 4;
}
//如果是两行
int topWidth = m_trayView->suitableSize(position).width() + m_quickIconWidget->suitableSize(position).width();
int bottomWidth = m_dateTimeWidget->suitableSize(position).width();
return qMax(topWidth, bottomWidth);
}
int trayHeight = m_trayView->suitableSize(position).height();
int traypluginHeight = trayHeight + m_quickIconWidget->suitableSize(position).height() + m_appPluginLayout->spacing();
return traypluginHeight + m_dateTimeWidget->suitableSize(position).height() + 10;
}
QSize TrayManagerWindow::suitableSize() const
{
return suitableSize(m_position);
}
QSize TrayManagerWindow::suitableSize(const Dock::Position &position) const
{
QMargins m = m_mainLayout->contentsMargins();
if (position == Dock::Position::Top || position == Dock::Position::Bottom) {
return QSize(appDatetimeSize(position) +
m_systemPluginWidget->suitableSize(position).width() + m_mainLayout->spacing() +
m.left() + m.right(), QWIDGETSIZE_MAX);
}
return QSize(QWIDGETSIZE_MAX, appDatetimeSize(position) +
m_systemPluginWidget->suitableSize(position).height() + m_mainLayout->spacing() +
m.top() + m.bottom());
}
// 用于返回需要绘制的圆形区域
QPainterPath TrayManagerWindow::roundedPaths()
{
QMargins mainMargin = m_mainLayout->contentsMargins();
int radius = m_borderRadius - mainMargin.top();
QPainterPath path;
if ((m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
&& m_singleShow) {
// 如果是上下方向,且只有一行
// 计算托盘和快捷插件区域
QPoint pointPlugin(mainMargin.left(), mainMargin.top());
QRect rctPlugin(QPoint(mainMargin.left(), mainMargin.top()), m_appPluginWidget->size());
path.addRoundedRect(rctPlugin, radius, radius);
// 计算日期时间区域
QPoint pointDateTime = m_dateTimeWidget->pos();
pointDateTime = m_dateTimeWidget->parentWidget()->mapTo(this, pointDateTime);
QRect rctDatetime(pointDateTime, m_dateTimeWidget->size());
path.addRoundedRect(rctDatetime, radius, radius);
// 计算系统插件区域
path.addRoundedRect(m_systemPluginWidget->geometry(), radius, radius);
} else {
// 添加系统插件区域的位置信息
path.addRoundedRect(m_appPluginDatetimeWidget->geometry(), radius, radius);
path.addRoundedRect(m_systemPluginWidget->geometry(), radius, radius);
}
return path;
}
void TrayManagerWindow::onTrayCountChanged()
{
resetChildWidgetSize();
Q_EMIT requestUpdate();
}
void TrayManagerWindow::onRequestUpdateWidget(const QList<int> &idxs)
{
for (int i = 0; i < idxs.size(); i++) {
int idx = idxs[i];
if (idx < m_model->rowCount()) {
QModelIndex index = m_model->index(idx);
m_trayView->closePersistentEditor(index);
m_trayView->openPersistentEditor(index);
}
}
}
void TrayManagerWindow::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
updateLayout();
}
void TrayManagerWindow::initUi()
{
m_systemPluginWidget->setDisplayMode(Dock::DisplayMode::Fashion);
m_trayView->setModel(m_model);
m_trayView->setItemDelegate(m_delegate);
m_trayView->setDragDistance(2);
m_splitLine->setFixedHeight(1);
QPalette pal;
QColor lineColor(Qt::black);
lineColor.setAlpha(static_cast<int>(255 * 0.1));
pal.setColor(QPalette::Background, lineColor);
m_splitLine->setAutoFillBackground(true);
m_splitLine->setPalette(pal);
// 左侧的区域,包括应用托盘插件和下方的日期时间区域
m_appPluginLayout->setContentsMargins(0, 0, 0, 0);
m_appPluginLayout->setSpacing(0);
m_appPluginWidget->setLayout(m_appPluginLayout);
m_appPluginLayout->addWidget(m_trayView);
m_appPluginLayout->addWidget(m_quickIconWidget);
setLayout(m_mainLayout);
// 通用情况下设置边距和间距都为7
m_mainLayout->setContentsMargins(CONTENTSPACE, CONTENTSPACE, CONTENTSPACE, CONTENTSPACE);
m_mainLayout->setSpacing(CONTENTSPACE);
m_mainLayout->addWidget(m_appPluginDatetimeWidget);
m_mainLayout->addWidget(m_systemPluginWidget);
}
void TrayManagerWindow::initConnection()
{
connect(m_trayView, &TrayGridView::requestRemove, m_model, &TrayModel::removeRow);
connect(m_model, &TrayModel::rowCountChanged, this, &TrayManagerWindow::onTrayCountChanged);
connect(m_model, &TrayModel::rowCountChanged, m_trayView, &TrayGridView::onUpdateEditorView);
connect(m_model, &TrayModel::requestRefreshEditor, m_trayView, &TrayGridView::onUpdateEditorView);
connect(m_quickIconWidget, &QuickPluginWindow::itemCountChanged, this, [ this ] {
// 当插件数量发生变化的时候,需要调整尺寸
m_quickIconWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
m_quickIconWidget->setFixedWidth(m_quickIconWidget->suitableSize().width());
else
m_quickIconWidget->setFixedHeight(m_quickIconWidget->suitableSize().height());
Q_EMIT requestUpdate();
});
connect(m_systemPluginWidget, &SystemPluginWindow::itemChanged, this, [ this ] {
// 当系统插件发生变化的时候,同样需要调整尺寸
m_systemPluginWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
m_systemPluginWidget->setFixedWidth(m_systemPluginWidget->suitableSize().width());
else
m_systemPluginWidget->setFixedHeight(m_systemPluginWidget->suitableSize().height());
Q_EMIT requestUpdate();
});
connect(m_trayView, &TrayGridView::dragLeaved, m_delegate, [ this ]{
Q_EMIT m_delegate->requestDrag(true);
});
connect(m_trayView, &TrayGridView::dragEntered, m_delegate, [ this ]{
Q_EMIT m_delegate->requestDrag(false);
});
connect(m_model, &TrayModel::requestUpdateWidget, this, &TrayManagerWindow::onRequestUpdateWidget);
connect(m_dateTimeWidget, &DateTimeDisplayer::requestUpdate, this, &TrayManagerWindow::requestUpdate);
m_trayView->installEventFilter(this);
m_quickIconWidget->installEventFilter(this);
installEventFilter(this);
QMetaObject::invokeMethod(this, &TrayManagerWindow::requestUpdate, Qt::QueuedConnection);
}
void TrayManagerWindow::resetChildWidgetSize()
{
int count = 0;
for (int i = 0; i < m_model->rowCount(); i++) {
if (!m_trayView->isRowHidden(i))
count++;
}
switch (m_position) {
case Dock::Position::Top:
case Dock::Position::Bottom: {
int trayWidth = m_trayView->suitableSize().width();
int appDateTimeWidth = appDatetimeSize(m_position);
QMargins m = m_appPluginLayout->contentsMargins();
if (m_singleShow) {
// 单行显示
int trayHeight = m_appPluginDatetimeWidget->height() - m.top() - m.bottom();
m_trayView->setFixedSize(trayWidth, trayHeight);
m_quickIconWidget->setFixedSize(m_quickIconWidget->suitableSize().width(), trayHeight);
m_appPluginWidget->setFixedSize(trayWidth + m_quickIconWidget->suitableSize().width(), trayHeight);
m_dateTimeWidget->setFixedSize(m_dateTimeWidget->suitableSize().width(), trayHeight);
// 设置右侧的电源按钮的尺寸
m_systemPluginWidget->setFixedSize(m_systemPluginWidget->suitableSize());
// 如果顶层窗体的高度为0则直接让其间距为0否则就会出现隐藏模式下有8个像素的高度依然显示
int space = topLevelWidget()->height() == 0 ? 0 : 4;
m_mainLayout->setContentsMargins(space, space, space ,space);
m_mainLayout->setSpacing(space);
// 单行显示需要重新设置插件和时间日期的位置,不显示分割线
m_splitLine->setVisible(false);
m_appPluginWidget->move(0, 0);
m_dateTimeWidget->move(m_appPluginWidget->x() + m_appPluginWidget->width() + 4, m_appPluginWidget->y());
} else {
// 多行显示
m_quickIconWidget->setFixedSize(m_quickIconWidget->suitableSize());
int trayHeight = m_appPluginDatetimeWidget->height() / 2 + 4 - m.top() - m.bottom();
m_trayView->setFixedSize(trayWidth, trayHeight);
m_appPluginWidget->setFixedSize(trayWidth + m_quickIconWidget->suitableSize().width(), trayHeight);
// 因为是两行,所以对于时间控件的尺寸,只能设置最小值
int dateTimeWidth = qMax(m_appPluginWidget->width(), m_dateTimeWidget->suitableSize().width());
int dateTimeHeight = m_appPluginDatetimeWidget->height() - - m.top() - m.bottom() - trayHeight;
m_dateTimeWidget->setFixedSize(dateTimeWidth, dateTimeHeight);
m_systemPluginWidget->setFixedSize(m_systemPluginWidget->suitableSize());
int contentSpace = qMin(MAXDIFF, qMax(height() - MINHIGHT, 0)) + MINSPACE;
m_mainLayout->setContentsMargins(contentSpace, contentSpace, contentSpace, contentSpace);
m_mainLayout->setSpacing(contentSpace);
// 调整插件和日期窗体的位置显示,这里没有用到布局,是因为在调整任务栏位置的时候,
// 随着布局方向的改变,显示有很大的问题
m_splitLine->setFixedWidth(appDateTimeWidth);
m_splitLine->setVisible(true);
if (m_position == Dock::Position::Bottom) {
m_appPluginWidget->move(0, 0);
m_splitLine->move(0, m_appPluginWidget->y() + m_appPluginWidget->height());
m_dateTimeWidget->move(0, m_appPluginWidget->y() + m_appPluginWidget->height() + m_splitLine->height());
} else {
m_dateTimeWidget->move(0, 0);
m_splitLine->move(0, m_dateTimeWidget->y() + m_dateTimeWidget->height());
m_appPluginWidget->move(0, m_dateTimeWidget->y() + m_dateTimeWidget->height() + m_splitLine->height());
}
}
QMargins margin = m_mainLayout->contentsMargins();
int appDateHeight = height() - margin.top() - margin.bottom();
m_appPluginDatetimeWidget->setFixedSize(appDateTimeWidth, appDateHeight);
break;
}
case Dock::Position::Left:
case Dock::Position::Right: {
int trayHeight = m_trayView->suitableSize().height();
int quickAreaHeight = m_quickIconWidget->suitableSize().height();
QMargins m = m_appPluginLayout->contentsMargins();
// 左右方向始终只有一列
int datetimeHeight = m_dateTimeWidget->suitableSize().height();
int sizeWidth = m_appPluginDatetimeWidget->width() - m.left() - m.right();
m_trayView->setFixedSize(sizeWidth, trayHeight);
m_quickIconWidget->setFixedSize(sizeWidth, quickAreaHeight);
m_dateTimeWidget->setFixedSize(sizeWidth, datetimeHeight);
m_appPluginWidget->setFixedSize(sizeWidth, trayHeight + quickAreaHeight);
m_systemPluginWidget->setFixedSize(m_systemPluginWidget->suitableSize());
int contentSpace = (qMin(MAXDIFF, qMax(width() - MINHIGHT, 0)) + MINSPACE);
m_mainLayout->setContentsMargins(contentSpace, contentSpace, contentSpace, contentSpace);
m_mainLayout->setSpacing(contentSpace);
int appDateWidth = width() - (contentSpace * 2);
m_appPluginDatetimeWidget->setFixedSize(appDateWidth, appDatetimeSize(m_position));
// 调整各个部件的位置
m_appPluginWidget->move(0, 0);
m_splitLine->setFixedWidth(width());
m_splitLine->setVisible(true);
m_splitLine->move(0, m_appPluginWidget->y() + m_appPluginWidget->height());
m_dateTimeWidget->move(0, m_appPluginWidget->y() + m_appPluginWidget->height() + m_splitLine->height());
break;
}
}
}
void TrayManagerWindow::resetSingleDirection()
{
switch (m_position) {
case Dock::Position::Top:
case Dock::Position::Bottom: {
m_appPluginLayout->setDirection(QBoxLayout::Direction::LeftToRight);
// 应用和时间在一行显示
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
m_splitLine->hide();
break;
}
case Dock::Position::Left:
case Dock::Position::Right:{
m_appPluginLayout->setDirection(QBoxLayout::Direction::TopToBottom);
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
m_splitLine->show();
break;
}
}
m_dateTimeWidget->setOneRow(true);
}
void TrayManagerWindow::resetMultiDirection()
{
switch (m_position) {
case Dock::Position::Top:
case Dock::Position::Bottom: {
m_appPluginLayout->setDirection(QBoxLayout::Direction::LeftToRight);
m_mainLayout->setDirection(QBoxLayout::Direction::LeftToRight);
m_splitLine->show();
m_dateTimeWidget->setOneRow(false);
break;
}
case Dock::Position::Left:
case Dock::Position::Right: {
m_appPluginLayout->setDirection(QBoxLayout::Direction::TopToBottom);
m_mainLayout->setDirection(QBoxLayout::Direction::TopToBottom);
m_splitLine->hide();
m_dateTimeWidget->setOneRow(true);
break;
}
}
}
void TrayManagerWindow::dragEnterEvent(QDragEnterEvent *e)
{
e->setDropAction(Qt::CopyAction);
e->accept();
}
void TrayManagerWindow::dragMoveEvent(QDragMoveEvent *e)
{
e->setDropAction(Qt::CopyAction);
e->accept();
}
void TrayManagerWindow::dropEvent(QDropEvent *e)
{
if (!e || !e->mimeData() || e->source() == this)
return;
if (qobject_cast<QuickSettingContainer *>(e->source())) {
const QuickPluginMimeData *mimeData = qobject_cast<const QuickPluginMimeData *>(e->mimeData());
if (!mimeData)
return;
PluginsItemInterface *pluginItem = static_cast<PluginsItemInterface *>(mimeData->pluginItemInterface());
if (pluginItem)
m_quickIconWidget->dragPlugin(pluginItem);
} else if (qobject_cast<TrayGridView *>(e->source())) {
// 将trayView中的dropEvent扩大到整个区域this这样便于随意拖动到这个区域都可以捕获。
// m_trayView中有e->accept不会导致事件重复处理。
m_trayView->handleDropEvent(e);
}
}
void TrayManagerWindow::dragLeaveEvent(QDragLeaveEvent *event)
{
event->accept();
}
void TrayManagerWindow::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainterPath path = roundedPaths();
QPainter painter(this);
painter.save();
painter.setRenderHint(QPainter::Antialiasing);
painter.setClipPath(path);
painter.fillRect(rect().adjusted(1, 1, -1, -1), maskColor(102));
painter.setPen(maskColor(110));
painter.drawPath(path);
painter.restore();
}
QColor TrayManagerWindow::maskColor(uint8_t alpha) const
{
QColor color = DGuiApplicationHelper::standardPalette(DGuiApplicationHelper::instance()->themeType()).window().color();
color.setAlpha(alpha);
return color;
}