dde-dock/frame/window/quicksettingcontainer.cpp
donghualin 541cdf60e7 feat: 支持从快捷面板拖动图标驻留到任务栏
增加从快捷面板拖动应用到任务栏的功能

Log:
Influence: 从快捷面板拖动图标到任务栏,观察是否驻留在任务栏
Bug: https://pms.uniontech.com/bug-view-171517.html
Change-Id: I3351be282ef8d3afbb55f227fc6ae8ce16c78a97
2022-12-14 01:30:20 +00:00

467 lines
16 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 "quicksettingcontainer.h"
#include "quicksettingcontroller.h"
#include "pluginsiteminterface.h"
#include "quicksettingitem.h"
#include "dockpopupwindow.h"
#include "slidercontainer.h"
#include "pluginchildpage.h"
#include "utils.h"
#include "quickdragcore.h"
#include <DListView>
#include <DStyle>
#include <QDrag>
#include <QVBoxLayout>
#include <QMetaObject>
#include <QStackedLayout>
#include <QMouseEvent>
#include <QLabel>
#include <QBitmap>
#include <QPainterPath>
DWIDGET_USE_NAMESPACE
struct QuickDragInfo {
QPoint dragPosition;
QWidget *dragItem = nullptr;
PluginsItemInterface *pluginInter = nullptr;
void reset() {
dragPosition.setX(0);
dragPosition.setY(0);
dragItem = nullptr;
pluginInter = nullptr;
}
bool isNull() {
return !dragItem;
}
} QuickDragInfo;
#define ITEMWIDTH 70
#define ITEMHEIGHT 60
#define ITEMSPACE 10
#define COLUMNCOUNT 4
DockPopupWindow *QuickSettingContainer::m_popWindow = nullptr;
Dock::Position QuickSettingContainer::m_position = Dock::Position::Bottom;
QuickSettingContainer::QuickSettingContainer(QWidget *parent)
: QWidget(parent)
, m_switchLayout(new QStackedLayout(this))
, m_mainWidget(new QWidget(this))
, m_pluginWidget(new QWidget(m_mainWidget))
, m_pluginLayout(new QGridLayout(m_pluginWidget))
, m_componentWidget(new QWidget(m_mainWidget))
, m_mainlayout(new QVBoxLayout(m_mainWidget))
, m_pluginLoader(QuickSettingController::instance())
, m_childPage(new PluginChildPage(this))
, m_dragInfo(new struct QuickDragInfo)
, m_childShowPlugin(nullptr)
{
initUi();
initConnection();
m_childPage->installEventFilter(this);
setMouseTracking(true);
}
QuickSettingContainer::~QuickSettingContainer()
{
delete m_dragInfo;
}
void QuickSettingContainer::showPage(QWidget *widget, PluginsItemInterface *pluginInter, bool canBack)
{
if (widget && pluginInter && widget != m_mainWidget) {
m_childShowPlugin = pluginInter;
m_childPage->setTitle(pluginInter->pluginDisplayName());
m_childPage->setCanBack(canBack);
m_childPage->pushWidget(widget);
m_switchLayout->setCurrentWidget(m_childPage);
} else {
m_childShowPlugin = nullptr;
m_switchLayout->setCurrentIndex(0);
}
onResizeView();
}
// 根据位置获取箭头的方向
static DArrowRectangle::ArrowDirection getDirection(const Dock::Position &position)
{
switch (position) {
case Dock::Position::Top:
return DArrowRectangle::ArrowDirection::ArrowTop;
case Dock::Position::Left:
return DArrowRectangle::ArrowDirection::ArrowLeft;
case Dock::Position::Right:
return DArrowRectangle::ArrowDirection::ArrowRight;
default:
return DArrowRectangle::ArrowDirection::ArrowBottom;
}
return DArrowRectangle::ArrowDirection::ArrowBottom;
}
DockPopupWindow *QuickSettingContainer::popWindow()
{
if (m_popWindow)
return m_popWindow;
m_popWindow = new DockPopupWindow;
m_popWindow->setShadowBlurRadius(20);
m_popWindow->setRadius(18);
m_popWindow->setShadowYOffset(2);
m_popWindow->setShadowXOffset(0);
m_popWindow->setArrowWidth(18);
m_popWindow->setArrowHeight(10);
m_popWindow->setArrowDirection(getDirection(m_position));
m_popWindow->setContent(new QuickSettingContainer(m_popWindow));
m_popWindow->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
return m_popWindow;
}
void QuickSettingContainer::setPosition(Position position)
{
if (m_position == position)
return;
m_position = position;
if (m_popWindow) {
m_popWindow->setArrowDirection(getDirection(m_position));
// 在任务栏位置发生变化的时候需要将当前的content获取后重新调用setContent接口
// 如果不调用,那么就会出现内容在容器内部的位置错误,界面上的布局会乱
QWidget *widget = m_popWindow->getContent();
m_popWindow->setContent(widget);
}
}
bool QuickSettingContainer::eventFilter(QObject *watched, QEvent *event)
{
switch (event->type()) {
case QEvent::Resize: {
onResizeView();
break;
}
case QEvent::MouseButtonPress: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
QuickSettingItem *item = qobject_cast<QuickSettingItem *>(watched);
if (item) {
m_dragInfo->dragPosition = mouseEvent->pos();
m_dragInfo->dragItem = item;
m_dragInfo->pluginInter = item->pluginItem();
}
break;
}
case QEvent::MouseButtonRelease: {
m_dragInfo->reset();
break;
}
default:
break;
}
return QWidget::eventFilter(watched, event);
}
void QuickSettingContainer::appendPlugin(PluginsItemInterface *itemInter, bool needLayout)
{
QuickSettingItem *quickItem = QuickSettingFactory::createQuickWidget(itemInter);
if (!quickItem)
return;
quickItem->setParent(m_pluginWidget);
quickItem->setMouseTracking(true);
quickItem->installEventFilter(this);
connect(quickItem, &QuickSettingItem::requestShowChildWidget, this, &QuickSettingContainer::onShowChildWidget);
m_quickSettings << quickItem;
if (quickItem->type() == QuickSettingItem::QuickSettingType::Full) {
// 插件位置占据整行,例如声音、亮度和音乐等
m_componentWidget->layout()->addWidget(quickItem);
updateFullItemLayout();
} else if (needLayout) {
// 插件占据两行或者一行
updateItemLayout();
}
onResizeView();
}
void QuickSettingContainer::onPluginRemove(PluginsItemInterface *itemInter)
{
QList<QuickSettingItem *>::Iterator removeItemIter = std::find_if(m_quickSettings.begin(), m_quickSettings.end(), [ = ](QuickSettingItem *item)->bool {
return item->pluginItem() == itemInter;
});
if (removeItemIter == m_quickSettings.end())
return;
QuickSettingItem *removeItem = *removeItemIter;
removeItem->detachPlugin();
if (removeItem->type() == QuickSettingItem::QuickSettingType::Full)
m_componentWidget->layout()->removeWidget(removeItem);
else
m_pluginLayout->removeWidget(removeItem);
m_quickSettings.removeOne(removeItem);
removeItem->deleteLater();
if (m_childShowPlugin == itemInter)
showPage(nullptr);
updateItemLayout();
updateFullItemLayout();
onResizeView();
}
void QuickSettingContainer::onShowChildWidget(QWidget *childWidget)
{
QuickSettingItem *quickWidget = qobject_cast<QuickSettingItem *>(sender());
if (!quickWidget)
return;
showPage(childWidget, quickWidget->pluginItem(), true);
}
void QuickSettingContainer::mouseMoveEvent(QMouseEvent *event)
{
if (m_dragInfo->isNull())
return;
QPoint pointCurrent = event->pos();
if (qAbs(m_dragInfo->dragPosition.x() - pointCurrent.x()) > 5
|| qAbs(m_dragInfo->dragPosition.y() - pointCurrent.y()) > 5) {
QuickSettingItem *moveItem = qobject_cast<QuickSettingItem *>(m_dragInfo->dragItem);
QuickIconDrag *drag = new QuickIconDrag(this, moveItem->dragPixmap());
QuickPluginMimeData *mimedata = new QuickPluginMimeData(m_dragInfo->pluginInter, drag);
drag->setMimeData(mimedata);
drag->setDragHotPot(m_dragInfo->dragPosition);
m_dragInfo->reset();
drag->exec(Qt::CopyAction);
}
}
void QuickSettingContainer::updateItemLayout()
{
// 清空之前的控件,重新添加
while (m_pluginLayout->count() > 0)
m_pluginLayout->takeAt(0);
// 将插件按照两列和一列的顺序来进行排序
QMap<QuickSettingItem::QuickSettingType, QList<QuickSettingItem *>> quickSettings;
QMap<QuickSettingItem::QuickSettingType, QMap<QuickSettingItem *, int>> orderQuickSettings;
QuickSettingController *quickController = QuickSettingController::instance();
for (QuickSettingItem *item : m_quickSettings) {
QuickSettingItem::QuickSettingType type = item->type();
if (type == QuickSettingItem::QuickSettingType::Full)
continue;
QJsonObject metaData = quickController->metaData(item->pluginItem());
if (metaData.contains("order"))
orderQuickSettings[type][item] = metaData.value("order").toInt();
else
quickSettings[type] << item;
}
// 将需要排序的插件按照顺序插入到原来的数组中
for (auto itQuick = orderQuickSettings.begin(); itQuick != orderQuickSettings.end(); itQuick++) {
QuickSettingItem::QuickSettingType type = itQuick.key();
QMap<QuickSettingItem *, int> &orderQuicks = itQuick.value();
for (auto it = orderQuicks.begin(); it != orderQuicks.end(); it++) {
int index = it.value();
if (index >= 0 && index < quickSettings[type].size())
quickSettings[type][index] = it.key();
else
quickSettings[type] << it.key();
}
}
auto insertQuickSetting = [ quickSettings, this ](QuickSettingItem::QuickSettingType type, int &row, int &column) {
if (!quickSettings.contains(type))
return;
int usedColumn = (type == QuickSettingItem::QuickSettingType::Multi ? 2 : 1);
QList<QuickSettingItem *> quickPlugins = quickSettings[type];
for (QuickSettingItem *quickItem : quickPlugins) {
quickItem->setVisible(true);
m_pluginLayout->addWidget(quickItem, row, column, 1, usedColumn);
column += usedColumn;
if (column >= COLUMNCOUNT) {
row++;
column = 0;
}
}
};
int row = 0;
int column = 0;
insertQuickSetting(QuickSettingItem::QuickSettingType::Multi, row, column);
insertQuickSetting(QuickSettingItem::QuickSettingType::Single, row, column);
}
void QuickSettingContainer::updateFullItemLayout()
{
while (m_componentWidget->layout()->count() > 0)
m_componentWidget->layout()->takeAt(0);
QuickSettingController *quickController = QuickSettingController::instance();
QList<QuickSettingItem *> fullItems;
QMap<QuickSettingItem *, int> fullItemOrder;
for (QuickSettingItem *item : m_quickSettings) {
if (item->type() != QuickSettingItem::QuickSettingType::Full)
continue;
fullItems << item;
int order = -1;
QJsonObject metaData = quickController->metaData(item->pluginItem());
if (metaData.contains("order"))
order = metaData.value("order").toInt();
fullItemOrder[item] = order;
}
std::sort(fullItems.begin(), fullItems.end(), [ fullItemOrder ](QuickSettingItem *item1, QuickSettingItem *item2) {
int order1 = fullItemOrder.value(item1, -1);
int order2 = fullItemOrder.value(item2, -1);
if (order1 == order2)
return true;
if (order1 == -1)
return false;
if (order2 == -1)
return true;
return order1 < order2;
});
for (QuickSettingItem *item : fullItems) {
item->setVisible(true);
m_componentWidget->layout()->addWidget(item);
}
}
void QuickSettingContainer::initUi()
{
m_mainlayout->setSpacing(ITEMSPACE);
m_mainlayout->setContentsMargins(ITEMSPACE, ITEMSPACE, ITEMSPACE, ITEMSPACE);
m_pluginLayout->setContentsMargins(0, 0, 0, 0);
m_pluginLayout->setSpacing(ITEMSPACE);
m_pluginLayout->setAlignment(Qt::AlignLeft);
for (int i = 0; i < COLUMNCOUNT; i++)
m_pluginLayout->setColumnMinimumWidth(i, ITEMWIDTH);
m_pluginWidget->setLayout(m_pluginLayout);
m_mainlayout->addWidget(m_pluginWidget);
QVBoxLayout *ctrlLayout = new QVBoxLayout(m_componentWidget);
ctrlLayout->setContentsMargins(0, 0, 0, 0);
ctrlLayout->setSpacing(ITEMSPACE);
ctrlLayout->setDirection(QBoxLayout::BottomToTop);
m_mainlayout->addWidget(m_componentWidget);
// 加载所有的插件
QList<PluginsItemInterface *> plugins = m_pluginLoader->pluginItems(QuickSettingController::PluginAttribute::Quick);
for (PluginsItemInterface *plugin : plugins)
appendPlugin(plugin, false);
m_switchLayout->addWidget(m_mainWidget);
m_switchLayout->addWidget(m_childPage);
setMouseTracking(true);
setAcceptDrops(true);
QMetaObject::invokeMethod(this, [ = ] {
if (plugins.size() > 0) {
updateItemLayout();
updateFullItemLayout();
}
// 设置当前窗口的大小
onResizeView();
setFixedWidth(ITEMWIDTH * 4 + (ITEMSPACE * 5));
}, Qt::QueuedConnection);
}
void QuickSettingContainer::initConnection()
{
connect(m_pluginLoader, &QuickSettingController::pluginInserted, this, [ = ](PluginsItemInterface *itemInter, const QuickSettingController::PluginAttribute pluginAttr) {
if (pluginAttr != QuickSettingController::PluginAttribute::Quick)
return;
appendPlugin(itemInter);
});
connect(m_pluginLoader, &QuickSettingController::pluginRemoved, this, &QuickSettingContainer::onPluginRemove);
connect(m_pluginLoader, &QuickSettingController::pluginUpdated, this, &QuickSettingContainer::onPluginUpdated);
connect(m_childPage, &PluginChildPage::back, this, [ this ] {
showPage(m_mainWidget);
});
connect(m_childPage, &PluginChildPage::closeSelf, this, [ this ] {
if (!m_childPage->isBack())
topLevelWidget()->hide();
});
}
// 调整尺寸
void QuickSettingContainer::onResizeView()
{
if (m_switchLayout->currentWidget() == m_mainWidget) {
int selfPluginCount = 0;
int fullItemHeight = 0;
int widgetCount = 0;
for (QuickSettingItem *item : m_quickSettings) {
if (item->type() == QuickSettingItem::QuickSettingType::Full) {
fullItemHeight += item->height();
widgetCount++;
continue;
}
// 如果是置顶的插件,则认为它占用两个普通插件的位置
int increCount = (item->type() == QuickSettingItem::QuickSettingType::Multi ? 2 : 1);
selfPluginCount += increCount;
}
int rowCount = selfPluginCount / COLUMNCOUNT;
if (selfPluginCount % COLUMNCOUNT > 0)
rowCount++;
m_pluginWidget->setFixedHeight(ITEMHEIGHT * rowCount + ITEMSPACE * (rowCount - 1));
m_componentWidget->setFixedHeight(fullItemHeight + (widgetCount - 1) * ITEMSPACE);
setFixedHeight(ITEMSPACE * 3 + m_pluginWidget->height() + m_componentWidget->height());
} else if (m_switchLayout->currentWidget() == m_childPage) {
setFixedHeight(m_childPage->height());
}
}
void QuickSettingContainer::onPluginUpdated(PluginsItemInterface *itemInter, const DockPart dockPart)
{
if (dockPart != DockPart::QuickPanel)
return;
for (QuickSettingItem *settingItem : m_quickSettings) {
if (settingItem->pluginItem() != itemInter)
continue;
settingItem->updateShow();
break;
}
}