dde-dock/plugins/tray/fashiontrayitem.cpp
listenerri 091b52bccd fix: dock hide problem and remove old imp
efficient dock still hide in auto/smart hide mode while applet of
plugins is showing and mouse leaved

https://github.com/linuxdeepin/developer-center/issues/707

Change-Id: I373ce94e802f79e1430a636d5019b24fdbaa650b
2018-12-04 09:28:12 +08:00

669 lines
21 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) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: listenerri <listenerri@gmail.com>
*
* Maintainer: listenerri <listenerri@gmail.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 "fashiontrayitem.h"
#include "system-trays/systemtrayitem.h"
#include <QDebug>
#include <QResizeEvent>
#define SpliterSize 2
#define TraySpace 10
#define TrayWidgetWidthMin 24
#define TrayWidgetHeightMin 24
#define ExpandedKey "fashion-tray-expanded"
int FashionTrayItem::TrayWidgetWidth = TrayWidgetWidthMin;
int FashionTrayItem::TrayWidgetHeight = TrayWidgetHeightMin;
FashionTrayItem::FashionTrayItem(TrayPlugin *trayPlugin, QWidget *parent)
: QWidget(parent),
m_mainBoxLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight)),
m_trayBoxLayout(new QBoxLayout(QBoxLayout::Direction::LeftToRight)),
m_leftSpliter(new QLabel),
m_rightSpliter(new QLabel),
m_attentionDelayTimer(new QTimer(this)),
m_dockPosistion(trayPlugin->dockPosition()),
m_trayPlugin(trayPlugin),
m_controlWidget(new FashionTrayControlWidget(m_dockPosistion)),
m_currentAttentionTray(nullptr),
m_currentDraggingTray(nullptr)
{
setAcceptDrops(true);
m_leftSpliter->setStyleSheet("background-color: rgba(255, 255, 255, 0.1);");
m_rightSpliter->setStyleSheet("background-color: rgba(255, 255, 255, 0.1);");
m_controlWidget->setFixedSize(QSize(TrayWidgetWidth, TrayWidgetHeight));
m_mainBoxLayout->setMargin(0);
m_mainBoxLayout->setContentsMargins(0, 0, 0, 0);
m_mainBoxLayout->setSpacing(TraySpace);
m_trayBoxLayout->setMargin(0);
m_trayBoxLayout->setContentsMargins(0, 0, 0, 0);
m_trayBoxLayout->setSpacing(TraySpace);
m_mainBoxLayout->addWidget(m_leftSpliter);
m_mainBoxLayout->addLayout(m_trayBoxLayout);
m_mainBoxLayout->addWidget(m_controlWidget);
m_mainBoxLayout->addWidget(m_rightSpliter);
m_mainBoxLayout->setAlignment(Qt::AlignCenter);
m_trayBoxLayout->setAlignment(Qt::AlignCenter);
m_mainBoxLayout->setAlignment(m_leftSpliter, Qt::AlignCenter);
m_mainBoxLayout->setAlignment(m_controlWidget, Qt::AlignCenter);
m_mainBoxLayout->setAlignment(m_rightSpliter, Qt::AlignCenter);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setLayout(m_mainBoxLayout);
m_attentionDelayTimer->setInterval(3000);
m_attentionDelayTimer->setSingleShot(true);
connect(m_controlWidget, &FashionTrayControlWidget::expandChanged, this, &FashionTrayItem::onTrayListExpandChanged);
// do not call init immediately the TrayPlugin has not be constructed for now
QMetaObject::invokeMethod(this, &FashionTrayItem::init, Qt::QueuedConnection);
}
void FashionTrayItem::setTrayWidgets(const QMap<QString, AbstractTrayWidget *> &itemTrayMap)
{
clearTrayWidgets();
for (auto it = itemTrayMap.constBegin(); it != itemTrayMap.constEnd(); ++it) {
trayWidgetAdded(it.key(), it.value());
}
}
void FashionTrayItem::trayWidgetAdded(const QString &itemKey, AbstractTrayWidget *trayWidget)
{
for (auto w : m_wrapperList) {
if (w->absTrayWidget() == trayWidget) {
qDebug() << "Reject! want to isert duplicate trayWidget:" << itemKey << trayWidget;
return;
}
}
FashionTrayWidgetWrapper *wrapper = new FashionTrayWidgetWrapper(itemKey, trayWidget);
wrapper->setFixedSize(QSize(TrayWidgetWidth, TrayWidgetHeight));
const int index = whereToInsert(wrapper);
m_trayBoxLayout->insertWidget(index, wrapper);
m_wrapperList.insert(index, wrapper);
wrapper->setVisible(m_controlWidget->expanded());
if (wrapper->attention()) {
setCurrentAttentionTray(wrapper);
}
connect(wrapper, &FashionTrayWidgetWrapper::attentionChanged, this, &FashionTrayItem::onTrayAttentionChanged, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
connect(wrapper, &FashionTrayWidgetWrapper::dragStart, this, &FashionTrayItem::onItemDragStart, Qt::UniqueConnection);
connect(wrapper, &FashionTrayWidgetWrapper::dragStop, this, &FashionTrayItem::onItemDragStop, Qt::UniqueConnection);
connect(wrapper, &FashionTrayWidgetWrapper::requestSwapWithDragging, this, &FashionTrayItem::onItemRequestSwapWithDragging, Qt::UniqueConnection);
requestResize();
}
void FashionTrayItem::trayWidgetRemoved(AbstractTrayWidget *trayWidget)
{
bool founded = false;
for (auto wrapper : m_wrapperList) {
// found the removed tray
if (wrapper->absTrayWidget() == trayWidget) {
// the removed tray is a attention tray
if (m_currentAttentionTray == wrapper) {
if (m_controlWidget->expanded()) {
m_trayBoxLayout->removeWidget(m_currentAttentionTray);
} else {
m_mainBoxLayout->removeWidget(m_currentAttentionTray);
}
m_currentAttentionTray = nullptr;
} else {
m_trayBoxLayout->removeWidget(wrapper);
}
// do not delete real tray object, just delete it's wrapper object
// the real tray object should be deleted in TrayPlugin class
trayWidget->setParent(nullptr);
wrapper->deleteLater();
m_wrapperList.removeAll(wrapper);
founded = true;
break;
}
}
if (!founded) {
qDebug() << "Error! can not find the tray widget in fashion tray list" << trayWidget;
}
requestResize();
}
void FashionTrayItem::clearTrayWidgets()
{
QList<QPointer<FashionTrayWidgetWrapper>> mList = m_wrapperList;
for (auto wrapper : mList) {
trayWidgetRemoved(wrapper->absTrayWidget());
}
m_wrapperList.clear();
requestResize();
}
void FashionTrayItem::setDockPostion(Dock::Position pos)
{
m_dockPosistion = pos;
m_controlWidget->setDockPostion(m_dockPosistion);
SystemTrayItem::setDockPostion(m_dockPosistion);
if (pos == Dock::Position::Top || pos == Dock::Position::Bottom) {
m_mainBoxLayout->setDirection(QBoxLayout::Direction::LeftToRight);
m_trayBoxLayout->setDirection(QBoxLayout::Direction::LeftToRight);
} else{
m_mainBoxLayout->setDirection(QBoxLayout::Direction::TopToBottom);
m_trayBoxLayout->setDirection(QBoxLayout::Direction::TopToBottom);
}
requestResize();
}
void FashionTrayItem::onTrayListExpandChanged(const bool expand)
{
m_trayPlugin->saveValue(ExpandedKey, expand);
if (!isVisible())
return;
if (expand) {
refreshTraysVisible();
} else {
// hide all tray immediately if Dock is in maxed size
// the property "DockIsMaxiedSize" of qApp is set by DockSettings class
if (qApp->property("DockIsMaxiedSize").toBool()) {
refreshTraysVisible();
} else {
// hide all tray widget delay for fold animation
QTimer::singleShot(350, this, [=] {refreshTraysVisible();});
}
requestResize();
}
}
// used by QMetaObject::invokeMethod in TrayPluginItem / MainPanel class
void FashionTrayItem::setSuggestIconSize(QSize size)
{
size = size * 0.6;
int length = qMin(size.width(), size.height());
// 设置最小值
// length = qMax(length, TrayWidgetWidthMin);
if (length == TrayWidgetWidth || length == TrayWidgetHeight) {
return;
}
TrayWidgetWidth = length;
TrayWidgetHeight = length;
QSize newSize(length, length);
m_controlWidget->setFixedSize(newSize);
for (auto wrapper : m_wrapperList) {
wrapper->setFixedSize(newSize);
}
requestResize();
}
void FashionTrayItem::setRightSplitVisible(const bool visible)
{
if (visible) {
m_rightSpliter->setStyleSheet("background-color: rgba(255, 255, 255, 0.1);");
} else {
m_rightSpliter->setStyleSheet("background-color: transparent;");
}
}
void FashionTrayItem::showEvent(QShowEvent *event)
{
requestResize();
QWidget::showEvent(event);
}
void FashionTrayItem::hideEvent(QHideEvent *event)
{
requestResize();
QWidget::hideEvent(event);
}
void FashionTrayItem::resizeEvent(QResizeEvent *event)
{
const QSize &mSize = event->size();
if (m_dockPosistion == Dock::Position::Top || m_dockPosistion == Dock::Position::Bottom) {
m_leftSpliter->setFixedSize(SpliterSize, mSize.height() * 0.8);
m_rightSpliter->setFixedSize(SpliterSize, mSize.height() * 0.8);
} else{
m_leftSpliter->setFixedSize(mSize.width() * 0.8, SpliterSize);
m_rightSpliter->setFixedSize(mSize.width() * 0.8, SpliterSize);
}
QWidget::resizeEvent(event);
}
void FashionTrayItem::dragEnterEvent(QDragEnterEvent *event)
{
// accept but do not handle the trays drag event
// in order to avoid the for forbidden label displayed on the mouse
if (event->mimeData()->hasFormat(TRAY_ITEM_DRAG_MIMEDATA)) {
event->accept();
return;
}
QWidget::dragEnterEvent(event);
}
QSize FashionTrayItem::sizeHint() const
{
return wantedTotalSize();
}
void FashionTrayItem::init()
{
qDebug() << "init Fashion mode tray plugin item";
m_controlWidget->setExpanded(m_trayPlugin->getValue(ExpandedKey, true).toBool());
setDockPostion(m_dockPosistion);
onTrayListExpandChanged(m_controlWidget->expanded());
}
QSize FashionTrayItem::wantedTotalSize() const
{
QSize size;
if (m_controlWidget->expanded()) {
if (m_dockPosistion == Dock::Position::Top || m_dockPosistion == Dock::Position::Bottom) {
size.setWidth(m_wrapperList.size() * TrayWidgetWidth // 所有插件
+ TrayWidgetWidth // 控制按钮
+ SpliterSize * 2 // 两个分隔条
+ 3 * TraySpace // MainBoxLayout所有space
+ (m_wrapperList.size() - 1) * TraySpace); // TrayBoxLayout所有space
size.setHeight(height());
} else {
size.setWidth(width());
size.setHeight(m_wrapperList.size() * TrayWidgetHeight // 所有插件
+ TrayWidgetHeight // 控制按钮
+ SpliterSize * 2 // 两个分隔条
+ 3 * TraySpace // MainBoxLayout所有space
+ (m_wrapperList.size() - 1) * TraySpace); // TrayBoxLayout所有space
}
} else {
if (m_dockPosistion == Dock::Position::Top || m_dockPosistion == Dock::Position::Bottom) {
size.setWidth(TrayWidgetWidth // 控制按钮
+ (m_currentAttentionTray ? TrayWidgetWidth : 0) // 活动状态的tray
+ SpliterSize * 2 // 两个分隔条
+ 3 * TraySpace); // MainBoxLayout所有space
size.setHeight(height());
} else {
size.setWidth(width());
size.setHeight(TrayWidgetHeight // 控制按钮
+ (m_currentAttentionTray ? TrayWidgetHeight : 0) // 活动状态的tray
+ SpliterSize * 2 // 两个分隔条
+ 3 * TraySpace); // MainBoxLayout所有space
}
}
return size;
}
int FashionTrayItem::whereToInsert(FashionTrayWidgetWrapper *wrapper) const
{
// 如果已经对图标进行过排序则完全按照从配置文件中获取的顺序来插入图标
if (m_trayPlugin->traysSortedInFashionMode()) {
return whereToInsertBySortKey(wrapper);
}
// 如果没有对图标进行过排序则使用下面的默认排序算法:
// 所有应用图标在系统图标的左侧
// 新的应用图标在最左侧的应用图标处插入
// 新的系统图标在最左侧的系统图标处插入
return whereToInsertByDefault(wrapper);
}
int FashionTrayItem::whereToInsertBySortKey(FashionTrayWidgetWrapper *wrapper) const
{
if (m_wrapperList.isEmpty()) {
return 0;
}
const int destSortKey = m_trayPlugin->itemSortKey(wrapper->itemKey());
if (destSortKey < -1) {
return 0;
}
if (destSortKey == -1) {
return m_wrapperList.size();
}
// 当目标插入位置为列表的大小时将从最后面追加到列表中
int destIndex = m_wrapperList.size();
for (int i = 0; i < m_wrapperList.size(); ++i) {
if (destSortKey > m_trayPlugin->itemSortKey(m_wrapperList.at(i)->itemKey())) {
continue;
}
destIndex = i;
break;
}
return destIndex;
}
int FashionTrayItem::whereToInsertByDefault(FashionTrayWidgetWrapper *wrapper) const
{
int index = 0;
switch (wrapper->absTrayWidget()->trayTyep()) {
case AbstractTrayWidget::TrayType::ApplicationTray:
index = whereToInsertAppTrayByDefault(wrapper);
break;
case AbstractTrayWidget::TrayType::SystemTray:
index = whereToInsertSystemTrayByDefault(wrapper);
break;
default:
Q_UNREACHABLE();
break;
}
return index;
}
int FashionTrayItem::whereToInsertAppTrayByDefault(FashionTrayWidgetWrapper *wrapper) const
{
if (m_wrapperList.isEmpty() || wrapper->absTrayWidget()->trayTyep() != AbstractTrayWidget::TrayType::ApplicationTray) {
return 0;
}
int lastAppTrayIndex = -1;
for (int i = 0; i < m_wrapperList.size(); ++i) {
if (m_wrapperList.at(i)->absTrayWidget()->trayTyep() == AbstractTrayWidget::TrayType::ApplicationTray) {
lastAppTrayIndex = i;
continue;
}
break;
}
// there is no AppTray
if (lastAppTrayIndex == -1) {
return 0;
}
// the inserting tray is not a AppTray
if (wrapper->absTrayWidget()->trayTyep() != AbstractTrayWidget::TrayType::ApplicationTray) {
return lastAppTrayIndex + 1;
}
int insertIndex = m_trayPlugin->itemSortKey(wrapper->itemKey());
// invalid index
if (insertIndex < -1) {
return 0;
}
for (int i = 0; i < m_wrapperList.size(); ++i) {
if (m_wrapperList.at(i)->absTrayWidget()->trayTyep() != AbstractTrayWidget::TrayType::ApplicationTray) {
insertIndex = i;
break;
}
if (insertIndex > m_trayPlugin->itemSortKey(m_wrapperList.at(i)->itemKey())) {
continue;
}
insertIndex = i;
break;
}
if (insertIndex > lastAppTrayIndex + 1) {
insertIndex = lastAppTrayIndex + 1;
}
return insertIndex;
}
int FashionTrayItem::whereToInsertSystemTrayByDefault(FashionTrayWidgetWrapper *wrapper) const
{
if (m_wrapperList.isEmpty()) {
return 0;
}
int firstSystemTrayIndex = -1;
for (int i = 0; i < m_wrapperList.size(); ++i) {
if (m_wrapperList.at(i)->absTrayWidget()->trayTyep() == AbstractTrayWidget::TrayType::SystemTray) {
firstSystemTrayIndex = i;
break;
}
}
// there is no SystemTray
if (firstSystemTrayIndex == -1) {
return m_wrapperList.size();
}
// the inserting tray is not a SystemTray
if (wrapper->absTrayWidget()->trayTyep() != AbstractTrayWidget::TrayType::SystemTray) {
return firstSystemTrayIndex;
}
int insertIndex = m_trayPlugin->itemSortKey(wrapper->itemKey());
// invalid index
if (insertIndex < -1) {
return firstSystemTrayIndex;
}
for (int i = 0; i < m_wrapperList.size(); ++i) {
if (m_wrapperList.at(i)->absTrayWidget()->trayTyep() != AbstractTrayWidget::TrayType::SystemTray) {
continue;
}
if (insertIndex > m_trayPlugin->itemSortKey(m_wrapperList.at(i)->itemKey())) {
continue;
}
insertIndex = i;
break;
}
if (insertIndex < firstSystemTrayIndex) {
return firstSystemTrayIndex;
}
return insertIndex;
}
void FashionTrayItem::saveCurrentOrderToConfig()
{
for (int i = 0; i < m_wrapperList.size(); ++i) {
m_trayPlugin->setSortKey(m_wrapperList.at(i)->itemKey(), i + 1);
}
}
void FashionTrayItem::onTrayAttentionChanged(const bool attention)
{
// 设置attention为false之后启动timer在timer处于Active状态期间不重设attention为true
if (!attention) {
m_attentionDelayTimer->start();
} else if (attention && m_attentionDelayTimer->isActive()) {
return;
}
FashionTrayWidgetWrapper *wrapper = static_cast<FashionTrayWidgetWrapper *>(sender());
Q_ASSERT(wrapper);
if (attention) {
setCurrentAttentionTray(wrapper);
} else {
if (m_currentAttentionTray != wrapper) {
return;
}
if (m_controlWidget->expanded()) {
m_currentAttentionTray = nullptr;
} else {
moveInAttionTray();
m_currentAttentionTray = nullptr;
requestResize();
}
}
}
void FashionTrayItem::setCurrentAttentionTray(FashionTrayWidgetWrapper *attentionWrapper)
{
if (!attentionWrapper) {
return;
}
if (m_controlWidget->expanded()) {
m_currentAttentionTray = attentionWrapper;
} else {
if (m_currentAttentionTray == attentionWrapper) {
return;
}
moveInAttionTray();
bool sizeChanged = !m_currentAttentionTray;
m_currentAttentionTray = attentionWrapper;
moveOutAttionTray();
if (sizeChanged) {
requestResize();
}
}
m_mainBoxLayout->setAlignment(m_currentAttentionTray, Qt::AlignCenter);
}
void FashionTrayItem::requestResize()
{
// reset property "FashionTraySize" to notify dock resize
// DockPluginsController will watch this property
setProperty("FashionTraySize", sizeHint());
}
void FashionTrayItem::moveOutAttionTray()
{
if (!m_currentAttentionTray) {
return;
}
m_trayBoxLayout->removeWidget(m_currentAttentionTray);
m_mainBoxLayout->insertWidget(m_mainBoxLayout->indexOf(m_rightSpliter), m_currentAttentionTray);
m_currentAttentionTray->setVisible(true);
}
void FashionTrayItem::moveInAttionTray()
{
if (!m_currentAttentionTray) {
return;
}
m_mainBoxLayout->removeWidget(m_currentAttentionTray);
m_trayBoxLayout->insertWidget(whereToInsert(m_currentAttentionTray), m_currentAttentionTray);
m_currentAttentionTray->setVisible(false);
m_currentAttentionTray->setAttention(false);
}
void FashionTrayItem::switchAttionTray(FashionTrayWidgetWrapper *attentionWrapper)
{
if (!m_currentAttentionTray || !attentionWrapper) {
return;
}
m_mainBoxLayout->replaceWidget(m_currentAttentionTray, attentionWrapper);
m_trayBoxLayout->removeWidget(attentionWrapper);
m_trayBoxLayout->insertWidget(whereToInsert(m_currentAttentionTray), m_currentAttentionTray);
attentionWrapper->setVisible(true);
m_currentAttentionTray->setVisible(m_controlWidget->expanded());
m_currentAttentionTray = attentionWrapper;
}
void FashionTrayItem::refreshTraysVisible()
{
const bool expand = m_controlWidget->expanded();
if (m_currentAttentionTray) {
if (expand) {
m_mainBoxLayout->removeWidget(m_currentAttentionTray);
m_trayBoxLayout->insertWidget(whereToInsert(m_currentAttentionTray), m_currentAttentionTray);
}
m_currentAttentionTray = nullptr;
}
for (auto wrapper : m_wrapperList) {
wrapper->setVisible(expand);
// reset all tray item attention state
wrapper->setAttention(false);
}
m_attentionDelayTimer->start();
requestResize();
}
void FashionTrayItem::onItemDragStart()
{
FashionTrayWidgetWrapper *wrapper = static_cast<FashionTrayWidgetWrapper *>(sender());
if (!wrapper) {
return;
}
m_currentDraggingTray = wrapper;
}
void FashionTrayItem::onItemDragStop()
{
FashionTrayWidgetWrapper *wrapper = static_cast<FashionTrayWidgetWrapper *>(sender());
if (!wrapper) {
return;
}
if (m_currentDraggingTray == wrapper) {
m_currentDraggingTray = nullptr;
} else {
Q_UNREACHABLE();
}
saveCurrentOrderToConfig();
}
void FashionTrayItem::onItemRequestSwapWithDragging()
{
FashionTrayWidgetWrapper *wrapper = static_cast<FashionTrayWidgetWrapper *>(sender());
if (!wrapper || !m_currentDraggingTray || wrapper == m_currentDraggingTray) {
return;
}
const int indexOfDest = m_trayBoxLayout->indexOf(wrapper);
const int indexOfDragging = m_trayBoxLayout->indexOf(m_currentDraggingTray);
m_trayBoxLayout->removeWidget(m_currentDraggingTray);
m_trayBoxLayout->insertWidget(indexOfDest, m_currentDraggingTray);
m_wrapperList.insert(indexOfDest, m_wrapperList.takeAt(indexOfDragging));
}