/* * 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 "recentapphelper.h" #include "appitem.h" #include 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 appDockItem = dockItems(false); // 获取应用区域图标在原来列表中的位置 QList dockIndex; for (DockItem *appItem : appDockItem) dockIndex << m_sequentDockItems.indexOf(appItem); // 按照从小到大排序 std::sort(dockIndex.begin(), dockIndex.end(), [ = ](int index1, int index2) { return index1 < index2; }); QMap 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(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(item); return (appItem && !appItem->isDocked()); } void RecentAppHelper::addAppAreaItem(int index, DockItem *wdg) { QBoxLayout *boxLayout = static_cast(m_appWidget->layout()); boxLayout->insertWidget(index, wdg); } void RecentAppHelper::addRecentAreaItem(int index, DockItem *wdg) { QBoxLayout *recentLayout = static_cast(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(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(m_recentWidget->layout()); recentLayout->removeWidget(wdg); updateRecentVisible(); } void RecentAppHelper::removeAppAreaItem(DockItem *wdg) { QBoxLayout *boxLayout = static_cast(m_appWidget->layout()); boxLayout->removeWidget(wdg); } QList RecentAppHelper::dockItemToAppArea() const { QList dockItems; if (m_dislayMode == Dock::DisplayMode::Efficient) { // 由特效模式变成高效模式,将所有的最近打开的应用移动到左侧的应用区域 for (int i = 0; i < m_recentWidget->layout()->count(); i++) { DockItem *appItem = qobject_cast(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(m_recentWidget->layout()->itemAt(i)->widget()); if (!appItem || appInRecent(appItem)) continue; dockItems << appItem; } } return dockItems; } void RecentAppHelper::resetDockItems() { // 先将所有的最近打开的区域移动到应用区域 QList recentAppItems = dockItemToAppArea(); // 从最近使用应用中移除 for (DockItem *appItem : recentAppItems) m_recentWidget->layout()->removeWidget(appItem); // 将这些图标添加到应用区域 QBoxLayout *boxLayout = static_cast(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 unDockItems; for (int i = 0; i < m_appWidget->layout()->count() ; i++) { DockItem *appItem = qobject_cast(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(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 sequeDockItems = m_sequentDockItems; if (isRecent) { // 如果是最近打开区域,需要按照时间从小到大排列(先打开的排在前面) std::sort(sequeDockItems.begin(), sequeDockItems.end(), [](DockItem *item1, DockItem *item2) { AppItem *appItem1 = qobject_cast(item1); AppItem *appItem2 = qobject_cast(item2); if (!appItem1 || !appItem2) return false; return appItem1->appOpenMSecs() < appItem2->appOpenMSecs(); }); } int index = sequeDockItems.indexOf(dockItem); // 查找所有在应用区域的图标 QList 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 RecentAppHelper::dockItems(bool isRecent) const { QLayout *layout = nullptr; if (isRecent) layout = m_recentWidget->layout(); else layout = m_appWidget->layout(); QList dockItems; for (int i = 0; i < layout->count(); i++) { DockItem *dockItem = qobject_cast(layout->itemAt(i)->widget()); if (!dockItem) continue; dockItems << dockItem; } return dockItems; }