forked from github_mirror/framelesshelper
parent
79ea80ea74
commit
b1f0e38fc8
|
@ -26,7 +26,6 @@
|
||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
#include <QtCore/qhash.h>
|
#include <QtCore/qhash.h>
|
||||||
#include <QtCore/qmutex.h>
|
#include <QtCore/qmutex.h>
|
||||||
#include <QtCore/qvariant.h>
|
|
||||||
#include <QtCore/qcoreapplication.h>
|
#include <QtCore/qcoreapplication.h>
|
||||||
#include <QtGui/qwindow.h>
|
#include <QtGui/qwindow.h>
|
||||||
#include "framelesswindowsmanager.h"
|
#include "framelesswindowsmanager.h"
|
||||||
|
@ -41,7 +40,6 @@ struct Win32Helper
|
||||||
QScopedPointer<FramelessHelperWin> nativeEventFilter;
|
QScopedPointer<FramelessHelperWin> nativeEventFilter;
|
||||||
QWindowList framelessWindows = {};
|
QWindowList framelessWindows = {};
|
||||||
QHash<WId, QWindow *> windowMapping = {};
|
QHash<WId, QWindow *> windowMapping = {};
|
||||||
QHash<HWND, WNDPROC> qtWindowProcs = {};
|
|
||||||
|
|
||||||
explicit Win32Helper() = default;
|
explicit Win32Helper() = default;
|
||||||
~Win32Helper() = default;
|
~Win32Helper() = default;
|
||||||
|
@ -52,127 +50,6 @@ private:
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
|
Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
|
||||||
|
|
||||||
[[nodiscard]] static inline LRESULT CALLBACK HookWindowProc
|
|
||||||
(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
|
|
||||||
{
|
|
||||||
g_win32Helper()->mutex.lock();
|
|
||||||
if (!g_win32Helper()->qtWindowProcs.contains(hWnd)) {
|
|
||||||
g_win32Helper()->mutex.unlock();
|
|
||||||
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
g_win32Helper()->mutex.unlock();
|
|
||||||
const auto winId = reinterpret_cast<WId>(hWnd);
|
|
||||||
const auto getGlobalPosFromMouse = [lParam]() -> QPointF {
|
|
||||||
return {qreal(GET_X_LPARAM(lParam)), qreal(GET_Y_LPARAM(lParam))};
|
|
||||||
};
|
|
||||||
const auto getGlobalPosFromKeyboard = [hWnd, winId]() -> QPointF {
|
|
||||||
RECT windowPos = {};
|
|
||||||
if (GetWindowRect(hWnd, &windowPos) == FALSE) {
|
|
||||||
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowRect"));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const bool maxOrFull = (IsMaximized(hWnd) || Utils::isFullScreen(winId));
|
|
||||||
const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true);
|
|
||||||
const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true);
|
|
||||||
const int titleBarHeight = Utils::getTitleBarHeight(winId, true);
|
|
||||||
const int horizontalOffset = ((maxOrFull || !Utils::isWindowFrameBorderVisible()) ? 0 : frameSizeX);
|
|
||||||
const int verticalOffset = (maxOrFull ? titleBarHeight : (titleBarHeight - frameSizeY));
|
|
||||||
return {qreal(windowPos.left + horizontalOffset), qreal(windowPos.top + verticalOffset)};
|
|
||||||
};
|
|
||||||
bool shouldShowSystemMenu = false;
|
|
||||||
QPointF globalPos = {};
|
|
||||||
if (uMsg == WM_NCRBUTTONUP) {
|
|
||||||
if (wParam == HTCAPTION) {
|
|
||||||
shouldShowSystemMenu = true;
|
|
||||||
globalPos = getGlobalPosFromMouse();
|
|
||||||
}
|
|
||||||
} else if (uMsg == WM_SYSCOMMAND) {
|
|
||||||
const WPARAM filteredWParam = (wParam & 0xFFF0);
|
|
||||||
if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) {
|
|
||||||
shouldShowSystemMenu = true;
|
|
||||||
globalPos = getGlobalPosFromKeyboard();
|
|
||||||
}
|
|
||||||
} else if ((uMsg == WM_KEYDOWN) || (uMsg == WM_SYSKEYDOWN)) {
|
|
||||||
const bool altPressed = ((wParam == VK_MENU) || (GetKeyState(VK_MENU) < 0));
|
|
||||||
const bool spacePressed = ((wParam == VK_SPACE) || (GetKeyState(VK_SPACE) < 0));
|
|
||||||
if (altPressed && spacePressed) {
|
|
||||||
shouldShowSystemMenu = true;
|
|
||||||
globalPos = getGlobalPosFromKeyboard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldShowSystemMenu) {
|
|
||||||
Utils::showSystemMenu(winId, globalPos);
|
|
||||||
// QPA's internal code will handle system menu events separately, and its
|
|
||||||
// behavior is not what we would want to see because it doesn't know our
|
|
||||||
// window doesn't have any window frame now, so return early here to avoid
|
|
||||||
// entering Qt's own handling logic.
|
|
||||||
return 0; // Return 0 means we have handled this event.
|
|
||||||
}
|
|
||||||
g_win32Helper()->mutex.lock();
|
|
||||||
const WNDPROC originalWindowProc = g_win32Helper()->qtWindowProcs.value(hWnd);
|
|
||||||
g_win32Helper()->mutex.unlock();
|
|
||||||
Q_ASSERT(originalWindowProc);
|
|
||||||
if (originalWindowProc) {
|
|
||||||
// Hand over to Qt's original window proc function for events we are not
|
|
||||||
// interested in.
|
|
||||||
return CallWindowProcW(originalWindowProc, hWnd, uMsg, wParam, lParam);
|
|
||||||
} else {
|
|
||||||
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static inline bool installWindowHook(const WId winId)
|
|
||||||
{
|
|
||||||
Q_ASSERT(winId);
|
|
||||||
if (!winId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
|
||||||
QMutexLocker locker(&g_win32Helper()->mutex);
|
|
||||||
if (g_win32Helper()->qtWindowProcs.contains(hwnd)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SetLastError(ERROR_SUCCESS);
|
|
||||||
const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC));
|
|
||||||
Q_ASSERT(originalWindowProc);
|
|
||||||
if (!originalWindowProc) {
|
|
||||||
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SetLastError(ERROR_SUCCESS);
|
|
||||||
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(HookWindowProc)) == 0) {
|
|
||||||
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
g_win32Helper()->qtWindowProcs.insert(hwnd, originalWindowProc);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static inline bool uninstallWindowHook(const WId winId)
|
|
||||||
{
|
|
||||||
Q_ASSERT(winId);
|
|
||||||
if (!winId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
|
||||||
QMutexLocker locker(&g_win32Helper()->mutex);
|
|
||||||
if (!g_win32Helper()->qtWindowProcs.contains(hwnd)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const WNDPROC originalWindowProc = g_win32Helper()->qtWindowProcs.value(hwnd);
|
|
||||||
Q_ASSERT(originalWindowProc);
|
|
||||||
if (!originalWindowProc) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SetLastError(ERROR_SUCCESS);
|
|
||||||
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(originalWindowProc)) == 0) {
|
|
||||||
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
g_win32Helper()->qtWindowProcs.remove(hwnd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
|
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
|
||||||
|
|
||||||
FramelessHelperWin::~FramelessHelperWin()
|
FramelessHelperWin::~FramelessHelperWin()
|
||||||
|
@ -204,9 +81,6 @@ void FramelessHelperWin::addWindow(QWindow *window)
|
||||||
Utils::updateWindowFrameMargins(winId, false);
|
Utils::updateWindowFrameMargins(winId, false);
|
||||||
const bool dark = Utils::shouldAppsUseDarkMode();
|
const bool dark = Utils::shouldAppsUseDarkMode();
|
||||||
Utils::updateWindowFrameBorderColor(winId, dark);
|
Utils::updateWindowFrameBorderColor(winId, dark);
|
||||||
if (!installWindowHook(winId)) {
|
|
||||||
qWarning() << "Failed to hook the window proc function.";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramelessHelperWin::removeWindow(QWindow *window)
|
void FramelessHelperWin::removeWindow(QWindow *window)
|
||||||
|
@ -224,9 +98,6 @@ void FramelessHelperWin::removeWindow(QWindow *window)
|
||||||
const WId winId = window->winId();
|
const WId winId = window->winId();
|
||||||
g_win32Helper()->windowMapping.remove(winId);
|
g_win32Helper()->windowMapping.remove(winId);
|
||||||
g_win32Helper()->mutex.unlock();
|
g_win32Helper()->mutex.unlock();
|
||||||
if (!uninstallWindowHook(winId)) {
|
|
||||||
qWarning() << "Failed to un-hook the window proc function.";
|
|
||||||
}
|
|
||||||
Utils::updateInternalWindowFrameMargins(window, false);
|
Utils::updateInternalWindowFrameMargins(window, false);
|
||||||
Utils::updateWindowFrameMargins(winId, true);
|
Utils::updateWindowFrameMargins(winId, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,6 @@
|
||||||
|
|
||||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
static const bool g_usePureQtImplementation = (qEnvironmentVariableIntValue("FRAMELESSHELPER_PURE_QT_IMPL") != 0);
|
|
||||||
#else
|
|
||||||
static constexpr const bool g_usePureQtImplementation = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(FramelessWindowsManager, g_manager)
|
Q_GLOBAL_STATIC(FramelessWindowsManager, g_manager)
|
||||||
|
|
||||||
FramelessWindowsManagerPrivate::FramelessWindowsManagerPrivate(FramelessWindowsManager *q)
|
FramelessWindowsManagerPrivate::FramelessWindowsManagerPrivate(FramelessWindowsManager *q)
|
||||||
|
@ -52,6 +46,25 @@ FramelessWindowsManagerPrivate::FramelessWindowsManagerPrivate(FramelessWindowsM
|
||||||
|
|
||||||
FramelessWindowsManagerPrivate::~FramelessWindowsManagerPrivate() = default;
|
FramelessWindowsManagerPrivate::~FramelessWindowsManagerPrivate() = default;
|
||||||
|
|
||||||
|
FramelessWindowsManagerPrivate *FramelessWindowsManagerPrivate::get(FramelessWindowsManager *manager)
|
||||||
|
{
|
||||||
|
Q_ASSERT(manager);
|
||||||
|
if (!manager) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return manager->d_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FramelessWindowsManagerPrivate::usePureQtImplementation() const
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
static const bool result = (qEnvironmentVariableIntValue("FRAMELESSHELPER_PURE_QT_IMPL") != 0);
|
||||||
|
#else
|
||||||
|
static constexpr const bool result = true;
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QUuid FramelessWindowsManagerPrivate::findIdByWindow(QWindow *value) const
|
QUuid FramelessWindowsManagerPrivate::findIdByWindow(QWindow *value) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(value);
|
Q_ASSERT(value);
|
||||||
|
@ -140,16 +153,18 @@ void FramelessWindowsManager::addWindow(QWindow *window)
|
||||||
}
|
}
|
||||||
const QUuid uuid = QUuid::createUuid();
|
const QUuid uuid = QUuid::createUuid();
|
||||||
d->windowMapping.insert(window, uuid);
|
d->windowMapping.insert(window, uuid);
|
||||||
d->winIdMapping.insert(window->winId(), uuid);
|
const WId winId = window->winId();
|
||||||
|
d->winIdMapping.insert(winId, uuid);
|
||||||
|
static const bool pureQt = d->usePureQtImplementation();
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
if (!g_usePureQtImplementation) {
|
if (!pureQt) {
|
||||||
// Work-around Win32 multi-monitor artifacts.
|
// Work-around Win32 multi-monitor artifacts.
|
||||||
const QMetaObject::Connection workaroundConnection =
|
const QMetaObject::Connection workaroundConnection =
|
||||||
connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){
|
connect(window, &QWindow::screenChanged, window, [winId, window](QScreen *screen){
|
||||||
Q_UNUSED(screen);
|
Q_UNUSED(screen);
|
||||||
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
|
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
|
||||||
// this is only necessary when the window is being moved cross monitors.
|
// this is only necessary when the window is being moved cross monitors.
|
||||||
Utils::triggerFrameChange(window->winId());
|
Utils::triggerFrameChange(winId);
|
||||||
// For some reason the window is not repainted correctly when moving cross monitors,
|
// For some reason the window is not repainted correctly when moving cross monitors,
|
||||||
// we workaround this issue by force a re-paint and re-layout of the window by triggering
|
// we workaround this issue by force a re-paint and re-layout of the window by triggering
|
||||||
// a resize event manually. Although the actual size does not change, the issue we
|
// a resize event manually. Although the actual size does not change, the issue we
|
||||||
|
@ -160,13 +175,14 @@ void FramelessWindowsManager::addWindow(QWindow *window)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
d->mutex.unlock();
|
d->mutex.unlock();
|
||||||
if (g_usePureQtImplementation) {
|
if (pureQt) {
|
||||||
FramelessHelperQt::addWindow(window);
|
FramelessHelperQt::addWindow(window);
|
||||||
}
|
}
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
if (!g_usePureQtImplementation) {
|
if (!pureQt) {
|
||||||
FramelessHelperWin::addWindow(window);
|
FramelessHelperWin::addWindow(window);
|
||||||
}
|
}
|
||||||
|
Utils::installSystemMenuHook(winId);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,20 +202,25 @@ void FramelessWindowsManager::removeWindow(QWindow *window)
|
||||||
if (uuid.isNull()) {
|
if (uuid.isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (g_usePureQtImplementation) {
|
const WId winId = window->winId();
|
||||||
FramelessHelperQt::removeWindow(window);
|
|
||||||
}
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
if (!g_usePureQtImplementation) {
|
|
||||||
FramelessHelperWin::removeWindow(window);
|
|
||||||
}
|
|
||||||
if (d->win32WorkaroundConnections.contains(uuid)) {
|
if (d->win32WorkaroundConnections.contains(uuid)) {
|
||||||
disconnect(d->win32WorkaroundConnections.value(uuid));
|
disconnect(d->win32WorkaroundConnections.value(uuid));
|
||||||
d->win32WorkaroundConnections.remove(uuid);
|
d->win32WorkaroundConnections.remove(uuid);
|
||||||
}
|
}
|
||||||
|
Utils::uninstallSystemMenuHook(winId);
|
||||||
|
#endif
|
||||||
|
static const bool pureQt = d->usePureQtImplementation();
|
||||||
|
if (pureQt) {
|
||||||
|
FramelessHelperQt::removeWindow(window);
|
||||||
|
}
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
if (!pureQt) {
|
||||||
|
FramelessHelperWin::removeWindow(window);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
d->windowMapping.remove(window);
|
d->windowMapping.remove(window);
|
||||||
d->winIdMapping.remove(window->winId());
|
d->winIdMapping.remove(winId);
|
||||||
}
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
|
@ -43,6 +43,10 @@ public:
|
||||||
explicit FramelessWindowsManagerPrivate(FramelessWindowsManager *q);
|
explicit FramelessWindowsManagerPrivate(FramelessWindowsManager *q);
|
||||||
~FramelessWindowsManagerPrivate();
|
~FramelessWindowsManagerPrivate();
|
||||||
|
|
||||||
|
[[nodiscard]] static FramelessWindowsManagerPrivate *get(FramelessWindowsManager *manager);
|
||||||
|
|
||||||
|
[[nodiscard]] bool usePureQtImplementation() const;
|
||||||
|
|
||||||
[[nodiscard]] QUuid findIdByWindow(QWindow *value) const;
|
[[nodiscard]] QUuid findIdByWindow(QWindow *value) const;
|
||||||
[[nodiscard]] QUuid findIdByWinId(const WId value) const;
|
[[nodiscard]] QUuid findIdByWinId(const WId value) const;
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId winId);
|
||||||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible();
|
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible();
|
||||||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized();
|
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized();
|
||||||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized();
|
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized();
|
||||||
|
FRAMELESSHELPER_CORE_API void installSystemMenuHook(const WId winId);
|
||||||
|
FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId winId);
|
||||||
#endif // Q_OS_WINDOWS
|
#endif // Q_OS_WINDOWS
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
|
#include <QtCore/qmutex.h>
|
||||||
|
#include <QtCore/qhash.h>
|
||||||
#include <QtGui/qguiapplication.h>
|
#include <QtGui/qguiapplication.h>
|
||||||
#include <QtCore/private/qsystemlibrary_p.h>
|
#include <QtCore/private/qsystemlibrary_p.h>
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||||
|
@ -39,6 +41,8 @@
|
||||||
#endif
|
#endif
|
||||||
#include "qwinregistry_p.h"
|
#include "qwinregistry_p.h"
|
||||||
#include "framelesshelper_windows.h"
|
#include "framelesshelper_windows.h"
|
||||||
|
#include "framelesswindowsmanager.h"
|
||||||
|
#include "framelesswindowsmanager_p.h"
|
||||||
#if 0
|
#if 0
|
||||||
#include <atlbase.h>
|
#include <atlbase.h>
|
||||||
#include <d2d1.h>
|
#include <d2d1.h>
|
||||||
|
@ -48,6 +52,20 @@ Q_DECLARE_METATYPE(QMargins)
|
||||||
|
|
||||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
struct Win32UtilsHelper
|
||||||
|
{
|
||||||
|
QMutex mutex = {};
|
||||||
|
QHash<HWND, WNDPROC> qtWindowProcs = {};
|
||||||
|
|
||||||
|
explicit Win32UtilsHelper() = default;
|
||||||
|
~Win32UtilsHelper() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY_MOVE(Win32UtilsHelper)
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper)
|
||||||
|
|
||||||
static const QString successErrorText = QStringLiteral("The operation completed successfully.");
|
static const QString successErrorText = QStringLiteral("The operation completed successfully.");
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
|
||||||
|
@ -154,6 +172,90 @@ static const QString successErrorText = QStringLiteral("The operation completed
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[[nodiscard]] static inline LRESULT CALLBACK SystemMenuHookWindowProc
|
||||||
|
(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
|
||||||
|
{
|
||||||
|
g_utilsHelper()->mutex.lock();
|
||||||
|
if (!g_utilsHelper()->qtWindowProcs.contains(hWnd)) {
|
||||||
|
g_utilsHelper()->mutex.unlock();
|
||||||
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
g_utilsHelper()->mutex.unlock();
|
||||||
|
const auto winId = reinterpret_cast<WId>(hWnd);
|
||||||
|
const auto getGlobalPosFromMouse = [lParam]() -> QPointF {
|
||||||
|
return {qreal(GET_X_LPARAM(lParam)), qreal(GET_Y_LPARAM(lParam))};
|
||||||
|
};
|
||||||
|
const auto getGlobalPosFromKeyboard = [hWnd, winId]() -> QPointF {
|
||||||
|
RECT windowPos = {};
|
||||||
|
if (GetWindowRect(hWnd, &windowPos) == FALSE) {
|
||||||
|
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowRect"));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const bool maxOrFull = (IsMaximized(hWnd) || Utils::isFullScreen(winId));
|
||||||
|
const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true);
|
||||||
|
const bool frameBorderVisible = Utils::isWindowFrameBorderVisible();
|
||||||
|
const int horizontalOffset = ((maxOrFull || !frameBorderVisible) ? 0 : frameSizeX);
|
||||||
|
const int verticalOffset = [winId, frameBorderVisible, maxOrFull]() -> int {
|
||||||
|
const int titleBarHeight = Utils::getTitleBarHeight(winId, true);
|
||||||
|
if (!frameBorderVisible) {
|
||||||
|
return titleBarHeight;
|
||||||
|
}
|
||||||
|
const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true);
|
||||||
|
if (Utils::isWin11OrGreater()) {
|
||||||
|
if (maxOrFull) {
|
||||||
|
return (titleBarHeight + frameSizeY);
|
||||||
|
}
|
||||||
|
return titleBarHeight;
|
||||||
|
}
|
||||||
|
if (maxOrFull) {
|
||||||
|
return titleBarHeight;
|
||||||
|
}
|
||||||
|
return (titleBarHeight - frameSizeY);
|
||||||
|
}();
|
||||||
|
return {qreal(windowPos.left + horizontalOffset), qreal(windowPos.top + verticalOffset)};
|
||||||
|
};
|
||||||
|
bool shouldShowSystemMenu = false;
|
||||||
|
QPointF globalPos = {};
|
||||||
|
if (uMsg == WM_NCRBUTTONUP) {
|
||||||
|
if (wParam == HTCAPTION) {
|
||||||
|
shouldShowSystemMenu = true;
|
||||||
|
globalPos = getGlobalPosFromMouse();
|
||||||
|
}
|
||||||
|
} else if (uMsg == WM_SYSCOMMAND) {
|
||||||
|
const WPARAM filteredWParam = (wParam & 0xFFF0);
|
||||||
|
if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) {
|
||||||
|
shouldShowSystemMenu = true;
|
||||||
|
globalPos = getGlobalPosFromKeyboard();
|
||||||
|
}
|
||||||
|
} else if ((uMsg == WM_KEYDOWN) || (uMsg == WM_SYSKEYDOWN)) {
|
||||||
|
const bool altPressed = ((wParam == VK_MENU) || (GetKeyState(VK_MENU) < 0));
|
||||||
|
const bool spacePressed = ((wParam == VK_SPACE) || (GetKeyState(VK_SPACE) < 0));
|
||||||
|
if (altPressed && spacePressed) {
|
||||||
|
shouldShowSystemMenu = true;
|
||||||
|
globalPos = getGlobalPosFromKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldShowSystemMenu) {
|
||||||
|
Utils::showSystemMenu(winId, globalPos);
|
||||||
|
// QPA's internal code will handle system menu events separately, and its
|
||||||
|
// behavior is not what we would want to see because it doesn't know our
|
||||||
|
// window doesn't have any window frame now, so return early here to avoid
|
||||||
|
// entering Qt's own handling logic.
|
||||||
|
return 0; // Return 0 means we have handled this event.
|
||||||
|
}
|
||||||
|
g_utilsHelper()->mutex.lock();
|
||||||
|
const WNDPROC originalWindowProc = g_utilsHelper()->qtWindowProcs.value(hWnd);
|
||||||
|
g_utilsHelper()->mutex.unlock();
|
||||||
|
Q_ASSERT(originalWindowProc);
|
||||||
|
if (originalWindowProc) {
|
||||||
|
// Hand over to Qt's original window proc function for events we are not
|
||||||
|
// interested in.
|
||||||
|
return CallWindowProcW(originalWindowProc, hWnd, uMsg, wParam, lParam);
|
||||||
|
} else {
|
||||||
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Utils::isWin8OrGreater()
|
bool Utils::isWin8OrGreater()
|
||||||
{
|
{
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||||
|
@ -860,12 +962,24 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges)
|
||||||
bool Utils::isWindowFrameBorderVisible()
|
bool Utils::isWindowFrameBorderVisible()
|
||||||
{
|
{
|
||||||
static const bool result = []() -> bool {
|
static const bool result = []() -> bool {
|
||||||
if (qEnvironmentVariableIntValue("FRAMELESSHELPER_FORCE_SHOW_FRAME_BORDER") != 0) {
|
FramelessWindowsManager *manager = FramelessWindowsManager::instance();
|
||||||
return true;
|
Q_ASSERT(manager);
|
||||||
|
if (manager) {
|
||||||
|
FramelessWindowsManagerPrivate *internal = FramelessWindowsManagerPrivate::get(manager);
|
||||||
|
Q_ASSERT(internal);
|
||||||
|
if (internal) {
|
||||||
|
if (internal->usePureQtImplementation()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If we preserve the window frame border on systems prior to Windows 10,
|
// If we preserve the window frame border on systems prior to Windows 10,
|
||||||
// the window will look rather ugly and I guess no one would like to see
|
// the window will look rather ugly and I guess no one would like to see
|
||||||
// such weired windows.
|
// such weired windows. But for the ones who really want to see what the
|
||||||
|
// window look like, I still provide a way to enter such scenarios.
|
||||||
|
if (qEnvironmentVariableIntValue("FRAMELESSHELPER_FORCE_SHOW_FRAME_BORDER") != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return (isWin10OrGreater() && !qEnvironmentVariableIsSet("FRAMELESSHELPER_HIDE_FRAME_BORDER"));
|
return (isWin10OrGreater() && !qEnvironmentVariableIsSet("FRAMELESSHELPER_HIDE_FRAME_BORDER"));
|
||||||
}();
|
}();
|
||||||
return result;
|
return result;
|
||||||
|
@ -886,4 +1000,54 @@ bool Utils::isFrameBorderColorized()
|
||||||
return isTitleBarColorized();
|
return isTitleBarColorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Utils::installSystemMenuHook(const WId winId)
|
||||||
|
{
|
||||||
|
Q_ASSERT(winId);
|
||||||
|
if (!winId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||||
|
QMutexLocker locker(&g_utilsHelper()->mutex);
|
||||||
|
if (g_utilsHelper()->qtWindowProcs.contains(hwnd)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC));
|
||||||
|
Q_ASSERT(originalWindowProc);
|
||||||
|
if (!originalWindowProc) {
|
||||||
|
qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SystemMenuHookWindowProc)) == 0) {
|
||||||
|
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_utilsHelper()->qtWindowProcs.insert(hwnd, originalWindowProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::uninstallSystemMenuHook(const WId winId)
|
||||||
|
{
|
||||||
|
Q_ASSERT(winId);
|
||||||
|
if (!winId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||||
|
QMutexLocker locker(&g_utilsHelper()->mutex);
|
||||||
|
if (!g_utilsHelper()->qtWindowProcs.contains(hwnd)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const WNDPROC originalWindowProc = g_utilsHelper()->qtWindowProcs.value(hwnd);
|
||||||
|
Q_ASSERT(originalWindowProc);
|
||||||
|
if (!originalWindowProc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(originalWindowProc)) == 0) {
|
||||||
|
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_utilsHelper()->qtWindowProcs.remove(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
|
@ -200,9 +200,11 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event)
|
||||||
#else
|
#else
|
||||||
const QPointF scenePos = mouseEvent->windowPos();
|
const QPointF scenePos = mouseEvent->windowPos();
|
||||||
#endif
|
#endif
|
||||||
if ((scenePos.x() < kDefaultResizeBorderThickness)
|
const QQuickWindow::Visibility visibility = window->visibility();
|
||||||
|
if ((visibility == QQuickWindow::Windowed)
|
||||||
|
&& ((scenePos.x() < kDefaultResizeBorderThickness)
|
||||||
|| (scenePos.x() >= (window->width() - kDefaultResizeBorderThickness))
|
|| (scenePos.x() >= (window->width() - kDefaultResizeBorderThickness))
|
||||||
|| (scenePos.y() < kDefaultResizeBorderThickness)) {
|
|| (scenePos.y() < kDefaultResizeBorderThickness))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const bool titleBar = isInTitleBarDraggableArea(window, scenePos);
|
const bool titleBar = isInTitleBarDraggableArea(window, scenePos);
|
||||||
|
@ -240,7 +242,6 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event)
|
||||||
if (!titleBar) {
|
if (!titleBar) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const QQuickWindow::Visibility visibility = window->visibility();
|
|
||||||
if ((visibility == QQuickWindow::Maximized) || (visibility == QQuickWindow::FullScreen)) {
|
if ((visibility == QQuickWindow::Maximized) || (visibility == QQuickWindow::FullScreen)) {
|
||||||
window->showNormal();
|
window->showNormal();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -212,9 +212,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event)
|
||||||
#else
|
#else
|
||||||
const QPoint scenePos = event->windowPos();
|
const QPoint scenePos = event->windowPos();
|
||||||
#endif
|
#endif
|
||||||
if ((scenePos.x() < kDefaultResizeBorderThickness)
|
if (shouldIgnoreMouseEvents(scenePos)) {
|
||||||
|| (scenePos.x() >= (q->width() - kDefaultResizeBorderThickness))
|
|
||||||
|| (scenePos.y() < kDefaultResizeBorderThickness)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isInTitleBarDraggableArea(scenePos)) {
|
if (!isInTitleBarDraggableArea(scenePos)) {
|
||||||
|
@ -237,9 +235,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
|
||||||
#else
|
#else
|
||||||
const QPoint scenePos = event->windowPos();
|
const QPoint scenePos = event->windowPos();
|
||||||
#endif
|
#endif
|
||||||
if ((scenePos.x() < kDefaultResizeBorderThickness)
|
if (shouldIgnoreMouseEvents(scenePos)) {
|
||||||
|| (scenePos.x() >= (q->width() - kDefaultResizeBorderThickness))
|
|
||||||
|| (scenePos.y() < kDefaultResizeBorderThickness)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isInTitleBarDraggableArea(scenePos)) {
|
if (!isInTitleBarDraggableArea(scenePos)) {
|
||||||
|
@ -270,9 +266,7 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event)
|
||||||
#else
|
#else
|
||||||
const QPoint scenePos = event->windowPos();
|
const QPoint scenePos = event->windowPos();
|
||||||
#endif
|
#endif
|
||||||
if ((scenePos.x() < kDefaultResizeBorderThickness)
|
if (shouldIgnoreMouseEvents(scenePos)) {
|
||||||
|| (scenePos.x() >= (q->width() - kDefaultResizeBorderThickness))
|
|
||||||
|| (scenePos.y() < kDefaultResizeBorderThickness)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isInTitleBarDraggableArea(scenePos)) {
|
if (!isInTitleBarDraggableArea(scenePos)) {
|
||||||
|
@ -439,6 +433,13 @@ bool FramelessWidgetsHelper::isCustomLayout() const
|
||||||
return (m_windowLayout == WindowLayout::Custom);
|
return (m_windowLayout == WindowLayout::Custom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FramelessWidgetsHelper::shouldIgnoreMouseEvents(const QPoint &pos) const
|
||||||
|
{
|
||||||
|
return (isNormal() && ((pos.x() < kDefaultResizeBorderThickness)
|
||||||
|
|| (pos.x() >= (q->width() - kDefaultResizeBorderThickness))
|
||||||
|
|| (pos.y() < kDefaultResizeBorderThickness)));
|
||||||
|
}
|
||||||
|
|
||||||
void FramelessWidgetsHelper::updateContentsMargins()
|
void FramelessWidgetsHelper::updateContentsMargins()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
|
|
|
@ -78,6 +78,7 @@ private:
|
||||||
Q_NODISCARD bool shouldDrawFrameBorder() const;
|
Q_NODISCARD bool shouldDrawFrameBorder() const;
|
||||||
Q_NODISCARD bool isStandardLayout() const;
|
Q_NODISCARD bool isStandardLayout() const;
|
||||||
Q_NODISCARD bool isCustomLayout() const;
|
Q_NODISCARD bool isCustomLayout() const;
|
||||||
|
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void updateContentsMargins();
|
void updateContentsMargins();
|
||||||
|
|
Loading…
Reference in New Issue