From 9c1eb32e242e31468ce01c0f1158f1ea1253df8e Mon Sep 17 00:00:00 2001 From: donghualin Date: Fri, 12 Aug 2022 08:13:00 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8B=96=E5=8A=A8=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=A0=8F=E5=9B=BE=E6=A0=87=E5=AE=9E=E7=8E=B0=E5=88=86=E5=B1=8F?= =?UTF-8?q?=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 拖动任务栏图标到屏幕中间,如果在左侧,显示左分屏效果,在右侧,显示右分屏效果 Log: 完成任务栏图标分屏效果的功能 Influence: 从任务栏拖动图标到屏幕上方,查看是否有分屏功能 Task: https://pms.uniontech.com/task-view-163465.html Change-Id: I1a7a33646edb6f55972b8e5fa2c5f39ce026fe8e --- CMakeLists.txt | 5 + debian/control | 5 +- frame/CMakeLists.txt | 18 +- frame/dbus/entryinterface.cpp | 18 +- frame/dbus/entryinterface.h | 1 + frame/display/displaymanager.cpp | 11 + frame/display/displaymanager.h | 1 + frame/item/appitem.cpp | 32 +- frame/item/appitem.h | 10 + frame/item/components/appdragwidget.cpp | 413 ++++++++++++------ frame/item/components/appdragwidget.h | 31 +- frame/screenspliter/screenspliter.cpp | 60 +++ frame/screenspliter/screenspliter.h | 71 +++ frame/screenspliter/screenspliter_wayland.cpp | 247 +++++++++++ frame/screenspliter/screenspliter_wayland.h | 103 +++++ frame/screenspliter/screenspliter_xcb.cpp | 240 ++++++++++ frame/screenspliter/screenspliter_xcb.h | 55 +++ frame/window/mainpanelcontrol.cpp | 41 +- frame/window/quickpluginwindow.cpp | 2 +- 19 files changed, 1194 insertions(+), 170 deletions(-) create mode 100644 frame/screenspliter/screenspliter.cpp create mode 100644 frame/screenspliter/screenspliter.h create mode 100644 frame/screenspliter/screenspliter_wayland.cpp create mode 100644 frame/screenspliter/screenspliter_wayland.h create mode 100644 frame/screenspliter/screenspliter_xcb.cpp create mode 100644 frame/screenspliter/screenspliter_xcb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f8c914dc..e3367c511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,11 @@ configure_file(dde-dock.pc.in dde-dock.pc @ONLY) project(dde-dock) +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_PTHREADS_INIT 1) +set(CMAKE_PREFER_PTHREAD_FLAG ON) + #set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_CXX_STANDARD 14) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/debian/control b/debian/control index 2edbb43a3..66aa5d90c 100644 --- a/debian/control +++ b/debian/control @@ -26,7 +26,10 @@ Build-Depends: debhelper (>= 8.0.0), libgtest-dev, libgmock-dev, qttools5-dev, - libxcursor-dev + libxcursor-dev, + libqt5waylandclient5-dev, + qtwayland5-private-dev, + libdwayland-dev Standards-Version: 3.9.8 Homepage: http://www.deepin.org/ diff --git a/frame/CMakeLists.txt b/frame/CMakeLists.txt index 20724aa79..1c6775afd 100644 --- a/frame/CMakeLists.txt +++ b/frame/CMakeLists.txt @@ -5,8 +5,8 @@ set(BIN_NAME dde-dock) configure_file(environments.h.in environments.h @ONLY) if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fsanitize=address -O2") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -O2") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fsanitize=address -O0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -O0") endif() # Sources files @@ -19,6 +19,8 @@ find_package(Qt5Concurrent REQUIRED) find_package(Qt5X11Extras REQUIRED) find_package(Qt5DBus REQUIRED) find_package(Qt5Svg REQUIRED) +find_package(Qt5WaylandClient REQUIRED) +find_package(Qt5XkbCommonSupport REQUIRED) find_package(DtkWidget REQUIRED) find_package(DtkCMake REQUIRED) find_package(dbusmenu-qt5 REQUIRED) @@ -28,6 +30,9 @@ pkg_check_modules(DFrameworkDBus REQUIRED dframeworkdbus) pkg_check_modules(QGSettings REQUIRED gsettings-qt) pkg_check_modules(DtkGUI REQUIRED dtkgui) +set(Wayland_INCLUDE_DIRS /usr/include/DWayland/Client) +set(Wayland_LIBRARIES /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/libDWaylandClient.so) + # driver-manager add_executable(${BIN_NAME} ${SRCS} @@ -45,6 +50,10 @@ target_include_directories(${BIN_NAME} PUBLIC ${DtkGUI_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS} ${dbusmenu-qt5_INCLUDE_DIRS} + ${Wayland_INCLUDE_DIRS} + ${Qt5WaylandClient_INCLUDE_DIRS} + ${Qt5WaylandClient_PRIVATE_INCLUDE_DIRS} + ${Qt5XkbCommonSupport_PRIVATE_INCLUDE_DIRS} ../interfaces ../widgets accessible @@ -54,6 +63,7 @@ target_include_directories(${BIN_NAME} PUBLIC item item/components model + screenspliter util window window/components @@ -82,6 +92,10 @@ target_link_libraries(${BIN_NAME} PRIVATE ${QGSettings_LIBRARIES} ${DtkGUI_LIBRARIES} ${Qt5Svg_LIBRARIES} + ${Wayland_LIBRARIES} + ${Qt5Wayland_LIBRARIES} + ${Qt5WaylandClient_LIBRARIES} + ${Qt5XkbCommonSupport_LIBRARIES} -lpthread -lm ) diff --git a/frame/dbus/entryinterface.cpp b/frame/dbus/entryinterface.cpp index f2245f921..18443a888 100644 --- a/frame/dbus/entryinterface.cpp +++ b/frame/dbus/entryinterface.cpp @@ -49,7 +49,7 @@ void registerWindowInfoMetaType() QDebug operator<<(QDebug argument, const WindowInfo &info) { - argument << '(' << info.title << ',' << info.attention << ')'; + argument << '(' << info.title << ',' << info.attention << info.uuid << ')'; return argument; } @@ -57,7 +57,7 @@ QDebug operator<<(QDebug argument, const WindowInfo &info) QDBusArgument &operator<<(QDBusArgument &argument, const WindowInfo &info) { argument.beginStructure(); - argument << info.title << info.attention; + argument << info.title << info.attention << info.uuid; argument.endStructure(); return argument; @@ -66,7 +66,7 @@ QDBusArgument &operator<<(QDBusArgument &argument, const WindowInfo &info) const QDBusArgument &operator>>(const QDBusArgument &argument, WindowInfo &info) { argument.beginStructure(); - argument >> info.title >> info.attention; + argument >> info.title >> info.attention >> info.uuid; argument.endStructure(); return argument; @@ -74,14 +74,20 @@ const QDBusArgument &operator>>(const QDBusArgument &argument, WindowInfo &info) bool WindowInfo::operator==(const WindowInfo &rhs) const { - return attention == rhs.attention && - title == rhs.title; + return (attention == rhs.attention && + title == rhs.title && + uuid == rhs.uuid); } class EntryPrivate { public: - EntryPrivate() = default; + EntryPrivate() + : CurrentWindow(0) + , IsActive(false) + , IsDocked(false) + , mode(0) + {} // begin member variables uint CurrentWindow; diff --git a/frame/dbus/entryinterface.h b/frame/dbus/entryinterface.h index 2e0b84a7f..4cce4fef6 100644 --- a/frame/dbus/entryinterface.h +++ b/frame/dbus/entryinterface.h @@ -53,6 +53,7 @@ public: public: bool attention; QString title; + QString uuid; }; Q_DECLARE_METATYPE(WindowInfo) diff --git a/frame/display/displaymanager.cpp b/frame/display/displaymanager.cpp index a5d43c299..e4406fa37 100644 --- a/frame/display/displaymanager.cpp +++ b/frame/display/displaymanager.cpp @@ -69,6 +69,17 @@ QScreen *DisplayManager::screen(const QString &screenName) const return nullptr; } +QScreen *DisplayManager::screenAt(const QPoint &pos) const +{ + for (QScreen *screen : m_screens) { + QRect screenGeometry = screen->geometry(); + if (screenGeometry.contains(pos)) + return screen; + } + + return nullptr; +} + /** * @brief DisplayManager::primary * @return 主屏幕名称 diff --git a/frame/display/displaymanager.h b/frame/display/displaymanager.h index 99673dc15..bc4ebfa6d 100644 --- a/frame/display/displaymanager.h +++ b/frame/display/displaymanager.h @@ -45,6 +45,7 @@ public: QList screens() const; QScreen *screen(const QString &screenName) const; + QScreen *screenAt(const QPoint &pos) const; QString primary() const; int screenRawWidth() const; int screenRawHeight() const; diff --git a/frame/item/appitem.cpp b/frame/item/appitem.cpp index 3ddd8529c..875071760 100644 --- a/frame/item/appitem.cpp +++ b/frame/item/appitem.cpp @@ -25,6 +25,7 @@ #include "xcb_misc.h" #include "appswingeffectbuilder.h" #include "utils.h" +#include "screenspliter.h" #include #include @@ -70,6 +71,7 @@ AppItem::AppItem(const QGSettings *appSettings, const QGSettings *activeAppSetti , m_refershIconTimer(new QTimer(this)) , m_themeType(DGuiApplicationHelper::instance()->themeType()) , m_createMSecs(QDateTime::currentMSecsSinceEpoch()) + , m_screenSpliter(ScreenSpliterFactory::createScreenSpliter(this, m_itemEntryInter)) { QHBoxLayout *centralLayout = new QHBoxLayout; centralLayout->setMargin(0); @@ -103,7 +105,6 @@ AppItem::AppItem(const QGSettings *appSettings, const QGSettings *activeAppSetti connect(this, &AppItem::requestUpdateEntryGeometries, this, &AppItem::updateWindowIconGeometries); updateWindowInfos(m_itemEntryInter->windowInfos()); - refreshIcon(); if (m_appSettings) connect(m_appSettings, &QGSettings::changed, this, &AppItem::onGSettingsChanged); @@ -182,6 +183,31 @@ void AppItem::setDockInfo(Dock::Position dockPosition, const QRect &dockGeometry } } +void AppItem::setDraging(bool drag) +{ + if (drag == isDragging()) + return; + + DockItem::setDraging(drag); + if (!drag) + m_screenSpliter->releaseSplit(); +} + +void AppItem::startSplit(const QRect &rect) +{ + m_screenSpliter->startSplit(rect); +} + +bool AppItem::supportSplitWindow() +{ + return m_screenSpliter->suportSplitScreen(); +} + +bool AppItem::splitWindowOnScreen(ScreenSpliter::SplitDirection direction) +{ + return m_screenSpliter->split(direction); +} + QString AppItem::accessibleName() { return m_itemEntryInter->name(); @@ -636,7 +662,9 @@ void AppItem::refreshIcon() update(); return; - } else if (m_retryTimes > 0) { + } + + if (m_retryTimes > 0) { // reset times m_retryTimes = 0; } diff --git a/frame/item/appitem.h b/frame/item/appitem.h index de95780d8..5709ff6a0 100644 --- a/frame/item/appitem.h +++ b/frame/item/appitem.h @@ -36,6 +36,7 @@ #include class QGSettings; +class ScreenSpliter; class AppItem : public DockItem { @@ -52,7 +53,14 @@ public: void undock(); QWidget *appDragWidget(); void setDockInfo(Dock::Position dockPosition, const QRect &dockGeometry); + void setDraging(bool drag) override; + void startSplit(const QRect &rect); + bool supportSplitWindow(); + bool splitWindowOnScreen(ScreenSpliter::SplitDirection direction); +#ifdef USE_AM + int mode() const; +#endif inline ItemType itemType() const override { return App; } QPixmap appIcon(){ return m_appIcon; } virtual QString accessibleName() override; @@ -146,6 +154,8 @@ private: qint64 m_createMSecs; static QPoint MousePressPos; + + ScreenSpliter *m_screenSpliter; }; #endif // APPITEM_H diff --git a/frame/item/components/appdragwidget.cpp b/frame/item/components/appdragwidget.cpp index 1d32f4892..9f10cff54 100644 --- a/frame/item/components/appdragwidget.cpp +++ b/frame/item/components/appdragwidget.cpp @@ -22,6 +22,15 @@ #include "../appitem.h" #include "appdragwidget.h" #include "utils.h" +#include "displaymanager.h" + +#include + +#define SPLIT_NONE 0 +#define SPLIT_LEFT 1 +#define SPLIT_RIGHT 2 + +using XEventMonitor = ::com::deepin::api::XEventMonitor; AppDragWidget::AppDragWidget(QWidget *parent) : QGraphicsView(parent) @@ -34,16 +43,11 @@ AppDragWidget::AppDragWidget(QWidget *parent) , m_animGroup(new QParallelAnimationGroup(this)) , m_goBackAnim(new QPropertyAnimation(this, "pos", this)) , m_dockPosition(Dock::Position::Bottom) - , m_removeTips(new TipsWidget(this)) , m_popupWindow(new DockPopupWindow(nullptr)) , m_distanceMultiple(Utils::SettingValue("com.deepin.dde.dock.distancemultiple", "/com/deepin/dde/dock/distancemultiple/", "distance-multiple", 1.5).toDouble()) , m_item(nullptr) + , m_dockScreen(nullptr) { - m_removeTips->setText(tr("Remove")); - m_removeTips->setObjectName("AppRemoveTips"); - m_removeTips->setVisible(false); - m_removeTips->installEventFilter(this); - m_popupWindow->setShadowBlurRadius(20); m_popupWindow->setRadius(18); m_popupWindow->setShadowYOffset(2); @@ -55,11 +59,13 @@ AppDragWidget::AppDragWidget(QWidget *parent) m_scene->addItem(m_object.get()); setScene(m_scene); - setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); setAttribute(Qt::WA_TranslucentBackground); if (Utils::IS_WAYLAND_DISPLAY) { - setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::Window); + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::Window | Qt::FramelessWindowHint); setAttribute(Qt::WA_NativeWindow); + initWaylandEnv(); + } else { + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); } viewport()->setAutoFillBackground(false); setFrameShape(QFrame::NoFrame); @@ -71,10 +77,20 @@ AppDragWidget::AppDragWidget(QWidget *parent) initAnimations(); - m_followMouseTimer->setInterval(16); - connect(m_followMouseTimer, &QTimer::timeout, this, &AppDragWidget::onFollowMouse); - m_followMouseTimer->start(); - QTimer::singleShot(0, this, &AppDragWidget::onFollowMouse); + if (!Utils::IS_WAYLAND_DISPLAY) { + m_followMouseTimer->setInterval(16); + connect(m_followMouseTimer, &QTimer::timeout, this, &AppDragWidget::onFollowMouse); + m_followMouseTimer->start(); + QTimer::singleShot(0, this, &AppDragWidget::onFollowMouse); + } +} + +void AppDragWidget::execFinished() +{ + if (!m_bDragDrop) + return; + + dropHandler(QCursor::pos()); } void AppDragWidget::mouseMoveEvent(QMouseEvent *event) @@ -95,11 +111,11 @@ void AppDragWidget::dragEnterEvent(QDragEnterEvent *event) void AppDragWidget::dragMoveEvent(QDragMoveEvent *event) { - Q_UNUSED(event); - showRemoveTips(); - if (isRemoveItem() && m_bDragDrop) { - emit requestRemoveItem(); - } + if (Utils::IS_WAYLAND_DISPLAY) + return QGraphicsView::dragMoveEvent(event); + + if (m_bDragDrop) + moveHandler(QCursor::pos()); } /**获取应用的左上角坐标 @@ -156,18 +172,32 @@ const QPoint AppDragWidget::popupMarkPoint(Dock::Position pos) void AppDragWidget::dropEvent(QDropEvent *event) { + if (Utils::IS_WAYLAND_DISPLAY) + return dropEvent(event); + m_followMouseTimer->stop(); + dropHandler(QCursor::pos()); +} + +void AppDragWidget::hideEvent(QHideEvent *event) +{ + deleteLater(); + if (Utils::IS_WAYLAND_DISPLAY) + QGraphicsView::hideEvent(event); +} + +void AppDragWidget::dropHandler(const QPoint &pos) +{ m_bDragDrop = false; - if (isRemoveAble(QCursor::pos())) { + if (canSplitWindow(pos)) { if (DWindowManagerHelper::instance()->hasComposite()) { showRemoveAnimation(); } else { hide(); } - AppItem *appItem = static_cast((Utils::IS_WAYLAND_DISPLAY && m_item) ? m_item : event->source()); - appItem->undock(); m_popupWindow->setVisible(false); + Q_EMIT requestSplitWindow(splitPosition()); } else { if (DWindowManagerHelper::instance()->hasComposite()) { showGoBackAnimation(); @@ -177,11 +207,26 @@ void AppDragWidget::dropEvent(QDropEvent *event) } } -void AppDragWidget::hideEvent(QHideEvent *event) +void AppDragWidget::moveHandler(const QPoint &pos) { - deleteLater(); - if (Utils::IS_WAYLAND_DISPLAY) - QGraphicsView::hideEvent(event); + if (canSplitWindow(pos)) { + QRect screenGeometry = splitGeometry(pos); + if (screenGeometry.isValid() && screenGeometry != m_lastMouseGeometry) { + qDebug() << "change area:" << screenGeometry; + Q_EMIT requestChangedArea(screenGeometry); + m_lastMouseGeometry = screenGeometry; + } + } +} + +void AppDragWidget::moveCurrent(const QPoint &destPos) +{ + if (DWindowManagerHelper::instance()->hasComposite()) { + move(destPos.x() - width() / 2, destPos.y() - height() / 2); + } else { + // 窗口特效未开启时会隐藏m_object绘制的图标,移动的图标为QDrag绘制的图标 + move(destPos.x(), destPos.y()); + } } void AppDragWidget::setAppPixmap(const QPixmap &pix) @@ -211,7 +256,7 @@ void AppDragWidget::setOriginPos(const QPoint position) void AppDragWidget::setPixmapOpacity(qreal opacity) { - if (isRemoveAble(QCursor::pos())) { + if (canSplitWindow(QCursor::pos())) { m_object->setOpacity(opacity); m_animOpacity->setStartValue(opacity); } else { @@ -220,34 +265,6 @@ void AppDragWidget::setPixmapOpacity(qreal opacity) } } -bool AppDragWidget::isRemoveable(const Position &dockPos, const QRect &doctRect) -{ - const QPoint &p = QCursor::pos(); - switch (dockPos) { - case Dock::Position::Left: - if ((p.x() - doctRect.topRight().x()) > (doctRect.width() * 3)) { - return true; - } - break; - case Dock::Position::Top: - if ((p.y() - doctRect.bottomLeft().y()) > (doctRect.height() * 3)) { - return true; - } - break; - case Dock::Position::Right: - if ((doctRect.topLeft().x() - p.x()) > (doctRect.width() * 3)) { - return true; - } - break; - case Dock::Position::Bottom: - if ((doctRect.topLeft().y() - p.y()) > (doctRect.height() * 3)) { - return true; - } - break; - } - return false; -} - void AppDragWidget::initAnimations() { m_animScale->setDuration(300); @@ -303,79 +320,212 @@ void AppDragWidget::onRemoveAnimationStateChanged(QAbstractAnimation::State newS } } -/**判断图标拖到一定高度(默认任务栏高度的1.5倍)后是否可以移除 - * @brief AppDragWidget::isRemoveAble - * @param curPos 当前鼠标所在位置 - * @return 返回true可移除,false不可移除 +/** 判断应用区域图标是否被拖出任务栏 + * @brief AppDragWidget::canSplitWindow + * @return 返回true应用移出任务栏,false应用在任务栏内 */ -bool AppDragWidget::isRemoveAble(const QPoint &curPos) +bool AppDragWidget::canSplitWindow(const QPoint &pos) const { - const QPoint &p = curPos; switch (m_dockPosition) { case Dock::Position::Left: - if ((p.x() - m_dockGeometry.topRight().x()) > (m_dockGeometry.width() * m_distanceMultiple)) { + if ((pos.x() > m_dockGeometry.topRight().x())) { return true; } break; case Dock::Position::Top: - if ((p.y() - m_dockGeometry.bottomLeft().y()) > (m_dockGeometry.height() * m_distanceMultiple)) { + if ((pos.y() > m_dockGeometry.bottomLeft().y())) { return true; } break; case Dock::Position::Right: - if ((m_dockGeometry.topLeft().x() - p.x()) > (m_dockGeometry.width() * m_distanceMultiple)) { + if ((m_dockGeometry.topLeft().x() > pos.x())) { return true; } break; case Dock::Position::Bottom: - if ((m_dockGeometry.topLeft().y() - p.y()) > (m_dockGeometry.height() * m_distanceMultiple)) { + if ((m_dockGeometry.topLeft().y() > pos.y())) { return true; } break; } + return false; } -/**判断应用区域图标是否被拖出任务栏 - * @brief AppDragWidget::isRemoveItem - * @return 返回true应用移出任务栏,false应用在任务栏内 +/** + * @brief AppDragWidget::splitPosition + * @return 1 左分屏;2 右分屏;5 左上;6 右上;9 左下;10 右下;15全屏。这些值是窗管给的 */ -bool AppDragWidget::isRemoveItem() +ScreenSpliter::SplitDirection AppDragWidget::splitPosition() const { - const QPoint &p = QCursor::pos(); + QPoint pos = QCursor::pos(); + QScreen *currentScreen = DisplayManager::instance()->screenAt(pos); + + if (!currentScreen) + return ScreenSpliter::None; + + int xCenter = currentScreen->geometry().x() + currentScreen->size().width() / 2; + // 1表示左分屏 + if (pos.x() < xCenter) + return ScreenSpliter::Left; + + // 2表示右分屏 + if (pos.x() > xCenter) + return ScreenSpliter::Right; + + return ScreenSpliter::None; +} + +void AppDragWidget::adjustDesktopGeometry(QRect &rect) const +{ + QRect rectGeometry = m_dockGeometry; + rectGeometry.setWidth(rectGeometry.width() * qApp->devicePixelRatio()); + rectGeometry.setHeight(rectGeometry.height() * qApp->devicePixelRatio()); switch (m_dockPosition) { - case Dock::Position::Left: - if ((p.x() > m_dockGeometry.topRight().x())) { - return true; - } - break; - case Dock::Position::Top: - if ((p.y() > m_dockGeometry.bottomLeft().y())) { - return true; - } - break; - case Dock::Position::Right: - if ((m_dockGeometry.topLeft().x() > p.x())) { - return true; - } - break; - case Dock::Position::Bottom: - if ((m_dockGeometry.topLeft().y() > p.y())) { - return true; + case Dock::Position::Left: { + int leftX = (rectGeometry.x() + rectGeometry.width()) * qApp->devicePixelRatio(); + if (rect.x() < leftX) { + rect.setX(leftX); + rect.setWidth(rect.width() - (leftX - rect.x())); } break; } - return false; + case Dock::Position::Top: { + int topY = (rectGeometry.y() + rectGeometry.height()) * qApp->devicePixelRatio(); + if (rect.y() < topY) { + rect.setY(topY); + rect.setHeight(rect.height() - (topY - rect.y())); + } + break; + } + case Dock::Position::Right: { + int rightX = rectGeometry.x() * qApp->devicePixelRatio(); + if (rightX < rect.x() + rect.width() * qApp->devicePixelRatio()) + rect.setWidth(rect.width() - (rect.x() + rect.width() - rightX)); + break; + } + case Dock::Position::Bottom: { + int bottomY = rectGeometry.y() * qApp->devicePixelRatio(); + if (bottomY < rect.y() + rect.height() * qApp->devicePixelRatio()) + rect.setHeight(rect.height() - (rect.y() + rect.height() - bottomY)); + break; + } + } +} + +QRect AppDragWidget::splitGeometry(const QPoint &pos) const +{ + QList screens = DisplayManager::instance()->screens(); + for (QScreen *screen : screens) { + QRect screenGeometry = screen->geometry(); + screenGeometry.setWidth(screenGeometry.width() * qApp->devicePixelRatio()); + screenGeometry.setHeight(screenGeometry.height() * qApp->devicePixelRatio()); + if (!screenGeometry.contains(pos)) + continue; + + // 左右分屏即可 + int centerX = screenGeometry.x() + screenGeometry.width() / 2; + if (pos.x() < centerX) { + // 左分屏 + QRect rectLeft = screenGeometry; + rectLeft.setWidth(screenGeometry.width() / 2); + adjustDesktopGeometry(rectLeft); + return rectLeft; + } + if (pos.x() > centerX) { + // 右分屏 + QRect rectRight = screenGeometry; + rectRight.setLeft(screenGeometry.x() + screenGeometry.width() / 2); + rectRight.setWidth(screenGeometry.width() / 2); + adjustDesktopGeometry(rectRight); + return rectRight; + } + break; + } + return QRect(); +} + +void AppDragWidget::initWaylandEnv() +{ + if (!Utils::IS_WAYLAND_DISPLAY) + return; + + // 由于在wayland环境下无法触发drop事件,导致鼠标无法释放,所以这里暂时用XEventMonitor的方式(具体原因待查) + Dock::Position position = qApp->property(PROP_POSITION).value(); + XEventMonitor *extralEventInter = new XEventMonitor("com.deepin.api.XEventMonitor", "/com/deepin/api/XEventMonitor", QDBusConnection::sessionBus()); + QList extralRectList; + QList screens = DisplayManager::instance()->screens(); + for (QScreen *screen : screens) { + MonitRect monitorRect; + QRect screenRect = screen->geometry(); + screenRect.setSize(screenRect.size() * screen->devicePixelRatio()); + + switch (position) { + case Top: { + monitorRect.x1 = screenRect.x(); + monitorRect.y1 = screenRect.y(); + monitorRect.x2 = screenRect.x() + screenRect.width(); + monitorRect.y2 = screenRect.y(); + } + break; + case Bottom: { + monitorRect.x1 = screenRect.x(); + monitorRect.y1 = screenRect.y() + screenRect.height(); + monitorRect.x2 = screenRect.x() + screenRect.width(); + monitorRect.y2 = screenRect.y() + screenRect.height(); + } + break; + case Left: { + monitorRect.x1 = screenRect.x(); + monitorRect.y1 = screenRect.y(); + monitorRect.x2 = screenRect.x(); + monitorRect.y2 = screenRect.y() + screenRect.height(); + } + break; + case Right: { + monitorRect.x1 = screenRect.x() + screenRect.width(); + monitorRect.y1 = screenRect.y(); + monitorRect.x2 = screenRect.x() + screenRect.width(); + monitorRect.y2 = screenRect.y() + screenRect.height(); + } + break; + } + + if (!extralRectList.contains(monitorRect)) + extralRectList << monitorRect; + } + + QString key = extralEventInter->RegisterAreas(extralRectList, 1 << 1); + connect(this, &AppDragWidget::destroyed, this, [ key, extralEventInter ] { + extralEventInter->UnregisterArea(key); + delete extralEventInter; + QDrag::cancel(); + }); + + connect(extralEventInter, &XEventMonitor::ButtonRelease, this, &AppDragWidget::onButtonRelease); + connect(extralEventInter, &XEventMonitor::CursorMove,this, &AppDragWidget::onCursorMove); +} + +void AppDragWidget::onButtonRelease(int, int x, int y, const QString &) +{ + if (!m_bDragDrop) + return; + + dropHandler(QPoint(x, y)); + + QDrag::cancel(); +} + +void AppDragWidget::onCursorMove(int x, int y, const QString &) +{ + QPoint pos = QPoint(x, y); + moveCurrent(pos); + moveHandler(pos); } void AppDragWidget::onFollowMouse() { - QPoint destPos = QCursor::pos(); - if (DWindowManagerHelper::instance()->hasComposite()) { - move(destPos.x() - width() / 2, destPos.y() - height() / 2); - } else { - move(destPos.x(), destPos.y()); // 窗口特效未开启时会隐藏m_object绘制的图标,移动的图标为QDrag绘制的图标 - } + moveCurrent(QCursor::pos()); } void AppDragWidget::enterEvent(QEvent *event) @@ -388,50 +538,6 @@ void AppDragWidget::enterEvent(QEvent *event) } } -/**显示移除应用提示窗口 - * @brief AppDragWidget::showRemoveTips - */ -void AppDragWidget::showRemoveTips() -{ - Dock::Position pos = Dock::Position::Bottom; - - DockPopupWindow *popup = m_popupWindow.data(); - if (isRemoveAble(QCursor::pos())) { - QWidget *lastContent = popup->getContent(); - if (lastContent) - lastContent->setVisible(false); - - switch (pos) { - case Top: popup->setArrowDirection(DockPopupWindow::ArrowTop); break; - case Bottom: popup->setArrowDirection(DockPopupWindow::ArrowBottom); break; - case Left: popup->setArrowDirection(DockPopupWindow::ArrowLeft); break; - case Right: popup->setArrowDirection(DockPopupWindow::ArrowRight); break; - } - popup->resize(m_removeTips->sizeHint()); - popup->setContent(m_removeTips); - - const QPoint p = popupMarkPoint(pos); - if (!popup->isVisible()) - QMetaObject::invokeMethod(popup, "show", Qt::QueuedConnection, Q_ARG(QPoint, p), Q_ARG(bool, true)); - else - popup->show(p, true); - - m_object->setOpacity(0.5); - m_animOpacity->setStartValue(0.5); - } else { - m_object->setOpacity(1.0); - m_animOpacity->setStartValue(1.0); - if (popup->isVisible()) - popup->setVisible(false); - } -} - -void AppDragWidget::moveEvent(QMoveEvent *event) -{ - Q_UNUSED(event); - showRemoveTips(); -} - QuickDragWidget::QuickDragWidget(QWidget *parent) : AppDragWidget(parent) { @@ -471,3 +577,36 @@ void QuickDragWidget::dragMoveEvent(QDragMoveEvent *event) AppDragWidget::dragMoveEvent(event); requestDragMove(event); } + +/**判断图标拖到一定高度(默认任务栏高度的1.5倍)后是否可以移除 + * @brief AppDragWidget::isRemoveAble + * @param curPos 当前鼠标所在位置 + * @return 返回true可移除,false不可移除 + */ +bool QuickDragWidget::isRemoveAble(const QPoint &curPos) +{ + const QPoint &p = curPos; + switch (m_dockPosition) { + case Dock::Position::Left: + if ((p.x() - m_dockGeometry.topRight().x()) > (m_dockGeometry.width() * m_distanceMultiple)) { + return true; + } + break; + case Dock::Position::Top: + if ((p.y() - m_dockGeometry.bottomLeft().y()) > (m_dockGeometry.height() * m_distanceMultiple)) { + return true; + } + break; + case Dock::Position::Right: + if ((m_dockGeometry.topLeft().x() - p.x()) > (m_dockGeometry.width() * m_distanceMultiple)) { + return true; + } + break; + case Dock::Position::Bottom: + if ((m_dockGeometry.topLeft().y() - p.y()) > (m_dockGeometry.height() * m_distanceMultiple)) { + return true; + } + break; + } + return false; +} diff --git a/frame/item/components/appdragwidget.h b/frame/item/components/appdragwidget.h index 666b78700..12304a7e6 100644 --- a/frame/item/components/appdragwidget.h +++ b/frame/item/components/appdragwidget.h @@ -23,6 +23,8 @@ #define APPDRAGWIDGET_H #include "constants.h" +#include "screenspliter.h" +#include "utils.h" #include #include @@ -38,6 +40,9 @@ #include "dockpopupwindow.h" #include "dockitem.h" +class QDrag; +class DockScreen; + class AppGraphicsObject : public QGraphicsObject { public: @@ -85,18 +90,18 @@ class AppDragWidget : public QGraphicsView public: explicit AppDragWidget(QWidget *parent = Q_NULLPTR); + void execFinished(); void setAppPixmap(const QPixmap &pix); void setDockInfo(Dock::Position dockPosition, const QRect &dockGeometry); void setOriginPos(const QPoint position); void setPixmapOpacity(qreal opacity); - bool isRemoveAble(const QPoint &curPos); void setItem(DockItem *item) { m_item = item; } - static bool isRemoveable(const Dock::Position &dockPos, const QRect &doctRect); void showRemoveAnimation(); void showGoBackAnimation(); signals: - void requestRemoveItem(); + void requestChangedArea(QRect); + void requestSplitWindow(ScreenSpliter::SplitDirection); protected: void mouseMoveEvent(QMouseEvent *event) override; @@ -104,7 +109,6 @@ protected: void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; void hideEvent(QHideEvent *event) override; - void moveEvent(QMoveEvent *event) override; void enterEvent(QEvent *event) override; private: @@ -113,11 +117,20 @@ private: QAbstractAnimation::State oldState); const QPoint popupMarkPoint(Dock::Position pos); const QPoint topleftPoint() const; - void showRemoveTips(); - bool isRemoveItem(); + bool canSplitWindow(const QPoint &pos) const; + ScreenSpliter::SplitDirection splitPosition() const; + QRect splitGeometry(const QPoint &pos) const; + void initWaylandEnv(); + + void dropHandler(const QPoint &pos); + void moveHandler(const QPoint &pos); + void moveCurrent(const QPoint &destPos); + void adjustDesktopGeometry(QRect &rect) const; private Q_SLOTS: void onFollowMouse(); + void onButtonRelease(int, int x, int y, const QString &); + void onCursorMove(int x, int y, const QString &); protected: QScopedPointer m_object; @@ -133,7 +146,6 @@ protected: QRect m_dockGeometry; QPoint m_originPoint; QSize m_iconSize; - Dock::TipsWidget *m_removeTips; QScopedPointer m_popupWindow; /** * @brief m_distanceMultiple: 倍数 @@ -143,6 +155,8 @@ protected: bool m_bDragDrop = false; // 图标是否被拖拽 DockItem *m_item; + QRect m_lastMouseGeometry; + DockScreen *m_dockScreen; }; class QuickDragWidget : public AppDragWidget @@ -160,6 +174,9 @@ public: protected: void dropEvent(QDropEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; + +private: + bool isRemoveAble(const QPoint &curPos); }; #endif /* APPDRAGWIDGET_H */ diff --git a/frame/screenspliter/screenspliter.cpp b/frame/screenspliter/screenspliter.cpp new file mode 100644 index 000000000..f900d3423 --- /dev/null +++ b/frame/screenspliter/screenspliter.cpp @@ -0,0 +1,60 @@ +/* + * 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 "screenspliter.h" +#include "appitem.h" +#include "utils.h" +#include "screenspliter_xcb.h" +#include "screenspliter_wayland.h" + +bool ScreenSpliter::releaseSplit() +{ + return true; +} + +ScreenSpliter::ScreenSpliter(AppItem *appItem, DockEntryInter *entryInter, QObject *parent) + : QObject(parent) + , m_appItem(appItem) + , m_entryInter(entryInter) +{ +} + +ScreenSpliter::~ScreenSpliter() +{ + m_appItem = nullptr; +} + +AppItem *ScreenSpliter::appItem() const +{ + return m_appItem; +} + +DockEntryInter *ScreenSpliter::entryInter() const +{ + return m_entryInter; +} + +ScreenSpliter *ScreenSpliterFactory::createScreenSpliter(AppItem *appItem, DockEntryInter *entryInter) +{ + if (Utils::IS_WAYLAND_DISPLAY) + return new ScreenSpliter_Wayland(appItem, entryInter, appItem); + + return new ScreenSpliter_Xcb(appItem, entryInter, appItem); +} diff --git a/frame/screenspliter/screenspliter.h b/frame/screenspliter/screenspliter.h new file mode 100644 index 000000000..7365b6c58 --- /dev/null +++ b/frame/screenspliter/screenspliter.h @@ -0,0 +1,71 @@ +/* + * 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 SCREENSPLITER_H +#define SCREENSPLITER_H + +#include "dbusutil.h" + +#include + +class AppItem; + +class ScreenSpliter : public QObject +{ + Q_OBJECT + +public: + enum SplitDirection { + None, // 无操作 + Left, // 左分屏 + Right, // 右分屏 + Top, // 上分屏 + Bottom, // 下分屏 + LeftTop, // 左上 + RightTop, // 右上 + LeftBottom, // 左下 + RightBottom, // 右下 + Full // 全屏 + }; + +public: + virtual void startSplit(const QRect &) = 0; // 触发分屏提示效果 + virtual bool split(SplitDirection) = 0; // 开始触发分屏 + virtual bool suportSplitScreen() = 0; // 是否支持分屏 + virtual bool releaseSplit(); // 释放分屏 + +protected: + explicit ScreenSpliter(AppItem *appItem, DockEntryInter *entryInter, QObject *parent = nullptr); + virtual ~ScreenSpliter(); + AppItem *appItem() const; + DockEntryInter *entryInter() const; + +private: + AppItem *m_appItem; + DockEntryInter *m_entryInter; +}; + +class ScreenSpliterFactory +{ +public: + static ScreenSpliter *createScreenSpliter(AppItem *appItem, DockEntryInter *entryInter); +}; + +#endif // SCREENSPLITER_H diff --git a/frame/screenspliter/screenspliter_wayland.cpp b/frame/screenspliter/screenspliter_wayland.cpp new file mode 100644 index 000000000..3be9ea623 --- /dev/null +++ b/frame/screenspliter/screenspliter_wayland.cpp @@ -0,0 +1,247 @@ +/* + * 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 "screenspliter_wayland.h" +#include "appitem.h" + +#include +#include +#include +#include +#define private public +#include +#include +#include +#include +#undef private + +#include +#include +#include +#include +#include +#include +#include + +SplitWindowManager *ScreenSpliter_Wayland::m_splitManager = nullptr; + +/** wayland下的分屏功能 + * @brief ScreenSpliter_Wayland::ScreenSpliter_Wayland + * @param parent + */ +ScreenSpliter_Wayland::ScreenSpliter_Wayland(AppItem *appItem, DockEntryInter *entryInter, QObject *parent) + : ScreenSpliter(appItem, entryInter, parent) + , m_checkedNotSupport(false) +{ + if (!m_splitManager) + m_splitManager = new SplitWindowManager; + + connect(m_splitManager, &SplitWindowManager::splitStateChange, this, &ScreenSpliter_Wayland::onSplitStateChange); +} + +ScreenSpliter_Wayland::~ScreenSpliter_Wayland() +{ +} + +void ScreenSpliter_Wayland::startSplit(const QRect &rect) +{ + if (entryInter()->windowInfos().size() == 0) { + // 如果默认打开的子窗口的数量为0,则无需操作,同时记录标记,在打开新的窗口的时候,设置遮罩 + m_splitRect = rect; + entryInter()->Activate(QX11Info::getTimestamp()); + return; + } + + setMaskVisible(rect, true); +} + +void ScreenSpliter_Wayland::setMaskVisible(const QRect &rect, bool visible) +{ + static QWidget *desktopWidget = nullptr; + if (!desktopWidget) { + desktopWidget = new QWidget; + DPalette palette = DGuiApplicationHelper::instance()->applicationPalette(); + QColor backColor = palette.color(QPalette::Highlight); + backColor.setAlpha(255 * 0.3); + palette.setBrush(QPalette::ColorRole::Background, backColor); + desktopWidget->setPalette(palette); + desktopWidget->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); + } + desktopWidget->setVisible(visible); + desktopWidget->setGeometry(rect); + desktopWidget->raise(); +} + +bool ScreenSpliter_Wayland::split(SplitDirection direction) +{ + setMaskVisible(QRect(), false); + const QString windowUuid = splitUuid(); + if (windowUuid.isEmpty()) + return false; + + std::string sUuid = windowUuid.toStdString(); + const char *uuid = sUuid.c_str(); + m_splitManager->requestSplitWindow(uuid, direction); + return true; +} + +QString ScreenSpliter_Wayland::splitUuid() const +{ +#ifdef USE_AM + WindowInfoMap windowsInfo = entryInter()->windowInfos(); + if (windowsInfo.isEmpty()) + return QString(); + + const QString uuid = windowsInfo.values()[0].uuid; + if (windowSupportSplit(uuid)) + return uuid; + +#endif + return QString(); +} + +bool ScreenSpliter_Wayland::windowSupportSplit(const QString &uuid) const +{ + return m_splitManager->canSplit(uuid); +} + +QString ScreenSpliter_Wayland::firstWindowUuid() const +{ +#ifdef USE_AM + WindowInfoMap winInfos = entryInter()->windowInfos(); + if (winInfos.size() == 0) + return QString(); + + return winInfos.begin().value().uuid; +#else + return QString(); +#endif +} + +void ScreenSpliter_Wayland::onSplitStateChange(const char *uuid, int splitable) +{ +#ifdef USE_AM + const QString windowUuid = firstWindowUuid(); + qDebug() << "Split State Changed, window uuid:" << windowUuid << "split uuid:" << uuid << "split value:" << splitable; + if (QString(uuid) != windowUuid) + return; + + if (m_splitRect.isEmpty()) + return; + + if (splitable > 0) { + setMaskVisible(m_splitRect, true); + } else { + // 如果不支持二分屏,则退出当前的窗体,且标记当前不支持二分屏,下次打开的时候不再进行打开窗口来检测 + entryInter()->ForceQuit(); + m_checkedNotSupport = true; + } + m_splitRect = QRect(0, 0, 0, 0); +#endif +} + +bool ScreenSpliter_Wayland::suportSplitScreen() +{ + // 如果之前检测过是否不支持分屏(m_checkedNotSupport默认为false,如果不支持分屏,m_checkedNotSupport就会变为true),则直接返回不支持分屏 + if (m_checkedNotSupport) + return false; + + // 如果存在未打开的窗口,就默认让其认为支持,后续会根据这个来打开一个新的窗口 + if (entryInter()->windowInfos().size() == 0) + return true; + + // 如果存在已经打开的窗口 + m_checkedNotSupport = splitUuid().isEmpty(); + return (!m_checkedNotSupport); +} + +/** + * @brief SplitWindowManager::SplitWindowManager + * @param wayland下的分屏的管理 + */ +SplitWindowManager::SplitWindowManager(QObject *parent) + : QObject(parent) + , m_clientManagement(nullptr) + , m_connectionThread(new QThread(nullptr)) + , m_connectionThreadObject(new ConnectionThread) +{ + connect(m_connectionThreadObject, &ConnectionThread::connected, this, &SplitWindowManager::onConnectionFinished, Qt::QueuedConnection); + + m_connectionThreadObject->moveToThread(m_connectionThread); + m_connectionThread->start(); + m_connectionThreadObject->initConnection(); +} + +SplitWindowManager::~SplitWindowManager() +{ +} + +bool SplitWindowManager::canSplit(const QString &uuid) const +{ + if (!m_clientManagement) + return false; + + const QVector &clientWindowStates = m_clientManagement->getWindowStates(); + qInfo() << "client window states count:" << clientWindowStates.size(); + for (ClientManagement::WindowState windowState : clientWindowStates) { + qDebug() << "window uuid:" << uuid << "window state uuid:" << windowState.uuid + << "active:" << windowState.isActive << "resource name:" << windowState.resourceName; + if (windowState.splitable > 0 && QString(windowState.uuid) == uuid) + return true; + }; + + return false; +} + +static ClientManagement::SplitType convertSplitType(ScreenSpliter::SplitDirection direction) +{ + static QMap direcionMapping = { + { ScreenSpliter::Left, ClientManagement::SplitType::Left }, + { ScreenSpliter::Right, ClientManagement::SplitType::Right}, + { ScreenSpliter::Top, ClientManagement::SplitType::Top }, + { ScreenSpliter::Bottom, ClientManagement::SplitType::Bottom }, + { ScreenSpliter::LeftTop, ClientManagement::SplitType::LeftTop }, + { ScreenSpliter::RightTop, ClientManagement::SplitType::RightTop }, + { ScreenSpliter::LeftBottom, ClientManagement::SplitType::LeftBottom }, + { ScreenSpliter::RightBottom, ClientManagement::SplitType::RightBottom } + }; + + return direcionMapping.value(direction, ClientManagement::SplitType::None); +} + +void SplitWindowManager::requestSplitWindow(const char *uuid, const ScreenSpliter::SplitDirection &direction) +{ + m_clientManagement->requestSplitWindow(uuid, convertSplitType(direction)); +} + +void SplitWindowManager::onConnectionFinished() +{ + EventQueue *eventQueue = new EventQueue(this); + eventQueue->setup(m_connectionThreadObject); + + Registry *registry = new Registry(this); + connect(registry, &Registry::clientManagementAnnounced, this, [ this, registry ](quint32 name, quint32 version) { + m_clientManagement = registry->createClientManagement(name, version, this); + connect(m_clientManagement, &ClientManagement::splitStateChange, this, &SplitWindowManager::splitStateChange); + }); + registry->setEventQueue(eventQueue); + registry->create(m_connectionThreadObject); + registry->setup(); +} diff --git a/frame/screenspliter/screenspliter_wayland.h b/frame/screenspliter/screenspliter_wayland.h new file mode 100644 index 000000000..79ef7c027 --- /dev/null +++ b/frame/screenspliter/screenspliter_wayland.h @@ -0,0 +1,103 @@ +/* + * 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 SCREENSPLITER_WAYLAND_H +#define SCREENSPLITER_WAYLAND_H + +#include "screenspliter.h" + +#include + +namespace KWayland { +namespace Client { +class Registry; +class DDEShell; +class DDEShellSurface; +class EventQueue; +class Compositor; +class Surface; +class ClientManagement; +class ConnectionThread; +} +} + +class AppItem; +class QWindow; +class QThread; +class SplitWindowManager; + +class WindowInfo; +typedef QMap WindowInfoMap; + +using namespace KWayland::Client; + +class ScreenSpliter_Wayland : public ScreenSpliter +{ + Q_OBJECT + +public: + explicit ScreenSpliter_Wayland(AppItem *appItem, DockEntryInter *entryInter, QObject *parent); + ~ScreenSpliter_Wayland() override; + + void startSplit(const QRect &rect) override; + bool split(SplitDirection direction) override; + bool suportSplitScreen() override; + +private: + void setMaskVisible(const QRect &rect, bool visible); + QString splitUuid() const; + bool windowSupportSplit(const QString &uuid) const; + QString firstWindowUuid() const; + +private Q_SLOTS: + void onSplitStateChange(const char* uuid, int splitable); + +private: + static SplitWindowManager *m_splitManager; + QRect m_splitRect; + bool m_checkedNotSupport; +}; + +class SplitWindowManager : public QObject +{ + Q_OBJECT + + friend class ScreenSpliter_Wayland; + +protected: + explicit SplitWindowManager(QObject *parent = Q_NULLPTR); + ~SplitWindowManager() override; + + bool canSplit(const QString &uuid) const; + void requestSplitWindow(const char *uuid, const ScreenSpliter::SplitDirection &direction); + +Q_SIGNALS: + void splitStateChange(const char *, int); + +private Q_SLOTS: + void onConnectionFinished(); + +private: + ClientManagement *m_clientManagement; + QThread *m_connectionThread; + ConnectionThread *m_connectionThreadObject; +}; + +#endif // SCREENSPLITER_WAYLAND_H diff --git a/frame/screenspliter/screenspliter_xcb.cpp b/frame/screenspliter/screenspliter_xcb.cpp new file mode 100644 index 000000000..3d954691c --- /dev/null +++ b/frame/screenspliter/screenspliter_xcb.cpp @@ -0,0 +1,240 @@ +/* + * 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 "screenspliter_xcb.h" +#include "appitem.h" + +#include + +#include +#include +#include + +#define LEFT 1 +#define RIGHT 2 +#define TOP 3 +#define BOTTOM 4 +#define LEFTTOP 5 +#define RIGHTTOP 6 +#define LEFTBOTTOM 9 +#define RIGHTBOTTOM 10 +#define SPLITUNKNOW 0 + +static xcb_atom_t internAtom(const char *name, bool only_if_exist) +{ + if (!name || *name == 0) + return XCB_NONE; + + xcb_connection_t *connection = QX11Info::connection(); + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, only_if_exist, strlen(name), name); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, cookie, 0); + if (!reply) + return XCB_NONE; + + xcb_atom_t atom = reply->atom; + free(reply); + + return atom; +} + +static QByteArray windowProperty(quint32 WId, xcb_atom_t propAtom, xcb_atom_t typeAtom, quint32 len) +{ + xcb_connection_t *conn = QX11Info::connection(); + xcb_get_property_cookie_t cookie = xcb_get_property(conn, false, WId, propAtom, typeAtom, 0, len); + xcb_generic_error_t *err = nullptr; + xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, &err); + + QByteArray data; + if (reply != nullptr) { + int valueLen = xcb_get_property_value_length(reply); + const char *buf = static_cast(xcb_get_property_value(reply)); + data.append(buf, valueLen); + free(reply); + } + + if (err != nullptr) { + free(err); + } + + return data; +} + +ScreenSpliter_Xcb::ScreenSpliter_Xcb(AppItem *appItem, DockEntryInter *entryInter, QObject *parent) + : ScreenSpliter(appItem, entryInter, parent) + , m_isSplitCreateWindow(false) +{ + connect(entryInter, &DockEntryInter::WindowInfosChanged, + this, &ScreenSpliter_Xcb::onUpdateWindowInfo, Qt::QueuedConnection); +} + +void ScreenSpliter_Xcb::startSplit(const QRect &rect) +{ + if (!openWindow()) { + m_effectRect = rect; + return; + } + showSplitScreenEffect(rect, true); +} + +bool ScreenSpliter_Xcb::split(ScreenSpliter::SplitDirection direction) +{ + showSplitScreenEffect(QRect(), false); + if (!openWindow()) + return false; + + // 如果当前的应用不支持分屏,也无需分屏,检查分屏的时候至少需要一个窗口,因此这里写在打开窗口之后 + quint32 WId = splittingWindowWId(); + if (WId == 0) { + // 如果当前存在主动打开的窗口,那么就关闭当前主动打开的窗口 + if (m_isSplitCreateWindow) { + entryInter()->ForceQuit(); + m_isSplitCreateWindow = false; + } + return false; + } + + xcb_client_message_event_t xev; + + xev.response_type = XCB_CLIENT_MESSAGE; + xev.type = internAtom("_DEEPIN_SPLIT_WINDOW", false); + xev.window = WId; + xev.format = 32; + xev.data.data32[0] = direction_x11(direction); // 1: 左分屏 2: 右分屏 5 左上 6 右上 9 左下 10 右下 15: 全屏 + xev.data.data32[1] = 1; // 1 进入预览 0 不进入预览 + + xcb_send_event(QX11Info::connection(), false, QX11Info::appRootWindow(QX11Info::appScreen()), + SubstructureNotifyMask, (const char *)&xev); + xcb_flush(QX11Info::connection()); + return true; +} + +uint32_t ScreenSpliter_Xcb::direction_x11(ScreenSpliter::SplitDirection direction) +{ + static QMap directionMapping = { + { ScreenSpliter::Left, LEFT }, + { ScreenSpliter::Right, RIGHT }, + { ScreenSpliter::Top, TOP }, + { ScreenSpliter::Bottom, TOP }, + { ScreenSpliter::LeftTop, LEFTTOP }, + { ScreenSpliter::RightTop, RIGHTTOP }, + { ScreenSpliter::LeftBottom, LEFTBOTTOM }, + { ScreenSpliter::RightBottom, RIGHTBOTTOM } + }; + + return directionMapping.value(direction, SPLITUNKNOW); +} + +bool ScreenSpliter_Xcb::openWindow() +{ + // 查看当前应用是否有打开的窗口,如果没有,则先打开一个窗口 + const WindowInfoMap windowlist = entryInter()->windowInfos(); + if (!windowlist.isEmpty()) + return true; + + if (!m_isSplitCreateWindow) { + // 如果当前没有打开窗口,且未执行打开操作 + entryInter()->Activate(QX11Info::getTimestamp()); + m_isSplitCreateWindow = true; + } + + return false; +} + +void ScreenSpliter_Xcb::showSplitScreenEffect(const QRect &rect, bool visible) +{ + quint32 WId = splittingWindowWId(); + if (WId == 0) + return; + + // 触发分屏的效果 + xcb_client_message_event_t xev; + + xev.response_type = XCB_CLIENT_MESSAGE; + xev.type = internAtom("_DEEPIN_SPLIT_OUTLINE", false); + xev.window = WId; + xev.format = 32; + xev.data.data32[0] = visible ? 1 : 0; // 1: 显示 0: 取消 + xev.data.data32[1] = rect.x(); // X坐标 + xev.data.data32[2] = rect.y(); // Y坐标 + xev.data.data32[3] = rect.width(); // width + xev.data.data32[4] = rect.height(); // height + + xcb_send_event(QX11Info::connection(), false, QX11Info::appRootWindow(QX11Info::appScreen()), + SubstructureNotifyMask, (const char *)&xev); + xcb_flush(QX11Info::connection()); +} + +void ScreenSpliter_Xcb::onUpdateWindowInfo(const WindowInfoMap &info) +{ + // 如果打开的是第一个窗口,且这个打开的窗口是通过拖动二分屏的方式打开,且当前是结束拖拽 + // 并且不支持分屏那么这个窗口就需要关闭 + if (!appItem()->isDragging()) { + releaseSplit(); + } else if (!m_effectRect.isEmpty() && info.size() > 0) { + // 只有当需要触发分屏效果的时候,发现当前没有窗口,则记录当前分屏的区域,保存在m_effectRect中 + // 在新增窗口的时候,如果返现m_effectRect有值,则重新触发分屏,并且清空m_effectRect,防止再次打开窗口的时候再次触发分屏效果 + showSplitScreenEffect(m_effectRect, true); + m_effectRect.setRect(0, 0, 0, 0); + } +} + +bool ScreenSpliter_Xcb::suportSplitScreen() +{ + // 如果当前的窗口的数量为0,则不知道它是否支持分屏,则始终让其返回true,然后打开窗口,因为窗口打开后, + // 要过一段事件才能收到信号,等收到信号后才知道它是否支持分屏,在窗口显示后会根据当前是否请求过执行分屏操作 + // 来决定是否执行分屏的操作 + if (entryInter()->windowInfos().size() == 0) + return true; + + return (splittingWindowWId() != 0); +} + +bool ScreenSpliter_Xcb::releaseSplit() +{ + if (!m_isSplitCreateWindow) + return false; + + if (!entryInter()->windowInfos().isEmpty() && splittingWindowWId() == 0) { + // 释放后,如果当前的窗口是通过验证是否支持二分屏的方式来新建的窗口(m_isSplitCreateWindow == true) + // 并且存在打开的窗口(也有可能不存在打开的窗口,打开的窗口最后才出来,时机上不好控制,所以这种情况 + // 在updateWindowInfos函数里面做了处理),并且打开的窗口不支持二分屏,则此时关闭新打开的窗口 + entryInter()->ForceQuit(); + } + + m_isSplitCreateWindow = false; + return true; +} + +quint32 ScreenSpliter_Xcb::splittingWindowWId() +{ + WindowInfoMap windowsInfo = entryInter()->windowInfos(); + if (windowsInfo.size() == 0) + return 0; + + quint32 WId = windowsInfo.keys().first(); + xcb_atom_t propAtom = internAtom("_DEEPIN_NET_SUPPORTED", true); + QByteArray data = windowProperty(WId, propAtom, XCB_ATOM_CARDINAL, 4); + + bool supported = false; + if (const char *cdata = data.constData()) + supported = *(reinterpret_cast(cdata)); + + return supported ? WId : 0; +} diff --git a/frame/screenspliter/screenspliter_xcb.h b/frame/screenspliter/screenspliter_xcb.h new file mode 100644 index 000000000..b8ac69a8b --- /dev/null +++ b/frame/screenspliter/screenspliter_xcb.h @@ -0,0 +1,55 @@ +/* + * 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 SCREENSPLITER_XCB_H +#define SCREENSPLITER_XCB_H + +#include "screenspliter.h" + +#include + +class WindowInfo; +typedef QMap WindowInfoMap; + +class ScreenSpliter_Xcb : public ScreenSpliter +{ +public: + explicit ScreenSpliter_Xcb(AppItem *appItem, DockEntryInter *entryInter, QObject *parent = nullptr); + + void startSplit(const QRect &rect) override; + bool split(ScreenSpliter::SplitDirection direction) override; + bool suportSplitScreen() override; + bool releaseSplit() override; + +private: + quint32 splittingWindowWId(); + uint32_t direction_x11(ScreenSpliter::SplitDirection direction); + void showSplitScreenEffect(const QRect &rect, bool visible); + bool openWindow(); + +private Q_SLOTS: + void onUpdateWindowInfo(const WindowInfoMap &info); + +private: + bool m_isSplitCreateWindow; + QRect m_effectRect; +}; + +#endif // SCREENSPLITER_XCB_H diff --git a/frame/window/mainpanelcontrol.cpp b/frame/window/mainpanelcontrol.cpp index 6d0c5f2fb..4495e7403 100755 --- a/frame/window/mainpanelcontrol.cpp +++ b/frame/window/mainpanelcontrol.cpp @@ -47,17 +47,16 @@ #include #include #include +#include + #include #include -#define protected public -#include -#undef protected -#include -#include #include #include +#include + #define SPLITER_SIZE 2 #define TRASH_MARGIN 20 #define PLUGIN_MAX_SIZE 40 @@ -794,8 +793,25 @@ void MainPanelControl::startDrag(DockItem *dockItem) m_appDragWidget = appDrag->appDragWidget(); + connect(m_appDragWidget, &AppDragWidget::requestChangedArea, this, [ = ](QRect rect) { + // 在区域改变的时候,出现分屏提示效果 + AppItem *appItem = static_cast(dockItem); + if (appItem->supportSplitWindow()) + appItem->startSplit(rect); + }); + + connect(m_appDragWidget, &AppDragWidget::requestSplitWindow, this, [ = ](ScreenSpliter::SplitDirection dir) { + AppItem *appItem = static_cast(dockItem); + if (appItem->supportSplitWindow()) + appItem->splitWindowOnScreen(dir); + }); + connect(m_appDragWidget, &AppDragWidget::destroyed, this, [ = ] { m_appDragWidget = nullptr; + AppItem *appItem = static_cast(dockItem); + if (appItem->supportSplitWindow()) + return; + if (!item.isNull() && qobject_cast(item)->isValid()) { // 如果是从最近打开区域移动到应用区域的,则需要将其固定 dockRecentApp(item); @@ -811,13 +827,6 @@ void MainPanelControl::startDrag(DockItem *dockItem) } }); - connect(m_appDragWidget, &AppDragWidget::requestRemoveItem, this, [ = ] { - if (-1 != m_appAreaSonLayout->indexOf(item)) { - m_dragIndex = m_appAreaSonLayout->indexOf(item); - removeItem(item); - } - }); - appDrag->appDragWidget()->setOriginPos((m_appAreaSonWidget->mapToGlobal(item->pos()))); appDrag->appDragWidget()->setDockInfo(m_position, QRect(mapToGlobal(pos()), size())); const QPixmap &dragPix = qobject_cast(item)->appIcon(); @@ -852,6 +861,11 @@ void MainPanelControl::startDrag(DockItem *dockItem) drag->setMimeData(new QMimeData); drag->exec(Qt::MoveAction); + if (item->itemType() == DockItem::App && m_appDragWidget) { + // TODO AppDragWidget中偶尔会出现拖拽结束后没有触发dropEvent的情况,因此exec结束后处理dropEvent中未执行的操作(临时处理方式) + m_appDragWidget->execFinished(); + } + if (item->itemType() != DockItem::App || m_dragIndex == -1) { m_appDragWidget = nullptr; item->setDraging(false); @@ -1368,8 +1382,7 @@ void MainPanelControl::calcuDockIconSize(int w, int h, int traySize) if (!layout || !layout->itemAt(0)) continue; - PluginsItem *pItem = static_cast(layout->itemAt(0)->widget()); - qInfo() << pItem->pluginItem()->pluginDisplayName(); + PluginsItem *pItem = qobject_cast(layout->itemAt(0)->widget()); if (!pItem) continue; diff --git a/frame/window/quickpluginwindow.cpp b/frame/window/quickpluginwindow.cpp index 1d2dac253..bf11670fa 100644 --- a/frame/window/quickpluginwindow.cpp +++ b/frame/window/quickpluginwindow.cpp @@ -282,7 +282,7 @@ void QuickPluginWindow::startDrag(PluginsItemInterface *moveItem) drag->setPixmap(dragPixmap); drag->setHotSpot(QPoint(0, 0)); - connect(drag->appDragWidget(), &AppDragWidget::requestRemoveItem, this, [ this, moveItem ] { + connect(drag->appDragWidget(), &AppDragWidget::requestSplitWindow, this, [ this, moveItem ] { removePlugin(moveItem); });