dde-dock/frame/window/mainwindow.cpp

630 lines
24 KiB
C++
Raw Normal View History

/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: sbw <sbw@sbw.so>
*
* Maintainer: sbw <sbw@sbw.so>
* zhaolong <zhaolong@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 "mainwindow.h"
#include "mainpanelcontrol.h"
#include "dockitemmanager.h"
#include "utils.h"
#include "menuworker.h"
#include <DStyle>
#include <DPlatformWindowHandle>
#include <DSysInfo>
#include <DPlatformTheme>
#include <DDBusSender>
#include <QDebug>
#include <QEvent>
#include <QResizeEvent>
#include <QScreen>
#include <QGuiApplication>
#include <QX11Info>
#include <qpa/qplatformwindow.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <com_deepin_dde_daemon_dock.h>
#define SNI_WATCHER_SERVICE "org.kde.StatusNotifierWatcher"
#define SNI_WATCHER_PATH "/StatusNotifierWatcher"
2019-09-06 14:44:07 +08:00
#define MAINWINDOW_MAX_SIZE DOCK_MAX_SIZE
#define MAINWINDOW_MIN_SIZE (40)
#define DRAG_AREA_SIZE (5)
#define DRAG_STATE_PROP "DRAG_STATE"
using org::kde::StatusNotifierWatcher;
using DBusDock = com::deepin::dde::daemon::Dock;
// let startdde know that we've already started.
void RegisterDdeSession()
{
QString envName("DDE_SESSION_PROCESS_COOKIE_ID");
QByteArray cookie = qgetenv(envName.toUtf8().data());
qunsetenv(envName.toUtf8().data());
if (!cookie.isEmpty()) {
QDBusPendingReply<bool> r = DDBusSender()
.interface("com.deepin.SessionManager")
.path("/com/deepin/SessionManager")
.service("com.deepin.SessionManager")
.method("Register")
.arg(QString(cookie))
.call();
qDebug() << Q_FUNC_INFO << r.value();
}
}
MainWindow::MainWindow(QWidget *parent)
: DBlurEffectWidget(parent)
, m_mainPanel(new MainPanelControl(this))
, m_platformWindowHandle(this)
, m_wmHelper(DWindowManagerHelper::instance())
, m_multiScreenWorker(new MultiScreenWorker(this, m_wmHelper))
, m_menuWorker(new MenuWorker(m_multiScreenWorker->dockInter(), this))
, m_shadowMaskOptimizeTimer(new QTimer(this))
, m_dbusDaemonInterface(QDBusConnection::sessionBus().interface())
, m_sniWatcher(new StatusNotifierWatcher(SNI_WATCHER_SERVICE, SNI_WATCHER_PATH, QDBusConnection::sessionBus(), this))
, m_dragWidget(new DragWidget(this))
, m_launched(false)
, m_updateDragAreaTimer(new QTimer(this))
{
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_X11DoNotAcceptFocus);
//1 确保这两行代码的先后顺序,否则会导致任务栏界面不再置顶
setWindowFlags(Qt::WindowDoesNotAcceptFocus);
const auto display = QX11Info::display();
if (!display) {
qWarning() << "QX11Info::display() is " << display;
} else {
//2 确保这两行代码的先后顺序,否则会导致任务栏界面不再置顶
XcbMisc::instance()->set_window_type(xcb_window_t(this->winId()), XcbMisc::Dock);
}
setMouseTracking(true);
setAcceptDrops(true);
DPlatformWindowHandle::enableDXcbForWindow(this, true);
m_platformWindowHandle.setEnableBlurWindow(true);
m_platformWindowHandle.setTranslucentBackground(true);
m_platformWindowHandle.setWindowRadius(0);
2019-09-12 19:14:31 +08:00
m_platformWindowHandle.setShadowOffset(QPoint(0, 5));
m_platformWindowHandle.setShadowColor(QColor(0, 0, 0, 0.3 * 255));
m_mainPanel->setDisplayMode(m_multiScreenWorker->displayMode());
initMember();
initSNIHost();
initComponents();
initConnections();
resetDragWindow();
for (auto item : DockItemManager::instance()->itemList())
m_mainPanel->insertItem(-1, item);
m_dragWidget->setMouseTracking(true);
m_dragWidget->setFocusPolicy(Qt::NoFocus);
if ((Top == m_multiScreenWorker->position()) || (Bottom == m_multiScreenWorker->position())) {
m_dragWidget->setCursor(Qt::SizeVerCursor);
} else {
m_dragWidget->setCursor(Qt::SizeHorCursor);
}
}
MainWindow::~MainWindow()
{
}
/**
* @brief MainWindow::launch
*
*/
void MainWindow::launch()
{
if (!qApp->property("CANSHOW").toBool())
return;
m_launched = true;
qApp->processEvents();
setVisible(true);
m_multiScreenWorker->initShow();
m_shadowMaskOptimizeTimer->start();
}
/**
* @brief MainWindow::callShow
* DBus调用的
* @note -r参数启动时DBus调用此接口之后才会显示界面
*
* startdde延后调用
*/
void MainWindow::callShow()
{
static bool flag = false;
if (flag) {
return;
}
flag = true;
qApp->setProperty("CANSHOW", true);
launch();
// 预留200ms提供给窗口初始化再通知startdde不影响启动速度
QTimer::singleShot(200, this, []{
qDebug() << "\n\ndde-dock startup RegisterDdeSession";
RegisterDdeSession();
});
}
/**
* @brief MainWindow::relaodPlugins
* 退
*/
void MainWindow::relaodPlugins()
{
if (qApp->property("PLUGINSLOADED").toBool()) {
return;
}
DockItemManager::instance()->startLoadPlugins();
qApp->setProperty("PLUGINSLOADED", true);
}
/**
* @brief MainWindow::mousePressEvent
* @param e
* @note
*/
void MainWindow::mousePressEvent(QMouseEvent *e)
{
e->ignore();
if (e->button() == Qt::RightButton) {
m_menuWorker->showDockSettingsMenu();
return;
}
}
void MainWindow::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
#ifdef QT_DEBUG
case Qt::Key_Escape: qApp->quit(); break;
#endif
default:;
}
}
void MainWindow::enterEvent(QEvent *e)
{
QWidget::enterEvent(e);
2019-10-25 11:12:27 +08:00
if (QApplication::overrideCursor() && QApplication::overrideCursor()->shape() != Qt::ArrowCursor)
QApplication::restoreOverrideCursor();
}
void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
Q_UNUSED(e);
//重写mouseMoveEvent 解决bug12866 leaveEvent事件失效
}
void MainWindow::moveEvent(QMoveEvent *event)
{
Q_UNUSED(event);
if (!qApp->property(DRAG_STATE_PROP).toBool())
m_updateDragAreaTimer->start();
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
if (!qApp->property(DRAG_STATE_PROP).toBool())
m_updateDragAreaTimer->start();
// 任务栏大小、位置、模式改变都会触发resize发射大小改变信号供依赖项目更新位置
Q_EMIT panelGeometryChanged();
m_mainPanel->updatePluginsLayout();
m_shadowMaskOptimizeTimer->start();
return DBlurEffectWidget::resizeEvent(event);
}
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
QWidget::dragEnterEvent(e);
}
void MainWindow::initMember()
{
//INFO 这里要大于动画的300ms否则可能动画过程中这个定时器就被触发了
m_updateDragAreaTimer->setInterval(500);
m_updateDragAreaTimer->setSingleShot(true);
}
/**
* @brief MainWindow::initSNIHost
* @note Dock注册到StatusNotifierWatcher服务上
*/
void MainWindow::initSNIHost()
{
// registor dock as SNI Host on dbus
QDBusConnection dbusConn = QDBusConnection::sessionBus();
m_sniHostService = QString("org.kde.StatusNotifierHost-") + QString::number(qApp->applicationPid());
dbusConn.registerService(m_sniHostService);
dbusConn.registerObject("/StatusNotifierHost", this);
if (m_sniWatcher->isValid()) {
m_sniWatcher->RegisterStatusNotifierHost(m_sniHostService);
} else {
qDebug() << SNI_WATCHER_SERVICE << "SNI watcher daemon is not exist for now!";
}
}
void MainWindow::initComponents()
{
m_shadowMaskOptimizeTimer->setSingleShot(true);
m_shadowMaskOptimizeTimer->setInterval(100);
QTimer::singleShot(1, this, &MainWindow::compositeChanged);
themeTypeChanged(DGuiApplicationHelper::instance()->themeType());
}
void MainWindow::compositeChanged()
{
const bool composite = m_wmHelper->hasComposite();
setComposite(composite);
m_shadowMaskOptimizeTimer->start();
}
void MainWindow::initConnections()
{
connect(m_shadowMaskOptimizeTimer, &QTimer::timeout, this, &MainWindow::adjustShadowMask, Qt::QueuedConnection);
connect(m_wmHelper, &DWindowManagerHelper::hasCompositeChanged, m_shadowMaskOptimizeTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
connect(&m_platformWindowHandle, &DPlatformWindowHandle::frameMarginsChanged, m_shadowMaskOptimizeTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
connect(&m_platformWindowHandle, &DPlatformWindowHandle::windowRadiusChanged, m_shadowMaskOptimizeTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
connect(m_dbusDaemonInterface, &QDBusConnectionInterface::serviceOwnerChanged, this, &MainWindow::onDbusNameOwnerChanged);
connect(DockItemManager::instance(), &DockItemManager::itemInserted, m_mainPanel, &MainPanelControl::insertItem, Qt::DirectConnection);
connect(DockItemManager::instance(), &DockItemManager::itemRemoved, m_mainPanel, &MainPanelControl::removeItem, Qt::DirectConnection);
2019-09-06 14:44:07 +08:00
connect(DockItemManager::instance(), &DockItemManager::itemUpdated, m_mainPanel, &MainPanelControl::itemUpdated, Qt::DirectConnection);
connect(DockItemManager::instance(), &DockItemManager::trayVisableCountChanged, this, &MainWindow::getTrayVisableItemCount, Qt::QueuedConnection);
connect(DockItemManager::instance(), &DockItemManager::requestWindowAutoHide, m_menuWorker, &MenuWorker::setAutoHide);
connect(m_mainPanel, &MainPanelControl::itemMoved, DockItemManager::instance(), &DockItemManager::itemMoved, Qt::DirectConnection);
connect(m_mainPanel, &MainPanelControl::itemAdded, DockItemManager::instance(), &DockItemManager::itemAdded, Qt::DirectConnection);
// -拖拽任务栏改变高度或宽度-------------------------------------------------------------------------------
connect(m_updateDragAreaTimer, &QTimer::timeout, this, &MainWindow::resetDragWindow);
//TODO 后端考虑删除这块,目前还不能删除,调整任务栏高度的时候,任务栏外部区域有变化
connect(m_updateDragAreaTimer, &QTimer::timeout, m_multiScreenWorker, &MultiScreenWorker::onRequestUpdateRegionMonitor);
connect(m_dragWidget, &DragWidget::dragPointOffset, this, [ = ] { qApp->setProperty(DRAG_STATE_PROP, true); });
connect(m_dragWidget, &DragWidget::dragFinished, this, [ = ] { qApp->setProperty(DRAG_STATE_PROP, false); });
connect(m_dragWidget, &DragWidget::dragPointOffset, this, &MainWindow::onMainWindowSizeChanged);
connect(m_dragWidget, &DragWidget::dragFinished, this, &MainWindow::resetDragWindow); // 更新拖拽区域
// ----------------------------------------------------------------------------------------------------
connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &MainWindow::themeTypeChanged);
connect(m_menuWorker, &MenuWorker::autoHideChanged, m_multiScreenWorker, &MultiScreenWorker::onAutoHideChanged);
connect(m_multiScreenWorker, &MultiScreenWorker::opacityChanged, this, &MainWindow::setMaskAlpha, Qt::QueuedConnection);
connect(m_multiScreenWorker, &MultiScreenWorker::displayModeChanegd, this, &MainWindow::adjustShadowMask, Qt::QueuedConnection);
connect(m_multiScreenWorker, &MultiScreenWorker::requestUpdateDockEntry, DockItemManager::instance(), &DockItemManager::requestUpdateDockItem);
// 响应后端触控屏拖拽任务栏高度长按信号
connect(TouchSignalManager::instance(), &TouchSignalManager::middleTouchPress, this, &MainWindow::touchRequestResizeDock);
connect(TouchSignalManager::instance(), &TouchSignalManager::touchMove, m_dragWidget, [ this ]() {
static QPoint lastPos;
QPoint curPos = QCursor::pos();
if (lastPos == curPos) {
return;
}
lastPos = curPos;
qApp->postEvent(m_dragWidget, new QMouseEvent(QEvent::MouseMove, m_dragWidget->mapFromGlobal(curPos)
, QPoint(), curPos, Qt::LeftButton, Qt::LeftButton
, Qt::NoModifier, Qt::MouseEventSynthesizedByApplication));
});
}
/**
* @brief MainWindow::getTrayVisableItemCount
*
*/
void MainWindow::getTrayVisableItemCount()
{
m_mainPanel->getTrayVisableItemCount();
}
/**
* @brief MainWindow::adjustShadowMask
*/
void MainWindow::adjustShadowMask()
{
if (!m_launched || m_shadowMaskOptimizeTimer->isActive())
return;
2019-09-03 09:36:33 +08:00
DStyleHelper dstyle(style());
int radius = 0;
if (m_wmHelper->hasComposite() && m_multiScreenWorker->displayMode() == DisplayMode::Fashion) {
if (Dtk::Core::DSysInfo::isCommunityEdition()) { // 社区版圆角与专业版不同
DPlatformTheme *theme = DGuiApplicationHelper::instance()->systemTheme();
radius = theme->windowRadius(radius);
} else {
radius = dstyle.pixelMetric(DStyle::PM_TopLevelWindowRadius);
}
}
m_platformWindowHandle.setWindowRadius(radius);
m_mainPanel->updatePluginsLayout();
}
void MainWindow::onDbusNameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
{
Q_UNUSED(oldOwner);
if (name == SNI_WATCHER_SERVICE && !newOwner.isEmpty()) {
qDebug() << SNI_WATCHER_SERVICE << "SNI watcher daemon started, register dock to watcher as SNI Host";
m_sniWatcher->RegisterStatusNotifierHost(m_sniHostService);
}
}
2019-08-22 13:52:02 +08:00
/**
* @brief MainWindow::setEffectEnabled
* @param enabled enabled特效来更新任务栏的外观样式
*/
2019-08-22 13:52:02 +08:00
void MainWindow::setEffectEnabled(const bool enabled)
{
setMaskColor(AutoColor);
2019-08-22 13:52:02 +08:00
setMaskAlpha(m_multiScreenWorker->opacity());
m_platformWindowHandle.setBorderWidth(enabled ? 1 : 0);
2019-08-22 13:52:02 +08:00
}
/**
* @brief MainWindow::setComposite
* @param hasComposite
*/
2019-08-22 13:52:02 +08:00
void MainWindow::setComposite(const bool hasComposite)
{
setEffectEnabled(hasComposite);
}
/**
* @brief MainWindow::resetDragWindow
* @note widget提拽支持geometry发生变化的时候geometry
*/
void MainWindow::resetDragWindow()
{
switch (m_multiScreenWorker->position()) {
case Dock::Top:
2019-09-25 14:09:02 +08:00
m_dragWidget->setGeometry(0, height() - DRAG_AREA_SIZE, width(), DRAG_AREA_SIZE);
break;
case Dock::Bottom:
2019-09-25 14:09:02 +08:00
m_dragWidget->setGeometry(0, 0, width(), DRAG_AREA_SIZE);
break;
case Dock::Left:
2019-09-25 14:09:02 +08:00
m_dragWidget->setGeometry(width() - DRAG_AREA_SIZE, 0, DRAG_AREA_SIZE, height());
break;
case Dock::Right:
2019-09-25 14:09:02 +08:00
m_dragWidget->setGeometry(0, 0, DRAG_AREA_SIZE, height());
break;
}
QRect rect = m_multiScreenWorker->dockRect(m_multiScreenWorker->deskScreen()
, m_multiScreenWorker->position()
, HideMode::KeepShowing
, m_multiScreenWorker->displayMode());
// 这个时候屏幕有可能是隐藏的不能直接使用this->width()这种去设置任务栏的高度,而应该保证原值
int dockSize = 0;
if (m_multiScreenWorker->position() == Position::Left
|| m_multiScreenWorker->position() == Position::Right) {
dockSize = this->width() == 0 ? rect.width() : this->width();
} else {
dockSize = this->height() == 0 ? rect.height() : this->height();
}
/** FIX ME
* dockSize的值在40100
* 1dockSize为39dock的mainwindow高度变成9939
* 2dockSize的值在这里不应该为39
*
*/
dockSize = qBound(MAINWINDOW_MIN_SIZE, dockSize, MAINWINDOW_MAX_SIZE);
// 通知窗管和后端更新数据
m_multiScreenWorker->updateDaemonDockSize(dockSize); // 1.先更新任务栏高度
m_multiScreenWorker->requestUpdateFrontendGeometry(); // 2.再更新任务栏位置,保证先1再2
m_multiScreenWorker->requestNotifyWindowManager();
m_multiScreenWorker->requestUpdateRegionMonitor(); // 界面发生变化,应更新监控区域
if ((Top == m_multiScreenWorker->position()) || (Bottom == m_multiScreenWorker->position())) {
m_dragWidget->setCursor(Qt::SizeVerCursor);
} else {
m_dragWidget->setCursor(Qt::SizeHorCursor);
}
}
/**
* @brief MainWindow::onMainWindowSizeChanged
* @param offset
*/
void MainWindow::onMainWindowSizeChanged(QPoint offset)
{
const QRect &rect = m_multiScreenWorker->dockRect(m_multiScreenWorker->deskScreen()
, m_multiScreenWorker->position()
, HideMode::KeepShowing,
m_multiScreenWorker->displayMode());
QRect newRect;
switch (m_multiScreenWorker->position()) {
case Top: {
newRect.setX(rect.x());
newRect.setY(rect.y());
newRect.setWidth(rect.width());
newRect.setHeight(qBound(MAINWINDOW_MIN_SIZE, rect.height() + offset.y(), MAINWINDOW_MAX_SIZE));
}
break;
case Bottom: {
newRect.setX(rect.x());
newRect.setY(rect.y() + rect.height() - qBound(MAINWINDOW_MIN_SIZE, rect.height() - offset.y(), MAINWINDOW_MAX_SIZE));
newRect.setWidth(rect.width());
newRect.setHeight(qBound(MAINWINDOW_MIN_SIZE, rect.height() - offset.y(), MAINWINDOW_MAX_SIZE));
}
break;
case Left: {
newRect.setX(rect.x());
newRect.setY(rect.y());
newRect.setWidth(qBound(MAINWINDOW_MIN_SIZE, rect.width() + offset.x(), MAINWINDOW_MAX_SIZE));
newRect.setHeight(rect.height());
}
break;
case Right: {
newRect.setX(rect.x() + rect.width() - qBound(MAINWINDOW_MIN_SIZE, rect.width() - offset.x(), MAINWINDOW_MAX_SIZE));
newRect.setY(rect.y());
newRect.setWidth(qBound(MAINWINDOW_MIN_SIZE, rect.width() - offset.x(), MAINWINDOW_MAX_SIZE));
newRect.setHeight(rect.height());
}
break;
}
// 更新界面大小
m_mainPanel->setFixedSize(newRect.size());
setFixedSize(newRect.size());
move(newRect.topLeft());
}
/**
* @brief MainWindow::themeTypeChanged
* @param themeType
*/
void MainWindow::themeTypeChanged(DGuiApplicationHelper::ColorType themeType)
{
if (m_wmHelper->hasComposite()) {
if (themeType == DGuiApplicationHelper::DarkType)
m_platformWindowHandle.setBorderColor(QColor(0, 0, 0, 255 * 0.3));
else
m_platformWindowHandle.setBorderColor(QColor(QColor::Invalid));
}
}
/**
* @brief MainWindow::touchRequestResizeDock
*/
void MainWindow::touchRequestResizeDock()
{
const QPoint touchPos(QCursor::pos());
QRect dockRect = m_multiScreenWorker->dockRect(m_multiScreenWorker->deskScreen()
, m_multiScreenWorker->position()
, HideMode::KeepShowing
, m_multiScreenWorker->displayMode());
// 隐藏状态返回
if (width() == 0 || height() == 0) {
return;
}
int resizeHeight = Utils::SettingValue("com.deepin.dde.dock.touch", QByteArray(), "resizeHeight", 7).toInt();
QRect touchRect;
// 任务栏屏幕 内侧边线 内外resizeHeight距离矩形区域内长按可拖动任务栏高度
switch (m_multiScreenWorker->position()) {
case Position::Top:
touchRect = QRect(dockRect.x(), dockRect.y() + dockRect.height() - resizeHeight, dockRect.width(), resizeHeight * 2);
break;
case Position::Bottom:
touchRect = QRect(dockRect.x(), dockRect.y() - resizeHeight, dockRect.width(), resizeHeight * 2);
break;
case Position::Left:
touchRect = QRect(dockRect.x() + dockRect.width() - resizeHeight, dockRect.y(), resizeHeight * 2, dockRect.height());
break;
case Position::Right:
touchRect = QRect(dockRect.x() - resizeHeight, dockRect.y(), resizeHeight * 2, dockRect.height());
break;
}
if (!touchRect.contains(touchPos)) {
return;
}
qApp->postEvent(m_dragWidget, new QMouseEvent(QEvent::MouseButtonPress, m_dragWidget->mapFromGlobal(touchPos)
, QPoint(), touchPos, Qt::LeftButton, Qt::NoButton
, Qt::NoModifier, Qt::MouseEventSynthesizedByApplication));
}
/**
* @brief MainWindow::setGeometry
* @param rect panelGeometryChanged信号DBus调用方使用
*/
void MainWindow::setGeometry(const QRect &rect)
{
if (rect == this->geometry()) {
return;
}
DBlurEffectWidget::setGeometry(rect);
emit panelGeometryChanged();
}
/**
* @brief
*/
void MainWindow::sendNotifications()
{
QStringList actionButton;
actionButton << "reload" << tr("Exit Safe Mode");
QVariantMap hints;
hints["x-deepin-action-reload"] = QString("dbus-send,--session,--dest=com.deepin.dde.Dock,--print-reply,/com/deepin/dde/Dock,com.deepin.dde.Dock.ReloadPlugins");
QTimer::singleShot(0, this, [=] {
DDBusSender()
.service("com.deepin.dde.Notification")
.path("/com/deepin/dde/Notification")
.interface("com.deepin.dde.Notification")
.method(QString("Notify"))
.arg(QString("dde-control-center")) // appname
.arg(static_cast<uint>(0)) // id
.arg(QString("preferences-system")) // icon
.arg(QString(tr("Dock - Safe Mode"))) // summary
.arg(QString(tr("The Dock is in safe mode, please exit to show it properly"))) // content
.arg(actionButton) // actions
.arg(hints) // hints
.arg(15000) // timeout
.call();
});
}