From a4172e676320dc5e2418393179e276756e24efb5 Mon Sep 17 00:00:00 2001 From: donghualin Date: Fri, 8 Jul 2022 09:05:54 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9C=80=E8=BF=91?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=BA=94=E7=94=A8=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在MainPanelControl面板中增加一块区域,用来存放最近打开应用 Log: 增加最近使用应用功能 Influence: 控制中心最近使用区域开启情况下,时尚模式,打开一个没有驻留在任务栏的应用,查看新打开的应用是否在最近打开应用区域 Task: https://pms.uniontech.com/task-view-158441.html Change-Id: Ibd28d16b08fcacf7ace23158725dd5dd2eae8d37 --- frame/controller/recentapphelper.cpp | 318 +++++++++++++++++++++++++++ frame/controller/recentapphelper.h | 82 +++++++ frame/item/appitem.cpp | 21 ++ frame/item/appitem.h | 5 + frame/item/dockitem.h | 15 +- frame/window/mainpanelcontrol.cpp | 161 +++++++++----- frame/window/mainpanelcontrol.h | 21 +- frame/window/systempluginwindow.h | 2 + 8 files changed, 556 insertions(+), 69 deletions(-) create mode 100644 frame/controller/recentapphelper.cpp create mode 100644 frame/controller/recentapphelper.h diff --git a/frame/controller/recentapphelper.cpp b/frame/controller/recentapphelper.cpp new file mode 100644 index 000000000..e6777a14e --- /dev/null +++ b/frame/controller/recentapphelper.cpp @@ -0,0 +1,318 @@ +/* + * 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; +} diff --git a/frame/controller/recentapphelper.h b/frame/controller/recentapphelper.h new file mode 100644 index 000000000..e0d1b0702 --- /dev/null +++ b/frame/controller/recentapphelper.h @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +#ifndef RECENTAPPHELPER_H +#define RECENTAPPHELPER_H + +#include "constants.h" + +#include + +class DockItem; +class QWidget; + +/** 用来管理最近打开区域和APP应用区域交互的类 + * @brief The RecentAppManager class + */ + +class RecentAppHelper : public QObject +{ + Q_OBJECT + +public: + explicit RecentAppHelper(QWidget *appWidget, QWidget *recentWidget, QObject *parent = nullptr); + void setDisplayMode(Dock::DisplayMode displayMode); + void resetAppInfo(); + void addAppItem(int index, DockItem *appItem); + void removeAppItem(DockItem *appItem); + bool recentIsVisible() const; + +Q_SIGNALS: + void requestUpdate(); + void recentVisibleChanged(bool); // 最近区域是否可见发生变化的信号 + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + bool appInRecent(DockItem *item) const; + void addAppAreaItem(int index, DockItem *wdg); + void addRecentAreaItem(int index, DockItem *wdg); + void updateRecentVisible(); + + void removeRecentAreaItem(DockItem *wdg); + void removeAppAreaItem(DockItem *wdg); + + QList dockItemToAppArea() const; + void resetDockItems(); + int getDockItemIndex(DockItem *dockItem, bool isRecent) const; + + QList dockItems(bool isRecent) const; + +private Q_SLOTS: + void onIsDockChanged(); + +private: + QWidget *m_appWidget; + QWidget *m_recentWidget; + + QList m_sequentDockItems; + + Dock::DisplayMode m_dislayMode; +}; + +#endif // RECENTAPPHELPER_H diff --git a/frame/item/appitem.cpp b/frame/item/appitem.cpp index 5040480f0..3ddd8529c 100644 --- a/frame/item/appitem.cpp +++ b/frame/item/appitem.cpp @@ -69,6 +69,7 @@ AppItem::AppItem(const QGSettings *appSettings, const QGSettings *activeAppSetti , m_retryObtainIconTimer(new QTimer(this)) , m_refershIconTimer(new QTimer(this)) , m_themeType(DGuiApplicationHelper::instance()->themeType()) + , m_createMSecs(QDateTime::currentMSecsSinceEpoch()) { QHBoxLayout *centralLayout = new QHBoxLayout; centralLayout->setMargin(0); @@ -94,6 +95,7 @@ AppItem::AppItem(const QGSettings *appSettings, const QGSettings *activeAppSetti connect(m_itemEntryInter, &DockEntryInter::IsActiveChanged, this, static_cast(&AppItem::update)); connect(m_itemEntryInter, &DockEntryInter::WindowInfosChanged, this, &AppItem::updateWindowInfos, Qt::QueuedConnection); connect(m_itemEntryInter, &DockEntryInter::IconChanged, this, &AppItem::refreshIcon); + connect(m_itemEntryInter, &DockEntryInter::IsDockedChanged, this, &AppItem::isDockChanged); connect(m_updateIconGeometryTimer, &QTimer::timeout, this, &AppItem::updateWindowIconGeometries, Qt::QueuedConnection); connect(m_retryObtainIconTimer, &QTimer::timeout, this, &AppItem::refreshIcon, Qt::QueuedConnection); @@ -185,6 +187,21 @@ QString AppItem::accessibleName() return m_itemEntryInter->name(); } +void AppItem::requestDock() +{ + m_itemEntryInter->RequestDock(); +} + +bool AppItem::isDocked() const +{ + return m_itemEntryInter->isDocked(); +} + +qint64 AppItem::appOpenMSecs() const +{ + return m_createMSecs; +} + void AppItem::moveEvent(QMoveEvent *e) { DockItem::moveEvent(e); @@ -564,6 +581,10 @@ QPoint AppItem::appIconPosition() const void AppItem::updateWindowInfos(const WindowInfoMap &info) { + // 如果是打开第一个窗口,则更新窗口时间 + if (m_windowInfos.isEmpty() && !info.isEmpty()) + m_createMSecs = QDateTime::currentMSecsSinceEpoch(); + m_windowInfos = info; if (m_appPreviewTips) m_appPreviewTips->setWindowInfos(m_windowInfos, m_itemEntryInter->GetAllowedCloseWindows().value()); diff --git a/frame/item/appitem.h b/frame/item/appitem.h index 69c556b64..de95780d8 100644 --- a/frame/item/appitem.h +++ b/frame/item/appitem.h @@ -56,6 +56,9 @@ public: inline ItemType itemType() const override { return App; } QPixmap appIcon(){ return m_appIcon; } virtual QString accessibleName() override; + void requestDock(); + bool isDocked() const; + qint64 appOpenMSecs() const; signals: void requestActivateWindow(const WId wid) const; @@ -64,6 +67,7 @@ signals: void dragReady(QWidget *dragWidget); void requestUpdateEntryGeometries() const; + void isDockChanged(bool) const; private: void moveEvent(QMoveEvent *e) override; @@ -139,6 +143,7 @@ private: QDate m_curDate; // 保存当前icon的日期来判断是否需要更新日历APP的ICON DGuiApplicationHelper::ColorType m_themeType; + qint64 m_createMSecs; static QPoint MousePressPos; }; diff --git a/frame/item/dockitem.h b/frame/item/dockitem.h index f7bb5a0b0..922a9c38c 100644 --- a/frame/item/dockitem.h +++ b/frame/item/dockitem.h @@ -41,13 +41,14 @@ class DockItem : public QWidget public: enum ItemType { - Launcher, - App, - Plugins, - FixedPlugin, + Launcher, // 启动器 + App, // 任务栏区域的应用 + Plugins, // 插件区域图标 + FixedPlugin, // 固定区域图标,例如多任务试图 Placeholder, - TrayPlugin, - QuickSettingPlugin + TrayPlugin, // 托盘插件 + QuickSettingPlugin, // 快捷设置区域插件 + StretchPlugin // 时尚模式下的固定在最右侧的插件,例如开关机插件 }; public: @@ -57,7 +58,7 @@ public: static void setDockPosition(const Position side); static void setDockDisplayMode(const DisplayMode mode); - inline virtual ItemType itemType() const {return App;} + inline virtual ItemType itemType() const = 0; QSize sizeHint() const override; virtual QString accessibleName(); diff --git a/frame/window/mainpanelcontrol.cpp b/frame/window/mainpanelcontrol.cpp index 09b8f19f2..ee40b24e3 100755 --- a/frame/window/mainpanelcontrol.cpp +++ b/frame/window/mainpanelcontrol.cpp @@ -34,6 +34,7 @@ #include "traymanagerwindow.h" #include "multiscreenworker.h" #include "displaymanager.h" +#include "recentapphelper.h" #include #include @@ -84,16 +85,19 @@ MainPanelControl::MainPanelControl(QWidget *parent) , m_trayAreaLayout(new QBoxLayout(QBoxLayout::LeftToRight, this)) , m_traySpliter(new QLabel(this)) , m_pluginAreaWidget(new QWidget(this)) + , m_recentAreaWidget(new QWidget(this)) + , m_recentLayout(new QBoxLayout(QBoxLayout::LeftToRight, this)) , m_trayManagerWidget(new TrayManagerWindow(this)) , m_pluginLayout(new QBoxLayout(QBoxLayout::LeftToRight, this)) , m_desktopWidget(new DesktopWidget(this)) , m_position(Position::Bottom) , m_placeholderItem(nullptr) , m_appDragWidget(nullptr) - , m_dislayMode(Efficient) + , m_displayMode(Efficient) , m_tray(nullptr) , m_trashItem(nullptr) , m_dockScreen(nullptr) + , m_recentHelper(new RecentAppHelper(m_appAreaSonWidget, m_recentAreaWidget, this)) { initUI(); initConnection(); @@ -107,11 +111,11 @@ MainPanelControl::MainPanelControl(QWidget *parent) m_trayAreaWidget->installEventFilter(this); m_pluginAreaWidget->installEventFilter(this); - //在设置每条线大小前,应该设置fixedsize(0,0) - //应为paintEvent函数会先调用设置背景颜色,大小为随机值 - m_fixedSpliter->setFixedSize(0,0); - m_appSpliter ->setFixedSize(0,0); - m_traySpliter->setFixedSize(0,0); + // 在设置每条线大小前,应该设置fixedsize(0,0) + // 应为paintEvent函数会先调用设置背景颜色,大小为随机值 + m_fixedSpliter->setFixedSize(0, 0); + m_appSpliter ->setFixedSize(0, 0); + m_traySpliter->setFixedSize(0, 0); } void MainPanelControl::initUI() @@ -137,6 +141,14 @@ void MainPanelControl::initUI() m_appSpliter->setObjectName("spliter_app"); m_mainPanelLayout->addWidget(m_appSpliter); + /* 最近打开应用 */ + m_recentAreaWidget->setObjectName("recentarea"); + m_recentAreaWidget->setAccessibleName("recentarea"); + m_recentAreaWidget->setLayout(m_recentLayout); + m_recentLayout->setSpacing(0); + m_recentLayout->setContentsMargins(0, 0, 0, 0); + m_mainPanelLayout->addWidget(m_recentAreaWidget); + /* 托盘区域 */ m_trayAreaWidget->setObjectName("trayarea"); m_trayAreaWidget->setLayout(m_trayAreaLayout); @@ -154,6 +166,7 @@ void MainPanelControl::initUI() m_pluginLayout->setContentsMargins(0, 0, 0, 0); m_mainPanelLayout->addWidget(m_pluginAreaWidget, 0, Qt::AlignCenter); + /* 时尚模式下右侧的插件区域 */ m_mainPanelLayout->addSpacing(15); m_mainPanelLayout->addWidget(m_trayManagerWidget); @@ -171,6 +184,8 @@ void MainPanelControl::initUI() void MainPanelControl::initConnection() { connect(m_trayManagerWidget, &TrayManagerWindow::requestUpdate, this, &MainPanelControl::onRequestUpdate); + connect(m_recentHelper, &RecentAppHelper::requestUpdate, this, &MainPanelControl::requestUpdate); + connect(m_recentHelper, &RecentAppHelper::recentVisibleChanged, this, &MainPanelControl::onRecentVisibleChanged); } /** @@ -179,10 +194,11 @@ void MainPanelControl::initConnection() */ void MainPanelControl::setDisplayMode(DisplayMode dislayMode) { - if (dislayMode == m_dislayMode) + if (dislayMode == m_displayMode) return; - m_dislayMode = dislayMode; + m_displayMode = dislayMode; + m_recentHelper->setDisplayMode(dislayMode); updateDisplayMode(); } @@ -204,6 +220,7 @@ void MainPanelControl::updateMainPanelLayout() m_pluginLayout->setDirection(QBoxLayout::LeftToRight); m_trayAreaLayout->setDirection(QBoxLayout::LeftToRight); m_appAreaSonLayout->setDirection(QBoxLayout::LeftToRight); + m_recentLayout->setDirection(QBoxLayout::LeftToRight); m_trayAreaLayout->setContentsMargins(0, 10, 0, 10); m_pluginLayout->setContentsMargins(10, 0, 10, 0); break; @@ -219,6 +236,7 @@ void MainPanelControl::updateMainPanelLayout() m_pluginLayout->setDirection(QBoxLayout::TopToBottom); m_trayAreaLayout->setDirection(QBoxLayout::TopToBottom); m_appAreaSonLayout->setDirection(QBoxLayout::TopToBottom); + m_recentLayout->setDirection(QBoxLayout::TopToBottom); m_trayAreaLayout->setContentsMargins(10, 0, 10, 0); m_pluginLayout->setContentsMargins(0, 10, 0, 10); break; @@ -247,22 +265,6 @@ void MainPanelControl::addFixedAreaItem(int index, QWidget *wdg) Q_EMIT requestUpdate(); } -/**往应用区域添加应用 - * @brief MainPanelControl::addAppAreaItem - * @param index 位置索引,如果为负数则插入到最后,为正则插入到指定位置 - * @param wdg 应用指针对象 - */ -void MainPanelControl::addAppAreaItem(int index, QWidget *wdg) -{ - if(m_position == Position::Top || m_position == Position::Bottom){ - wdg->setMaximumSize(height(),height()); - } else { - wdg->setMaximumSize(width(),width()); - } - m_appAreaSonLayout->insertWidget(index, wdg); - Q_EMIT requestUpdate(); -} - /**往托盘插件区域添加应用 * @brief MainPanelControl::addTrayAreaItem * @param index 位置索引,如果为负数则插入到最后,为正则插入到指定位置 @@ -330,8 +332,8 @@ void MainPanelControl::removeTrayAreaItem(QWidget *wdg) */ void MainPanelControl::removePluginAreaItem(QWidget *wdg) { - //因为日期时间插件大小和其他插件有异,为了方便设置边距,各插件中增加一层布局 - //因此remove插件图标时,需要从多的一层布局中取widget进行判断是否需要移除的插件 + // 因为日期时间插件大小和其他插件有异,为了方便设置边距,各插件中增加一层布局 + // 因此remove插件图标时,需要从多的一层布局中取widget进行判断是否需要移除的插件 // 清空保存的垃圾箱插件指针 PluginsItem *pluginsItem = qobject_cast(wdg); if (pluginsItem && pluginsItem->pluginName() == "trash") @@ -351,9 +353,9 @@ void MainPanelControl::removePluginAreaItem(QWidget *wdg) void MainPanelControl::resizeEvent(QResizeEvent *event) { - //先通过消息循环让各部件调整好size后再计算图标大小 - //避免因为部件size没有调整完导致计算的图标大小不准确 - //然后重复触发m_pluginAreaWidget的reszie事件并重复计算,造成任务栏图标抖动问题 + // 先通过消息循环让各部件调整好size后再计算图标大小 + // 避免因为部件size没有调整完导致计算的图标大小不准确 + // 然后重复触发m_pluginAreaWidget的reszie事件并重复计算,造成任务栏图标抖动问题 QWidget::resizeEvent(event); resizeDesktopWidget(); resizeDockIcon(); @@ -367,21 +369,43 @@ void MainPanelControl::resetRadius() qApp->setProperty("EffectBorderRadius", radius); } +/** 当用户从最近使用区域拖动应用到左侧应用区域的时候,将该应用驻留 + * @brief MainPanelControl::dockRecentApp + * @param dockItem + */ +void MainPanelControl::dockRecentApp(DockItem *dockItem) +{ + // 如果当前不是特效模式,则无需做驻留操作 + if (m_displayMode != Dock::DisplayMode::Fashion) + return; + + AppItem *appItem = qobject_cast(dockItem); + if (!appItem) + return; + + // TODO 如果控制中心设置不开启最近应用,则不让其驻留 + + // 如果控制中心开启了最近应用并且当前应用是未驻留应用,则可以驻留 + if (!appItem->isDocked()) + appItem->requestDock(); +} + /**根据任务栏所在位置, 设置应用区域控件的大小 * @brief MainPanelControl::updateAppAreaSonWidgetSize */ void MainPanelControl::updateAppAreaSonWidgetSize() { if ((m_position == Position::Top) || (m_position == Position::Bottom)) { - m_appAreaSonWidget->setMaximumHeight(this->height()); + m_appAreaSonWidget->setMaximumHeight(height()); m_appAreaSonWidget->setMaximumWidth(m_appAreaWidget->width()); + m_recentAreaWidget->setFixedHeight(height()); } else { - m_appAreaSonWidget->setMaximumWidth(this->width()); + m_appAreaSonWidget->setMaximumWidth(width()); m_appAreaSonWidget->setMaximumHeight(m_appAreaWidget->height()); + m_recentAreaWidget->setFixedWidth(width()); } m_appAreaSonWidget->adjustSize(); - moveAppSonWidget(); } @@ -422,7 +446,7 @@ void MainPanelControl::insertItem(int index, DockItem *item) break; case DockItem::App: case DockItem::Placeholder: - addAppAreaItem(index, item); + m_recentHelper->addAppItem(index, item); break; case DockItem::TrayPlugin: // 此处只会有一个tray系统托盘插件,微信、声音、网络蓝牙等等,都在系统托盘插件中处理的 addTrayAreaItem(index, item); @@ -454,7 +478,7 @@ void MainPanelControl::removeItem(DockItem *item) break; case DockItem::App: case DockItem::Placeholder: - removeAppAreaItem(item); + m_recentHelper->removeAppItem(item); break; case DockItem::TrayPlugin: removeTrayAreaItem(item); @@ -538,6 +562,7 @@ void MainPanelControl::dragLeaveEvent(QDragLeaveEvent *e) removeAppAreaItem(m_placeholderItem); m_placeholderItem->deleteLater(); + m_placeholderItem = nullptr; } } @@ -675,7 +700,7 @@ bool MainPanelControl::eventFilter(QObject *watched, QEvent *event) // NormalContainer部件尺寸变化完成之前就已经结束,导致 // NormalContainer没有更新自己的尺寸,引起插件区域拥挤 if (m_tray && watched == m_tray && event->type() == QEvent::Resize) - m_tray->pluginItem()->displayModeChanged(m_dislayMode); + m_tray->pluginItem()->displayModeChanged(m_displayMode); // 更新应用区域大小和任务栏图标大小 if (watched == m_appAreaSonWidget) { @@ -793,12 +818,17 @@ void MainPanelControl::startDrag(DockItem *dockItem) connect(m_appDragWidget, &AppDragWidget::destroyed, this, [ = ] { m_appDragWidget = nullptr; if (!item.isNull() && qobject_cast(item)->isValid()) { + // 如果是从最近打开区域移动到应用区域的,则需要将其固定 + dockRecentApp(item); if (-1 == m_appAreaSonLayout->indexOf(item) && m_dragIndex != -1) { insertItem(m_dragIndex, item); m_dragIndex = -1; } item->setDraging(false); item->update(); + + // 发送拖拽完成事件 + m_recentHelper->resetAppInfo(); } }); @@ -921,13 +951,12 @@ void MainPanelControl::updateDisplayMode() void MainPanelControl::updateModeChange() { - m_trayAreaWidget->setVisible(m_dislayMode == DisplayMode::Efficient); - m_traySpliter->setVisible(m_dislayMode == DisplayMode::Efficient); - m_appSpliter->setVisible(m_dislayMode == DisplayMode::Efficient); - m_pluginAreaWidget->setVisible(m_dislayMode == DisplayMode::Efficient); - m_trayManagerWidget->setVisible(m_dislayMode != DisplayMode::Efficient); + m_trayAreaWidget->setVisible(m_displayMode == DisplayMode::Efficient); + m_traySpliter->setVisible(m_displayMode == DisplayMode::Efficient); + m_pluginAreaWidget->setVisible(m_displayMode == DisplayMode::Efficient); + m_trayManagerWidget->setVisible(m_displayMode != DisplayMode::Efficient); if (m_tray) - m_tray->setVisible(m_dislayMode == DisplayMode::Efficient); + m_tray->setVisible(m_displayMode == DisplayMode::Efficient); } /**把驻留应用和被打开的应用所在窗口移动到指定位置 @@ -936,7 +965,7 @@ void MainPanelControl::updateModeChange() void MainPanelControl::moveAppSonWidget() { QRect rect(QPoint(0, 0), m_appAreaSonWidget->size()); - if (DisplayMode::Efficient == m_dislayMode) { + if (DisplayMode::Efficient == m_displayMode) { switch (m_position) { case Top: case Bottom : @@ -999,22 +1028,29 @@ void MainPanelControl::setDockScreen(DockScreen *dockScreen) QPainterPath MainPanelControl::areaPath() { - if (m_dislayMode == DisplayMode::Efficient) + if (m_displayMode == DisplayMode::Efficient) return QPainterPath(); int radius = qApp->property("EffectBorderRadius").toInt(); QPainterPath path; if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom) { int leftWidth = m_fixedAreaWidget->width() + m_fixedSpliter->width() + m_appAreaWidget->width(); + if (m_recentLayout->count() > 0) + leftWidth += m_recentAreaWidget->width(); + int roundHeight = height(); path.addRoundedRect(QRect(0, 0, leftWidth, roundHeight), radius, radius); path.addRoundedRect(QRect(m_trayManagerWidget->x(), 0, m_trayManagerWidget->width(), roundHeight), radius, radius); } else { int roundWidth = width(); int topHeight = m_fixedAreaWidget->height() + m_fixedSpliter->height() + m_appAreaWidget->height(); + if (m_recentLayout->count() > 0) + topHeight += m_recentAreaWidget->height(); + path.addRoundedRect(QRect(0, 0, roundWidth, topHeight), radius, radius); path.addRoundedRect(QRect(0, m_trayManagerWidget->y(), roundWidth, m_trayManagerWidget->height()), radius, radius); } + return path; } @@ -1027,7 +1063,7 @@ QSize MainPanelControl::suitableSize(int screenSize, double deviceRatio) const if (ratio <= 0) ratio = qApp->devicePixelRatio(); - if (m_dislayMode == DisplayMode::Efficient) { + if (m_displayMode == DisplayMode::Efficient) { // 如果是高效模式 if (m_position == Position::Top || m_position == Position::Bottom) return QSize(static_cast(screenSize / ratio), height()); @@ -1041,7 +1077,7 @@ QSize MainPanelControl::suitableSize(int screenSize, double deviceRatio) const // 减去右侧托盘和快捷设置还有插件区域的尺寸 totalLength -= (((m_position == Position::Top || m_position == Position::Bottom) ? traySuitableSize.width() : traySuitableSize.height()) / ratio); // 需要参与计算的图标的总数 - int iconCount = m_fixedAreaLayout->count() + m_appAreaSonLayout->count(); + int iconCount = m_fixedAreaLayout->count() + m_appAreaSonLayout->count() + m_recentLayout->count(); if (iconCount <= 0) { if (m_position == Position::Top || m_position == Position::Bottom) return QSize((static_cast((traySuitableSize.width() + 20) / ratio)), height()); @@ -1126,13 +1162,14 @@ void MainPanelControl::resizeDockIcon() int tray_item_size = 0; int iconCount = 0; // 总宽度 - if (m_dislayMode == DisplayMode::Fashion) { + if (m_displayMode == DisplayMode::Fashion) { + // 时尚模式 int totalLength = getScreenSize(); QSize trayManagerSize = m_trayManagerWidget->suitableSize(); // 减去右侧托盘和插件区域的宽度 totalLength -= ((m_position == Position::Top) || (m_position == Position::Bottom)) ? trayManagerSize.width() : trayManagerSize.height(); - iconCount = m_fixedAreaLayout->count() + m_appAreaSonLayout->count(); + iconCount = m_fixedAreaLayout->count() + m_appAreaSonLayout->count() + m_recentLayout->count(); if (iconCount <= 0) return; @@ -1255,9 +1292,8 @@ void MainPanelControl::calcuDockIconSize(int w, int h, int traySize) { int appItemSize = qMin(w, h); - for (int i = 0; i < m_fixedAreaLayout->count(); ++i) { + for (int i = 0; i < m_fixedAreaLayout->count(); ++i) m_fixedAreaLayout->itemAt(i)->widget()->setFixedSize(appItemSize, appItemSize); - } if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom) { m_fixedSpliter->setFixedSize(SPLITER_SIZE, int(w * 0.6)); @@ -1269,16 +1305,26 @@ void MainPanelControl::calcuDockIconSize(int w, int h, int traySize) m_traySpliter->setFixedSize(int(h * 0.5), SPLITER_SIZE); } - for (int i = 0; i < m_appAreaSonLayout->count(); ++i) { + for (int i = 0; i < m_appAreaSonLayout->count(); ++i) m_appAreaSonLayout->itemAt(i)->widget()->setFixedSize(appItemSize, appItemSize); + + // 时尚模式下判断是否需要显示最近打开的应用区域 + if (m_displayMode == Dock::DisplayMode::Fashion && m_recentLayout->count() > 0) { + for (int i = 0; i < m_recentLayout->count(); ++i) + m_recentLayout->itemAt(i)->widget()->setFixedSize(appItemSize, appItemSize); + + // 时尚模式下计算最近打开应用区域的尺寸 + if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom) + m_recentAreaWidget->setFixedWidth(appItemSize * m_recentLayout->count()); + else + m_recentAreaWidget->setFixedHeight(appItemSize * m_recentLayout->count()); } - if (m_tray) { + if (m_tray) m_tray->centralWidget()->setProperty("iconSize", traySize); - } - //因为日期时间大小和其他插件大小有异,为了设置边距,在各插件中增加了一层布局 - //因此需要通过多一层布局来获取各插件 + // 因为日期时间大小和其他插件大小有异,为了设置边距,在各插件中增加了一层布局 + // 因此需要通过多一层布局来获取各插件 if ((m_position == Position::Top) || (m_position == Position::Bottom)) { // 三方插件 for (int i = 0; i < m_pluginLayout->count(); ++ i) { @@ -1352,6 +1398,11 @@ void MainPanelControl::onRequestUpdate() Q_EMIT requestUpdate(); } +void MainPanelControl::onRecentVisibleChanged(bool visible) +{ + m_appSpliter->setVisible(visible); +} + /**时尚模式没有‘显示桌面’区域 * @brief MainPanelControl::resizeDesktopWidget */ @@ -1375,7 +1426,7 @@ void MainPanelControl::resizeDesktopWidget() m_trayManagerWidget->setFixedWidth(suitableSize.width()); } - if (DisplayMode::Fashion == m_dislayMode) + if (DisplayMode::Fashion == m_displayMode) m_desktopWidget->setFixedSize(0, 0); } diff --git a/frame/window/mainpanelcontrol.h b/frame/window/mainpanelcontrol.h index a525ab008..b0f6451c4 100755 --- a/frame/window/mainpanelcontrol.h +++ b/frame/window/mainpanelcontrol.h @@ -40,10 +40,12 @@ class AppDragWidget; class DesktopWidget; class TrayManagerWindow; class DockScreen; +class RecentAppHelper; class MainPanelControl : public QWidget { Q_OBJECT + public: explicit MainPanelControl(QWidget *parent = nullptr); @@ -77,7 +79,6 @@ private: void addFixedAreaItem(int index, QWidget *wdg); void removeFixedAreaItem(QWidget *wdg); - void addAppAreaItem(int index, QWidget *wdg); void removeAppAreaItem(QWidget *wdg); void addTrayAreaItem(int index, QWidget *wdg); void removeTrayAreaItem(QWidget *wdg); @@ -95,9 +96,11 @@ private: bool checkNeedShowDesktop(); bool appIsOnDock(const QString &appDesktop); void resetRadius(); + void dockRecentApp(DockItem *dockItem); private Q_SLOTS: void onRequestUpdate(); + void onRecentVisibleChanged(bool visible); protected: void dragMoveEvent(QDragMoveEvent *e) override; @@ -114,31 +117,35 @@ private: QBoxLayout *m_mainPanelLayout; QWidget *m_fixedAreaWidget; // 固定区域 - QBoxLayout *m_fixedAreaLayout; // + QBoxLayout *m_fixedAreaLayout; // 固定区域布局 QLabel *m_fixedSpliter; // 固定区域与应用区域间的分割线 QWidget *m_appAreaWidget; // 应用区域 QWidget *m_appAreaSonWidget; // 子应用区域,所在位置根据显示模式手动指定 - QBoxLayout *m_appAreaSonLayout; // + QBoxLayout *m_appAreaSonLayout; // 子应用区域布局 QLabel *m_appSpliter; // 应用区域与托盘区域间的分割线 QWidget *m_trayAreaWidget; // 托盘区域 - QBoxLayout *m_trayAreaLayout; // + QBoxLayout *m_trayAreaLayout; // 托盘区域布局 QLabel *m_traySpliter; // 托盘区域与插件区域间的分割线 QWidget *m_pluginAreaWidget; // 插件区域 + QWidget *m_recentAreaWidget; // 最近打开应用 + QBoxLayout *m_recentLayout; + TrayManagerWindow *m_trayManagerWidget; - QBoxLayout *m_pluginLayout; // + QBoxLayout *m_pluginLayout; // 插件区域布局 DesktopWidget *m_desktopWidget; // 桌面预览区域 Position m_position; QPointer m_placeholderItem; QString m_draggingMimeKey; AppDragWidget *m_appDragWidget; - DisplayMode m_dislayMode; + DisplayMode m_displayMode; QPoint m_mousePressPos; TrayPluginItem *m_tray; - int m_dragIndex = -1; // 记录应用区域被拖拽图标的位置 + int m_dragIndex = -1; // 记录应用区域被拖拽图标的位置 PluginsItem *m_trashItem; // 垃圾箱插件(需要特殊处理一下) DockScreen *m_dockScreen; + RecentAppHelper *m_recentHelper; }; #endif // MAINPANELCONTROL_H diff --git a/frame/window/systempluginwindow.h b/frame/window/systempluginwindow.h index 1c260e5f7..bb8e5ec13 100644 --- a/frame/window/systempluginwindow.h +++ b/frame/window/systempluginwindow.h @@ -75,6 +75,8 @@ public: QString itemKey() const; QSize suitableSize() const; + inline ItemType itemType() const override { return DockItem::StretchPlugin; } + protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *e) override;