feat: 拖动任务栏图标实现分屏效果

拖动任务栏图标到屏幕中间,如果在左侧,显示左分屏效果,在右侧,显示右分屏效果

Log: 完成任务栏图标分屏效果的功能
Influence: 从任务栏拖动图标到屏幕上方,查看是否有分屏功能
Task: https://pms.uniontech.com/task-view-163465.html
Change-Id: I1a7a33646edb6f55972b8e5fa2c5f39ce026fe8e
This commit is contained in:
donghualin 2022-08-12 08:13:00 +00:00
parent f938f66bb5
commit 9c1eb32e24
19 changed files with 1194 additions and 170 deletions

View File

@ -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)

5
debian/control vendored
View File

@ -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/

View File

@ -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
)

View File

@ -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;

View File

@ -53,6 +53,7 @@ public:
public:
bool attention;
QString title;
QString uuid;
};
Q_DECLARE_METATYPE(WindowInfo)

View File

@ -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

View File

@ -45,6 +45,7 @@ public:
QList<QScreen *> screens() const;
QScreen *screen(const QString &screenName) const;
QScreen *screenAt(const QPoint &pos) const;
QString primary() const;
int screenRawWidth() const;
int screenRawHeight() const;

View File

@ -25,6 +25,7 @@
#include "xcb_misc.h"
#include "appswingeffectbuilder.h"
#include "utils.h"
#include "screenspliter.h"
#include <X11/X.h>
#include <X11/Xlib.h>
@ -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;
}

View File

@ -36,6 +36,7 @@
#include <DGuiApplicationHelper>
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

View File

@ -22,6 +22,15 @@
#include "../appitem.h"
#include "appdragwidget.h"
#include "utils.h"
#include "displaymanager.h"
#include <com_deepin_api_xeventmonitor.h>
#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<AppItem *>((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<QScreen *> 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<Dock::Position>();
XEventMonitor *extralEventInter = new XEventMonitor("com.deepin.api.XEventMonitor", "/com/deepin/api/XEventMonitor", QDBusConnection::sessionBus());
QList<MonitRect> extralRectList;
QList<QScreen *> 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;
}

View File

@ -23,6 +23,8 @@
#define APPDRAGWIDGET_H
#include "constants.h"
#include "screenspliter.h"
#include "utils.h"
#include <QPixmap>
#include <QGraphicsObject>
@ -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<AppGraphicsObject> m_object;
@ -133,7 +146,6 @@ protected:
QRect m_dockGeometry;
QPoint m_originPoint;
QSize m_iconSize;
Dock::TipsWidget *m_removeTips;
QScopedPointer<DockPopupWindow> 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 */

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "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);
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCREENSPLITER_H
#define SCREENSPLITER_H
#include "dbusutil.h"
#include <QObject>
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

View File

@ -0,0 +1,247 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "screenspliter_wayland.h"
#include "appitem.h"
#include <QWindow>
#include <QApplication>
#include <QX11Info>
#include <QtWaylandClient>
#define private public
#include <private/qwaylandintegration_p.h>
#include <private/qwaylandshellsurface_p.h>
#include <private/qwaylandwindow_p.h>
#include <private/qwaylandcursor_p.h>
#undef private
#include <registry.h>
#include <ddeshell.h>
#include <event_queue.h>
#include <plasmashell.h>
#include <compositor.h>
#include <clientmanagement.h>
#include <connection_thread.h>
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 <ClientManagement::WindowState> &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<ScreenSpliter::SplitDirection, ClientManagement::SplitType> 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();
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCREENSPLITER_WAYLAND_H
#define SCREENSPLITER_WAYLAND_H
#include "screenspliter.h"
#include <QWidget>
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<quint32, WindowInfo> 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

View File

@ -0,0 +1,240 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "screenspliter_xcb.h"
#include "appitem.h"
#include <QX11Info>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <xcb/xproto.h>
#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<const char *>(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<ScreenSpliter::SplitDirection, int> 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<const quint8 *>(cdata));
return supported ? WId : 0;
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd.
*
* Author: donghualin <donghualin@uniontech.com>
*
* Maintainer: donghualin <donghualin@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCREENSPLITER_XCB_H
#define SCREENSPLITER_XCB_H
#include "screenspliter.h"
#include <QRect>
class WindowInfo;
typedef QMap<quint32, WindowInfo> 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

View File

@ -47,17 +47,16 @@
#include <QLabel>
#include <QPixmap>
#include <QtConcurrent/QtConcurrentRun>
#include <QX11Info>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h>
#define protected public
#include <QtGui/private/qsimpledrag_p.h>
#undef protected
#include <QtGui/private/qshapedpixmapdndwindow_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <DGuiApplicationHelper>
#include <DWindowManagerHelper>
#include <X11/Xlib.h>
#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<AppItem *>(dockItem);
if (appItem->supportSplitWindow())
appItem->startSplit(rect);
});
connect(m_appDragWidget, &AppDragWidget::requestSplitWindow, this, [ = ](ScreenSpliter::SplitDirection dir) {
AppItem *appItem = static_cast<AppItem *>(dockItem);
if (appItem->supportSplitWindow())
appItem->splitWindowOnScreen(dir);
});
connect(m_appDragWidget, &AppDragWidget::destroyed, this, [ = ] {
m_appDragWidget = nullptr;
AppItem *appItem = static_cast<AppItem *>(dockItem);
if (appItem->supportSplitWindow())
return;
if (!item.isNull() && qobject_cast<AppItem *>(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<AppItem *>(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<PluginsItem *>(layout->itemAt(0)->widget());
qInfo() << pItem->pluginItem()->pluginDisplayName();
PluginsItem *pItem = qobject_cast<PluginsItem *>(layout->itemAt(0)->widget());
if (!pItem)
continue;

View File

@ -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);
});