parent
8e69a57039
commit
0e4f95fe2c
|
@ -1,11 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(FramelessHelper LANGUAGES CXX)
|
||||
|
||||
option(BUILD_EXAMPLES "Build examples." ON)
|
||||
option(TEST_UNIX "Test UNIX version (from Win32)." OFF)
|
||||
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
if(NOT DEFINED BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
@ -27,6 +28,7 @@ set(SOURCES
|
|||
framelesshelper.h
|
||||
framelesshelper.cpp
|
||||
framelesswindowsmanager.h
|
||||
framelesswindowsmanager_p.h
|
||||
framelesswindowsmanager.cpp
|
||||
utilities.h
|
||||
utilities.cpp
|
||||
|
@ -72,17 +74,12 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
|||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_KEYWORDS
|
||||
QT_USE_QSTRINGBUILDER
|
||||
QT_DEPRECATED_WARNINGS
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060400
|
||||
FRAMELESSHELPER_BUILD_LIBRARY
|
||||
)
|
||||
|
||||
if(TEST_UNIX)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
FRAMELESSHELPER_TEST_UNIX
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::CorePrivate
|
||||
Qt${QT_VERSION_MAJOR}::GuiPrivate
|
||||
|
@ -95,9 +92,9 @@ if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
|||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||
)
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
#add_subdirectory(examples)
|
||||
endif()
|
||||
|
|
|
@ -4,6 +4,7 @@ DEFINES += \
|
|||
QT_NO_CAST_FROM_ASCII \
|
||||
QT_NO_CAST_TO_ASCII \
|
||||
QT_NO_KEYWORDS \
|
||||
QT_USE_QSTRINGBUILDER \
|
||||
QT_DEPRECATED_WARNINGS \
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060400
|
||||
RESOURCES += $$PWD/images.qrc
|
||||
|
|
|
@ -32,6 +32,7 @@ target_compile_definitions(MainWindow PRIVATE
|
|||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_KEYWORDS
|
||||
QT_USE_QSTRINGBUILDER
|
||||
QT_DEPRECATED_WARNINGS
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060400
|
||||
)
|
||||
|
|
|
@ -30,6 +30,7 @@ target_compile_definitions(Quick PRIVATE
|
|||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_KEYWORDS
|
||||
QT_USE_QSTRINGBUILDER
|
||||
QT_DEPRECATED_WARNINGS
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060400
|
||||
)
|
||||
|
|
|
@ -30,6 +30,7 @@ target_compile_definitions(Widget PRIVATE
|
|||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_KEYWORDS
|
||||
QT_USE_QSTRINGBUILDER
|
||||
QT_DEPRECATED_WARNINGS
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060400
|
||||
)
|
||||
|
|
|
@ -23,20 +23,72 @@
|
|||
*/
|
||||
|
||||
#include "framelesshelper.h"
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtGui/qevent.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include "framelesswindowsmanager.h"
|
||||
#include "utilities.h"
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
static constexpr const int g_resizeBorderThickness = kDefaultResizeBorderThicknessAero;
|
||||
|
||||
[[nodiscard]] static inline Qt::CursorShape calculateCursorShape
|
||||
(const QWindow * const window, const QPointF &pos)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
if (window->visibility() != QWindow::Windowed) {
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
if (((pos.x() < g_resizeBorderThickness) && (pos.y() < g_resizeBorderThickness))
|
||||
|| ((pos.x() >= (window->width() - g_resizeBorderThickness)) && (pos.y() >= (window->height() - g_resizeBorderThickness)))) {
|
||||
return Qt::SizeFDiagCursor;
|
||||
}
|
||||
if (((pos.x() >= (window->width() - g_resizeBorderThickness)) && (pos.y() < g_resizeBorderThickness))
|
||||
|| ((pos.x() < g_resizeBorderThickness) && (pos.y() >= (window->height() - g_resizeBorderThickness)))) {
|
||||
return Qt::SizeBDiagCursor;
|
||||
}
|
||||
if ((pos.x() < g_resizeBorderThickness) || (pos.x() >= (window->width() - g_resizeBorderThickness))) {
|
||||
return Qt::SizeHorCursor;
|
||||
}
|
||||
if ((pos.y() < g_resizeBorderThickness) || (pos.y() >= (window->height() - g_resizeBorderThickness))) {
|
||||
return Qt::SizeVerCursor;
|
||||
}
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline Qt::Edges calculateWindowEdges
|
||||
(const QWindow * const window, const QPointF &pos)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return {};
|
||||
}
|
||||
if (window->visibility() != QWindow::Windowed) {
|
||||
return {};
|
||||
}
|
||||
Qt::Edges edges = {};
|
||||
if (pos.x() < g_resizeBorderThickness) {
|
||||
edges |= Qt::LeftEdge;
|
||||
}
|
||||
if (pos.x() >= (window->width() - g_resizeBorderThickness)) {
|
||||
edges |= Qt::RightEdge;
|
||||
}
|
||||
if (pos.y() < g_resizeBorderThickness) {
|
||||
edges |= Qt::TopEdge;
|
||||
}
|
||||
if (pos.y() >= (window->height() - g_resizeBorderThickness)) {
|
||||
edges |= Qt::BottomEdge;
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) {}
|
||||
|
||||
void FramelessHelper::removeWindowFrame(QWindow *window)
|
||||
FramelessHelper::~FramelessHelper() = default;
|
||||
|
||||
void FramelessHelper::addWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
|
@ -44,10 +96,9 @@ void FramelessHelper::removeWindowFrame(QWindow *window)
|
|||
}
|
||||
window->setFlags(window->flags() | Qt::FramelessWindowHint);
|
||||
window->installEventFilter(this);
|
||||
window->setProperty(Constants::kFramelessModeFlag, true);
|
||||
}
|
||||
|
||||
void FramelessHelper::bringBackWindowFrame(QWindow *window)
|
||||
void FramelessHelper::removeWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
|
@ -55,7 +106,6 @@ void FramelessHelper::bringBackWindowFrame(QWindow *window)
|
|||
}
|
||||
window->removeEventFilter(this);
|
||||
window->setFlags(window->flags() & ~Qt::FramelessWindowHint);
|
||||
window->setProperty(Constants::kFramelessModeFlag, false);
|
||||
}
|
||||
|
||||
bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
|
||||
|
@ -76,137 +126,28 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
|
|||
return false;
|
||||
}
|
||||
const auto window = qobject_cast<QWindow *>(object);
|
||||
const int resizeBorderThickness = FramelessWindowsManager::getResizeBorderThickness(window);
|
||||
const int titleBarHeight = FramelessWindowsManager::getTitleBarHeight(window);
|
||||
const bool resizable = FramelessWindowsManager::getResizable(window);
|
||||
const int windowWidth = window->width();
|
||||
const auto mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
const QPoint localMousePosition = mouseEvent->position().toPoint();
|
||||
const QPointF localPos = mouseEvent->position();
|
||||
#else
|
||||
const QPoint localMousePosition = mouseEvent->windowPos().toPoint();
|
||||
const QPointF localPos = mouseEvent->windowPos();
|
||||
#endif
|
||||
const Qt::Edges edges = [window, resizeBorderThickness, windowWidth, &localMousePosition] {
|
||||
const int windowHeight = window->height();
|
||||
if (localMousePosition.y() <= resizeBorderThickness) {
|
||||
if (localMousePosition.x() <= resizeBorderThickness) {
|
||||
return Qt::TopEdge | Qt::LeftEdge;
|
||||
}
|
||||
if (localMousePosition.x() >= (windowWidth - resizeBorderThickness)) {
|
||||
return Qt::TopEdge | Qt::RightEdge;
|
||||
}
|
||||
return Qt::Edges{Qt::TopEdge};
|
||||
if (type == QEvent::MouseMove) {
|
||||
const Qt::CursorShape cs = calculateCursorShape(window, localPos);
|
||||
if (cs == Qt::ArrowCursor) {
|
||||
window->unsetCursor();
|
||||
} else {
|
||||
window->setCursor(cs);
|
||||
}
|
||||
if (localMousePosition.y() >= (windowHeight - resizeBorderThickness)) {
|
||||
if (localMousePosition.x() <= resizeBorderThickness) {
|
||||
return Qt::BottomEdge | Qt::LeftEdge;
|
||||
}
|
||||
if (localMousePosition.x() >= (windowWidth - resizeBorderThickness)) {
|
||||
return Qt::BottomEdge | Qt::RightEdge;
|
||||
}
|
||||
return Qt::Edges{Qt::BottomEdge};
|
||||
}
|
||||
if (localMousePosition.x() <= resizeBorderThickness) {
|
||||
return Qt::Edges{Qt::LeftEdge};
|
||||
}
|
||||
if (localMousePosition.x() >= (windowWidth - resizeBorderThickness)) {
|
||||
return Qt::Edges{Qt::RightEdge};
|
||||
}
|
||||
return Qt::Edges{};
|
||||
} ();
|
||||
const bool hitTestVisible = Utilities::isHitTestVisible(window);
|
||||
bool isInTitlebarArea = false;
|
||||
if ((window->windowState() == Qt::WindowMaximized)
|
||||
|| (window->windowState() == Qt::WindowFullScreen)) {
|
||||
isInTitlebarArea = (localMousePosition.y() >= 0)
|
||||
&& (localMousePosition.y() <= titleBarHeight)
|
||||
&& (localMousePosition.x() >= 0)
|
||||
&& (localMousePosition.x() <= windowWidth)
|
||||
&& !hitTestVisible;
|
||||
}
|
||||
if (window->windowState() == Qt::WindowNoState) {
|
||||
isInTitlebarArea = (localMousePosition.y() > resizeBorderThickness)
|
||||
&& (localMousePosition.y() <= titleBarHeight)
|
||||
&& (localMousePosition.x() > resizeBorderThickness)
|
||||
&& (localMousePosition.x() < (windowWidth - resizeBorderThickness))
|
||||
&& !hitTestVisible;
|
||||
}
|
||||
|
||||
// Determine if the mouse click occurred in the title bar
|
||||
static bool titlebarClicked = false;
|
||||
if (type == QEvent::MouseButtonPress) {
|
||||
if (isInTitlebarArea)
|
||||
titlebarClicked = true;
|
||||
else
|
||||
titlebarClicked = false;
|
||||
}
|
||||
|
||||
if (type == QEvent::MouseButtonDblClick) {
|
||||
if (mouseEvent->button() != Qt::MouseButton::LeftButton) {
|
||||
return false;
|
||||
}
|
||||
if (isInTitlebarArea) {
|
||||
if (window->windowState() == Qt::WindowState::WindowFullScreen) {
|
||||
return false;
|
||||
}
|
||||
if (window->windowState() == Qt::WindowState::WindowMaximized) {
|
||||
window->showNormal();
|
||||
} else {
|
||||
window->showMaximized();
|
||||
}
|
||||
window->setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
} else if (type == QEvent::MouseMove) {
|
||||
// Display resize indicators
|
||||
static bool cursorChanged = false;
|
||||
if ((window->windowState() == Qt::WindowState::WindowNoState) && resizable) {
|
||||
if (((edges & Qt::TopEdge) && (edges & Qt::LeftEdge))
|
||||
|| ((edges & Qt::BottomEdge) && (edges & Qt::RightEdge))) {
|
||||
window->setCursor(Qt::SizeFDiagCursor);
|
||||
cursorChanged = true;
|
||||
} else if (((edges & Qt::TopEdge) && (edges & Qt::RightEdge))
|
||||
|| ((edges & Qt::BottomEdge) && (edges & Qt::LeftEdge))) {
|
||||
window->setCursor(Qt::SizeBDiagCursor);
|
||||
cursorChanged = true;
|
||||
} else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) {
|
||||
window->setCursor(Qt::SizeVerCursor);
|
||||
cursorChanged = true;
|
||||
} else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) {
|
||||
window->setCursor(Qt::SizeHorCursor);
|
||||
cursorChanged = true;
|
||||
} else {
|
||||
if (cursorChanged) {
|
||||
window->setCursor(Qt::ArrowCursor);
|
||||
cursorChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((mouseEvent->buttons() & Qt::LeftButton) && titlebarClicked) {
|
||||
if (edges == Qt::Edges{}) {
|
||||
if (isInTitlebarArea) {
|
||||
if (!window->startSystemMove()) {
|
||||
// ### FIXME: TO BE IMPLEMENTED!
|
||||
qWarning() << "Current OS doesn't support QWindow::startSystemMove().";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type == QEvent::MouseButtonPress) {
|
||||
const Qt::Edges edges = calculateWindowEdges(window, localPos);
|
||||
if (edges != Qt::Edges{}) {
|
||||
if ((window->windowState() == Qt::WindowState::WindowNoState) && !hitTestVisible && resizable) {
|
||||
if (!window->startSystemResize(edges)) {
|
||||
// ### FIXME: TO BE IMPLEMENTED!
|
||||
qWarning() << "Current OS doesn't support QWindow::startSystemResize().";
|
||||
}
|
||||
if (!window->startSystemResize(edges)) {
|
||||
qWarning() << "Current platform doesn't support \"QWindow::startSystemResize()\".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,13 +25,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_FORWARD_DECLARE_CLASS(QWindow)
|
||||
class QWindow;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
@ -43,15 +40,13 @@ class FRAMELESSHELPER_API FramelessHelper : public QObject
|
|||
|
||||
public:
|
||||
explicit FramelessHelper(QObject *parent = nullptr);
|
||||
~FramelessHelper() override = default;
|
||||
~FramelessHelper() override;
|
||||
|
||||
void removeWindowFrame(QWindow *window);
|
||||
void bringBackWindowFrame(QWindow *window);
|
||||
void addWindow(QWindow *window);
|
||||
void removeWindow(QWindow *window);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
};
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,107 +28,134 @@
|
|||
#include <QtCore/qobject.h>
|
||||
|
||||
#ifndef FRAMELESSHELPER_API
|
||||
#ifdef FRAMELESSHELPER_STATIC
|
||||
#define FRAMELESSHELPER_API
|
||||
#else
|
||||
#ifdef FRAMELESSHELPER_BUILD_LIBRARY
|
||||
#define FRAMELESSHELPER_API Q_DECL_EXPORT
|
||||
#else
|
||||
#define FRAMELESSHELPER_API Q_DECL_IMPORT
|
||||
#endif
|
||||
#endif
|
||||
# ifdef FRAMELESSHELPER_STATIC
|
||||
# define FRAMELESSHELPER_API
|
||||
# else
|
||||
# ifdef FRAMELESSHELPER_BUILD_LIBRARY
|
||||
# define FRAMELESSHELPER_API Q_DECL_EXPORT
|
||||
# else
|
||||
# define FRAMELESSHELPER_API Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINDOWS)
|
||||
#define Q_OS_WINDOWS
|
||||
# define Q_OS_WINDOWS
|
||||
#endif
|
||||
|
||||
#ifndef Q_DISABLE_COPY_MOVE
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
# define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
|
||||
#define qAsConst(i) std::as_const(i)
|
||||
# define qAsConst(i) std::as_const(i)
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
|
||||
#define QStringView const QString &
|
||||
# define QStringView const QString &
|
||||
#else
|
||||
#include <QtCore/qstringview.h>
|
||||
# include <QtCore/qstringview.h>
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
|
||||
#define qExchange(a, b) std::exchange(a, b)
|
||||
# define qExchange(a, b) std::exchange(a, b)
|
||||
# define Q_NAMESPACE_EXPORT(ns) Q_NAMESPACE
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
#define Q_NODISCARD [[nodiscard]]
|
||||
# define Q_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
#define Q_NODISCARD
|
||||
# define Q_NODISCARD
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_WINDOWS) || defined(FRAMELESSHELPER_TEST_UNIX)
|
||||
#define FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
# define FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
#endif
|
||||
|
||||
#ifndef FRAMELESSHELPER_NAMESPACE
|
||||
#define FRAMELESSHELPER_NAMESPACE __flh_ns
|
||||
# define FRAMELESSHELPER_NAMESPACE __flh_ns
|
||||
#endif
|
||||
|
||||
#ifndef FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
#define FRAMELESSHELPER_BEGIN_NAMESPACE namespace FRAMELESSHELPER_NAMESPACE {
|
||||
# define FRAMELESSHELPER_BEGIN_NAMESPACE namespace FRAMELESSHELPER_NAMESPACE {
|
||||
#endif
|
||||
|
||||
#ifndef FRAMELESSHELPER_END_NAMESPACE
|
||||
#define FRAMELESSHELPER_END_NAMESPACE }
|
||||
# define FRAMELESSHELPER_END_NAMESPACE }
|
||||
#endif
|
||||
|
||||
#ifndef FRAMELESSHELPER_USE_NAMESPACE
|
||||
#define FRAMELESSHELPER_USE_NAMESPACE using namespace FRAMELESSHELPER_NAMESPACE;
|
||||
# define FRAMELESSHELPER_USE_NAMESPACE using namespace FRAMELESSHELPER_NAMESPACE;
|
||||
#endif
|
||||
|
||||
#ifndef FRAMELESSHELPER_PREPEND_NAMESPACE
|
||||
#define FRAMELESSHELPER_PREPEND_NAMESPACE(X) ::FRAMELESSHELPER_NAMESPACE::X
|
||||
# define FRAMELESSHELPER_PREPEND_NAMESPACE(X) ::FRAMELESSHELPER_NAMESPACE::X
|
||||
#endif
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
Q_NAMESPACE_EXPORT(FRAMELESSHELPER_API)
|
||||
#else
|
||||
Q_NAMESPACE
|
||||
#endif
|
||||
|
||||
namespace Constants
|
||||
[[maybe_unused]] static constexpr const int kDefaultResizeBorderThicknessClassic = 4;
|
||||
[[maybe_unused]] static constexpr const int kDefaultResizeBorderThicknessAero = 8;
|
||||
[[maybe_unused]] static constexpr const int kDefaultCaptionHeight = 23;
|
||||
[[maybe_unused]] static constexpr const int kDefaultTitleBarHeight = 30;
|
||||
[[maybe_unused]] static constexpr const int kDefaultWindowFrameBorderThickness = 1;
|
||||
|
||||
[[maybe_unused]] static const QString kWindow = QStringLiteral("window");
|
||||
[[maybe_unused]] static const QString kFramelessHelper = QStringLiteral("framelessHelper");
|
||||
[[maybe_unused]] static const QString kResizeBorderThicknessH = QStringLiteral("resizeBorderThicknessH");
|
||||
[[maybe_unused]] static const QString kResizeBorderThicknessV = QStringLiteral("resizeBorderThicknessV");
|
||||
[[maybe_unused]] static const QString kCaptionHeight = QStringLiteral("captionHeight");
|
||||
[[maybe_unused]] static const QString kTitleBarHeight = QStringLiteral("titleBarHeight");
|
||||
[[maybe_unused]] static const QString kResizable = QStringLiteral("resizable");
|
||||
|
||||
enum class Theme : int
|
||||
{
|
||||
|
||||
[[maybe_unused]] static constexpr const char kFramelessModeFlag[] = "_FRAMELESSHELPER_FRAMELESS_MODE";
|
||||
[[maybe_unused]] static constexpr const char kResizeBorderThicknessFlag[] = "_FRAMELESSHELPER_RESIZE_BORDER_THICKNESS";
|
||||
[[maybe_unused]] static constexpr const char kCaptionHeightFlag[] = "_FRAMELESSHELPER_CAPTION_HEIGHT";
|
||||
[[maybe_unused]] static constexpr const char kTitleBarHeightFlag[] = "_FRAMELESSHELPER_TITLE_BAR_HEIGHT";
|
||||
[[maybe_unused]] static constexpr const char kHitTestVisibleFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE";
|
||||
[[maybe_unused]] static constexpr const char kWindowFixedSizeFlag[] = "_FRAMELESSHELPER_WINDOW_FIXED_SIZE";
|
||||
|
||||
}
|
||||
|
||||
enum class SystemMetric : int
|
||||
{
|
||||
ResizeBorderThickness = 0,
|
||||
CaptionHeight,
|
||||
TitleBarHeight
|
||||
Unknown = 0,
|
||||
Light = 1,
|
||||
Dark = 2,
|
||||
HighContrast = 3
|
||||
};
|
||||
Q_ENUM_NS(SystemMetric)
|
||||
Q_ENUM_NS(Theme)
|
||||
|
||||
enum class ColorizationArea : int
|
||||
enum class DwmColorizationArea : int
|
||||
{
|
||||
None = 0,
|
||||
StartMenu_TaskBar_ActionCenter,
|
||||
TitleBar_WindowBorder,
|
||||
All
|
||||
StartMenu_TaskBar_ActionCenter = 1,
|
||||
TitleBar_WindowBorder = 2,
|
||||
All = 3
|
||||
};
|
||||
Q_ENUM_NS(ColorizationArea)
|
||||
Q_ENUM_NS(DwmColorizationArea)
|
||||
|
||||
enum class Property : int
|
||||
{
|
||||
PrimaryScreenDpi_Horizontal = 0,
|
||||
PrimaryScreenDpi_Vertical = 1,
|
||||
WindowDpi_Horizontal = 2,
|
||||
WindowDpi_Vertical = 3,
|
||||
ResizeBorderThickness_Horizontal_Unscaled = 4,
|
||||
ResizeBorderThickness_Horizontal_Scaled = 5,
|
||||
ResizeBorderThickness_Vertical_Unscaled = 6,
|
||||
ResizeBorderThickness_Vertical_Scaled = 7,
|
||||
CaptionHeight_Unscaled = 8,
|
||||
CaptionHeight_Scaled = 9,
|
||||
TitleBarHeight_Unscaled = 10,
|
||||
TitleBarHeight_Scaled = 11,
|
||||
FrameBorderThickness_Unscaled = 12,
|
||||
FrameBorderThickness_Scaled = 13,
|
||||
FrameBorderColor_Active = 14,
|
||||
FrameBorderColor_Inactive = 15,
|
||||
SystemAccentColor = 16,
|
||||
SystemColorizationArea = 17,
|
||||
SystemTheme = 18,
|
||||
WallpaperBackgroundColor = 19,
|
||||
WallpaperAspectStyle = 20,
|
||||
WallpaperFilePath = 21
|
||||
};
|
||||
Q_ENUM_NS(Property)
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -23,11 +23,10 @@
|
|||
*/
|
||||
|
||||
#include "framelesshelper_win32.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/private/qsystemlibrary_p.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include "framelesswindowsmanager_p.h"
|
||||
#include "utilities.h"
|
||||
#include "framelesshelper_windows.h"
|
||||
|
||||
|
@ -35,102 +34,48 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
|
|||
|
||||
struct FramelessHelperWinData
|
||||
{
|
||||
QMutex mutex = {};
|
||||
QScopedPointer<FramelessHelperWin> instance;
|
||||
|
||||
explicit FramelessHelperWinData() = default;
|
||||
~FramelessHelperWinData() = default;
|
||||
|
||||
[[nodiscard]] bool create() {
|
||||
if (!m_instance.isNull()) {
|
||||
return false;
|
||||
}
|
||||
m_instance.reset(new FramelessHelperWin);
|
||||
return !m_instance.isNull();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool release() {
|
||||
if (!m_instance.isNull()) {
|
||||
m_instance.reset();
|
||||
}
|
||||
return m_instance.isNull();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNull() const {
|
||||
return m_instance.isNull();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool install() {
|
||||
if (isInstalled()) {
|
||||
return true;
|
||||
}
|
||||
if (isNull()) {
|
||||
if (!create()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QCoreApplication::instance()->installNativeEventFilter(m_instance.data());
|
||||
m_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool uninstall() {
|
||||
if (!isInstalled()) {
|
||||
return true;
|
||||
}
|
||||
if (isNull()) {
|
||||
return false;
|
||||
}
|
||||
QCoreApplication::instance()->removeNativeEventFilter(m_instance.data());
|
||||
m_installed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isInstalled() const {
|
||||
return m_installed;
|
||||
}
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY_MOVE(FramelessHelperWinData)
|
||||
QScopedPointer<FramelessHelperWin> m_instance;
|
||||
bool m_installed = false;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(FramelessHelperWinData, g_framelessHelperWinData)
|
||||
Q_GLOBAL_STATIC(FramelessHelperWinData, g_helper)
|
||||
|
||||
static inline void installHelper(QWindow *window, const bool enable)
|
||||
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
|
||||
|
||||
FramelessHelperWin::~FramelessHelperWin()
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
Utilities::updateQtFrameMargins(window, enable);
|
||||
const WId winId = window->winId();
|
||||
Utilities::updateFrameMargins(winId, !enable);
|
||||
window->setProperty(Constants::kFramelessModeFlag, enable);
|
||||
qApp->removeNativeEventFilter(this);
|
||||
}
|
||||
|
||||
FramelessHelperWin::FramelessHelperWin() = default;
|
||||
|
||||
FramelessHelperWin::~FramelessHelperWin() = default;
|
||||
|
||||
void FramelessHelperWin::addFramelessWindow(QWindow *window)
|
||||
void FramelessHelperWin::addWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
if (g_framelessHelperWinData()->install()) {
|
||||
installHelper(window, true);
|
||||
} else {
|
||||
qCritical() << "Failed to install native event filter.";
|
||||
QMutexLocker locker(&g_helper()->mutex);
|
||||
if (g_helper()->instance.isNull()) {
|
||||
g_helper()->instance.reset(new FramelessHelperWin);
|
||||
qApp->installNativeEventFilter(g_helper()->instance.data());
|
||||
}
|
||||
Utilities::updateInternalWindowFrameMargins(window, true);
|
||||
Utilities::updateWindowFrameMargins(window->winId(), false);
|
||||
}
|
||||
|
||||
void FramelessHelperWin::removeFramelessWindow(QWindow *window)
|
||||
void FramelessHelperWin::removeWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
installHelper(window, false);
|
||||
Utilities::updateInternalWindowFrameMargins(window, false);
|
||||
Utilities::updateWindowFrameMargins(window->winId(), true);
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
|
@ -149,14 +94,17 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
const auto msg = static_cast<LPMSG>(message);
|
||||
#endif
|
||||
if (!msg->hwnd) {
|
||||
// Why sometimes the window handle is null? Is it designed to be?
|
||||
// Anyway, we should skip it in this case.
|
||||
// Why sometimes the window handle is null? Is it designed to be like this?
|
||||
// Anyway, we should skip the entire function in this case.
|
||||
return false;
|
||||
}
|
||||
const QWindow * const window = Utilities::findWindow(reinterpret_cast<WId>(msg->hwnd));
|
||||
if (!window || !window->property(Constants::kFramelessModeFlag).toBool()) {
|
||||
const auto manager = Private::FramelessManager::instance();
|
||||
manager->mutex.lock();
|
||||
if (!manager->winId.contains(reinterpret_cast<WId>(msg->hwnd))) {
|
||||
manager->mutex.unlock();
|
||||
return false;
|
||||
}
|
||||
manager->mutex.unlock();
|
||||
switch (msg->message) {
|
||||
case WM_NCCALCSIZE: {
|
||||
// Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容)
|
||||
|
@ -355,13 +303,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
// This does however work fine for maximized.
|
||||
if (top) {
|
||||
// Peculiarly, when we're fullscreen,
|
||||
clientRect->top += kAutoHideTaskbarThickness;
|
||||
clientRect->top += kAutoHideTaskBarThickness;
|
||||
} else if (bottom) {
|
||||
clientRect->bottom -= kAutoHideTaskbarThickness;
|
||||
clientRect->bottom -= kAutoHideTaskBarThickness;
|
||||
} else if (left) {
|
||||
clientRect->left += kAutoHideTaskbarThickness;
|
||||
clientRect->left += kAutoHideTaskBarThickness;
|
||||
} else if (right) {
|
||||
clientRect->right -= kAutoHideTaskbarThickness;
|
||||
clientRect->right -= kAutoHideTaskBarThickness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -404,7 +352,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
case WM_NCPAINT: {
|
||||
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
|
||||
|
||||
if (!Utilities::isDwmCompositionAvailable()) {
|
||||
if (!Utilities::isDwmCompositionEnabled()) {
|
||||
// Only block WM_NCPAINT when DWM composition is disabled. If
|
||||
// it's blocked when DWM composition is enabled, the frame
|
||||
// shadow won't be drawn.
|
||||
|
@ -415,12 +363,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
}
|
||||
}
|
||||
case WM_NCACTIVATE: {
|
||||
if (Utilities::isDwmCompositionAvailable()) {
|
||||
// DefWindowProc won't repaint the window border if lParam
|
||||
// (normally a HRGN) is -1. See the following link's "lParam" section:
|
||||
if (Utilities::isDwmCompositionEnabled()) {
|
||||
// DefWindowProc won't repaint the window border if lParam (normally a HRGN)
|
||||
// is -1. See the following link's "lParam" section:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate
|
||||
// Don't use "*result = 0" here, otherwise the window won't respond
|
||||
// to the window activation state change.
|
||||
// Don't use "*result = 0" here, otherwise the window won't respond to the
|
||||
// window activation state change.
|
||||
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
|
||||
} else {
|
||||
if (static_cast<BOOL>(msg->wParam) == FALSE) {
|
||||
|
@ -509,24 +457,9 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
break;
|
||||
}
|
||||
const LONG windowWidth = clientRect.right;
|
||||
const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true);
|
||||
const int titleBarHeight = Utilities::getSystemMetric(window, SystemMetric::TitleBarHeight, true);
|
||||
bool isTitleBar = false;
|
||||
const bool max = IsMaximized(msg->hwnd);
|
||||
if (max || Utilities::isFullScreen(reinterpret_cast<WId>(msg->hwnd))) {
|
||||
isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight)
|
||||
&& (localMouse.x() >= 0) && (localMouse.x() <= windowWidth)
|
||||
&& !Utilities::isHitTestVisible(window);
|
||||
}
|
||||
if (Utilities::isWindowNoState(reinterpret_cast<WId>(msg->hwnd))) {
|
||||
isTitleBar = (localMouse.y() > resizeBorderThickness) && (localMouse.y() <= titleBarHeight)
|
||||
&& (localMouse.x() > resizeBorderThickness) && (localMouse.x() < (windowWidth - resizeBorderThickness))
|
||||
&& !Utilities::isHitTestVisible(window);
|
||||
}
|
||||
const bool isTop = localMouse.y() <= resizeBorderThickness;
|
||||
*result = [clientRect, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window, max](){
|
||||
const bool mousePressed = (GetSystemMetrics(SM_SWAPBUTTON)
|
||||
? (GetAsyncKeyState(VK_RBUTTON) < 0) : (GetAsyncKeyState(VK_LBUTTON) < 0));
|
||||
if (max) {
|
||||
if (isTitleBar && mousePressed) {
|
||||
return HTCAPTION;
|
||||
|
@ -615,11 +548,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
#endif
|
||||
case WM_DPICHANGED: {
|
||||
// Sync the internal window frame margins with the latest DPI.
|
||||
Utilities::updateQtFrameMargins(const_cast<QWindow *>(window), true);
|
||||
Utilities::updateInternalWindowFrameMargins(window, true);
|
||||
} break;
|
||||
case WM_DWMCOMPOSITIONCHANGED: {
|
||||
// Re-apply the custom window frame if recovered from the basic theme.
|
||||
Utilities::updateFrameMargins(reinterpret_cast<WId>(msg->hwnd), false);
|
||||
Utilities::updateWindowFrameMargins(reinterpret_cast<WId>(msg->hwnd), false);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -26,10 +26,9 @@
|
|||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtCore/qabstractnativeeventfilter.h>
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_FORWARD_DECLARE_CLASS(QWindow)
|
||||
class QWindow;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
@ -42,8 +41,8 @@ public:
|
|||
explicit FramelessHelperWin();
|
||||
~FramelessHelperWin() override;
|
||||
|
||||
static void addFramelessWindow(QWindow *window);
|
||||
static void removeFramelessWindow(QWindow *window);
|
||||
static void addWindow(QWindow *window);
|
||||
static void removeWindow(QWindow *window);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
|
|
|
@ -25,49 +25,49 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
# define UNICODE
|
||||
#endif
|
||||
|
||||
#ifndef _UNICODE
|
||||
#define _UNICODE
|
||||
# define _UNICODE
|
||||
#endif
|
||||
|
||||
#ifndef _CRT_NON_CONFORMING_SWPRINTFS
|
||||
#define _CRT_NON_CONFORMING_SWPRINTFS
|
||||
# define _CRT_NON_CONFORMING_SWPRINTFS
|
||||
#endif
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <sdkddkver.h>
|
||||
|
||||
#ifndef _WIN32_WINNT_WIN10
|
||||
#define _WIN32_WINNT_WIN10 0x0A00
|
||||
# define _WIN32_WINNT_WIN10 0x0A00
|
||||
#endif
|
||||
|
||||
#ifndef NTDDI_WIN10_CO
|
||||
#define NTDDI_WIN10_CO 0x0A00000B
|
||||
# define NTDDI_WIN10_CO 0x0A00000B
|
||||
#endif
|
||||
|
||||
#ifdef WINVER
|
||||
#undef WINVER
|
||||
# undef WINVER
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32_WINNT
|
||||
#undef _WIN32_WINNT
|
||||
# undef _WIN32_WINNT
|
||||
#endif
|
||||
|
||||
#ifdef NTDDI_VERSION
|
||||
#undef NTDDI_VERSION
|
||||
# undef NTDDI_VERSION
|
||||
#endif
|
||||
|
||||
#define WINVER _WIN32_WINNT_WIN10
|
||||
|
@ -75,70 +75,63 @@
|
|||
#define NTDDI_VERSION NTDDI_WIN10_CO
|
||||
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <uxtheme.h>
|
||||
#include <shellapi.h>
|
||||
#include <dwmapi.h>
|
||||
#include <timeapi.h>
|
||||
#include <shellscalingapi.h>
|
||||
|
||||
#ifndef WM_NCUAHDRAWCAPTION
|
||||
#define WM_NCUAHDRAWCAPTION (0x00AE)
|
||||
# define WM_NCUAHDRAWCAPTION (0x00AE)
|
||||
#endif
|
||||
|
||||
#ifndef WM_NCUAHDRAWFRAME
|
||||
#define WM_NCUAHDRAWFRAME (0x00AF)
|
||||
# define WM_NCUAHDRAWFRAME (0x00AF)
|
||||
#endif
|
||||
|
||||
#ifndef WM_DWMCOMPOSITIONCHANGED
|
||||
#define WM_DWMCOMPOSITIONCHANGED (0x031E)
|
||||
# define WM_DWMCOMPOSITIONCHANGED (0x031E)
|
||||
#endif
|
||||
|
||||
#ifndef WM_DWMCOLORIZATIONCOLORCHANGED
|
||||
#define WM_DWMCOLORIZATIONCOLORCHANGED (0x0320)
|
||||
# define WM_DWMCOLORIZATIONCOLORCHANGED (0x0320)
|
||||
#endif
|
||||
|
||||
#ifndef WM_DPICHANGED
|
||||
#define WM_DPICHANGED (0x02E0)
|
||||
# define WM_DPICHANGED (0x02E0)
|
||||
#endif
|
||||
|
||||
#ifndef SM_CXPADDEDBORDER
|
||||
#define SM_CXPADDEDBORDER (92)
|
||||
# define SM_CXPADDEDBORDER (92)
|
||||
#endif
|
||||
|
||||
#ifndef ABM_GETAUTOHIDEBAREX
|
||||
#define ABM_GETAUTOHIDEBAREX (0x0000000b)
|
||||
# define ABM_GETAUTOHIDEBAREX (0x0000000b)
|
||||
#endif
|
||||
|
||||
#ifndef GET_X_LPARAM
|
||||
#define GET_X_LPARAM(lp) (static_cast<int>(static_cast<short>(LOWORD(lp))))
|
||||
# define GET_X_LPARAM(lp) (static_cast<int>(static_cast<short>(LOWORD(lp))))
|
||||
#endif
|
||||
|
||||
#ifndef GET_Y_LPARAM
|
||||
#define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(HIWORD(lp))))
|
||||
# define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(HIWORD(lp))))
|
||||
#endif
|
||||
|
||||
#ifndef IsMinimized
|
||||
#define IsMinimized(window) (IsIconic(window) != FALSE)
|
||||
# define IsMinimized(hwnd) (IsIconic(hwnd) != FALSE)
|
||||
#endif
|
||||
|
||||
#ifndef IsMaximized
|
||||
#define IsMaximized(window) (IsZoomed(window) != FALSE)
|
||||
# define IsMaximized(hwnd) (IsZoomed(hwnd) != FALSE)
|
||||
#endif
|
||||
|
||||
struct flh_timecaps_tag
|
||||
{
|
||||
UINT wPeriodMin; // minimum period supported
|
||||
UINT wPeriodMax; // maximum period supported
|
||||
};
|
||||
using flh_TIMECAPS = flh_timecaps_tag;
|
||||
using flh_PTIMECAPS = flh_timecaps_tag *;
|
||||
using flh_NPTIMECAPS = flh_timecaps_tag * NEAR;
|
||||
using flh_LPTIMECAPS = flh_timecaps_tag * FAR;
|
||||
#include <QtCore/qstring.h>
|
||||
|
||||
[[maybe_unused]] static constexpr const UINT kAutoHideTaskbarThickness = 2; // The thickness of an auto-hide taskbar in pixels
|
||||
[[maybe_unused]] static constexpr const int kAutoHideTaskBarThickness = 2; // The thickness of an auto-hide taskbar in pixels.
|
||||
|
||||
[[maybe_unused]] static constexpr const char kDwmRegistryKey[] = R"(Software\Microsoft\Windows\DWM)";
|
||||
[[maybe_unused]] static constexpr const char kPersonalizeRegistryKey[] = R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)";
|
||||
|
||||
[[maybe_unused]] static constexpr const UINT kDefaultResizeBorderThicknessClassic = 4;
|
||||
[[maybe_unused]] static constexpr const UINT kDefaultResizeBorderThicknessAero = 8;
|
||||
[[maybe_unused]] static constexpr const UINT kDefaultCaptionHeight = 23;
|
||||
[[maybe_unused]] static const QString kDwmRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\DWM)");
|
||||
[[maybe_unused]] static const QString kPersonalizeRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
|
||||
|
||||
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
|
||||
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
||||
[[maybe_unused]] static constexpr const DWORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;
|
||||
|
|
|
@ -42,7 +42,7 @@ class FRAMELESSHELPER_API FramelessQuickHelper : public QQuickItem
|
|||
|
||||
public:
|
||||
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
|
||||
~FramelessQuickHelper() override = default;
|
||||
~FramelessQuickHelper() override;
|
||||
|
||||
Q_NODISCARD qreal resizeBorderThickness() const;
|
||||
void setResizeBorderThickness(const qreal val);
|
||||
|
|
|
@ -23,152 +23,75 @@
|
|||
*/
|
||||
|
||||
#include "framelesswindowsmanager.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include "framelesswindowsmanager_p.h"
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
#include "framelesshelper.h"
|
||||
#else
|
||||
#include <QtGui/qscreen.h>
|
||||
#include "framelesshelper_win32.h"
|
||||
#endif
|
||||
#include "utilities.h"
|
||||
#ifdef Q_OS_WINDOWS
|
||||
# include "framelesshelper_win32.h"
|
||||
#endif
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix)
|
||||
#ifdef Q_OS_WINDOWS
|
||||
static const bool g_usePureQtImplementation = (qEnvironmentVariableIntValue("FRAMELESSHELPER_PURE_QT_IMPL") != 0);
|
||||
#else
|
||||
static constexpr const bool g_usePureQtImplementation = true;
|
||||
#endif
|
||||
|
||||
namespace Private
|
||||
{
|
||||
|
||||
Q_GLOBAL_STATIC(FramelessManager, g_manager)
|
||||
|
||||
FramelessManager::FramelessManager() = default;
|
||||
|
||||
FramelessManager::~FramelessManager() = default;
|
||||
|
||||
FramelessManager *FramelessManager::instance()
|
||||
{
|
||||
return g_manager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FramelessWindowsManager::addWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
// If you encounter with any issues when do the painting through OpenGL,
|
||||
// just comment out the following two lines, they are here to workaround
|
||||
// some strange Windows bugs but to be honest they don't have much to do
|
||||
// with our custom window frame handling functionality.
|
||||
if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) {
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
QMutexLocker locker(&Private::g_manager()->mutex);
|
||||
if (Private::g_manager()->qwindow.contains(window)) {
|
||||
return;
|
||||
}
|
||||
QVariantHash data = {};
|
||||
data.insert(kWindow, QVariant::fromValue(window));
|
||||
if (g_usePureQtImplementation) {
|
||||
const auto qtFramelessHelper = new FramelessHelper(window);
|
||||
qtFramelessHelper->addWindow(window);
|
||||
data.insert(kFramelessHelper, QVariant::fromValue(qtFramelessHelper));
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
else {
|
||||
FramelessHelperWin::addWindow(window);
|
||||
}
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
framelessHelperUnix()->removeWindowFrame(window);
|
||||
#else
|
||||
FramelessHelperWin::addFramelessWindow(window);
|
||||
#ifdef Q_OS_WIN
|
||||
// Work-around Win32 multi-monitor artifacts.
|
||||
QObject::connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){
|
||||
Q_UNUSED(screen);
|
||||
QObject::connect(window, &QWindow::screenChanged, window, [window](){
|
||||
// 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.
|
||||
Utilities::triggerFrameChange(window->winId());
|
||||
// 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
|
||||
// a resize event manually. Although the actual size does not change, the issue we
|
||||
// observed disappeared indeed, amazingly.
|
||||
window->resize(window->size());
|
||||
// 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.
|
||||
Utilities::triggerFrameChange(window->winId());
|
||||
});
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void FramelessWindowsManager::setHitTestVisible(QWindow *window, QObject *object, const bool value)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
Q_ASSERT(object);
|
||||
if (!window || !object) {
|
||||
return;
|
||||
}
|
||||
if (!object->isWidgetType() && !object->inherits("QQuickItem")) {
|
||||
qWarning() << object << "is not a QWidget or QQuickItem.";
|
||||
return;
|
||||
}
|
||||
auto objList = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleFlag));
|
||||
if (value) {
|
||||
if (objList.isEmpty() || !objList.contains(object)) {
|
||||
objList.append(object);
|
||||
}
|
||||
} else {
|
||||
if (!objList.isEmpty() && objList.contains(object)) {
|
||||
objList.removeAll(object);
|
||||
}
|
||||
}
|
||||
window->setProperty(Constants::kHitTestVisibleFlag, QVariant::fromValue(objList));
|
||||
}
|
||||
|
||||
int FramelessWindowsManager::getResizeBorderThickness(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return 8;
|
||||
}
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
const int value = window->property(Constants::kResizeBorderThicknessFlag).toInt();
|
||||
return value <= 0 ? 8 : value;
|
||||
#else
|
||||
return Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FramelessWindowsManager::setResizeBorderThickness(QWindow *window, const int value)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window || (value <= 0)) {
|
||||
return;
|
||||
}
|
||||
window->setProperty(Constants::kResizeBorderThicknessFlag, value);
|
||||
}
|
||||
|
||||
int FramelessWindowsManager::getTitleBarHeight(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return 31;
|
||||
}
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
const int value = window->property(Constants::kTitleBarHeightFlag).toInt();
|
||||
return value <= 0 ? 31 : value;
|
||||
#else
|
||||
return Utilities::getSystemMetric(window, SystemMetric::TitleBarHeight, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FramelessWindowsManager::setTitleBarHeight(QWindow *window, const int value)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window || (value <= 0)) {
|
||||
return;
|
||||
}
|
||||
window->setProperty(Constants::kTitleBarHeightFlag, value);
|
||||
}
|
||||
|
||||
bool FramelessWindowsManager::getResizable(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
return !window->property(Constants::kWindowFixedSizeFlag).toBool();
|
||||
#else
|
||||
return !Utilities::isWindowFixedSize(window);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FramelessWindowsManager::setResizable(QWindow *window, const bool value)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
window->setProperty(Constants::kWindowFixedSizeFlag, !value);
|
||||
#else
|
||||
window->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value);
|
||||
#endif
|
||||
const QUuid uuid = QUuid::createUuid();
|
||||
Private::g_manager()->qwindow.insert(window, uuid);
|
||||
Private::g_manager()->winId.insert(window->winId(), uuid);
|
||||
Private::g_manager()->data.insert(uuid, data);
|
||||
}
|
||||
|
||||
void FramelessWindowsManager::removeWindow(QWindow *window)
|
||||
|
@ -177,20 +100,27 @@ void FramelessWindowsManager::removeWindow(QWindow *window)
|
|||
if (!window) {
|
||||
return;
|
||||
}
|
||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||
framelessHelperUnix()->bringBackWindowFrame(window);
|
||||
#else
|
||||
FramelessHelperWin::removeFramelessWindow(window);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FramelessWindowsManager::isWindowFrameless(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
QMutexLocker locker(&Private::g_manager()->mutex);
|
||||
if (!Private::g_manager()->qwindow.contains(window)) {
|
||||
return;
|
||||
}
|
||||
return window->property(Constants::kFramelessModeFlag).toBool();
|
||||
const QUuid uuid = Private::g_manager()->qwindow.value(window);
|
||||
Q_ASSERT(Private::g_manager()->data.contains(uuid));
|
||||
if (!Private::g_manager()->data.contains(uuid)) {
|
||||
return;
|
||||
}
|
||||
const QVariantHash data = Private::g_manager()->data.value(uuid);
|
||||
if (data.contains(kFramelessHelper)) {
|
||||
const auto qtFramelessHelper = qvariant_cast<FramelessHelper *>(data.value(kFramelessHelper));
|
||||
Q_ASSERT(qtFramelessHelper);
|
||||
if (qtFramelessHelper) {
|
||||
qtFramelessHelper->removeWindow(window);
|
||||
delete qtFramelessHelper;
|
||||
}
|
||||
}
|
||||
Private::g_manager()->qwindow.remove(window);
|
||||
Private::g_manager()->winId.remove(window->winId());
|
||||
Private::g_manager()->data.remove(uuid);
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -27,8 +27,7 @@
|
|||
#include "framelesshelper_global.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_FORWARD_DECLARE_CLASS(QObject)
|
||||
QT_FORWARD_DECLARE_CLASS(QWindow)
|
||||
class QWindow;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
@ -38,14 +37,6 @@ namespace FramelessWindowsManager
|
|||
|
||||
FRAMELESSHELPER_API void addWindow(QWindow *window);
|
||||
FRAMELESSHELPER_API void removeWindow(QWindow *window);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWindowFrameless(const QWindow *window);
|
||||
FRAMELESSHELPER_API void setHitTestVisible(QWindow *window, QObject *object, const bool value = true);
|
||||
[[nodiscard]] FRAMELESSHELPER_API int getResizeBorderThickness(const QWindow *window);
|
||||
FRAMELESSHELPER_API void setResizeBorderThickness(QWindow *window, const int value);
|
||||
[[nodiscard]] FRAMELESSHELPER_API int getTitleBarHeight(const QWindow *window);
|
||||
FRAMELESSHELPER_API void setTitleBarHeight(QWindow *window, const int value);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool getResizable(const QWindow *window);
|
||||
FRAMELESSHELPER_API void setResizable(QWindow *window, const bool value = true);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/quuid.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
namespace Private
|
||||
{
|
||||
|
||||
class FramelessManager
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(FramelessManager)
|
||||
|
||||
public:
|
||||
explicit FramelessManager();
|
||||
~FramelessManager();
|
||||
|
||||
[[nodiscard]] static FramelessManager *instance();
|
||||
|
||||
QMutex mutex = {};
|
||||
QHash<QWindow *, QUuid> qwindow = {};
|
||||
QHash<WId, QUuid> winId = {};
|
||||
QHash<QUuid, QVariantHash> data = {};
|
||||
};
|
||||
|
||||
} // namespace Private
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
2
lib.pro
2
lib.pro
|
@ -8,6 +8,7 @@ DEFINES += \
|
|||
QT_NO_CAST_FROM_ASCII \
|
||||
QT_NO_CAST_TO_ASCII \
|
||||
QT_NO_KEYWORDS \
|
||||
QT_USE_QSTRINGBUILDER \
|
||||
QT_DEPRECATED_WARNINGS \
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060400 \
|
||||
FRAMELESSHELPER_BUILD_LIBRARY
|
||||
|
@ -15,6 +16,7 @@ HEADERS += \
|
|||
framelesshelper_global.h \
|
||||
framelesshelper.h \
|
||||
framelesswindowsmanager.h \
|
||||
framelesswindowsmanager_p.h \
|
||||
utilities.h
|
||||
SOURCES += \
|
||||
framelesshelper.cpp \
|
||||
|
|
|
@ -23,97 +23,7 @@
|
|||
*/
|
||||
|
||||
#include "utilities.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
QWindow *Utilities::findWindow(const WId winId)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return nullptr;
|
||||
}
|
||||
const QWindowList windows = QGuiApplication::topLevelWindows();
|
||||
if (windows.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto &&window : qAsConst(windows)) {
|
||||
if (window && window->handle()) {
|
||||
if (window->winId() == winId) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Utilities::isWindowFixedSize(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
if (window->flags() & Qt::MSWindowsFixedSizeDialogHint) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
const QSize minSize = window->minimumSize();
|
||||
const QSize maxSize = window->maximumSize();
|
||||
if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Utilities::isHitTestVisible(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
const auto objs = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleFlag));
|
||||
if (objs.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (auto &&obj : qAsConst(objs)) {
|
||||
if (!obj || !(obj->isWidgetType() || obj->inherits("QQuickItem"))) {
|
||||
continue;
|
||||
}
|
||||
if (!obj->property("visible").toBool()) {
|
||||
continue;
|
||||
}
|
||||
const QPointF originPoint = mapOriginPointToWindow(obj);
|
||||
const qreal width = obj->property("width").toReal();
|
||||
const qreal height = obj->property("height").toReal();
|
||||
const QRectF rect = {originPoint.x(), originPoint.y(), width, height};
|
||||
if (rect.contains(QCursor::pos(window->screen()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QPointF Utilities::mapOriginPointToWindow(const QObject *object)
|
||||
{
|
||||
Q_ASSERT(object);
|
||||
if (!object) {
|
||||
return {};
|
||||
}
|
||||
if (!object->isWidgetType() && !object->inherits("QQuickItem")) {
|
||||
qWarning() << object << "is not a QWidget or a QQuickItem.";
|
||||
return {};
|
||||
}
|
||||
QPointF point = {object->property("x").toReal(), object->property("y").toReal()};
|
||||
for (QObject *parent = object->parent(); parent; parent = parent->parent()) {
|
||||
point += {parent->property("x").toReal(), parent->property("y").toReal()};
|
||||
if (parent->isWindowType()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
40
utilities.h
40
utilities.h
|
@ -32,34 +32,38 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
|
|||
namespace Utilities
|
||||
{
|
||||
|
||||
[[nodiscard]] FRAMELESSHELPER_API int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue = false);
|
||||
[[nodiscard]] FRAMELESSHELPER_API QWindow *findWindow(const WId winId);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isHitTestVisible(const QWindow *window);
|
||||
[[nodiscard]] FRAMELESSHELPER_API QPointF mapOriginPointToWindow(const QObject *object);
|
||||
[[nodiscard]] FRAMELESSHELPER_API QColor getColorizationColor();
|
||||
[[nodiscard]] FRAMELESSHELPER_API int getWindowVisibleFrameBorderThickness(const WId winId);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool shouldAppsUseDarkMode();
|
||||
[[nodiscard]] FRAMELESSHELPER_API ColorizationArea getColorizationArea();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isThemeChanged(const void *data);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isSystemMenuRequested(const void *data, QPointF *pos);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool showSystemMenu(const WId winId, const QPointF &pos);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWin8OrGreater();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWin8Point1OrGreater();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWin10OrGreater();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWin101809OrGreater();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWin11OrGreater();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isDwmCompositionAvailable();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isDwmCompositionEnabled();
|
||||
FRAMELESSHELPER_API void triggerFrameChange(const WId winId);
|
||||
FRAMELESSHELPER_API void updateFrameMargins(const WId winId, const bool reset);
|
||||
FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable);
|
||||
FRAMELESSHELPER_API void updateWindowFrameMargins(const WId winId, const bool reset);
|
||||
FRAMELESSHELPER_API void updateInternalWindowFrameMargins(QWindow *window, const bool enable);
|
||||
[[nodiscard]] FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isFullScreen(const WId winId);
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isWindowNoState(const WId winId);
|
||||
FRAMELESSHELPER_API void syncWmPaintWithDwm();
|
||||
#endif
|
||||
FRAMELESSHELPER_API void showSystemMenu(const WId winId, const QPointF &pos);
|
||||
[[nodiscard]] FRAMELESSHELPER_API QColor getDwmColorizationColor();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool shouldAppsUseDarkMode();
|
||||
[[nodiscard]] FRAMELESSHELPER_API DwmColorizationArea getDwmColorizationArea();
|
||||
[[nodiscard]] FRAMELESSHELPER_API bool isHighContrastModeEnabled();
|
||||
[[nodiscard]] FRAMELESSHELPER_API QColor getWallpaperBackgroundColor();
|
||||
[[nodiscard]] FRAMELESSHELPER_API int getWallpaperAspectStyle();
|
||||
[[nodiscard]] FRAMELESSHELPER_API QString getWallpaperFilePath();
|
||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getPrimaryScreenDpi(const bool horizontal);
|
||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getWindowDpi(const WId winId, const bool horizontal);
|
||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getResizeBorderThickness(const WId winId, const bool horizontal, const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getCaptionHeight(const WId winId, const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getTitleBarHeight(const WId winId, const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getFrameBorderThickness(const WId winId, const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_API QColor getFrameBorderColor(const bool active);
|
||||
FRAMELESSHELPER_API void updateWindowFrameColor(const WId winId, const bool dark);
|
||||
#endif // Q_OS_WINDOWS
|
||||
|
||||
}
|
||||
} // namespace Utilities
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -23,19 +23,18 @@
|
|||
*/
|
||||
|
||||
#include "utilities.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/private/qsystemlibrary_p.h>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
#include <QtCore/qoperatingsystemversion.h>
|
||||
# include <QtCore/qoperatingsystemversion.h>
|
||||
#else
|
||||
#include <QtCore/qsysinfo.h>
|
||||
# include <QtCore/qsysinfo.h>
|
||||
#endif
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qpa/qplatformwindow.h>
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
# include <QtGui/qguiapplication.h>
|
||||
# include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
#else
|
||||
#include <QtGui/qpa/qplatformwindow_p.h>
|
||||
# include <QtGui/qpa/qplatformwindow_p.h>
|
||||
#endif
|
||||
#include "qwinregistry_p.h"
|
||||
#include "framelesshelper_windows.h"
|
||||
|
@ -62,30 +61,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
|
|||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] static inline bool isWin10RS5OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
|
||||
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1809);
|
||||
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763));
|
||||
#else
|
||||
static const bool result = isWindowsVersionOrGreater(10, 0, 17763);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline bool isWin1019H1OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
|
||||
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1903);
|
||||
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 18362));
|
||||
#else
|
||||
static const bool result = isWindowsVersionOrGreater(10, 0, 18362);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline QString __getSystemErrorMessage(const QString &function, const DWORD code)
|
||||
{
|
||||
Q_ASSERT(!function.isEmpty());
|
||||
|
@ -119,6 +94,27 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
|
|||
return __getSystemErrorMessage(function, dwError);
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline int getSystemMetrics2(const WId winId, const int index,
|
||||
const bool horizontal, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return 0;
|
||||
}
|
||||
const UINT windowDpi = Utilities::getWindowDpi(winId, horizontal);
|
||||
static const auto pGetSystemMetricsForDpi =
|
||||
reinterpret_cast<decltype(&GetSystemMetricsForDpi)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("user32"), "GetSystemMetricsForDpi"));
|
||||
if (pGetSystemMetricsForDpi) {
|
||||
const UINT dpi = (scaled ? windowDpi : USER_DEFAULT_SCREEN_DPI);
|
||||
return pGetSystemMetricsForDpi(index, dpi);
|
||||
} else {
|
||||
// The returned value is already scaled, we need to divide the dpr to get the unscaled value.
|
||||
const qreal dpr = (scaled ? 1.0 : (qreal(windowDpi) / qreal(USER_DEFAULT_SCREEN_DPI)));
|
||||
return static_cast<int>(qRound(qreal(GetSystemMetrics(index)) / dpr));
|
||||
}
|
||||
}
|
||||
|
||||
bool Utilities::isWin8OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
|
@ -161,19 +157,20 @@ bool Utilities::isWin11OrGreater()
|
|||
return result;
|
||||
}
|
||||
|
||||
bool Utilities::isDwmCompositionAvailable()
|
||||
bool Utilities::isDwmCompositionEnabled()
|
||||
{
|
||||
// DWM composition is always enabled and can't be disabled since Windows 8.
|
||||
if (isWin8OrGreater()) {
|
||||
return true;
|
||||
}
|
||||
const auto resultFromRegistry = []() -> bool {
|
||||
QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey));
|
||||
const QWinRegistryKey registry(HKEY_CURRENT_USER, kDwmRegistryKey);
|
||||
const auto result = registry.dwordValue(QStringLiteral("Composition"));
|
||||
return (result.second && (result.first != 0));
|
||||
};
|
||||
static const auto pDwmIsCompositionEnabled =
|
||||
reinterpret_cast<HRESULT(WINAPI *)(BOOL *)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmIsCompositionEnabled"));
|
||||
reinterpret_cast<decltype(&DwmIsCompositionEnabled)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmIsCompositionEnabled"));
|
||||
if (!pDwmIsCompositionEnabled) {
|
||||
return resultFromRegistry();
|
||||
}
|
||||
|
@ -186,79 +183,6 @@ bool Utilities::isDwmCompositionAvailable()
|
|||
return (enabled != FALSE);
|
||||
}
|
||||
|
||||
int Utilities::getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
const qreal devicePixelRatio = window->devicePixelRatio();
|
||||
const qreal scaleFactor = (dpiScale ? devicePixelRatio : 1.0);
|
||||
switch (metric) {
|
||||
case SystemMetric::ResizeBorderThickness: {
|
||||
const int resizeBorderThickness = window->property(Constants::kResizeBorderThicknessFlag).toInt();
|
||||
if ((resizeBorderThickness > 0) && !forceSystemValue) {
|
||||
return qRound(static_cast<qreal>(resizeBorderThickness) * scaleFactor);
|
||||
} else {
|
||||
const int result = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||
if (result > 0) {
|
||||
if (dpiScale) {
|
||||
return result;
|
||||
} else {
|
||||
return qRound(static_cast<qreal>(result) / devicePixelRatio);
|
||||
}
|
||||
} else {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMetrics"));
|
||||
// The padded border will disappear if DWM composition is disabled.
|
||||
const int defaultResizeBorderThickness = (isDwmCompositionAvailable() ? kDefaultResizeBorderThicknessAero : kDefaultResizeBorderThicknessClassic);
|
||||
if (dpiScale) {
|
||||
return qRound(static_cast<qreal>(defaultResizeBorderThickness) * devicePixelRatio);
|
||||
} else {
|
||||
return defaultResizeBorderThickness;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case SystemMetric::CaptionHeight: {
|
||||
const int captionHeight = window->property(Constants::kCaptionHeightFlag).toInt();
|
||||
if ((captionHeight > 0) && !forceSystemValue) {
|
||||
return qRound(static_cast<qreal>(captionHeight) * scaleFactor);
|
||||
} else {
|
||||
const int result = GetSystemMetrics(SM_CYCAPTION);
|
||||
if (result > 0) {
|
||||
if (dpiScale) {
|
||||
return result;
|
||||
} else {
|
||||
return qRound(static_cast<qreal>(result) / devicePixelRatio);
|
||||
}
|
||||
} else {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMetrics"));
|
||||
if (dpiScale) {
|
||||
return qRound(static_cast<qreal>(kDefaultCaptionHeight) * devicePixelRatio);
|
||||
} else {
|
||||
return kDefaultCaptionHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case SystemMetric::TitleBarHeight: {
|
||||
const int titleBarHeight = window->property(Constants::kTitleBarHeightFlag).toInt();
|
||||
if ((titleBarHeight > 0) && !forceSystemValue) {
|
||||
return qRound(static_cast<qreal>(titleBarHeight) * scaleFactor);
|
||||
} else {
|
||||
const int captionHeight = getSystemMetric(window,SystemMetric::CaptionHeight,
|
||||
dpiScale, forceSystemValue);
|
||||
const int resizeBorderThickness = getSystemMetric(window, SystemMetric::ResizeBorderThickness,
|
||||
dpiScale, forceSystemValue);
|
||||
return (((window->windowState() == Qt::WindowMaximized)
|
||||
|| (window->windowState() == Qt::WindowFullScreen))
|
||||
? captionHeight : (captionHeight + resizeBorderThickness));
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Utilities::triggerFrameChange(const WId winId)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
|
@ -272,11 +196,11 @@ void Utilities::triggerFrameChange(const WId winId)
|
|||
}
|
||||
}
|
||||
|
||||
void Utilities::updateFrameMargins(const WId winId, const bool reset)
|
||||
void Utilities::updateWindowFrameMargins(const WId winId, const bool reset)
|
||||
{
|
||||
// DwmExtendFrameIntoClientArea() will always fail if DWM composition is disabled.
|
||||
// No need to try in this case.
|
||||
if (!isDwmCompositionAvailable()) {
|
||||
if (!isDwmCompositionEnabled()) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(winId);
|
||||
|
@ -284,7 +208,8 @@ void Utilities::updateFrameMargins(const WId winId, const bool reset)
|
|||
return;
|
||||
}
|
||||
static const auto pDwmExtendFrameIntoClientArea =
|
||||
reinterpret_cast<HRESULT(WINAPI *)(HWND, const MARGINS *)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmExtendFrameIntoClientArea"));
|
||||
reinterpret_cast<decltype(&DwmExtendFrameIntoClientArea)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmExtendFrameIntoClientArea"));
|
||||
if (!pDwmExtendFrameIntoClientArea) {
|
||||
return;
|
||||
}
|
||||
|
@ -298,15 +223,17 @@ void Utilities::updateFrameMargins(const WId winId, const bool reset)
|
|||
triggerFrameChange(winId);
|
||||
}
|
||||
|
||||
void Utilities::updateQtFrameMargins(QWindow *window, const bool enable)
|
||||
void Utilities::updateInternalWindowFrameMargins(QWindow *window, const bool enable)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
const int resizeBorderThickness = enable ? Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true, true) : 0;
|
||||
const int titleBarHeight = enable ? Utilities::getSystemMetric(window, SystemMetric::TitleBarHeight, true, true) : 0;
|
||||
const QMargins margins = {-resizeBorderThickness, -titleBarHeight, -resizeBorderThickness, -resizeBorderThickness};
|
||||
const WId winId = window->winId();
|
||||
const int resizeBorderThicknessH = enable ? getResizeBorderThickness(winId, true, true) : 0;
|
||||
const int resizeBorderThicknessV = enable ? getResizeBorderThickness(winId, false, true) : 0;
|
||||
const int titleBarHeight = enable ? getTitleBarHeight(winId, true) : 0;
|
||||
const QMargins margins = {-resizeBorderThicknessH, -titleBarHeight, -resizeBorderThicknessH, -resizeBorderThicknessV};
|
||||
const QVariant marginsVar = QVariant::fromValue(margins);
|
||||
window->setProperty("_q_windowsCustomMargins", marginsVar);
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
|
@ -327,7 +254,7 @@ void Utilities::updateQtFrameMargins(QWindow *window, const bool enable)
|
|||
return;
|
||||
}
|
||||
#endif
|
||||
triggerFrameChange(window->winId());
|
||||
triggerFrameChange(winId);
|
||||
}
|
||||
|
||||
QString Utilities::getSystemErrorMessage(const QString &function)
|
||||
|
@ -343,15 +270,16 @@ QString Utilities::getSystemErrorMessage(const QString &function)
|
|||
return __getSystemErrorMessage(function, code);
|
||||
}
|
||||
|
||||
QColor Utilities::getColorizationColor()
|
||||
QColor Utilities::getDwmColorizationColor()
|
||||
{
|
||||
const auto resultFromRegistry = []() -> QColor {
|
||||
QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey));
|
||||
const QWinRegistryKey registry(HKEY_CURRENT_USER, kDwmRegistryKey);
|
||||
const auto result = registry.dwordValue(QStringLiteral("ColorizationColor"));
|
||||
return (result.second ? QColor::fromRgba(result.first) : Qt::darkGray);
|
||||
};
|
||||
static const auto pDwmGetColorizationColor =
|
||||
reinterpret_cast<HRESULT(WINAPI *)(DWORD *, BOOL *)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetColorizationColor"));
|
||||
reinterpret_cast<decltype(&DwmGetColorizationColor)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetColorizationColor"));
|
||||
if (!pDwmGetColorizationColor) {
|
||||
return resultFromRegistry();
|
||||
}
|
||||
|
@ -365,42 +293,14 @@ QColor Utilities::getColorizationColor()
|
|||
return QColor::fromRgba(color);
|
||||
}
|
||||
|
||||
int Utilities::getWindowVisibleFrameBorderThickness(const WId winId)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return 1;
|
||||
}
|
||||
if (!isWin10OrGreater()) {
|
||||
return 1;
|
||||
}
|
||||
static const auto pDwmGetWindowAttribute =
|
||||
reinterpret_cast<HRESULT(WINAPI *)(HWND, DWORD, PVOID, DWORD)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetWindowAttribute"));
|
||||
if (!pDwmGetWindowAttribute) {
|
||||
return 1;
|
||||
}
|
||||
const auto hWnd = reinterpret_cast<HWND>(winId);
|
||||
UINT value = 0;
|
||||
const HRESULT hr = pDwmGetWindowAttribute(hWnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &value, sizeof(value));
|
||||
if (SUCCEEDED(hr)) {
|
||||
const QWindow *w = findWindow(winId);
|
||||
return static_cast<int>(qRound(static_cast<qreal>(value) / (w ? w->devicePixelRatio() : 1.0)));
|
||||
} else {
|
||||
// We just eat this error because this enum value was introduced in a very
|
||||
// late Windows 10 version, so querying it's value will always result in
|
||||
// a "parameter error" (code: 87) on systems before that value was introduced.
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Utilities::shouldAppsUseDarkMode()
|
||||
{
|
||||
// The dark mode was introduced in Windows 10 1809.
|
||||
if (!isWin10RS5OrGreater()) {
|
||||
// The global dark mode was first introduced in Windows 10 1809.
|
||||
if (!isWin101809OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
const auto resultFromRegistry = []() -> bool {
|
||||
QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kPersonalizeRegistryKey));
|
||||
const QWinRegistryKey registry(HKEY_CURRENT_USER, kPersonalizeRegistryKey);
|
||||
const auto result = registry.dwordValue(QStringLiteral("AppsUseLightTheme"));
|
||||
return (result.second && (result.first == 0));
|
||||
};
|
||||
|
@ -408,98 +308,43 @@ bool Utilities::shouldAppsUseDarkMode()
|
|||
// (actually, a random non-zero number at runtime), so we can't use it due to
|
||||
// this unreliability. In this case, we just simply read the user's setting from
|
||||
// the registry instead, it's not elegant but at least it works well.
|
||||
if (isWin1019H1OrGreater()) {
|
||||
return resultFromRegistry();
|
||||
} else {
|
||||
static const auto pShouldAppsUseDarkMode =
|
||||
reinterpret_cast<BOOL(WINAPI *)(VOID)>(QSystemLibrary::resolve(QStringLiteral("uxtheme"), MAKEINTRESOURCEA(132)));
|
||||
return (pShouldAppsUseDarkMode ? (pShouldAppsUseDarkMode() != FALSE) : resultFromRegistry());
|
||||
}
|
||||
return resultFromRegistry();
|
||||
}
|
||||
|
||||
ColorizationArea Utilities::getColorizationArea()
|
||||
DwmColorizationArea Utilities::getDwmColorizationArea()
|
||||
{
|
||||
// It's a Win10 only feature.
|
||||
if (!isWin10OrGreater()) {
|
||||
return ColorizationArea::None;
|
||||
return DwmColorizationArea::None;
|
||||
}
|
||||
const QString keyName = QStringLiteral("ColorPrevalence");
|
||||
QWinRegistryKey themeRegistry(HKEY_CURRENT_USER, QString::fromUtf8(kPersonalizeRegistryKey));
|
||||
const QWinRegistryKey themeRegistry(HKEY_CURRENT_USER, kPersonalizeRegistryKey);
|
||||
const auto themeValue = themeRegistry.dwordValue(keyName);
|
||||
QWinRegistryKey dwmRegistry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey));
|
||||
const QWinRegistryKey dwmRegistry(HKEY_CURRENT_USER, kDwmRegistryKey);
|
||||
const auto dwmValue = dwmRegistry.dwordValue(keyName);
|
||||
const bool theme = themeValue.second && (themeValue.first != 0);
|
||||
const bool dwm = dwmValue.second && (dwmValue.first != 0);
|
||||
if (theme && dwm) {
|
||||
return ColorizationArea::All;
|
||||
return DwmColorizationArea::All;
|
||||
} else if (theme) {
|
||||
return ColorizationArea::StartMenu_TaskBar_ActionCenter;
|
||||
return DwmColorizationArea::StartMenu_TaskBar_ActionCenter;
|
||||
} else if (dwm) {
|
||||
return ColorizationArea::TitleBar_WindowBorder;
|
||||
return DwmColorizationArea::TitleBar_WindowBorder;
|
||||
}
|
||||
return ColorizationArea::None;
|
||||
return DwmColorizationArea::None;
|
||||
}
|
||||
|
||||
bool Utilities::isThemeChanged(const void *data)
|
||||
{
|
||||
Q_ASSERT(data);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
const auto msg = static_cast<const MSG *>(data);
|
||||
if (msg->message == WM_THEMECHANGED) {
|
||||
return true;
|
||||
} else if (msg->message == WM_DWMCOLORIZATIONCOLORCHANGED) {
|
||||
return true;
|
||||
} else if (msg->message == WM_SETTINGCHANGE) {
|
||||
if ((msg->wParam == 0) && (_wcsicmp(reinterpret_cast<LPCWSTR>(msg->lParam), L"ImmersiveColorSet") == 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Utilities::isSystemMenuRequested(const void *data, QPointF *pos)
|
||||
{
|
||||
Q_ASSERT(data);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
bool result = false;
|
||||
const auto msg = static_cast<const MSG *>(data);
|
||||
if (msg->message == WM_NCRBUTTONUP) {
|
||||
if (msg->wParam == HTCAPTION) {
|
||||
result = true;
|
||||
}
|
||||
} else if (msg->message == WM_SYSCOMMAND) {
|
||||
const WPARAM filteredWParam = (msg->wParam & 0xFFF0);
|
||||
if ((filteredWParam == SC_KEYMENU) && (msg->lParam == VK_SPACE)) {
|
||||
result = true;
|
||||
}
|
||||
} else if (msg->message == WM_CONTEXTMENU) {
|
||||
//
|
||||
}
|
||||
if (result) {
|
||||
if (pos) {
|
||||
*pos = [msg](){
|
||||
const POINT nativePos = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
|
||||
return QPointF(static_cast<qreal>(nativePos.x), static_cast<qreal>(nativePos.y));
|
||||
}();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Utilities::showSystemMenu(const WId winId, const QPointF &pos)
|
||||
void Utilities::showSystemMenu(const WId winId, const QPointF &pos)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
const auto hWnd = reinterpret_cast<HWND>(winId);
|
||||
const HMENU menu = GetSystemMenu(hWnd, FALSE);
|
||||
if (!menu) {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMenu"));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
// Update the options based on window state.
|
||||
MENUITEMINFOW mii;
|
||||
|
@ -517,36 +362,34 @@ bool Utilities::showSystemMenu(const WId winId, const QPointF &pos)
|
|||
};
|
||||
const bool max = IsMaximized(hWnd);
|
||||
if (!setState(SC_RESTORE, max)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (!setState(SC_MOVE, !max)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (!setState(SC_SIZE, !max)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (!setState(SC_MINIMIZE, true)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (!setState(SC_MAXIMIZE, !max)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (!setState(SC_CLOSE, true)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (SetMenuDefaultItem(menu, UINT_MAX, FALSE) == FALSE) {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("SetMenuDefaultItem"));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
const QPoint roundedPos = pos.toPoint();
|
||||
const auto ret = TrackPopupMenu(menu, TPM_RETURNCMD, roundedPos.x(), roundedPos.y(), 0, hWnd, nullptr);
|
||||
if (ret != 0) {
|
||||
if (PostMessageW(hWnd, WM_SYSCOMMAND, ret, 0) == FALSE) {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("PostMessageW"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Utilities::isFullScreen(const WId winId)
|
||||
|
@ -561,8 +404,7 @@ bool Utilities::isFullScreen(const WId winId)
|
|||
qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowRect"));
|
||||
return false;
|
||||
}
|
||||
// According to Microsoft Docs, we should compare to primary
|
||||
// screen's geometry.
|
||||
// According to Microsoft Docs, we should compare to primary screen's geometry.
|
||||
const HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
|
||||
if (!mon) {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("MonitorFromWindow"));
|
||||
|
@ -590,7 +432,7 @@ bool Utilities::isWindowNoState(const WId winId)
|
|||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||
WINDOWPLACEMENT wp;
|
||||
SecureZeroMemory(&wp, sizeof(wp));
|
||||
wp.length = sizeof(wp);
|
||||
wp.length = sizeof(wp); // This line is important! Don't miss it!
|
||||
if (GetWindowPlacement(hwnd, &wp) == FALSE) {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowPlacement"));
|
||||
return false;
|
||||
|
@ -601,40 +443,41 @@ bool Utilities::isWindowNoState(const WId winId)
|
|||
void Utilities::syncWmPaintWithDwm()
|
||||
{
|
||||
// No need to sync with DWM if DWM composition is disabled.
|
||||
if (!isDwmCompositionAvailable()) {
|
||||
if (!isDwmCompositionEnabled()) {
|
||||
return;
|
||||
}
|
||||
QSystemLibrary winmmLib(QStringLiteral("winmm"));
|
||||
static const auto ptimeGetDevCaps =
|
||||
reinterpret_cast</*MMRESULT*/UINT(WINAPI *)(flh_LPTIMECAPS, UINT)>(winmmLib.resolve("timeGetDevCaps"));
|
||||
reinterpret_cast<decltype(&timeGetDevCaps)>(winmmLib.resolve("timeGetDevCaps"));
|
||||
static const auto ptimeBeginPeriod =
|
||||
reinterpret_cast</*MMRESULT*/UINT(WINAPI *)(UINT)>(winmmLib.resolve("timeBeginPeriod"));
|
||||
reinterpret_cast<decltype(&timeBeginPeriod)>(winmmLib.resolve("timeBeginPeriod"));
|
||||
static const auto ptimeEndPeriod =
|
||||
reinterpret_cast</*MMRESULT*/UINT(WINAPI *)(UINT)>(winmmLib.resolve("timeEndPeriod"));
|
||||
reinterpret_cast<decltype(&timeEndPeriod)>(winmmLib.resolve("timeEndPeriod"));
|
||||
static const auto pDwmGetCompositionTimingInfo =
|
||||
reinterpret_cast<HRESULT(WINAPI *)(HWND, DWM_TIMING_INFO *)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetCompositionTimingInfo"));
|
||||
reinterpret_cast<decltype(&DwmGetCompositionTimingInfo)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetCompositionTimingInfo"));
|
||||
if (!ptimeGetDevCaps || !ptimeBeginPeriod || !ptimeEndPeriod || !pDwmGetCompositionTimingInfo) {
|
||||
return;
|
||||
}
|
||||
// Dirty hack to workaround the resize flicker caused by DWM.
|
||||
LARGE_INTEGER freq = {};
|
||||
if (QueryPerformanceFrequency(&freq) == FALSE) {
|
||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceFrequency"));
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("QueryPerformanceFrequency"));
|
||||
return;
|
||||
}
|
||||
flh_TIMECAPS tc = {};
|
||||
if (ptimeGetDevCaps(&tc, sizeof(tc)) != /*MMSYSERR_NOERROR*/0) {
|
||||
TIMECAPS tc = {};
|
||||
if (ptimeGetDevCaps(&tc, sizeof(tc)) != MMSYSERR_NOERROR) {
|
||||
qWarning() << "timeGetDevCaps() failed.";
|
||||
return;
|
||||
}
|
||||
const UINT ms_granularity = tc.wPeriodMin;
|
||||
if (ptimeBeginPeriod(ms_granularity) != /*TIMERR_NOERROR*/0) {
|
||||
if (ptimeBeginPeriod(ms_granularity) != TIMERR_NOERROR) {
|
||||
qWarning() << "timeBeginPeriod() failed.";
|
||||
return;
|
||||
}
|
||||
LARGE_INTEGER now0 = {};
|
||||
if (QueryPerformanceCounter(&now0) == FALSE) {
|
||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter"));
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter"));
|
||||
return;
|
||||
}
|
||||
// ask DWM where the vertical blank falls
|
||||
|
@ -643,12 +486,12 @@ void Utilities::syncWmPaintWithDwm()
|
|||
dti.cbSize = sizeof(dti);
|
||||
const HRESULT hr = pDwmGetCompositionTimingInfo(nullptr, &dti);
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("DwmGetCompositionTimingInfo"));
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("DwmGetCompositionTimingInfo"));
|
||||
return;
|
||||
}
|
||||
LARGE_INTEGER now1 = {};
|
||||
if (QueryPerformanceCounter(&now1) == FALSE) {
|
||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter"));
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter"));
|
||||
return;
|
||||
}
|
||||
// - DWM told us about SOME vertical blank
|
||||
|
@ -669,9 +512,234 @@ void Utilities::syncWmPaintWithDwm()
|
|||
Q_ASSERT(m < period);
|
||||
const qreal m_ms = 1000.0 * static_cast<qreal>(m) / static_cast<qreal>(freq.QuadPart);
|
||||
Sleep(static_cast<DWORD>(qRound(m_ms)));
|
||||
if (ptimeEndPeriod(ms_granularity) != /*TIMERR_NOERROR*/0) {
|
||||
if (ptimeEndPeriod(ms_granularity) != TIMERR_NOERROR) {
|
||||
qWarning() << "timeEndPeriod() failed.";
|
||||
}
|
||||
}
|
||||
|
||||
bool Utilities::isWin101809OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
|
||||
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_1809);
|
||||
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763));
|
||||
#else
|
||||
static const bool result = isWindowsVersionOrGreater(10, 0, 17763);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Utilities::isHighContrastModeEnabled()
|
||||
{
|
||||
HIGHCONTRASTW hc;
|
||||
SecureZeroMemory(&hc, sizeof(hc));
|
||||
hc.cbSize = sizeof(hc);
|
||||
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0) == FALSE) {
|
||||
qWarning() << getSystemErrorMessage(QStringLiteral("SystemParametersInfoW"));
|
||||
return false;
|
||||
}
|
||||
return (hc.dwFlags & HCF_HIGHCONTRASTON);
|
||||
}
|
||||
|
||||
QColor Utilities::getWallpaperBackgroundColor()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int Utilities::getWallpaperAspectStyle()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString Utilities::getWallpaperFilePath()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
quint32 Utilities::getPrimaryScreenDpi(const bool horizontal)
|
||||
{
|
||||
static const auto pGetDpiForMonitor =
|
||||
reinterpret_cast<decltype(&GetDpiForMonitor)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("shcore"), "GetDpiForMonitor"));
|
||||
if (pGetDpiForMonitor) {
|
||||
const HMONITOR monitor = MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY);
|
||||
if (monitor) {
|
||||
UINT dpiX = 0, dpiY = 0;
|
||||
if (SUCCEEDED(pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
|
||||
return (horizontal ? dpiX : dpiY);
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo: d2d
|
||||
const HDC hdc = GetDC(nullptr);
|
||||
if (hdc) {
|
||||
const int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
const int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
if (horizontal && (dpiX > 0)) {
|
||||
return dpiX;
|
||||
}
|
||||
if (!horizontal && (dpiY > 0)) {
|
||||
return dpiY;
|
||||
}
|
||||
}
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
|
||||
quint32 Utilities::getWindowDpi(const WId winId, const bool horizontal)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||
QSystemLibrary user32Lib(QStringLiteral("user32"));
|
||||
static const auto pGetDpiForWindow =
|
||||
reinterpret_cast<decltype(&GetDpiForWindow)>(user32Lib.resolve("GetDpiForWindow"));
|
||||
if (pGetDpiForWindow) {
|
||||
return pGetDpiForWindow(hwnd);
|
||||
}
|
||||
static const auto pGetSystemDpiForProcess =
|
||||
reinterpret_cast<decltype(&GetSystemDpiForProcess)>(user32Lib.resolve("GetSystemDpiForProcess"));
|
||||
if (pGetSystemDpiForProcess) {
|
||||
return pGetSystemDpiForProcess(GetCurrentProcess());
|
||||
}
|
||||
static const auto pGetDpiForSystem =
|
||||
reinterpret_cast<decltype(&GetDpiForSystem)>(user32Lib.resolve("GetDpiForSystem"));
|
||||
if (pGetDpiForSystem) {
|
||||
return pGetDpiForSystem();
|
||||
}
|
||||
static const auto pGetDpiForMonitor =
|
||||
reinterpret_cast<decltype(&GetDpiForMonitor)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("shcore"), "GetDpiForMonitor"));
|
||||
if (pGetDpiForMonitor) {
|
||||
const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
if (monitor) {
|
||||
UINT dpiX = 0, dpiY = 0;
|
||||
if (SUCCEEDED(pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
|
||||
return (horizontal ? dpiX : dpiY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getPrimaryScreenDpi(horizontal);
|
||||
}
|
||||
|
||||
quint32 Utilities::getResizeBorderThickness(const WId winId, const bool horizontal, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return 0;
|
||||
}
|
||||
const int paddedBorderWidth = getSystemMetrics2(winId, SM_CXPADDEDBORDER, true, scaled);
|
||||
if (horizontal) {
|
||||
return (getSystemMetrics2(winId, SM_CXSIZEFRAME, true, scaled) + paddedBorderWidth);
|
||||
} else {
|
||||
return (getSystemMetrics2(winId, SM_CYSIZEFRAME, false, scaled) + paddedBorderWidth);
|
||||
}
|
||||
}
|
||||
|
||||
quint32 Utilities::getCaptionHeight(const WId winId, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return 0;
|
||||
}
|
||||
return getSystemMetrics2(winId, SM_CYCAPTION, false, scaled);
|
||||
}
|
||||
|
||||
quint32 Utilities::getTitleBarHeight(const WId winId, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return 0;
|
||||
}
|
||||
return (getCaptionHeight(winId, scaled) + getResizeBorderThickness(winId, false, scaled));
|
||||
}
|
||||
|
||||
quint32 Utilities::getFrameBorderThickness(const WId winId, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return 0;
|
||||
}
|
||||
// There's no window frame border before Windows 10.
|
||||
if (!isWin10OrGreater()) {
|
||||
return 0;
|
||||
}
|
||||
static const auto pDwmGetWindowAttribute =
|
||||
reinterpret_cast<decltype(&DwmGetWindowAttribute)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetWindowAttribute"));
|
||||
if (!pDwmGetWindowAttribute) {
|
||||
return 0;
|
||||
}
|
||||
const UINT dpi = getWindowDpi(winId, true);
|
||||
const qreal scaleFactor = (qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI));
|
||||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||
UINT value = 0;
|
||||
if (SUCCEEDED(pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &value, sizeof(value)))) {
|
||||
const qreal dpr = (scaled ? 1.0 : scaleFactor);
|
||||
return static_cast<int>(qRound(qreal(value) / dpr));
|
||||
} else {
|
||||
const qreal dpr = (scaled ? scaleFactor : 1.0);
|
||||
return static_cast<int>(qRound(qreal(kDefaultWindowFrameBorderThickness) * dpr));
|
||||
}
|
||||
}
|
||||
|
||||
QColor Utilities::getFrameBorderColor(const bool active)
|
||||
{
|
||||
// There's no window frame border before Windows 10.
|
||||
// So we just return a default value which is based on most window managers.
|
||||
if (!isWin10OrGreater()) {
|
||||
return (active ? Qt::black : Qt::darkGray);
|
||||
}
|
||||
const bool dark = shouldAppsUseDarkMode();
|
||||
if (active) {
|
||||
const DwmColorizationArea area = getDwmColorizationArea();
|
||||
if ((area == DwmColorizationArea::TitleBar_WindowBorder) || (area == DwmColorizationArea::All)) {
|
||||
return getDwmColorizationColor();
|
||||
} else {
|
||||
return (dark ? QColor(QStringLiteral("#4d4d4d")) : QColor(Qt::white));
|
||||
}
|
||||
} else {
|
||||
return (dark ? QColor(QStringLiteral("#575959")) : QColor(QStringLiteral("#999999")));
|
||||
}
|
||||
}
|
||||
|
||||
void Utilities::updateWindowFrameColor(const WId winId, const bool dark)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return;
|
||||
}
|
||||
// There's no global dark theme before Win10 1809.
|
||||
if (!isWin101809OrGreater()) {
|
||||
return;
|
||||
}
|
||||
static const auto pSetWindowTheme =
|
||||
reinterpret_cast<decltype(&SetWindowTheme)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("uxtheme"), "SetWindowTheme"));
|
||||
static const auto pDwmSetWindowAttribute =
|
||||
reinterpret_cast<decltype(&DwmSetWindowAttribute)>(
|
||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmSetWindowAttribute"));
|
||||
if (!pSetWindowTheme || !pDwmSetWindowAttribute) {
|
||||
return;
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||
const BOOL value = (dark ? TRUE : FALSE);
|
||||
HRESULT hr = pDwmSetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, &value, sizeof(value));
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmSetWindowAttribute"), hr);
|
||||
//return;
|
||||
}
|
||||
hr = pDwmSetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmSetWindowAttribute"), hr);
|
||||
return;
|
||||
}
|
||||
hr = pSetWindowTheme(hwnd, (dark ? L"DarkMode_Explorer" : L" "), nullptr);
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << __getSystemErrorMessage(QStringLiteral("SetWindowTheme"), hr);
|
||||
}
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
Loading…
Reference in New Issue