/* * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. * * Author: donghualin * * Maintainer: donghualin * * 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 . */ #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 #include #include #include #include #include #include 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(event); QuickSettingItem *item = qobject_cast(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::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(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(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> quickSettings; QMap> 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 &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 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 fullItems; QMap 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 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; } }