dde-dock/frame/controller/recentapphelper.cpp
donghualin a4172e6763 feat: 增加最近使用应用的功能
在MainPanelControl面板中增加一块区域,用来存放最近打开应用

Log: 增加最近使用应用功能
Influence: 控制中心最近使用区域开启情况下,时尚模式,打开一个没有驻留在任务栏的应用,查看新打开的应用是否在最近打开应用区域
Task: https://pms.uniontech.com/task-view-158441.html
Change-Id: Ibd28d16b08fcacf7ace23158725dd5dd2eae8d37
2022-07-08 09:06:12 +00:00

319 lines
11 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 "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;
}