Fix build on Qt before 5.14

1. The QWinRegistryKey class was introduced in 5.14.0. The implementation is quite clean, so just copy the sources files from Qt repo in case the user is linking against a version older than 5.14.
2. Fixes the rounding error of the self-painted frame border of the examples.
3. Other minor tweaks.

Fixes: #95
Task-number: #94

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-01-06 11:18:33 +08:00
parent fc307c83bf
commit e3adb917a6
11 changed files with 249 additions and 30 deletions

View File

@ -42,17 +42,17 @@ endif()
if(WIN32) if(WIN32)
list(APPEND SOURCES list(APPEND SOURCES
framelesshelper_windows.h framelesshelper_windows.h
qwinregistry_p.h
qwinregistry.cpp
utilities_win32.cpp utilities_win32.cpp
framelesshelper_win32.h framelesshelper_win32.h
framelesshelper_win32.cpp framelesshelper_win32.cpp
) )
else() elseif(APPLE)
if(MACOS)
list(APPEND SOURCES utilities_macos.mm) list(APPEND SOURCES utilities_macos.mm)
else() elseif(UNIX)
list(APPEND SOURCES utilities_linux.cpp) list(APPEND SOURCES utilities_linux.cpp)
endif() endif()
endif()
if(WIN32 AND BUILD_SHARED_LIBS) if(WIN32 AND BUILD_SHARED_LIBS)
enable_language(RC) enable_language(RC)

View File

@ -17,6 +17,6 @@ win32 {
win32 { win32 {
CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../debug -lFramelessHelperd CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../debug -lFramelessHelperd
else: CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../release -lFramelessHelper else: CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../release -lFramelessHelper
} else: unix { } else {
LIBS += -L$$OUT_PWD/../../bin -lFramelessHelper LIBS += -L$$OUT_PWD/../../bin -lFramelessHelper
} }

View File

@ -31,9 +31,9 @@ int main(int argc, char *argv[])
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif #endif
QApplication application(argc, argv); QApplication application(argc, argv);

View File

@ -52,14 +52,14 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(par
connect(this, &MainWindow::windowTitleChanged, titleBarWidget->titleLabel, &QLabel::setText); connect(this, &MainWindow::windowTitleChanged, titleBarWidget->titleLabel, &QLabel::setText);
connect(titleBarWidget->closeButton, &QPushButton::clicked, this, &MainWindow::close); connect(titleBarWidget->closeButton, &QPushButton::clicked, this, &MainWindow::close);
connect(titleBarWidget->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized); connect(titleBarWidget->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized);
connect(titleBarWidget->maximizeButton, &QPushButton::clicked, [this](){ connect(titleBarWidget->maximizeButton, &QPushButton::clicked, this, [this](){
if (isMaximized() || isFullScreen()) { if (isMaximized() || isFullScreen()) {
showNormal(); showNormal();
} else { } else {
showMaximized(); showMaximized();
} }
}); });
connect(this, &MainWindow::windowStateChanged, [this](){ connect(this, &MainWindow::windowStateChanged, this, [this](){
titleBarWidget->maximizeButton->setChecked(isMaximized()); titleBarWidget->maximizeButton->setChecked(isMaximized());
titleBarWidget->maximizeButton->setToolTip(isMaximized() ? tr("Restore") : tr("Maximize")); titleBarWidget->maximizeButton->setToolTip(isMaximized() ? tr("Restore") : tr("Maximize"));
}); });
@ -122,7 +122,6 @@ void MainWindow::paintEvent(QPaintEvent *event)
{ {
QMainWindow::paintEvent(event); QMainWindow::paintEvent(event);
if (windowState() == Qt::WindowNoState) { if (windowState() == Qt::WindowNoState) {
QPainter painter(this);
const int w = width(); const int w = width();
const int h = height(); const int h = height();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -136,7 +135,9 @@ void MainWindow::paintEvent(QPaintEvent *event)
{w, h - 1, 0, h - 1}, {w, h - 1, 0, h - 1},
{0, h, 0, 0} {0, h, 0, 0}
}; };
QPainter painter(this);
painter.save(); painter.save();
painter.setRenderHint(QPainter::Antialiasing, false);
const ColorizationArea area = Utilities::getColorizationArea(); const ColorizationArea area = Utilities::getColorizationArea();
const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder) const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder)
|| (area == ColorizationArea::All)); || (area == ColorizationArea::All));

View File

@ -33,9 +33,9 @@ int main(int argc, char *argv[])
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif #endif
QGuiApplication application(argc, argv); QGuiApplication application(argc, argv);

View File

@ -31,9 +31,9 @@ int main(int argc, char *argv[])
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif #endif
QApplication application(argc, argv); QApplication application(argc, argv);

View File

@ -137,9 +137,6 @@ void Widget::paintEvent(QPaintEvent *event)
{ {
QWidget::paintEvent(event); QWidget::paintEvent(event);
if (!isMaximized() && !isFullScreen()) { if (!isMaximized() && !isFullScreen()) {
QPainter painter(this);
painter.save();
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
const auto w = static_cast<qreal>(width()); const auto w = static_cast<qreal>(width());
const auto h = static_cast<qreal>(height()); const auto h = static_cast<qreal>(height());
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -158,6 +155,9 @@ void Widget::paintEvent(QPaintEvent *event)
|| (area == ColorizationArea::All)); || (area == ColorizationArea::All));
const QColor borderColor = (isActiveWindow() ? (colorizedBorder ? Utilities::getColorizationColor() : Qt::black) : Qt::darkGray); const QColor borderColor = (isActiveWindow() ? (colorizedBorder ? Utilities::getColorizationColor() : Qt::black) : Qt::darkGray);
const auto borderThickness = static_cast<qreal>(Utilities::getWindowVisibleFrameBorderThickness(winId())); const auto borderThickness = static_cast<qreal>(Utilities::getWindowVisibleFrameBorderThickness(winId()));
QPainter painter(this);
painter.save();
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setPen({borderColor, borderThickness}); painter.setPen({borderColor, borderThickness});
painter.drawLines(lines); painter.drawLines(lines);
painter.restore(); painter.restore();

10
lib.pro
View File

@ -1,7 +1,7 @@
TARGET = $$qtLibraryTarget(FramelessHelper) TARGET = $$qtLibraryTarget(FramelessHelper)
TEMPLATE = lib TEMPLATE = lib
win32: DLLDESTDIR = $$OUT_PWD/bin win32: DLLDESTDIR = $$OUT_PWD/bin
else: unix: DESTDIR = $$OUT_PWD/bin else: DESTDIR = $$OUT_PWD/bin
QT += core-private gui-private QT += core-private gui-private
CONFIG += c++17 strict_c++ utf8_source warn_on CONFIG += c++17 strict_c++ utf8_source warn_on
DEFINES += \ DEFINES += \
@ -28,10 +28,14 @@ qtHaveModule(quick) {
win32 { win32 {
HEADERS += \ HEADERS += \
framelesshelper_windows.h \ framelesshelper_windows.h \
framelesshelper_win32.h framelesshelper_win32.h \
qwinregistry_p.h
SOURCES += \ SOURCES += \
utilities_win32.cpp \ utilities_win32.cpp \
framelesshelper_win32.cpp framelesshelper_win32.cpp \
qwinregistry.cpp
LIBS += -luser32 -lshell32 LIBS += -luser32 -lshell32
RC_FILE = framelesshelper.rc RC_FILE = framelesshelper.rc
} }
linux*: SOURCES += utilities_linux.cpp
macx: SOURCES += utilities_macos.mm

122
qwinregistry.cpp Normal file
View File

@ -0,0 +1,122 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwinregistry_p.h"
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
#include <QtCore/qvarlengtharray.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
QWinRegistryKey::QWinRegistryKey() :
m_key(nullptr)
{
}
// Open a key with the specified permissions (KEY_READ/KEY_WRITE).
// "access" is to explicitly use the 32- or 64-bit branch.
QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions, REGSAM access)
{
if (RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(subKey.utf16()),
0, permissions | access, &m_key) != ERROR_SUCCESS) {
m_key = nullptr;
}
}
QWinRegistryKey::~QWinRegistryKey()
{
close();
}
void QWinRegistryKey::close()
{
if (isValid()) {
RegCloseKey(m_key);
m_key = nullptr;
}
}
QString QWinRegistryKey::stringValue(QStringView subKey) const
{
QString result;
if (!isValid())
return result;
DWORD type;
DWORD size;
auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, &size) != ERROR_SUCCESS
|| (type != REG_SZ && type != REG_EXPAND_SZ) || size <= 2) {
return result;
}
// Reserve more for rare cases where trailing '\0' are missing in registry.
// Rely on 0-termination since strings of size 256 padded with 0 have been
// observed (QTBUG-84455).
size += 2;
QVarLengthArray<unsigned char> buffer(static_cast<int>(size));
std::fill(buffer.data(), buffer.data() + size, 0u);
if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, buffer.data(), &size) == ERROR_SUCCESS)
result = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(buffer.constData()));
return result;
}
QPair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
{
if (!isValid())
return qMakePair(0, false);
DWORD type;
auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS
|| type != REG_DWORD) {
return qMakePair(0, false);
}
DWORD value = 0;
DWORD size = sizeof(value);
const bool ok =
RegQueryValueEx(m_key, subKeyC, nullptr, nullptr,
reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS;
return qMakePair(value, ok);
}
QT_END_NAMESPACE
#endif // QT_VERSION < QT_VERSION_CHECK(5, 14, 0)

92
qwinregistry_p.h Normal file
View File

@ -0,0 +1,92 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qglobal.h>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
#include <QtCore/private/qwinregistry_p.h>
#else // QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
#include <QtCore/qpair.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringview.h>
#include <QtCore/qt_windows.h>
QT_BEGIN_NAMESPACE
class QWinRegistryKey
{
public:
Q_DISABLE_COPY(QWinRegistryKey)
QWinRegistryKey();
explicit QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions = KEY_READ, REGSAM access = 0);
~QWinRegistryKey();
QWinRegistryKey(QWinRegistryKey &&other) noexcept
: m_key(qExchange(other.m_key, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWinRegistryKey)
void swap(QWinRegistryKey &other) noexcept { qSwap(m_key, other.m_key); }
bool isValid() const { return m_key != nullptr; }
operator HKEY() const { return m_key; }
void close();
QString stringValue(QStringView subKey) const;
QPair<DWORD, bool> dwordValue(QStringView subKey) const;
private:
HKEY m_key;
};
QT_END_NAMESPACE
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)

View File

@ -24,7 +24,6 @@
#include "utilities.h" #include "utilities.h"
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
#include <QtCore/private/qwinregistry_p.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))
#include <QtCore/qoperatingsystemversion.h> #include <QtCore/qoperatingsystemversion.h>
@ -38,6 +37,7 @@
#else #else
#include <QtGui/qpa/qplatformwindow_p.h> #include <QtGui/qpa/qplatformwindow_p.h>
#endif #endif
#include "qwinregistry_p.h"
#include "framelesshelper_windows.h" #include "framelesshelper_windows.h"
Q_DECLARE_METATYPE(QMargins) Q_DECLARE_METATYPE(QMargins)
@ -319,22 +319,22 @@ QString Utilities::getSystemErrorMessage(const QString &function)
QColor Utilities::getColorizationColor() QColor Utilities::getColorizationColor()
{ {
const auto resultFromRegistry = []() -> QColor {
QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey));
const auto result = registry.dwordValue(QStringLiteral("ColorizationColor"));
return (result.second ? QColor::fromRgba(result.first) : Qt::darkGray);
};
static const auto pDwmGetColorizationColor = static const auto pDwmGetColorizationColor =
reinterpret_cast<HRESULT(WINAPI *)(DWORD *, BOOL *)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetColorizationColor")); reinterpret_cast<HRESULT(WINAPI *)(DWORD *, BOOL *)>(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetColorizationColor"));
if (!pDwmGetColorizationColor) { if (!pDwmGetColorizationColor) {
return Qt::darkGray; return resultFromRegistry();
} }
DWORD color = 0; DWORD color = 0;
BOOL opaque = FALSE; BOOL opaque = FALSE;
const HRESULT hr = pDwmGetColorizationColor(&color, &opaque); const HRESULT hr = pDwmGetColorizationColor(&color, &opaque);
if (FAILED(hr)) { if (FAILED(hr)) {
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr); qWarning() << __getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr);
QWinRegistryKey registry(HKEY_CURRENT_USER, QString::fromUtf8(kDwmRegistryKey)); return resultFromRegistry();
const auto result = registry.dwordValue(QStringLiteral("ColorizationColor"));
if (!result.second) {
return Qt::darkGray;
}
return QColor::fromRgba(result.first);
} }
return QColor::fromRgba(color); return QColor::fromRgba(color);
} }