dde-dock/frame/window/quicksettingcontainer.cpp

485 lines
16 KiB
C++
Raw Normal View History

/*
* 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 <DListView>
#include <DStyle>
#include <QDrag>
#include <QVBoxLayout>
#include <QMetaObject>
#include <QStackedLayout>
#include <QMouseEvent>
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));
if (Utils::IS_WAYLAND_DISPLAY)
m_popWindow->setWindowFlags(m_popWindow->windowFlags() | Qt::FramelessWindowHint);
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);
}
QPoint QuickSettingContainer::hotSpot(const QPixmap &pixmap)
{
if (m_position == Dock::Position::Left)
return QPoint(0, pixmap.height());
if (m_position == Dock::Position::Top)
return QPoint(pixmap.width(), 0);
return QPoint(pixmap.width(), pixmap.height());
}
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) {
QDrag *drag = new QDrag(this);
QuickSettingItem *moveItem = qobject_cast<QuickSettingItem *>(m_dragInfo->dragItem);
QuickPluginMimeData *mimedata = new QuickPluginMimeData(m_dragInfo->pluginInter);
drag->setMimeData(mimedata);
if (moveItem) {
QPixmap dragPixmap = moveItem->dragPixmap();
drag->setPixmap(dragPixmap);
drag->setHotSpot(hotSpot(dragPixmap));
} else {
// 如果拖动的是声音等插件
QPixmap dragPixmap = m_dragInfo->dragItem->grab();
drag->setPixmap(dragPixmap);
drag->setHotSpot(hotSpot(dragPixmap));
}
m_dragInfo->reset();
drag->exec(Qt::MoveAction | 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;
}
}