dde-dock/frame/controller/recentapphelper.cpp

319 lines
11 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 "recentapphelper.h"
#include "appitem.h"
#include <QWidget>
RecentAppHelper::RecentAppHelper(QWidget *appWidget, QWidget *recentWidget, QObject *parent)
: QObject(parent)
, m_appWidget(appWidget)
, m_recentWidget(recentWidget)
{
m_appWidget->installEventFilter(this);
m_recentWidget->installEventFilter(this);
}
void RecentAppHelper::setDisplayMode(Dock::DisplayMode displayMode)
{
m_dislayMode = displayMode;
resetDockItems();
updateRecentVisible();
}
// 当在应用区域调整位置的时候,需要重新设置索引
void RecentAppHelper::resetAppInfo()
{
// 获取应用区域和最近打开区域的app图标
QList<DockItem *> appDockItem = dockItems(false);
// 获取应用区域图标在原来列表中的位置
QList<int> dockIndex;
for (DockItem *appItem : appDockItem)
dockIndex << m_sequentDockItems.indexOf(appItem);
// 按照从小到大排序
std::sort(dockIndex.begin(), dockIndex.end(), [ = ](int index1, int index2) { return index1 < index2; });
QMap<DockItem *, int> dockItemIndex;
for (int i = 0; i < appDockItem.size(); i++) {
DockItem *item = appDockItem[i];
dockItemIndex[item] = dockIndex[i];
}
// 替换原来的位置
for (DockItem *appItem : appDockItem) {
int index = -1;
if (dockItemIndex.contains(appItem))
index = dockItemIndex.value(appItem);
if (index >= 0)
m_sequentDockItems[index] = appItem;
else
m_sequentDockItems << appItem;
}
}
void RecentAppHelper::addAppItem(int index, DockItem *dockItem)
{
if (appInRecent(dockItem))
addRecentAreaItem(index, dockItem);
else
addAppAreaItem(index, dockItem);
updateRecentVisible();
connect(dockItem, &QWidget::destroyed, this, [ this, dockItem ] {
if (m_sequentDockItems.contains(dockItem))
m_sequentDockItems.removeOne(dockItem);
});
AppItem *appItem = qobject_cast<AppItem *>(dockItem);
connect(appItem, &AppItem::isDockChanged, this, &RecentAppHelper::onIsDockChanged);
// 如果索引值大于0说明它是插入到固定位置的否则则认为它是顺序排列的
if (index >= 0 && index < m_sequentDockItems.size())
m_sequentDockItems.insert(index, dockItem);
else
m_sequentDockItems << dockItem;
}
void RecentAppHelper::removeAppItem(DockItem *appItem)
{
if (appInRecent(appItem))
removeRecentAreaItem(appItem);
else
removeAppAreaItem(appItem);
}
bool RecentAppHelper::recentIsVisible() const
{
return m_recentWidget->isVisible();
}
bool RecentAppHelper::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_appWidget || watched == m_recentWidget) {
switch(event->type()) {
case QEvent::ChildAdded:
case QEvent::ChildRemoved: {
QMetaObject::invokeMethod(this, [ this ] {
/* 这里用异步的方式因为收到QEvent::ChildAdded信号的时候
Widget中QEvent::ChildRemoved信号的时候
*/
Q_EMIT requestUpdate();
}, Qt::QueuedConnection);
}
break;
default:
break;
}
}
return QObject::eventFilter(watched, event);
}
void RecentAppHelper::onIsDockChanged()
{
resetDockItems();
updateRecentVisible();
}
bool RecentAppHelper::appInRecent(DockItem *item) const
{
// 先判断当前是否为时尚模式,只有时尚模式下才支持最近打开的应用
if (m_dislayMode != Dock::DisplayMode::Fashion)
return false;
// TODO 当控制中心不开启最近打开应用的功能的时候,则始终让其显示在应用区域
// 只有当应用没有固定到任务栏上才认为它是最新打开的应用
AppItem *appItem = qobject_cast<AppItem *>(item);
return (appItem && !appItem->isDocked());
}
void RecentAppHelper::addAppAreaItem(int index, DockItem *wdg)
{
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(m_appWidget->layout());
boxLayout->insertWidget(index, wdg);
}
void RecentAppHelper::addRecentAreaItem(int index, DockItem *wdg)
{
QBoxLayout *recentLayout = static_cast<QBoxLayout *>(m_recentWidget->layout());
recentLayout->insertWidget(index, wdg);
}
void RecentAppHelper::updateRecentVisible()
{
bool oldVisible = recentIsVisible();
if (m_dislayMode == Dock::DisplayMode::Efficient) {
// 如果是高效模式,不显示最近打开应用区域
m_recentWidget->setVisible(false);
} else {
QBoxLayout *recentLayout = static_cast<QBoxLayout *>(m_recentWidget->layout());
qInfo() << "recent Widget count:" << recentLayout->count();
// 如果是特效模式则判断当前打开应用数量是否为0为0则不显示否则显示
m_recentWidget->setVisible(recentLayout->count() > 0);
}
bool recentVisible = recentIsVisible();
if (oldVisible != recentVisible)
Q_EMIT recentVisibleChanged(recentVisible);
}
void RecentAppHelper::removeRecentAreaItem(DockItem *wdg)
{
QBoxLayout *recentLayout = static_cast<QBoxLayout *>(m_recentWidget->layout());
recentLayout->removeWidget(wdg);
updateRecentVisible();
}
void RecentAppHelper::removeAppAreaItem(DockItem *wdg)
{
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(m_appWidget->layout());
boxLayout->removeWidget(wdg);
}
QList<DockItem *> RecentAppHelper::dockItemToAppArea() const
{
QList<DockItem *> dockItems;
if (m_dislayMode == Dock::DisplayMode::Efficient) {
// 由特效模式变成高效模式,将所有的最近打开的应用移动到左侧的应用区域
for (int i = 0; i < m_recentWidget->layout()->count(); i++) {
DockItem *appItem = qobject_cast<DockItem *>(m_recentWidget->layout()->itemAt(i)->widget());
if (!appItem)
continue;
dockItems << appItem;
}
} else {
// 如果是特效模式下,则查找所有已驻留的应用,将其移动到应用区域
for (int i = 0; i < m_recentWidget->layout()->count(); i++) {
DockItem *appItem = qobject_cast<DockItem *>(m_recentWidget->layout()->itemAt(i)->widget());
if (!appItem || appInRecent(appItem))
continue;
dockItems << appItem;
}
}
return dockItems;
}
void RecentAppHelper::resetDockItems()
{
// 先将所有的最近打开的区域移动到应用区域
QList<DockItem *> recentAppItems = dockItemToAppArea();
// 从最近使用应用中移除
for (DockItem *appItem : recentAppItems)
m_recentWidget->layout()->removeWidget(appItem);
// 将这些图标添加到应用区域
QBoxLayout *boxLayout = static_cast<QBoxLayout *>(m_appWidget->layout());
for (DockItem *appItem : recentAppItems) {
int index = getDockItemIndex(appItem, false);
if (index >= 0)
boxLayout->insertWidget(index, appItem);
else
boxLayout->addWidget(appItem);
}
if (m_dislayMode == Dock::DisplayMode::Fashion) {
// 由高效模式变成特效模式后,将左侧未驻留的应用移动到右侧的最近打开应用中
QList<DockItem *> unDockItems;
for (int i = 0; i < m_appWidget->layout()->count() ; i++) {
DockItem *appItem = qobject_cast<DockItem *>(m_appWidget->layout()->itemAt(i)->widget());
if (!appInRecent(appItem))
continue;
unDockItems << appItem;
}
// 从应用区域中删除未驻留的应用
for (DockItem *appItem : unDockItems)
m_appWidget->layout()->removeWidget(appItem);
// 将这些图标添加到最近打开区域
QBoxLayout *recentLayout = static_cast<QBoxLayout *>(m_recentWidget->layout());
for (DockItem *appItem : unDockItems) {
int index = getDockItemIndex(appItem, true);
if (index >= 0)
recentLayout->insertWidget(index, appItem);
else
recentLayout->addWidget(appItem);
}
}
}
int RecentAppHelper::getDockItemIndex(DockItem *dockItem, bool isRecent) const
{
// 当从最近区域移动到应用区域的时候,重新计算插入索引值
if (!m_sequentDockItems.contains(dockItem))
return -1;
QList<DockItem *> sequeDockItems = m_sequentDockItems;
if (isRecent) {
// 如果是最近打开区域,需要按照时间从小到大排列(先打开的排在前面)
std::sort(sequeDockItems.begin(), sequeDockItems.end(), [](DockItem *item1, DockItem *item2) {
AppItem *appItem1 = qobject_cast<AppItem *>(item1);
AppItem *appItem2 = qobject_cast<AppItem *>(item2);
if (!appItem1 || !appItem2)
return false;
return appItem1->appOpenMSecs() < appItem2->appOpenMSecs();
});
}
int index = sequeDockItems.indexOf(dockItem);
// 查找所有在应用区域的图标
QList<DockItem *> dockApps = dockItems(isRecent);
for (int i = index + 1; i < sequeDockItems.size(); i++) {
DockItem *item = sequeDockItems[i];
if (dockApps.contains(item))
return dockApps.indexOf(item);
}
return -1;
}
QList<DockItem *> RecentAppHelper::dockItems(bool isRecent) const
{
QLayout *layout = nullptr;
if (isRecent)
layout = m_recentWidget->layout();
else
layout = m_appWidget->layout();
QList<DockItem *> dockItems;
for (int i = 0; i < layout->count(); i++) {
DockItem *dockItem = qobject_cast<DockItem *>(layout->itemAt(i)->widget());
if (!dockItem)
continue;
dockItems << dockItem;
}
return dockItems;
}