Minor tweaks.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2020-10-24 23:45:23 +08:00
parent c3d54c616d
commit 955961cf9b
9 changed files with 349 additions and 117 deletions

View File

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file alias="qml/+windows/main.qml">qml/main_windows.qml</file>
<file alias="qml/+unix/main.qml">qml/main_unix.qml</file>
<file>qml/MinimizeButton.qml</file>
<file>qml/MaximizeButton.qml</file>
<file>qml/CloseButton.qml</file>

View File

@ -50,9 +50,8 @@ Window {
Text {
id: titleBarText
text: window.title
font.family: "Noto Sans CJK SC"
font.pointSize: 13
color: "black"
color: window.active ? "black" : "gray"
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter

View File

@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (C) 2020 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.
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import wangwenx190.Utils 1.0
Window {
id: window
visible: true
width: 800
height: 600
title: qsTr("Hello, World!")
FramelessHelper {
id: framelessHelper
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: (window.active && framelessHelper.colorizationEnabled) ? framelessHelper.colorizationColor : "white"
anchors {
top: parent.top
left: parent.left
right: parent.right
}
Text {
id: titleBarText
text: window.title
font.pointSize: 13
color: window.active ? (framelessHelper.colorizationEnabled ? "white" : "black") : "gray"
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
}
Row {
anchors.top: parent.top
anchors.right: parent.right
MinimizeButton {
id: minimizeButton
onClicked: window.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(
minimizeButton)
}
MaximizeButton {
id: maximizeButton
maximized: window.visibility === Window.Maximized
onClicked: {
if (maximized) {
window.showNormal()
} else {
window.showMaximized()
}
}
Component.onCompleted: framelessHelper.addIgnoreObject(
maximizeButton)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.addIgnoreObject(
closeButton)
}
}
}
Rectangle {
id: content
color: "#f0f0f0"
anchors {
top: titleBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
Rectangle {
id: topFrame
visible: framelessHelper.canHaveWindowFrame && (window.visibility === Window.Windowed)
color: window.active ? (framelessHelper.colorizationEnabled ? framelessHelper.colorizationColor : "#707070") : "#aaaaaa"
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: 1
}
Component.onCompleted: {
framelessHelper.setWindowFrameVisible(framelessHelper.canHaveWindowFrame)
framelessHelper.removeWindowFrame(true)
}
}

View File

@ -24,7 +24,6 @@
#include "widget.h"
#include "../../winnativeeventfilter.h"
#include <dwmapi.h>
#include <QCheckBox>
#include <QColorDialog>
#include <QEvent>
@ -34,7 +33,6 @@
#include <QOperatingSystemVersion>
#include <QPainter>
#include <QPushButton>
#include <QSettings>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <qt_windows.h>
@ -54,12 +52,7 @@
namespace {
enum : WORD { DwmwaUseImmersiveDarkMode = 20, DwmwaUseImmersiveDarkModeBefore20h1 = 19 };
const Widget::Win10Version g_vAcrylicEffectVersion = Widget::Win10Version::Win10_1803;
const Widget::Win10Version g_vLightThemeVersion = Widget::Win10Version::Win10_1809;
const Widget::Win10Version g_vDarkThemeVersion = g_vLightThemeVersion; // check
const Widget::Win10Version g_vDarkFrameVersion = g_vDarkThemeVersion; // check
const QColor g_cDefaultActiveBorderColor = {"#707070"};
const QColor g_cDefaultInactiveBorderColor = {"#aaaaaa"};
@ -70,10 +63,6 @@ const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
const char g_sDontExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME";
const QLatin1String g_sDwmRegistryKey(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)");
const QLatin1String g_sPersonalizeRegistryKey(
R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
const QLatin1String g_sSystemButtonsStyleSheet(R"(
#iconButton, #minimizeButton, #maximizeButton, #closeButton {
background-color: transparent;
@ -296,17 +285,18 @@ bool Widget::shouldDrawBorder(const bool ignoreWindowState) const
bool Widget::shouldDrawThemedBorder(const bool ignoreWindowState) const
{
return (shouldDrawBorder(ignoreWindowState) && colorizationColorEnabled());
return (shouldDrawBorder(ignoreWindowState) && WinNativeEventFilter::colorizationEnabled());
}
bool Widget::shouldDrawThemedTitleBar() const
{
return m_bIsWin10OrGreater && colorizationColorEnabled();
return m_bIsWin10OrGreater && WinNativeEventFilter::colorizationEnabled();
}
QColor Widget::activeBorderColor()
{
return colorizationColorEnabled() ? g_cColorizationColor : g_cDefaultActiveBorderColor;
return WinNativeEventFilter::colorizationEnabled() ? g_cColorizationColor
: g_cDefaultActiveBorderColor;
}
QColor Widget::inactiveBorderColor()
@ -330,91 +320,6 @@ bool Widget::isWin10OrGreater(const Win10Version subVer)
static_cast<int>(subVer))));
}
QColor Widget::colorizationColor()
{
#if 0
DWORD color = 0;
BOOL opaqueBlend = FALSE;
return SUCCEEDED(DwmGetColorizationColor(&color, &opaqueBlend)) ? QColor::fromRgba(color)
: Qt::white;
#else
bool ok = false;
const QSettings registry(g_sDwmRegistryKey, QSettings::NativeFormat);
const quint64 color = registry.value(QLatin1String("ColorizationColor"), 0).toULongLong(&ok);
return ok ? QColor::fromRgba(color) : Qt::white;
#endif
}
bool Widget::colorizationColorEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_sDwmRegistryKey, QSettings::NativeFormat);
const bool colorPrevalence = registry.value(QLatin1String("ColorPrevalence"), 0).toULongLong(&ok)
!= 0;
return (ok && colorPrevalence);
}
bool Widget::lightThemeEnabled()
{
if (!isWin10OrGreater(g_vLightThemeVersion)) {
return false;
}
bool ok = false;
const QSettings registry(g_sPersonalizeRegistryKey, QSettings::NativeFormat);
const bool appsUseLightTheme
= registry.value(QLatin1String("AppsUseLightTheme"), 0).toULongLong(&ok) != 0;
return (ok && appsUseLightTheme);
}
bool Widget::darkThemeEnabled()
{
if (!isWin10OrGreater(g_vDarkThemeVersion)) {
return false;
}
return !lightThemeEnabled();
}
bool Widget::highContrastModeEnabled()
{
HIGHCONTRASTW hc;
SecureZeroMemory(&hc, sizeof(hc));
hc.cbSize = sizeof(hc);
return SystemParametersInfoW(SPI_GETHIGHCONTRAST, 0, &hc, 0) ? (hc.dwFlags & HCF_HIGHCONTRASTON)
: false;
}
bool Widget::darkFrameEnabled(void *handle)
{
Q_ASSERT(handle);
if (!isWin10OrGreater(g_vDarkFrameVersion)) {
return false;
}
const auto hwnd = reinterpret_cast<HWND>(handle);
BOOL result = FALSE;
const bool ok
= SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|| SUCCEEDED(DwmGetWindowAttribute(hwnd,
DwmwaUseImmersiveDarkModeBefore20h1,
&result,
sizeof(result)));
return (ok && result);
}
bool Widget::transparencyEffectEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_sPersonalizeRegistryKey, QSettings::NativeFormat);
const bool enableTransparency
= registry.value(QLatin1String("EnableTransparency"), 0).toULongLong(&ok) != 0;
return (ok && enableTransparency);
}
void *Widget::rawHandle() const
{
return reinterpret_cast<void *>(winId());
@ -650,7 +555,7 @@ void Widget::setupConnections()
}
WinNativeEventFilter::setBlurEffectEnabled(rawHandle(), enable, color);
updateWindow();
if (useAcrylicEffect && enable && transparencyEffectEnabled()) {
if (useAcrylicEffect && enable && WinNativeEventFilter::transparencyEffectEnabled()) {
QMessageBox::warning(this,
tr("BUG Warning!"),
tr("You have enabled the transparency effect in the personalize "
@ -697,7 +602,7 @@ void Widget::initializeVariables()
m_bIsWin10OrGreater = isWin10OrGreater();
if (m_bIsWin10OrGreater) {
m_bCanAcrylicBeEnabled = isWin10OrGreater(g_vAcrylicEffectVersion);
g_cColorizationColor = colorizationColor();
g_cColorizationColor = WinNativeEventFilter::colorizationColor();
}
}

View File

@ -80,13 +80,6 @@ public:
QColor borderColor() const;
static bool isWin10OrGreater(const Win10Version subVer = Win10Version::Windows10);
static QColor colorizationColor();
static bool colorizationColorEnabled();
static bool lightThemeEnabled();
static bool darkThemeEnabled();
static bool highContrastModeEnabled();
static bool darkFrameEnabled(void *handle);
static bool transparencyEffectEnabled();
public Q_SLOTS:
void retranslateUi();

View File

@ -26,8 +26,26 @@
#include "framelesswindowsmanager.h"
#include <QQuickWindow>
#ifdef Q_OS_WINDOWS
#include "winnativeeventfilter.h"
#include <QOperatingSystemVersion>
#endif
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent) {}
#ifdef Q_OS_WINDOWS
namespace {
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char g_sDontExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME";
} // namespace
#endif
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent)
{
#ifdef Q_OS_WINDOWS
startTimer(500);
#endif
}
int FramelessQuickHelper::borderWidth() const
{
@ -84,6 +102,23 @@ void FramelessQuickHelper::setTitleBarEnabled(const bool val)
Q_EMIT titleBarEnabledChanged(val);
}
#ifdef Q_OS_WINDOWS
bool FramelessQuickHelper::canHaveWindowFrame() const
{
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
}
bool FramelessQuickHelper::colorizationEnabled() const
{
return WinNativeEventFilter::colorizationEnabled();
}
QColor FramelessQuickHelper::colorizationColor() const
{
return WinNativeEventFilter::colorizationColor();
}
#endif
QSize FramelessQuickHelper::minimumSize() const
{
return FramelessWindowsManager::getMinimumSize(window());
@ -152,3 +187,23 @@ void FramelessQuickHelper::addDraggableObject(QQuickItem *val)
Q_ASSERT(val);
FramelessWindowsManager::addDraggableObject(window(), val);
}
#ifdef Q_OS_WINDOWS
void FramelessQuickHelper::timerEvent(QTimerEvent *event)
{
QQuickItem::timerEvent(event);
Q_EMIT colorizationEnabledChanged(colorizationEnabled());
Q_EMIT colorizationColorChanged(colorizationColor());
}
void FramelessQuickHelper::setWindowFrameVisible(const bool value)
{
if (value) {
qputenv(g_sPreserveWindowFrame, "1");
qputenv(g_sDontExtendFrame, "1");
} else {
qunsetenv(g_sPreserveWindowFrame);
qunsetenv(g_sDontExtendFrame);
}
}
#endif

View File

@ -27,6 +27,11 @@
#include "framelesshelper_global.h"
#include <QQuickItem>
#if (defined(Q_OS_WIN) || defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)) \
&& !defined(Q_OS_WINDOWS)
#define Q_OS_WINDOWS
#endif
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
@ -53,6 +58,11 @@ class FRAMELESSHELPER_EXPORT FramelessQuickHelper : public QQuickItem
Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged)
Q_PROPERTY(bool titleBarEnabled READ titleBarEnabled WRITE setTitleBarEnabled NOTIFY
titleBarEnabledChanged)
#ifdef Q_OS_WINDOWS
Q_PROPERTY(bool canHaveWindowFrame READ canHaveWindowFrame CONSTANT)
Q_PROPERTY(bool colorizationEnabled READ colorizationEnabled NOTIFY colorizationEnabledChanged)
Q_PROPERTY(QColor colorizationColor READ colorizationColor NOTIFY colorizationColorChanged)
#endif
public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
@ -79,6 +89,12 @@ public:
bool titleBarEnabled() const;
void setTitleBarEnabled(const bool val);
#ifdef Q_OS_WINDOWS
bool canHaveWindowFrame() const;
bool colorizationEnabled() const;
QColor colorizationColor() const;
#endif
public Q_SLOTS:
void removeWindowFrame(const bool center = false);
@ -94,6 +110,15 @@ public Q_SLOTS:
void addIgnoreObject(QQuickItem *val);
void addDraggableObject(QQuickItem *val);
#ifdef Q_OS_WINDOWS
void setWindowFrameVisible(const bool value = true);
#endif
#ifdef Q_OS_WINDOWS
protected:
void timerEvent(QTimerEvent *event) override;
#endif
Q_SIGNALS:
void borderWidthChanged(int);
void borderHeightChanged(int);
@ -102,4 +127,8 @@ Q_SIGNALS:
void minimumSizeChanged(const QSize &);
void maximumSizeChanged(const QSize &);
void titleBarEnabledChanged(bool);
#ifdef Q_OS_WINDOWS
void colorizationEnabledChanged(bool);
void colorizationColorChanged(const QColor &);
#endif
};

View File

@ -37,6 +37,7 @@
#include <QLibrary>
#include <QMargins>
#include <QScreen>
#include <QSettings>
#include <QWindow>
#include <QtMath>
#include <qt_windows.h>
@ -154,6 +155,8 @@ Q_DECLARE_METATYPE(QMargins)
namespace {
enum : WORD { DwmwaUseImmersiveDarkMode = 20, DwmwaUseImmersiveDarkModeBefore20h1 = 19 };
using WINDOWCOMPOSITIONATTRIB = enum _WINDOWCOMPOSITIONATTRIB { WCA_ACCENT_POLICY = 19 };
using WINDOWCOMPOSITIONATTRIBDATA = struct _WINDOWCOMPOSITIONATTRIBDATA
@ -198,6 +201,15 @@ bool isWin8Point1OrGreater()
#endif
}
bool isWin10OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
}
bool isWin10OrGreater(const int ver)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
@ -487,6 +499,7 @@ using WNEF_CORE_DATA = struct _WNEF_CORE_DATA
WNEF_GENERATE_WINAPI(TrackPopupMenu, BOOL, HMENU, UINT, int, int, int, HWND, CONST RECT *)
WNEF_GENERATE_WINAPI(PostMessageW, BOOL, HWND, UINT, WPARAM, LPARAM)
WNEF_GENERATE_WINAPI(GetMessagePos, DWORD)
WNEF_GENERATE_WINAPI(SystemParametersInfoW, BOOL, UINT, UINT, PVOID, UINT)
#endif // WNEF_LINK_SYSLIB
@ -566,6 +579,7 @@ using WNEF_CORE_DATA = struct _WNEF_CORE_DATA
}
resolved = true;
// Available since Windows 2000.
WNEF_RESOLVE_WINAPI(User32, SystemParametersInfoW)
WNEF_RESOLVE_WINAPI(User32, GetMessagePos)
WNEF_RESOLVE_WINAPI(User32, GetSystemMenu)
WNEF_RESOLVE_WINAPI(User32, SetMenuItemInfoW)
@ -685,11 +699,7 @@ bool shouldHaveWindowFrame()
if (should) {
// If you preserve the window frame on Win7~8.1,
// the window will have a terrible appearance.
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
return isWin10OrGreater();
}
}
return false;
@ -1208,6 +1218,10 @@ const int m_defaultBorderWidth = 8, m_defaultBorderHeight = 8, m_defaultTitleBar
const int kAutoHideTaskbarThicknessPx = 2;
const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
const QLatin1String g_sDwmRegistryKey(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)");
const QLatin1String g_sPersonalizeRegistryKey(
R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
} // namespace
WinNativeEventFilter::WinNativeEventFilter()
@ -2489,3 +2503,97 @@ void WinNativeEventFilter::setWindowResizable(void *handle, const bool resizable
updateWindow(hwnd, true, false);
}
}
bool WinNativeEventFilter::colorizationEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_sDwmRegistryKey, QSettings::NativeFormat);
const bool colorPrevalence = registry.value(QLatin1String("ColorPrevalence"), 0).toULongLong(&ok)
!= 0;
return (ok && colorPrevalence);
}
QColor WinNativeEventFilter::colorizationColor()
{
/*
DWORD color = 0;
BOOL opaqueBlend = FALSE;
return SUCCEEDED(DwmGetColorizationColor(&color, &opaqueBlend)) ? QColor::fromRgba(color)
: Qt::white;
*/
bool ok = false;
const QSettings registry(g_sDwmRegistryKey, QSettings::NativeFormat);
const quint64 color = registry.value(QLatin1String("ColorizationColor"), 0).toULongLong(&ok);
return ok ? QColor::fromRgba(color) : Qt::white;
}
bool WinNativeEventFilter::lightThemeEnabled()
{
if (!isWin10OrGreater(17763)) {
return false;
}
bool ok = false;
const QSettings registry(g_sPersonalizeRegistryKey, QSettings::NativeFormat);
const bool appsUseLightTheme
= registry.value(QLatin1String("AppsUseLightTheme"), 0).toULongLong(&ok) != 0;
return (ok && appsUseLightTheme);
}
bool WinNativeEventFilter::darkThemeEnabled()
{
if (!isWin10OrGreater(17763)) {
return false;
}
return !lightThemeEnabled();
}
bool WinNativeEventFilter::highContrastModeEnabled()
{
HIGHCONTRASTW hc;
SecureZeroMemory(&hc, sizeof(hc));
hc.cbSize = sizeof(hc);
return WNEF_EXECUTE_WINAPI_RETURN(SystemParametersInfoW, FALSE, SPI_GETHIGHCONTRAST, 0, &hc, 0)
? (hc.dwFlags & HCF_HIGHCONTRASTON)
: false;
}
bool WinNativeEventFilter::darkFrameEnabled(void *handle)
{
Q_ASSERT(handle);
if (!isWin10OrGreater(17763)) {
return false;
}
const auto hwnd = reinterpret_cast<HWND>(handle);
if (WNEF_EXECUTE_WINAPI_RETURN(IsWindow, FALSE, hwnd)) {
BOOL result = FALSE;
const bool ok = SUCCEEDED(WNEF_EXECUTE_WINAPI_RETURN(DwmGetWindowAttribute,
E_FAIL,
hwnd,
DwmwaUseImmersiveDarkMode,
&result,
sizeof(result)))
|| SUCCEEDED(WNEF_EXECUTE_WINAPI_RETURN(DwmGetWindowAttribute,
E_FAIL,
hwnd,
DwmwaUseImmersiveDarkModeBefore20h1,
&result,
sizeof(result)));
return (ok && result);
}
return false;
}
bool WinNativeEventFilter::transparencyEffectEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_sPersonalizeRegistryKey, QSettings::NativeFormat);
const bool enableTransparency
= registry.value(QLatin1String("EnableTransparency"), 0).toULongLong(&ok) != 0;
return (ok && enableTransparency);
}

View File

@ -153,6 +153,27 @@ public:
// window can only be moved and minimized, it can't be resized and maximized.
static void setWindowResizable(void *handle /* HWND */, const bool resizable = true);
// Query whether colorization is enabled or not.
static bool colorizationEnabled();
// Acquire the theme/colorization color set by the user.
static QColor colorizationColor();
// Query whether the user is using the light theme or not.
static bool lightThemeEnabled();
// Query whether the user is using the dark theme or not.
static bool darkThemeEnabled();
// Query whether the high contrast mode is enabled or not.
static bool highContrastModeEnabled();
// Query whether the given window is using dark frame or not.
static bool darkFrameEnabled(void *handle /* HWND */);
// Query whether the transparency effect is enabled or not.
static bool transparencyEffectEnabled();
///////////////////////////////////////////////
/// CORE FUNCTION - THE SOUL OF THIS CODE
///////////////////////////////////////////////