diff --git a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.cpp b/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.cpp new file mode 100644 index 00000000..d4e1eac1 --- /dev/null +++ b/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.cpp @@ -0,0 +1,217 @@ +#include "Frameless/TaoFrameLessView.h" +#include +#include +#include +#include + +struct TaoFrameLessViewPrivate +{ + uint32_t mPressedEdge; + QRect mPressedGeometry; +}; +TaoFrameLessView::TaoFrameLessView(QWindow* parent) + : Super(parent) + , d(std::make_unique()) +{ + setFlags(flags() | Qt::FramelessWindowHint | Qt::Window); + setResizeMode(SizeRootObjectToView); +} +TaoFrameLessView::~TaoFrameLessView() { } +QRect TaoFrameLessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSize) +{ + int w = normalSize.width(); + int h = normalSize.height(); + int x = screenGeo.x() + (screenGeo.width() - w) / 2; + int y = screenGeo.y() + (screenGeo.height() - h) / 2; + if (screenGeo.width() < w) + { + x = screenGeo.x(); + w = screenGeo.width(); + } + if (screenGeo.height() < h) + { + y = screenGeo.y(); + h = screenGeo.height(); + } + + return { x, y, w, h }; +} +void TaoFrameLessView::moveToScreenCenter() +{ + auto geo = calcCenterGeo(screen()->availableGeometry(), size()); + if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) + { + setMinimumSize(geo.size()); + } + setGeometry(geo); + update(); +} + +void TaoFrameLessView::move(int x, int y) +{ + setPosition(x, y); +} + +QPoint TaoFrameLessView::mousePosition() const +{ + return QCursor::pos(); +} + +QPoint TaoFrameLessView::globalPosToWindowPos(const QPoint& pos) const +{ + return mapFromGlobal(pos); +} + +void TaoFrameLessView::mousePressEvent(QMouseEvent* event) +{ + auto mousePos = event->pos(); + emit mousePressed(mousePos.x(), mousePos.y(), event->button()); + d->mPressedEdge = getPosEdges(mousePos); + if (d->mPressedEdge) + { + d->mPressedGeometry = geometry(); + } + Super::mousePressEvent(event); +} + +void TaoFrameLessView::mouseReleaseEvent(QMouseEvent* event) +{ + d->mPressedEdge = 0; + d->mPressedGeometry = {}; + Super::mouseReleaseEvent(event); +} + +void TaoFrameLessView::mouseMoveEvent(QMouseEvent* event) +{ + { + // 实时计算光标 + auto edges = getPosEdges(event->pos()); + auto shape = getCursorShapeByEdge(edges); + setCursor(shape); + } + { + // 根据按下时位置执行move + if (d->mPressedEdge) + { + doMoveTo(event->pos()); + } + } + Super::mouseMoveEvent(event); +} +Qt::CursorShape TaoFrameLessView::getCursorShapeByEdge(const Qt::Edges& edges) +{ + switch (edges) + { + case Qt::Edge::TopEdge: + case Qt::Edge::BottomEdge: { + return Qt::CursorShape::SizeVerCursor; + break; + } + case Qt::Edge::LeftEdge: + case Qt::Edge::RightEdge: { + return Qt::CursorShape::SizeHorCursor; + break; + } + case (Qt::Edge::TopEdge | Qt::Edge::LeftEdge): + case (Qt::Edge::BottomEdge | Qt::Edge::RightEdge): { + return Qt::CursorShape::SizeFDiagCursor; + break; + } + case (Qt::Edge::TopEdge | Qt::Edge::RightEdge): + case (Qt::Edge::BottomEdge | Qt::Edge::LeftEdge): { + return Qt::CursorShape::SizeBDiagCursor; + break; + } + default: + break; + } + return Qt::CursorShape::ArrowCursor; +} + +void TaoFrameLessView::doMoveTo(const QPoint& nowPos) +{ + switch (d->mPressedEdge) + { + case Qt::Edge::TopEdge: { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(0, gPos.y() - d->mPressedGeometry.top(), 0, 0); + // setGeometry(newGeo); + setY(newGeo.y()); + setHeight(newGeo.height()); + break; + } + case Qt::Edge::BottomEdge: { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(0, 0, 0, gPos.y() - d->mPressedGeometry.bottom()); + setGeometry(newGeo); + break; + } + case Qt::Edge::LeftEdge: { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(gPos.x() - d->mPressedGeometry.left(), 0, 0, 0); + setGeometry(newGeo); + break; + } + case Qt::Edge::RightEdge: { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(0, 0, gPos.x() - d->mPressedGeometry.right(), 0); + setGeometry(newGeo); + break; + } + case (Qt::Edge::TopEdge | Qt::Edge::LeftEdge): { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(gPos.x() - d->mPressedGeometry.left(), gPos.y() - d->mPressedGeometry.top(), 0, 0); + setGeometry(newGeo); + break; + } + case (Qt::Edge::BottomEdge | Qt::Edge::RightEdge): { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(0, 0, gPos.x() - d->mPressedGeometry.right(), gPos.y() - d->mPressedGeometry.bottom()); + setGeometry(newGeo); + break; + } + case (Qt::Edge::TopEdge | Qt::Edge::RightEdge): { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(0, gPos.y() - d->mPressedGeometry.top(), gPos.x() - d->mPressedGeometry.right(), 0); + setGeometry(newGeo); + break; + } + case (Qt::Edge::BottomEdge | Qt::Edge::LeftEdge): { + auto gPos = mapToGlobal(nowPos); + auto newGeo = d->mPressedGeometry.adjusted(gPos.x() - d->mPressedGeometry.left(), 0, 0, gPos.y() - d->mPressedGeometry.bottom()); + setGeometry(newGeo); + break; + } + default: + break; + } +} +Qt::Edges TaoFrameLessView::getPosEdges(const QPoint& pos) const +{ + uint32_t edges = 0; + if (pos.x() < 0 || pos.x() > width()) + { + return (Qt::Edges)edges; + } + if (pos.y() < 0 || pos.y() > height()) + { + return (Qt::Edges)edges; + } + if (pos.x() <= borderWidth()) + { + edges |= Qt::Edge::LeftEdge; + } + else if (width() - borderWidth() <= pos.x()) + { + edges |= Qt::Edge::RightEdge; + } + if (pos.y() <= borderWidth()) + { + edges |= Qt::Edge::TopEdge; + } + else if (height() - borderWidth() <= pos.y()) + { + edges |= Qt::Edge::BottomEdge; + } + return (Qt::Edges)edges; +} diff --git a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.h b/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.h index c66fc92c..2c2ce49a 100644 --- a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.h +++ b/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView.h @@ -1,56 +1,43 @@ #pragma once +#include "Common/PropertyHelper.h" #include "TaoCommonGlobal.h" #include #include #include -// 无边框窗口,主要用来实现自定义标题栏。 -// Windows平台支持拖动和改变大小,支持Aero效果 -// 非Windows平台,去掉边框,不做其它处理。由Qml模拟resize和拖动。 -class TaoFrameLessViewPrivate; +// 简易的无边框窗口,主要用来实现自定义标题栏。 +// 支持标题栏拖动和边缘改变大小,不做深度处理 +struct TaoFrameLessViewPrivate; class TAO_API TaoFrameLessView : public QQuickView { Q_OBJECT using Super = QQuickView; - Q_PROPERTY(bool isMax READ isMax NOTIFY isMaxChanged) - Q_PROPERTY(bool isFull READ isFull NOTIFY isFullChanged) + AUTO_PROPERTY(int, borderWidth, 4) public: explicit TaoFrameLessView(QWindow* parent = nullptr); ~TaoFrameLessView(); - void moveToScreenCenter(); - bool isMax() const; - bool isFull() const; - QQuickItem* titleItem() const; static QRect calcCenterGeo(const QRect& screenGeo, const QSize& normalSize); public slots: - void setIsMax(bool isMax); - void setIsFull(bool isFull); - void setTitleItem(QQuickItem* item); + void moveToScreenCenter(); + void move(int x, int y); + + QPoint mousePosition() const; + QPoint globalPosToWindowPos(const QPoint& pos) const; signals: - void isMaxChanged(bool isMax); - void isFullChanged(bool isFull); void mousePressed(int xPos, int yPos, int button); protected: - void showEvent(QShowEvent* e) override; - void resizeEvent(QResizeEvent* e) override; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override; -#else - bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; -#endif - void mousePressEvent(QMouseEvent* event) override - { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - emit mousePressed(event->position().x(), event->position().y(), event->button()); -#else - emit mousePressed(event->x(), event->y(), event->button()); -#endif - Super::mousePressEvent(event); - } + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + Qt::Edges getPosEdges(const QPoint& pos) const; + Qt::CursorShape getCursorShapeByEdge(const Qt::Edges& edges); + + void doMoveTo(const QPoint& nowPos); private: - TaoFrameLessViewPrivate* d; + std::unique_ptr d; }; diff --git a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView_unix.cpp b/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView_unix.cpp deleted file mode 100644 index 76e3c7ec..00000000 --- a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView_unix.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "Frameless/TaoFrameLessView.h" -#include -#include -#include -#include - -class TaoFrameLessViewPrivate -{ -public: - bool m_isMax = false; - bool m_isFull = false; - QQuickItem* m_titleItem = nullptr; -}; -TaoFrameLessView::TaoFrameLessView(QWindow* parent) - : Super(parent) - , d(new TaoFrameLessViewPrivate) -{ - setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); - setResizeMode(SizeRootObjectToView); - - setIsMax(windowState() == Qt::WindowMaximized); - setIsFull(windowState() == Qt::WindowFullScreen); - connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) { - (void)state; - setIsMax(windowState() == Qt::WindowMaximized); - setIsFull(windowState() == Qt::WindowFullScreen); - }); -} -TaoFrameLessView::~TaoFrameLessView() -{ - delete d; -} -void TaoFrameLessView::showEvent(QShowEvent* e) -{ - Super::showEvent(e); -} -QRect TaoFrameLessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSize) -{ - int w = normalSize.width(); - int h = normalSize.height(); - int x = screenGeo.x() + (screenGeo.width() - w) / 2; - int y = screenGeo.y() + (screenGeo.height() - h) / 2; - if (screenGeo.width() < w) - { - x = screenGeo.x(); - w = screenGeo.width(); - } - if (screenGeo.height() < h) - { - y = screenGeo.y(); - h = screenGeo.height(); - } - - return { x, y, w, h }; -} -void TaoFrameLessView::moveToScreenCenter() -{ - auto geo = calcCenterGeo(screen()->availableGeometry(), size()); - if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) - { - setMinimumSize(geo.size()); - } - setGeometry(geo); - update(); -} -bool TaoFrameLessView::isMax() const -{ - return d->m_isMax; -} -bool TaoFrameLessView::isFull() const -{ - return d->m_isFull; -} -QQuickItem* TaoFrameLessView::titleItem() const -{ - return d->m_titleItem; -} -void TaoFrameLessView::setIsMax(bool isMax) -{ - if (d->m_isMax == isMax) - return; - - d->m_isMax = isMax; - emit isMaxChanged(d->m_isMax); -} -void TaoFrameLessView::setIsFull(bool isFull) -{ - if (d->m_isFull == isFull) - return; - - d->m_isFull = isFull; - emit isFullChanged(d->m_isFull); -} -void TaoFrameLessView::setTitleItem(QQuickItem* item) -{ - d->m_titleItem = item; -} -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) -#else -bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, long* result) -#endif - -{ - return Super::nativeEvent(eventType, message, result); -} - -void TaoFrameLessView::resizeEvent(QResizeEvent* e) -{ - Super::resizeEvent(e); -} diff --git a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView_win.cpp b/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView_win.cpp deleted file mode 100644 index bb0e2626..00000000 --- a/3rdparty/TaoCommon/src/TaoCommon/Frameless/TaoFrameLessView_win.cpp +++ /dev/null @@ -1,363 +0,0 @@ -#include "Frameless/TaoFrameLessView.h" - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include // Fixes error C2504: 'IUnknown' : base class undefined - -#include -#include -#pragma comment(lib, "Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved -#pragma comment(lib, "User32.lib") -#pragma comment(lib, "Gdi32.lib") -// we cannot just use WS_POPUP style -// WS_THICKFRAME: without this the window cannot be resized and so aero snap, de-maximizing and minimizing won't work -// WS_SYSMENU: enables the context menu with the move, close, maximize, minize... commands (shift + right-click on the task bar item) -// WS_CAPTION: enables aero minimize animation/transition -// WS_MAXIMIZEBOX, WS_MINIMIZEBOX: enable minimize/maximize -enum class Style : DWORD -{ - windowed = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, - aero_borderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, - basic_borderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX -}; -static bool isCompositionEnabled() -{ - BOOL composition_enabled = FALSE; - bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK; - return composition_enabled && success; -} -static Style selectBorderLessStyle() -{ - return isCompositionEnabled() ? Style::aero_borderless : Style::basic_borderless; -} -static void setShadow(HWND handle, bool enabled) -{ - if (isCompositionEnabled()) - { - static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } }; - ::DwmExtendFrameIntoClientArea(handle, &shadow_state[enabled]); - } -} -static long hitTest(RECT winrect, long x, long y, int borderWidth) -{ - // 鼠标区域位于窗体边框,进行缩放 - if ((x >= winrect.left) && (x < winrect.left + borderWidth) && (y >= winrect.top) && (y < winrect.top + borderWidth)) - { - return HTTOPLEFT; - } - else if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth) - { - return HTTOPRIGHT; - } - else if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) - { - return HTBOTTOMLEFT; - } - else if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) - { - return HTBOTTOMRIGHT; - } - else if (x >= winrect.left && x < winrect.left + borderWidth) - { - return HTLEFT; - } - else if (x < winrect.right && x >= winrect.right - borderWidth) - { - return HTRIGHT; - } - else if (y >= winrect.top && y < winrect.top + borderWidth) - { - return HTTOP; - } - else if (y < winrect.bottom && y >= winrect.bottom - borderWidth) - { - return HTBOTTOM; - } - else - { - return 0; - } -} - -static bool isMaxWin(QWindow* win) -{ - return win->windowState() == Qt::WindowMaximized; -} -static bool isFullWin(QQuickView* win) -{ - return win->windowState() == Qt::WindowFullScreen; -} - -class TaoFrameLessViewPrivate -{ -public: - bool m_firstRun = true; - bool m_isMax = false; - bool m_isFull = false; - QQuickItem* m_titleItem = nullptr; - HMENU mMenuHandler = NULL; - bool borderless = true; // is the window currently borderless - bool borderless_resize = true; // should the window allow resizing by dragging the borders while borderless - bool borderless_drag = true; // should the window allow moving my dragging the client area - bool borderless_shadow = true; // should the window display a native aero shadow while borderless - void setBorderLess(HWND handle, bool enabled) - { - auto newStyle = enabled ? selectBorderLessStyle() : Style::windowed; - auto oldStyle = static_cast