Add cross-platform Acrylic Effect

Currently still lacking Linux and macOS support,
but will be added sooner.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2021-03-05 15:26:14 +08:00
parent ac30e108a3
commit f70158a276
86 changed files with 2605 additions and 3675 deletions

View File

@ -20,26 +20,45 @@ endif()
find_package(QT NAMES Qt6 Qt5 COMPONENTS Gui REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui REQUIRED)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
set(SOURCES
qtacrylichelper.qrc
framelesshelper_global.h
framelesswindowsmanager.h
framelesswindowsmanager.cpp
utilities.h
utilities.cpp
qtacryliceffecthelper.h
qtacryliceffecthelper.cpp
)
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
list(APPEND SOURCES
qtacrylicwidget.h
qtacrylicwidget.cpp
)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
list(APPEND SOURCES
framelessquickhelper.h
framelessquickhelper.cpp
qtacrylicitem.h
qtacrylicitem.cpp
)
endif()
if(WIN32)
list(APPEND SOURCES
winnativeeventfilter.h
winnativeeventfilter.cpp
utilities_win32.cpp
framelesshelper_win32.h
framelesshelper_win32.cpp
qtacryliceffecthelper_win32.h
qtacryliceffecthelper_win32.cpp
)
else()
list(APPEND SOURCES
@ -60,6 +79,7 @@ if(NOT BUILD_SHARED_LIBS)
FRAMELESSHELPER_STATIC
)
endif()
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /utf-8)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
@ -67,29 +87,44 @@ if(MSVC)
target_link_options(${PROJECT_NAME} PRIVATE /GUARD:CF)
endif()
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_KEYWORDS
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060000
FRAMELESSHELPER_BUILD_LIBRARY
)
if(WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE
WIN32_LEAN_AND_MEAN
_CRT_SECURE_NO_WARNINGS
WNEF_LINK_SYSLIB
UNICODE
_UNICODE
)
target_link_libraries(${PROJECT_NAME} PRIVATE
user32 shell32 gdi32 dwmapi shcore uxtheme d2d1
user32 shell32 gdi32 dwmapi
)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::GuiPrivate
)
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
)

View File

@ -42,7 +42,7 @@ If you are using part of or all the code from this repository in your own projec
- Won't cover the task bar when maximized (Win32 only).
- Won't block the auto-hide task bar when maximized (Win32 only).
- No flickers when resizing.
- Load all native APIs at run-time, no need to link to any system libraries directly (Win32 only).
- Support the Acrylic Effect.
## Usage
@ -121,7 +121,7 @@ Please refer to <https://github.com/wangwenx190/framelesshelper/issues> for more
```text
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Copyright (C) 2021 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

View File

@ -47,7 +47,7 @@ helper.setIgnoreAreas(&widget, {{0, 0, 30, 40}, {40, 0, 30, 40}});
```text
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Copyright (C) 2021 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

View File

@ -25,7 +25,7 @@
- Won't cover the task bar when maximized.
- Won't block the auto-hide task bar when maximized.
- No flickers when resizing.
- Load all Win32 APIs at run-time, no need to link to any system libraries directly.
- Support the Acrylic Effect.
## Usage
@ -165,7 +165,7 @@ Thanks [**Julien Maille**](https://github.com/JulienMaille) for adding the `QMai
```text
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Copyright (C) 2021 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

View File

@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>348</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../windows.ico</normaloff>../windows.ico</iconset>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuItem_1">
<property name="title">
<string>Menu 1</string>
</property>
<addaction name="actionItem_1"/>
<addaction name="actionItem_2"/>
</widget>
<widget class="QMenu" name="menuItem_2">
<property name="title">
<string>Menu 2</string>
</property>
</widget>
<widget class="QMenu" name="menuMenu_3">
<property name="title">
<string>Menu 3</string>
</property>
</widget>
<addaction name="menuItem_1"/>
<addaction name="menuItem_2"/>
<addaction name="menuMenu_3"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="dockWidget">
<property name="windowTitle">
<string>Dock 1</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents"/>
</widget>
<widget class="QDockWidget" name="dockWidget_2">
<property name="windowTitle">
<string>Dock 2</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_2"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionAction_1"/>
<addaction name="actionAction_2"/>
</widget>
<action name="actionAction_1">
<property name="text">
<string>Action 1</string>
</property>
</action>
<action name="actionAction_2">
<property name="text">
<string>Action 2</string>
</property>
</action>
<action name="actionItem_1">
<property name="text">
<string>Item 1</string>
</property>
</action>
<action name="actionItem_2">
<property name="text">
<string>Item 2</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,6 +0,0 @@
TARGET = QMainWindow
TEMPLATE = app
QT += widgets
SOURCES += main.cpp
FORMS += TitleBar.ui MainWindow.ui
include($$PWD/../common.pri)

View File

@ -1,267 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TitleBar</class>
<widget class="QWidget" name="TitleBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>423</width>
<height>31</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>31</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#iconButton, #minimizeButton, #maximizeButton, #closeButton {
border-style: none;
background-color: transparent;
}
#minimizeButton:hover, #maximizeButton:hover {
background-color: #80c7c7c7;
}
#minimizeButton:pressed, #maximizeButton:pressed {
background-color: #80808080;
}
#closeButton:hover {
background-color: #e81123;
}
#closeButton:pressed {
background-color: #8c0a15;
}</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="iconButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>26</width>
<height>26</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>26</width>
<height>26</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>262</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="minimizeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Minimize</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/button_minimize_black.svg</normaloff>:/images/button_minimize_black.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="maximizeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Maximize</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/button_maximize_black.svg</normaloff>
<normalon>:/images/button_restore_black.svg</normalon>:/images/button_maximize_black.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Close</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/button_close_black.svg</normaloff>:/images/button_close_black.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>46</width>
<height>31</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="../images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,119 +0,0 @@
/*
* 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.
*/
#include "../../framelesswindowsmanager.h"
#include "ui_MainWindow.h"
#include "ui_TitleBar.h"
#include <QApplication>
#include <QStyleOption>
#include <QWidget>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
QMainWindow *mainWindow = new QMainWindow;
Ui::MainWindow appMainWindow;
appMainWindow.setupUi(mainWindow);
QWidget *widget = new QWidget;
Ui::TitleBar titleBarWidget;
titleBarWidget.setupUi(widget);
QMenuBar *menuBar = mainWindow->menuBar();
titleBarWidget.horizontalLayout->insertWidget(1, menuBar);
menuBar->setMaximumHeight(20);
mainWindow->setMenuWidget(widget);
QObject::connect(mainWindow,
&QMainWindow::windowIconChanged,
titleBarWidget.iconButton,
&QPushButton::setIcon);
QObject::connect(mainWindow,
&QMainWindow::windowTitleChanged,
titleBarWidget.titleLabel,
&QLabel::setText);
QObject::connect(titleBarWidget.closeButton,
&QPushButton::clicked,
mainWindow,
&QMainWindow::close);
QObject::connect(titleBarWidget.minimizeButton,
&QPushButton::clicked,
mainWindow,
&QMainWindow::showMinimized);
QObject::connect(titleBarWidget.maximizeButton,
&QPushButton::clicked,
[mainWindow, titleBarWidget]() {
if (mainWindow->isMaximized()) {
mainWindow->showNormal();
titleBarWidget.maximizeButton->setToolTip(QObject::tr("Maximize"));
} else {
mainWindow->showMaximized();
titleBarWidget.maximizeButton->setToolTip(QObject::tr("Restore"));
}
});
QStyleOption option;
option.initFrom(mainWindow);
const QIcon icon = mainWindow->style()->standardIcon(QStyle::SP_ComputerIcon, &option);
mainWindow->setWindowIcon(icon);
mainWindow->setWindowTitle(QObject::tr("Hello, World!"));
mainWindow->createWinId(); // Qt's internal function, make sure it's a top level window.
const QWindow *win = mainWindow->windowHandle();
FramelessWindowsManager::addWindow(win);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.minimizeButton);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.maximizeButton);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.closeButton);
FramelessWindowsManager::addIgnoreObject(win, appMainWindow.menubar);
mainWindow->resize(800, 600);
mainWindow->show();
return QApplication::exec();
}

View File

@ -1,33 +0,0 @@
cmake_minimum_required(VERSION 3.15)
project(QQPlayer LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5 COMPONENTS Quick REQUIRED)
set(source_files qml.qrc images.qrc main.cpp ../../framelessquickhelper.h ../../framelessquickhelper.cpp)
if(WIN32)
enable_language(RC)
list(APPEND source_files QQPlayer.exe.manifest QQPlayer.rc ../../winnativeeventfilter.h ../../winnativeeventfilter.cpp)
else()
list(APPEND source_files ../../framelesshelper.h ../../framelesshelper.cpp)
endif()
add_executable(QQPlayer WIN32 ${source_files})
if(MSVC)
# Silence a compiler warning caused by the Chinese comments in the source file.
target_compile_options(QQPlayer PRIVATE -utf-8)
endif()
target_compile_definitions(QQPlayer PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(QQPlayer PRIVATE Qt::Quick Qt::GuiPrivate)

View File

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="com.wangwenx190.demo.qqplayer" version="1.0.0.0"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 7 and Windows Server 2008 R2 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 and Windows Server 2012 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 and Windows Server 2012 R2 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

View File

@ -1,68 +0,0 @@
/*
* 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.
*/
#include <windows.h>
IDI_ICON1 ICON "QQPlayer.ico"
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "QQPlayer.exe.manifest"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "Built by the Qt Toolkit."
VALUE "CompanyName", "wangwenx190"
VALUE "FileDescription", "QQPlayer DEMO"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "qqplayer-demo"
VALUE "LegalCopyright", "MIT License"
#ifdef _DEBUG
VALUE "OriginalFilename", "QQPlayerd.exe"
#else
VALUE "OriginalFilename", "QQPlayer.exe"
#endif
VALUE "ProductName", "QQPlayer"
VALUE "ProductVersion", "1.0.0.0"
VALUE "LegalTrademarks", "Tencent"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -1,12 +0,0 @@
Qt Quick 模仿的 QQ播放器
只做了主界面,而且只有界面,没有功能,仅供展示
可以看出只要有贴图Qt Quick 做这种界面很容易的
按钮的图片有些糊,而且边缘呈锯齿状,是因为我是直接从截图里扣下来的
,没有仔细处理。由于只是示例项目,因此不会再进行任何额外处理了。
如果是公司的项目,肯定会有正规的高清贴图的。
所有来自QQ播放器的图片以及图标版权均归腾讯所有。
此仓库里提供的所有相关的素材文件,仅供学习交流,请勿传播甚至商用。

View File

@ -1,13 +0,0 @@
@echo off
call "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
call "%SystemDrive%\Qt\5.15.0\msvc2019_64\bin\qtenv2.bat"
cd /d "%~dp0"
if exist build rd /s /q build
md build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=bin -GNinja ..
cmake --build .
windeployqt --force --no-translations --no-system-d3d-compiler --no-virtualkeyboard --no-compiler-runtime --no-angle --no-opengl-sw --dir "%~dp0build\bin\qml" --libdir "%~dp0build\bin" --plugindir "%~dp0build\bin\plugins" --qmldir "%~dp0resources\qml" "%~dp0build\bin\QQPlayer.exe"
cd /d "%~dp0"
start explorer /select,"%~dp0build\bin\QQPlayer.exe"
exit /b

View File

@ -1,28 +0,0 @@
<RCC>
<qresource prefix="/images">
<file alias="background.png">resources/images/background.png</file>
<file alias="button_close_black.svg">../../resources/images/button_close_black.svg</file>
<file alias="button_close_white.svg">../../resources/images/button_close_white.svg</file>
<file alias="button_maximize_black.svg">../../resources/images/button_maximize_black.svg</file>
<file alias="button_maximize_white.svg">../../resources/images/button_maximize_white.svg</file>
<file alias="button_minimize_black.svg">../../resources/images/button_minimize_black.svg</file>
<file alias="button_minimize_white.svg">../../resources/images/button_minimize_white.svg</file>
<file alias="button_restore_black.svg">../../resources/images/button_restore_black.svg</file>
<file alias="button_restore_white.svg">../../resources/images/button_restore_white.svg</file>
<file alias="logo.png">resources/images/logo.png</file>
<file alias="open_file_blue.png">resources/images/open_file_blue.png</file>
<file alias="open_file_white.png">resources/images/open_file_white.png</file>
<file alias="main_menu_blue.png">resources/images/main_menu_blue.png</file>
<file alias="main_menu_white.png">resources/images/main_menu_white.png</file>
<file alias="play_blue.png">resources/images/play_blue.png</file>
<file alias="play_blue_light.png">resources/images/play_blue_light.png</file>
<file alias="stop_blue.png">resources/images/stop_blue.png</file>
<file alias="stop_blue_light.png">resources/images/stop_blue_light.png</file>
<file alias="previous_blue.png">resources/images/previous_blue.png</file>
<file alias="previous_blue_light.png">resources/images/previous_blue_light.png</file>
<file alias="next_blue.png">resources/images/next_blue.png</file>
<file alias="next_blue_light.png">resources/images/next_blue_light.png</file>
<file alias="volume_blue.png">resources/images/volume_blue.png</file>
<file alias="volume_blue_light.png">resources/images/volume_blue_light.png</file>
</qresource>
</RCC>

View File

@ -1,71 +0,0 @@
/*
* 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.
*/
#include "../../framelessquickhelper.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[]) {
// Enable Qt RHI, the default backend on Windows is D3D 11.1
qputenv("QSG_RHI", "1");
qputenv("QSG_INFO", "1");
#if (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(
Qt::ApplicationAttribute::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(
Qt::ApplicationAttribute::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QGuiApplication::setApplicationName(QString::fromUtf8("QQPlayer"));
QGuiApplication::setApplicationDisplayName(QString::fromUtf8("QQPlayer"));
#ifndef Q_OS_WINDOWS
QGuiApplication::setApplicationVersion(QString::fromUtf8("1.0.0"));
#endif
QGuiApplication::setOrganizationName(QString::fromUtf8("wangwenx190"));
QGuiApplication::setOrganizationDomain(
QString::fromUtf8("wangwenx190.github.io"));
QGuiApplication application(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0,
"FramelessHelper");
const QUrl mainQmlUrl(QString::fromUtf8("qrc:/qml/main.qml"));
const QMetaObject::Connection connection = QObject::connect(
&engine, &QQmlApplicationEngine::objectCreated, &application,
[&mainQmlUrl, &connection](QObject *object, const QUrl &url) {
if (url != mainQmlUrl) {
return;
}
if (!object) {
QGuiApplication::exit(-1);
} else {
QObject::disconnect(connection);
}
},
Qt::QueuedConnection);
engine.load(mainQmlUrl);
return QGuiApplication::exec();
}

View File

@ -1,16 +0,0 @@
<RCC>
<qresource prefix="/qml">
<file alias="main.qml">resources/qml/main.qml</file>
<file alias="CloseButton.qml">resources/qml/CloseButton.qml</file>
<file alias="MaximizeButton.qml">resources/qml/MaximizeButton.qml</file>
<file alias="MinimizeButton.qml">resources/qml/MinimizeButton.qml</file>
<file alias="OpenFileButton.qml">resources/qml/OpenFileButton.qml</file>
<file alias="MainMenuButton.qml">resources/qml/MainMenuButton.qml</file>
<file alias="PlayButton.qml">resources/qml/PlayButton.qml</file>
<file alias="StopButton.qml">resources/qml/StopButton.qml</file>
<file alias="PreviousButton.qml">resources/qml/PreviousButton.qml</file>
<file alias="NextButton.qml">resources/qml/NextButton.qml</file>
<file alias="VolumeButton.qml">resources/qml/VolumeButton.qml</file>
<file alias="ProgressSlider.qml">resources/qml/ProgressSlider.qml</file>
</qresource>
</RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 75
implicitHeight: 15
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/main_menu_blue.png" : "qrc:/images/main_menu_white.png"
}
background: Item {}
}

View File

@ -1,47 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 45
implicitHeight: 30
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Minimize")
contentItem: Image {
anchors.fill: control
source: "qrc:/images/button_minimize_white.svg"
}
background: Rectangle {
visible: control.down || control.hovered
color: control.down ? "#808080" : (control.hovered ? "#c7c7c7" : "transparent")
}
}

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 16
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/next_blue_light.png" : "qrc:/images/next_blue.png"
}
background: Item {}
}

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 187
implicitHeight: 50
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/open_file_blue.png" : "qrc:/images/open_file_white.png"
}
background: Item {}
}

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 45
implicitHeight: 45
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/play_blue_light.png" : "qrc:/images/play_blue.png"
}
background: Item {}
}

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 16
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/previous_blue_light.png" : "qrc:/images/previous_blue.png"
}
background: Item {}
}

View File

@ -1,92 +0,0 @@
/*
* 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.Controls 2.15
Slider {
id: control
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
property int progressDuration: 200
property int heightDuration: 100
property int hoveredHeight: 10
property int normalHeight: 4
property color backgroundColor: "#bdbebf"
property color foregroundColor: "#21be2b"
property color handleColor: "#f0f0f0"
property int handleWidth: 14
property int handleBorderWidth: 0
property color handleBorderColor: "transparent"
property bool handleVisibility: true
background: Rectangle {
x: 0
y: control.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: control.hovered ? control.hoveredHeight : control.normalHeight
width: control.availableWidth
height: implicitHeight
color: control.backgroundColor
Behavior on implicitHeight {
NumberAnimation {
duration: control.heightDuration
}
}
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: control.foregroundColor
Behavior on width {
NumberAnimation {
duration: control.progressDuration
}
}
}
}
handle: Rectangle {
visible: control.handleVisibility
x: control.visualPosition * (control.availableWidth - width)
y: control.availableHeight / 2 - height / 2
implicitWidth: control.handleWidth
implicitHeight: implicitWidth
radius: implicitHeight / 2
color: control.handleColor
border.width: control.handleBorderWidth
border.color: control.handleBorderColor
Behavior on x {
NumberAnimation {
duration: control.progressDuration
}
}
}
}

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 14
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/stop_blue_light.png" : "qrc:/images/stop_blue.png"
}
background: Item {}
}

View File

@ -1,41 +0,0 @@
/*
* 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.Controls 2.15
Button {
id: control
implicitWidth: 18
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/volume_blue_light.png" : "qrc:/images/volume_blue.png"
}
background: Item {}
}

View File

@ -1,170 +0,0 @@
/*
* 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: 540
title: qsTr("QQ Player")
property color themeColor: "#111111"
FramelessHelper {
id: framelessHelper
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: window.themeColor
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
MainMenuButton {
id: mainMenuButton
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
Component.onCompleted: framelessHelper.addIgnoreObject(
mainMenuButton)
}
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)
}
}
}
Image {
id: backgroundImage
anchors.top: titleBar.bottom
anchors.bottom: controlPanel.top
anchors.left: parent.left
anchors.right: parent.right
source: "qrc:/images/background.png"
Image {
id: logoImage
width: 123
height: 99
anchors.centerIn: parent
source: "qrc:/images/logo.png"
}
OpenFileButton {
id: openFileButton
anchors.top: logoImage.bottom
anchors.topMargin: 25
anchors.horizontalCenter: parent.horizontalCenter
}
}
Rectangle {
id: controlPanel
height: 63
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: window.themeColor
ProgressSlider {
id: progressSlider
anchors.bottom: parent.top
anchors.bottomMargin: -2
anchors.left: parent.left
anchors.right: parent.right
foregroundColor: "#1ab7e4"
handleColor: "#923cf2"
}
StopButton {
id: stopButton
anchors.right: previousButton.left
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
PreviousButton {
id: previousButton
anchors.right: playButton.left
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
PlayButton {
id: playButton
anchors.centerIn: parent
}
NextButton {
id: nextButton
anchors.left: playButton.right
anchors.leftMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
VolumeButton {
id: volumeButton
anchors.left: nextButton.right
anchors.leftMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
}
Component.onCompleted: framelessHelper.removeWindowFrame()
}

View File

@ -1,5 +0,0 @@
TARGET = QWidget
TEMPLATE = app
QT += widgets
SOURCES += main.cpp
include($$PWD/../common.pri)

View File

@ -1,109 +0,0 @@
/*
* 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.
*/
#include "../../framelesswindowsmanager.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
QWidget widget;
widget.setContentsMargins(2, 2, 2, 2);
QLabel *label = new QLabel;
QObject::connect(&widget, &QWidget::windowTitleChanged, label, &QLabel::setText);
QPushButton *minimizeButton = new QPushButton;
minimizeButton->setText(QObject::tr("Minimize"));
QObject::connect(minimizeButton, &QPushButton::clicked, &widget, &QWidget::showMinimized);
QPushButton *maximizeButton = new QPushButton;
maximizeButton->setText(QObject::tr("Maximize"));
QObject::connect(maximizeButton, &QPushButton::clicked, [&widget, &maximizeButton]() {
if (widget.isMaximized()) {
widget.showNormal();
maximizeButton->setText(QObject::tr("Maximize"));
} else {
widget.showMaximized();
maximizeButton->setText(QObject::tr("Restore"));
}
});
QPushButton *closeButton = new QPushButton;
closeButton->setText(QObject::tr("Close"));
QObject::connect(closeButton, &QPushButton::clicked, &widget, &QWidget::close);
QHBoxLayout *tbLayout = new QHBoxLayout;
tbLayout->setContentsMargins(0, 0, 0, 0);
tbLayout->setSpacing(0);
tbLayout->addSpacing(15);
tbLayout->addWidget(label);
tbLayout->addStretch();
tbLayout->addWidget(minimizeButton);
tbLayout->addWidget(maximizeButton);
tbLayout->addWidget(closeButton);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addLayout(tbLayout);
mainLayout->addStretch();
widget.setLayout(mainLayout);
widget.setWindowTitle(QObject::tr("Hello, World!"));
widget.createWinId(); // Qt's internal function, make sure it's a top level window.
const QWindow *win = widget.windowHandle();
FramelessWindowsManager::addWindow(win);
FramelessWindowsManager::addIgnoreObject(win, minimizeButton);
FramelessWindowsManager::addIgnoreObject(win, maximizeButton);
FramelessWindowsManager::addIgnoreObject(win, closeButton);
widget.resize(800, 600);
widget.show();
return QApplication::exec();
}

View File

@ -1,258 +0,0 @@
/*
* 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.
*/
#include "widget.h"
#include "../../framelesshelper.h"
#include <QEvent>
#include <QGraphicsDropShadowEffect>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QPushButton>
#include <QSpacerItem>
#include <QVBoxLayout>
Q_GLOBAL_STATIC(FramelessHelper, framelessHelper)
ContentsWidget::ContentsWidget(QWidget *parent) : QWidget(parent)
{
setAttribute(Qt::WA_StyledBackground);
}
void ContentsWidget::setShouldDrawWindowBorder(const bool val)
{
m_bShouldDrawWindowBorder = val;
update();
}
bool ContentsWidget::getShouldDrawWindowBorder() const
{
return m_bShouldDrawWindowBorder;
}
void ContentsWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (m_bShouldDrawWindowBorder) {
QPainter painter(this);
painter.save();
painter.setPen({window()->isActiveWindow() ? Qt::black : Qt::darkGray, 1.5});
painter.drawLine(0, 0, width(), 0);
painter.drawLine(0, height(), width(), height());
painter.drawLine(0, 0, 0, height());
painter.drawLine(width(), 0, width(), height());
painter.restore();
}
}
Widget::Widget(QWidget *parent) : QWidget(parent)
{
titleBarHeight = framelessHelper()->getTitleBarHeight();
createWinId();
setupUi();
initBackgroundWindow();
installEventFilter(this);
}
bool Widget::isNormal() const
{
return (!isMinimized() && !isMaximized() && !isFullScreen());
}
void Widget::setupUi()
{
contentsWidget = new ContentsWidget(this);
contentsWidget->setObjectName(QString::fromUtf8("contentsWidget"));
contentsWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
const QSize systemButtonSize = {qRound(titleBarHeight * 1.5), titleBarHeight};
const QSize systemIconSize = {17, 17};
titleBarWidget = new QWidget(contentsWidget);
titleBarWidget->setObjectName(QString::fromUtf8("titleBarWidget"));
titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
titleBarWidget->setFixedHeight(titleBarHeight);
windowIconButton = new QPushButton(this);
windowIconButton->setObjectName(QString::fromUtf8("windowIconButton"));
windowIconButton->setFixedSize(systemIconSize);
windowIconButton->setIconSize(systemIconSize);
connect(this, &Widget::windowIconChanged, windowIconButton, &QPushButton::setIcon);
windowTitleLabel = new QLabel(this);
QFont f = font();
f.setPointSizeF(8.63);
windowTitleLabel->setFont(f);
connect(this, &Widget::windowTitleChanged, windowTitleLabel, &QLabel::setText);
minimizeButton = new QPushButton(this);
minimizeButton->setObjectName(QString::fromUtf8("minimizeButton"));
minimizeButton->setFixedSize(systemButtonSize);
minimizeButton->setIconSize(systemButtonSize);
minimizeButton->setIcon(QIcon(QString::fromUtf8(":/images/button_minimize_black.svg")));
connect(minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
maximizeButton = new QPushButton(this);
maximizeButton->setObjectName(QString::fromUtf8("maximizeButton"));
maximizeButton->setFixedSize(systemButtonSize);
maximizeButton->setIconSize(systemButtonSize);
maximizeButton->setIcon(QIcon(QString::fromUtf8(":/images/button_maximize_black.svg")));
connect(maximizeButton, &QPushButton::clicked, this, [this]() {
if (isMaximized()) {
showNormal();
} else {
showMaximized();
}
});
closeButton = new QPushButton(this);
closeButton->setObjectName(QString::fromUtf8("closeButton"));
closeButton->setFixedSize(systemButtonSize);
closeButton->setIconSize(systemButtonSize);
closeButton->setIcon(QIcon(QString::fromUtf8(":/images/button_close_black.svg")));
connect(closeButton, &QPushButton::clicked, this, &Widget::close);
const auto titleBarWidgetLayout = new QHBoxLayout(titleBarWidget);
titleBarWidgetLayout->setSpacing(0);
titleBarWidgetLayout->setContentsMargins(0, 0, 0, 0);
titleBarWidgetLayout->addSpacerItem(
new QSpacerItem(7, 20, QSizePolicy::Fixed, QSizePolicy::Fixed));
titleBarWidgetLayout->addWidget(windowIconButton);
titleBarWidgetLayout->addSpacerItem(
new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Fixed));
titleBarWidgetLayout->addWidget(windowTitleLabel);
titleBarWidgetLayout->addStretch();
titleBarWidgetLayout->addWidget(minimizeButton);
titleBarWidgetLayout->addWidget(maximizeButton);
titleBarWidgetLayout->addWidget(closeButton);
titleBarWidget->setLayout(titleBarWidgetLayout);
const auto contentsWidgetLayout = new QVBoxLayout(contentsWidget);
contentsWidgetLayout->setSpacing(0);
contentsWidgetLayout->setContentsMargins(1, 1, 1, 0);
contentsWidgetLayout->addWidget(titleBarWidget);
contentsWidgetLayout->addStretch();
contentsWidget->setLayout(contentsWidgetLayout);
const auto backgroundWindowLayout = new QVBoxLayout(this);
backgroundWindowLayout->setSpacing(0);
backgroundWindowLayout->addWidget(contentsWidget);
setLayout(backgroundWindowLayout);
setStyleSheet(QString::fromUtf8(R"(
#contentsWidget {
background-color: #f0f0f0;
}
#titleBarWidget {
background-color: white;
}
#windowIconButton, #minimizeButton, #maximizeButton, #closeButton {
border-style: none;
background-color: transparent;
}
#minimizeButton:hover, #maximizeButton:hover {
background-color: #80c7c7c7;
}
#minimizeButton:pressed, #maximizeButton:pressed {
background-color: #80808080;
}
#closeButton:hover {
background-color: #e81123;
}
#closeButton:pressed {
background-color: #8c0a15;
}
)"));
}
void Widget::initBackgroundWindow()
{
QWindow *win = windowHandle();
framelessHelper()->removeWindowFrame(win);
framelessHelper()->addIgnoreObject(win, windowIconButton);
framelessHelper()->addIgnoreObject(win, minimizeButton);
framelessHelper()->addIgnoreObject(win, maximizeButton);
framelessHelper()->addIgnoreObject(win, closeButton);
framelessHelper()->setTitleBarHeight(titleBarHeight + framelessHelper()->getBorderHeight());
//setAttribute(Qt::WA_Hover);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
shadowEffect = new QGraphicsDropShadowEffect(this);
shadowEffect->setOffset(0, 0);
contentsWidget->setGraphicsEffect(shadowEffect);
setFrameShadowEnabled();
setFrameShadowActive();
}
void Widget::setFrameShadowEnabled(const bool enable)
{
if (enable) {
const int bw = framelessHelper()->getBorderWidth();
const int bh = framelessHelper()->getBorderHeight();
layout()->setContentsMargins(bw, bh, bw, bh);
shadowEffect->setEnabled(true);
} else {
shadowEffect->setEnabled(false);
layout()->setContentsMargins(0, 0, 0, 0);
}
}
void Widget::setFrameShadowActive(const bool active)
{
if (active) {
shadowEffect->setColor({0, 0, 0, 80});
shadowEffect->setBlurRadius(25);
} else {
shadowEffect->setColor({0, 0, 0, 60});
shadowEffect->setBlurRadius(20);
}
}
bool Widget::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (object == this) {
switch (event->type()) {
case QEvent::WindowStateChange: {
const bool normal = isNormal();
contentsWidget->setShouldDrawWindowBorder(normal);
setFrameShadowEnabled(normal);
framelessHelper()->setTitleBarHeight(
titleBarHeight + (normal ? framelessHelper()->getBorderHeight() : 0));
const QString maxIconPath = QString::fromUtf8(":/images/button_maximize_black.svg");
const QString restoreIconPath = QString::fromUtf8(":/images/button_restore_black.svg");
maximizeButton->setIcon(QIcon(normal ? maxIconPath : restoreIconPath));
} break;
case QEvent::WindowActivate: {
if (isNormal()) {
setFrameShadowActive();
}
} break;
case QEvent::WindowDeactivate: {
if (isNormal()) {
setFrameShadowActive(false);
}
} break;
default:
break;
}
}
return QWidget::eventFilter(object, event);
}

View File

@ -1,92 +0,0 @@
/*
* 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.
*/
#pragma once
#include <QWidget>
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class QGraphicsDropShadowEffect;
class QPushButton;
class QLabel;
class ContentsWidget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(ContentsWidget)
public:
explicit ContentsWidget(QWidget *parent = nullptr);
~ContentsWidget() override = default;
void setShouldDrawWindowBorder(const bool val);
bool getShouldDrawWindowBorder() const;
protected:
void paintEvent(QPaintEvent *event) override;
private:
bool m_bShouldDrawWindowBorder = true;
};
class Widget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Widget)
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override = default;
bool isNormal() const;
protected:
bool eventFilter(QObject *object, QEvent *event) override;
private:
void setupUi();
void initBackgroundWindow();
void setFrameShadowEnabled(const bool enable = true);
void setFrameShadowActive(const bool active = true);
private:
int titleBarHeight = 30;
ContentsWidget *contentsWidget = nullptr;
QGraphicsDropShadowEffect *shadowEffect = nullptr;
QWidget *titleBarWidget = nullptr;
QPushButton *windowIconButton = nullptr;
QLabel *windowTitleLabel = nullptr;
QPushButton *minimizeButton = nullptr;
QPushButton *maximizeButton = nullptr;
QPushButton *closeButton = nullptr;
};

View File

@ -1,177 +0,0 @@
/*
* 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 QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import wangwenx190.Utils 1.0
Window {
id: window
visible: true
width: 800
height: 600
title: qsTr("Hello, World!")
color: blurEffectCheckBox.checked ? "transparent" : "#f0f0f0"
FramelessHelper {
id: framelessHelper
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: extendToTitleBarCheckBox.checked ? "transparent" : ((window.active && framelessHelper.colorizationEnabled) ? framelessHelper.colorizationColor : "white")
anchors {
top: parent.top
left: parent.left
right: parent.right
}
Label {
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: "transparent"
anchors {
top: titleBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
ColumnLayout {
anchors.centerIn: parent
spacing: 25
Label {
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Current system theme: %1").arg(framelessHelper.darkThemeEnabled ? "dark theme" : "light theme")
}
Label {
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Transparency effect: %1").arg(framelessHelper.transparencyEffectEnabled ? "enabled" : "disabled")
}
CheckBox {
id: blurEffectCheckBox
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Enable blur effect")
onCheckedChanged: framelessHelper.setBlurEffectEnabled(checked, forceAcrylicCheckBox.checked)
}
CheckBox {
id: forceAcrylicCheckBox
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Force enabling Acrylic effect")
enabled: framelessHelper.canHaveWindowFrame
}
CheckBox {
id: extendToTitleBarCheckBox
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Extend to title bar")
}
Button {
Layout.alignment: Qt.AlignCenter
text: qsTr("Move to desktop center")
font.pointSize: 15
font.bold: true
onClicked: framelessHelper.moveWindowToDesktopCenter(true)
}
}
}
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()
}
}

View File

@ -1,6 +0,0 @@
TARGET = Win32Demo
TEMPLATE = app
QT += gui-private widgets
HEADERS += widget.h
SOURCES += widget.cpp main.cpp
include($$PWD/../common.pri)

View File

@ -1,67 +0,0 @@
/*
* 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.
*/
#include "widget.h"
#include <QApplication>
#include <QStyleOption>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
QApplication::setFont({QString::fromUtf8("Microsoft YaHei")});
Widget widget;
QStyleOption option;
option.initFrom(&widget);
widget.setWindowIcon(widget.style()->standardIcon(QStyle::SP_ComputerIcon, &option));
widget.setWindowTitle(QObject::tr("Hello, World!"));
widget.show();
return QApplication::exec();
}

View File

@ -1,632 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "widget.h"
#include "../../winnativeeventfilter.h"
#include <QCheckBox>
#include <QColorDialog>
#include <QEvent>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QOperatingSystemVersion>
#include <QPainter>
#include <QPushButton>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <QWindow>
#include <qt_windows.h>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
#include <qpa/qplatformnativeinterface.h>
#else
#include <qpa/qplatformwindow.h>
#include <qpa/qplatformwindow_p.h>
#endif
Q_DECLARE_METATYPE(QMargins)
// Some old SDK doesn't have this value.
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0
#endif
// Copied from windowsx.h
#define GET_X_LPARAM(lp) ((int) (short) LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int) (short) HIWORD(lp))
namespace {
const Widget::Win10Version g_vAcrylicEffectVersion = Widget::Win10Version::Win10_1803;
const QColor g_cDefaultActiveBorderColor = {"#707070"};
const QColor g_cDefaultInactiveBorderColor = {"#aaaaaa"};
QColor g_cColorizationColor = Qt::white;
const char g_sUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
const QString g_sSystemButtonsStyleSheet = QString::fromUtf8(R"(
#iconButton, #minimizeButton, #maximizeButton, #closeButton {
border-style: none;
background-color: transparent;
}
#minimizeButton:hover, #maximizeButton:hover {
background-color: #80c7c7c7;
}
#minimizeButton:pressed, #maximizeButton:pressed {
background-color: #80808080;
}
#closeButton:hover {
background-color: #e81123;
}
#closeButton:pressed {
background-color: #8c0a15;
}
)");
const QString g_sTitleLabelStyleSheet = QString::fromUtf8(R"(
#titleLabel {
color: rgb(%1, %2, %3);
}
)");
const QString g_sTitleBarStyleSheet = QString::fromUtf8(R"(
#titleBarWidget {
background-color: rgba(%1, %2, %3, %4);
border-top: 1px solid rgba(%5, %6, %7, %8);
}
)");
const QString g_sMinimizeButtonImageDark = QString::fromUtf8(":/images/button_minimize_black.svg");
const QString g_sMaximizeButtonImageDark = QString::fromUtf8(":/images/button_maximize_black.svg");
const QString g_sRestoreButtonImageDark = QString::fromUtf8(":/images/button_restore_black.svg");
const QString g_sCloseButtonImageDark = QString::fromUtf8(":/images/button_close_black.svg");
const QString g_sMinimizeButtonImageLight = QString::fromUtf8(":/images/button_minimize_white.svg");
const QString g_sMaximizeButtonImageLight = QString::fromUtf8(":/images/button_maximize_white.svg");
const QString g_sRestoreButtonImageLight = QString::fromUtf8(":/images/button_restore_white.svg");
const QString g_sCloseButtonImageLight = QString::fromUtf8(":/images/button_close_white.svg");
void updateQtFrame(const QWindow *window, const int tbh)
{
Q_ASSERT(window);
QMargins margins = {0, -tbh, 0, 0};
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPlatformWindow *platformWindow = window->handle();
if (platformWindow) {
QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow,
QString::fromUtf8(
"WindowsCustomMargins"),
QVariant::fromValue(margins));
}
#else
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
window->handle());
if (platformWindow) {
platformWindow->setCustomMargins(margins);
}
#endif
}
} // namespace
Widget::Widget(QWidget *parent) : QWidget(parent)
{
createWinId(); // Qt's internal function, make sure it's a top level window.
initializeWindow();
}
void Widget::triggerFrameChange()
{
SetWindowPos(reinterpret_cast<HWND>(windowHandle()->winId()),
nullptr,
0,
0,
0,
0,
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER
| SWP_NOOWNERZORDER);
}
void Widget::retranslateUi()
{
setWindowTitle(tr("Widget"));
iconButton->setText({});
titleLabel->setText({});
minimizeButton->setText({});
maximizeButton->setText({});
closeButton->setText({});
customizeTitleBarCB->setText(tr("Enable customized title bar"));
preserveWindowFrameCB->setText(tr("Preserve window frame"));
blurEffectCB->setText(tr("Enable blur effect"));
extendToTitleBarCB->setText(tr("Extend to title bar"));
forceAcrylicCB->setText(tr("Force enabling Acrylic effect"));
resizableCB->setText(tr("Resizable"));
}
void Widget::setupUi()
{
resize(1056, 600);
verticalLayout_3 = new QVBoxLayout(this);
titleBarWidget = new QWidget(this);
titleBarWidget->setObjectName(QString::fromUtf8("titleBarWidget"));
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(titleBarWidget->sizePolicy().hasHeightForWidth());
titleBarWidget->setSizePolicy(sizePolicy);
const int titleBarHeight
= WinNativeEventFilter::getSystemMetric(windowHandle(),
WinNativeEventFilter::SystemMetric::TitleBarHeight,
false);
titleBarWidget->setMinimumSize(QSize(0, titleBarHeight));
titleBarWidget->setMaximumSize(QSize(16777215, titleBarHeight));
horizontalLayout = new QHBoxLayout(titleBarWidget);
horizontalLayout->setSpacing(0);
horizontalLayout->setContentsMargins(0, 0, 0, 0);
horizontalSpacer_7 = new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
horizontalLayout->addSpacerItem(horizontalSpacer_7);
iconButton = new QPushButton(titleBarWidget);
iconButton->setObjectName(QString::fromUtf8("iconButton"));
horizontalLayout->addWidget(iconButton);
horizontalSpacer = new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
horizontalLayout->addSpacerItem(horizontalSpacer);
titleLabel = new QLabel(titleBarWidget);
titleLabel->setObjectName(QString::fromUtf8("titleLabel"));
QFont font;
font.setPointSize(10);
titleLabel->setFont(font);
horizontalLayout->addWidget(titleLabel);
horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout->addSpacerItem(horizontalSpacer_2);
minimizeButton = new QPushButton(titleBarWidget);
minimizeButton->setObjectName(QString::fromUtf8("minimizeButton"));
QSizePolicy sizePolicy1(QSizePolicy::Fixed, QSizePolicy::Fixed);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(minimizeButton->sizePolicy().hasHeightForWidth());
minimizeButton->setSizePolicy(sizePolicy1);
const QSize systemButtonSize = {qRound(titleBarHeight * 1.5), titleBarHeight};
minimizeButton->setMinimumSize(systemButtonSize);
minimizeButton->setMaximumSize(systemButtonSize);
QIcon icon;
icon.addFile(g_sMinimizeButtonImageDark, {}, QIcon::Normal, QIcon::Off);
minimizeButton->setIcon(icon);
minimizeButton->setIconSize(systemButtonSize);
horizontalLayout->addWidget(minimizeButton);
maximizeButton = new QPushButton(titleBarWidget);
maximizeButton->setObjectName(QString::fromUtf8("maximizeButton"));
sizePolicy1.setHeightForWidth(maximizeButton->sizePolicy().hasHeightForWidth());
maximizeButton->setSizePolicy(sizePolicy1);
maximizeButton->setMinimumSize(systemButtonSize);
maximizeButton->setMaximumSize(systemButtonSize);
QIcon icon1;
icon1.addFile(g_sMaximizeButtonImageDark, {}, QIcon::Normal, QIcon::Off);
maximizeButton->setIcon(icon1);
maximizeButton->setIconSize(systemButtonSize);
horizontalLayout->addWidget(maximizeButton);
closeButton = new QPushButton(titleBarWidget);
closeButton->setObjectName(QString::fromUtf8("closeButton"));
sizePolicy1.setHeightForWidth(closeButton->sizePolicy().hasHeightForWidth());
closeButton->setSizePolicy(sizePolicy1);
closeButton->setMinimumSize(systemButtonSize);
closeButton->setMaximumSize(systemButtonSize);
QIcon icon2;
icon2.addFile(g_sCloseButtonImageDark, {}, QIcon::Normal, QIcon::Off);
closeButton->setIcon(icon2);
closeButton->setIconSize(systemButtonSize);
horizontalLayout->addWidget(closeButton);
verticalLayout_3->addWidget(titleBarWidget);
contentsWidget = new QWidget(this);
QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy2.setHorizontalStretch(0);
sizePolicy2.setVerticalStretch(0);
sizePolicy2.setHeightForWidth(contentsWidget->sizePolicy().hasHeightForWidth());
contentsWidget->setSizePolicy(sizePolicy2);
verticalLayout_2 = new QVBoxLayout(contentsWidget);
verticalSpacer_2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
verticalLayout_2->addSpacerItem(verticalSpacer_2);
horizontalLayout_2 = new QHBoxLayout();
horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_2->addSpacerItem(horizontalSpacer_3);
controlPanelWidget = new QWidget(contentsWidget);
QSizePolicy sizePolicy3(QSizePolicy::Maximum, QSizePolicy::Maximum);
sizePolicy3.setHorizontalStretch(0);
sizePolicy3.setVerticalStretch(0);
sizePolicy3.setHeightForWidth(controlPanelWidget->sizePolicy().hasHeightForWidth());
controlPanelWidget->setSizePolicy(sizePolicy3);
verticalLayout = new QVBoxLayout(controlPanelWidget);
customizeTitleBarCB = new QCheckBox(controlPanelWidget);
QFont font1;
font1.setPointSize(15);
font1.setBold(true);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
font1.setWeight(QFont::Bold);
#else
font1.setWeight(75);
#endif
customizeTitleBarCB->setFont(font1);
verticalLayout->addWidget(customizeTitleBarCB);
preserveWindowFrameCB = new QCheckBox(controlPanelWidget);
preserveWindowFrameCB->setFont(font1);
verticalLayout->addWidget(preserveWindowFrameCB);
blurEffectCB = new QCheckBox(controlPanelWidget);
blurEffectCB->setFont(font1);
verticalLayout->addWidget(blurEffectCB);
extendToTitleBarCB = new QCheckBox(controlPanelWidget);
extendToTitleBarCB->setFont(font1);
verticalLayout->addWidget(extendToTitleBarCB);
forceAcrylicCB = new QCheckBox(controlPanelWidget);
forceAcrylicCB->setFont(font1);
forceAcrylicCB->setEnabled(m_bCanAcrylicBeEnabled);
verticalLayout->addWidget(forceAcrylicCB);
resizableCB = new QCheckBox(controlPanelWidget);
resizableCB->setFont(font1);
verticalLayout->addWidget(resizableCB);
horizontalLayout_2->addWidget(controlPanelWidget);
horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_2->addSpacerItem(horizontalSpacer_4);
verticalLayout_2->addLayout(horizontalLayout_2);
verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
verticalLayout_2->addSpacerItem(verticalSpacer);
horizontalLayout_3 = new QHBoxLayout();
horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_3->addSpacerItem(horizontalSpacer_5);
horizontalSpacer_6 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_3->addSpacerItem(horizontalSpacer_6);
verticalLayout_2->addLayout(horizontalLayout_3);
verticalLayout_3->addWidget(contentsWidget);
if (shouldDrawBorder()) {
layout()->setContentsMargins(1, 1, 1, 1);
} else {
layout()->setContentsMargins(0, 0, 0, 0);
}
retranslateUi();
}
bool Widget::isNormaled() const
{
return !isMinimized() && !isMaximized() && !isFullScreen();
}
bool Widget::shouldDrawBorder(const bool ignoreWindowState) const
{
return m_bIsWin10OrGreater && (ignoreWindowState ? true : isNormaled())
&& !preserveWindowFrameCB->isChecked() && customizeTitleBarCB->isChecked();
}
bool Widget::shouldDrawThemedBorder(const bool ignoreWindowState) const
{
return (shouldDrawBorder(ignoreWindowState) && WinNativeEventFilter::isColorizationEnabled());
}
bool Widget::shouldDrawThemedTitleBar() const
{
return m_bIsWin10OrGreater && WinNativeEventFilter::isColorizationEnabled();
}
QColor Widget::activeBorderColor()
{
return WinNativeEventFilter::isColorizationEnabled() ? g_cColorizationColor
: g_cDefaultActiveBorderColor;
}
QColor Widget::inactiveBorderColor()
{
return g_cDefaultInactiveBorderColor;
}
QColor Widget::borderColor() const
{
return isActiveWindow() ? activeBorderColor() : inactiveBorderColor();
}
bool Widget::isWin10OrGreater(const Win10Version subVer)
{
return (QOperatingSystemVersion::current()
>= ((subVer == Win10Version::Windows10)
? QOperatingSystemVersion::Windows10
: QOperatingSystemVersion(QOperatingSystemVersion::Windows,
10,
0,
static_cast<int>(subVer))));
}
bool Widget::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (object == this) {
switch (event->type()) {
case QEvent::WindowStateChange: {
if (shouldDrawBorder(true)) {
if (isMaximized()) {
layout()->setContentsMargins(0, 0, 0, 0);
}
if (isNormaled()) {
layout()->setContentsMargins(1, 1, 1, 1);
}
}
updateTitleBar();
break;
}
case QEvent::WinIdChange:
WinNativeEventFilter::addFramelessWindow(windowHandle());
break;
case QEvent::WindowActivate:
case QEvent::WindowDeactivate:
updateTitleBar();
break;
default:
break;
}
}
return QWidget::eventFilter(object, event);
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool Widget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
#else
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
Q_ASSERT(eventType == "windows_generic_MSG");
Q_ASSERT(message);
Q_ASSERT(result);
if (customizeTitleBarCB && customizeTitleBarCB->isChecked()) {
const auto msg = static_cast<LPMSG>(message);
switch (msg->message) {
case WM_DWMCOLORIZATIONCOLORCHANGED: {
g_cColorizationColor = QColor::fromRgba(msg->wParam);
if (shouldDrawThemedBorder()) {
update();
}
break;
}
case WM_DPICHANGED:
case WM_NCPAINT:
update();
break;
default:
break;
}
}
return QWidget::nativeEvent(eventType, message, result);
}
void Widget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (shouldDrawBorder()) {
QPainter painter(this);
painter.save();
painter.setPen({borderColor(), 1});
painter.drawLine(0, 0, width(), 0);
painter.drawLine(width() - 1, 0, width() - 1, height());
painter.drawLine(width(), height() - 1, 0, height() - 1);
painter.drawLine(0, height(), 0, 0);
painter.restore();
}
}
void Widget::updateTitleBar()
{
const bool themedTitleBar = shouldDrawThemedTitleBar() && isActiveWindow();
if (themedTitleBar && !m_bExtendToTitleBar) {
minimizeButton->setIcon(QIcon(g_sMinimizeButtonImageLight));
closeButton->setIcon(QIcon(g_sCloseButtonImageLight));
if (isMaximized()) {
maximizeButton->setIcon(QIcon(g_sRestoreButtonImageLight));
}
if (isNormaled()) {
maximizeButton->setIcon(QIcon(g_sMaximizeButtonImageLight));
}
} else {
minimizeButton->setIcon(QIcon(g_sMinimizeButtonImageDark));
closeButton->setIcon(QIcon(g_sCloseButtonImageDark));
if (isMaximized()) {
maximizeButton->setIcon(QIcon(g_sRestoreButtonImageDark));
}
if (isNormaled()) {
maximizeButton->setIcon(QIcon(g_sMaximizeButtonImageDark));
}
}
const QColor titleBarBackgroundColor = m_bExtendToTitleBar
? Qt::transparent
: (themedTitleBar ? g_cColorizationColor : Qt::white);
const QColor titleBarTextColor = isActiveWindow()
? ((!themedTitleBar || m_bExtendToTitleBar) ? Qt::black
: Qt::white)
: QColor("#999999");
const QColor titleBarBorderColor = (!m_bIsWin10OrGreater || shouldDrawBorder() || isMaximized()
|| isFullScreen())
? Qt::transparent
: borderColor();
titleBarWidget->setStyleSheet(
g_sSystemButtonsStyleSheet
+ g_sTitleLabelStyleSheet.arg(QString::number(titleBarTextColor.red()),
QString::number(titleBarTextColor.green()),
QString::number(titleBarTextColor.blue()))
+ g_sTitleBarStyleSheet.arg(QString::number(titleBarBackgroundColor.red()),
QString::number(titleBarBackgroundColor.green()),
QString::number(titleBarBackgroundColor.blue()),
QString::number(titleBarBackgroundColor.alpha()),
QString::number(titleBarBorderColor.red()),
QString::number(titleBarBorderColor.green()),
QString::number(titleBarBorderColor.blue()),
QString::number(titleBarBorderColor.alpha())));
}
void Widget::initializeOptions()
{
if (m_bIsWin10OrGreater) {
//preserveWindowFrameCB->click();
if (m_bCanAcrylicBeEnabled) {
forceAcrylicCB->click();
}
}
customizeTitleBarCB->click();
extendToTitleBarCB->click();
blurEffectCB->click();
resizableCB->click();
m_bCanShowColorDialog = true;
}
void Widget::setupConnections()
{
connect(minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
connect(maximizeButton, &QPushButton::clicked, this, [this]() {
if (isMaximized()) {
showNormal();
} else {
showMaximized();
}
});
connect(closeButton, &QPushButton::clicked, this, &Widget::close);
connect(this, &Widget::windowTitleChanged, titleLabel, &QLabel::setText);
connect(this, &Widget::windowIconChanged, iconButton, &QPushButton::setIcon);
connect(customizeTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
preserveWindowFrameCB->setEnabled(enable);
updateQtFrame(windowHandle(),
enable ? WinNativeEventFilter::getSystemMetric(
windowHandle(),
WinNativeEventFilter::SystemMetric::TitleBarHeight,
true,
true)
: 0);
titleBarWidget->setVisible(enable);
if (enable) {
qunsetenv(g_sUseNativeTitleBar);
} else {
qputenv(g_sUseNativeTitleBar, "1");
}
triggerFrameChange();
update();
});
connect(preserveWindowFrameCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
if (enable) {
qputenv(g_sPreserveWindowFrame, "1");
} else {
qunsetenv(g_sPreserveWindowFrame);
}
if (!enable && shouldDrawBorder()) {
layout()->setContentsMargins(1, 1, 1, 1);
} else {
layout()->setContentsMargins(0, 0, 0, 0);
}
triggerFrameChange();
updateTitleBar();
update();
});
connect(blurEffectCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
QColor color = {0, 0, 0, 127};
const bool useAcrylicEffect = m_bCanAcrylicBeEnabled && forceAcrylicCB->isChecked();
if (useAcrylicEffect) {
if (enable && m_bCanShowColorDialog) {
color = QColorDialog::getColor(color,
this,
tr("Please select a gradient color"),
QColorDialog::ShowAlphaChannel);
}
}
// Qt will paint a solid white background to the window,
// it will cover the blurred effect, so we need to
// make the background become totally transparent. Achieve
// this by setting a palette to the window.
QPalette palette = {};
if (enable) {
palette.setColor(QPalette::Window, Qt::transparent);
}
setPalette(palette);
WinNativeEventFilter::setBlurEffectEnabled(windowHandle(), enable, color);
update();
if (useAcrylicEffect && enable && WinNativeEventFilter::isTransparencyEffectEnabled()) {
QMessageBox::warning(this,
tr("BUG Warning!"),
tr("You have enabled the transparency effect in the personalize "
"settings.\nDragging will be very laggy when the Acrylic "
"effect is enabled.\nDisabling the transparency effect can "
"solve this issue temporarily."));
}
});
connect(extendToTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) {
m_bExtendToTitleBar = state == Qt::Checked;
updateTitleBar();
});
connect(forceAcrylicCB, &QCheckBox::stateChanged, this, [this](int state) {
if (!m_bCanAcrylicBeEnabled) {
return;
}
if (state == Qt::Checked) {
qputenv(g_sForceUseAcrylicEffect, "1");
} else {
qunsetenv(g_sForceUseAcrylicEffect);
}
if (blurEffectCB->isChecked()) {
blurEffectCB->click();
blurEffectCB->click();
}
});
connect(resizableCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
maximizeButton->setEnabled(enable);
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, !enable);
show();
});
}
void Widget::initializeFramelessFunctions()
{
WinNativeEventFilter::addFramelessWindow(windowHandle());
WinNativeEventFilter::setIgnoredObjects(windowHandle(),
{minimizeButton, maximizeButton, closeButton});
installEventFilter(this);
}
void Widget::initializeVariables()
{
m_bIsWin10OrGreater = isWin10OrGreater();
if (m_bIsWin10OrGreater) {
m_bCanAcrylicBeEnabled = isWin10OrGreater(g_vAcrylicEffectVersion);
g_cColorizationColor = WinNativeEventFilter::getColorizationColor();
}
}
void Widget::initializeWindow()
{
initializeVariables();
setupUi();
updateTitleBar();
initializeFramelessFunctions();
setupConnections();
initializeOptions();
}

View File

@ -1,123 +0,0 @@
/*
* 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.
*/
#pragma once
#include <QWidget>
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
QT_FORWARD_DECLARE_CLASS(QSpacerItem)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QCheckBox)
class Widget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Widget)
public:
enum class Win10Version : int {
Win10_1507 = 10240,
Win10_1511 = 10586,
Win10_1607 = 14393,
Win10_1703 = 15063,
Win10_1709 = 16299,
Win10_1803 = 17134,
Win10_1809 = 17763,
Win10_1903 = 18362,
Win10_1909 = 18363,
Win10_2004 = 19041,
Win10_20H2 = 19042,
Windows10 = Win10_1507
};
Q_ENUM(Win10Version)
explicit Widget(QWidget *parent = nullptr);
~Widget() override = default;
bool isNormaled() const;
bool shouldDrawBorder(const bool ignoreWindowState = false) const;
bool shouldDrawThemedBorder(const bool ignoreWindowState = false) const;
bool shouldDrawThemedTitleBar() const;
static QColor activeBorderColor();
static QColor inactiveBorderColor();
QColor borderColor() const;
static bool isWin10OrGreater(const Win10Version subVer = Win10Version::Windows10);
public Q_SLOTS:
void retranslateUi();
protected:
bool eventFilter(QObject *object, QEvent *event) override;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
#else
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif
void paintEvent(QPaintEvent *event) override;
private:
void setupUi();
void updateTitleBar();
void initializeOptions();
void setupConnections();
void initializeFramelessFunctions();
void initializeVariables();
void initializeWindow();
void triggerFrameChange();
private:
bool m_bIsWin10OrGreater = false, m_bCanAcrylicBeEnabled = false, m_bExtendToTitleBar = false,
m_bCanShowColorDialog = false;
QVBoxLayout *verticalLayout_3 = nullptr, *verticalLayout_2 = nullptr, *verticalLayout = nullptr;
QWidget *titleBarWidget = nullptr, *contentsWidget = nullptr, *controlPanelWidget = nullptr;
QHBoxLayout *horizontalLayout = nullptr, *horizontalLayout_2 = nullptr,
*horizontalLayout_3 = nullptr;
QSpacerItem *horizontalSpacer_7 = nullptr, *horizontalSpacer = nullptr,
*horizontalSpacer_2 = nullptr, *verticalSpacer_2 = nullptr,
*horizontalSpacer_3 = nullptr, *horizontalSpacer_4 = nullptr,
*verticalSpacer = nullptr, *horizontalSpacer_5 = nullptr,
*horizontalSpacer_6 = nullptr;
QPushButton *iconButton = nullptr, *minimizeButton = nullptr, *maximizeButton = nullptr,
*closeButton = nullptr;
QLabel *titleLabel = nullptr;
QCheckBox *customizeTitleBarCB = nullptr, *preserveWindowFrameCB = nullptr,
*blurEffectCB = nullptr, *extendToTitleBarCB = nullptr, *forceAcrylicCB = nullptr,
*resizableCB = nullptr;
};

View File

@ -10,10 +10,12 @@ RESOURCES += $$PWD/images.qrc
win32 {
DEFINES += \
WIN32_LEAN_AND_MEAN \
_CRT_SECURE_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS \
UNICODE \
_UNICODE
CONFIG += windeployqt
CONFIG -= embed_manifest_exe
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi -lshcore -ld2d1 -luxtheme
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi
RC_FILE = $$PWD/windows.rc
OTHER_FILES += $$PWD/windows.manifest
}

View File

@ -1,8 +1,4 @@
TEMPLATE = subdirs
CONFIG -= ordered
qtHaveModule(widgets) {
SUBDIRS += QWidget QMainWindow
win32: SUBDIRS += Win32Demo
versionAtLeast(QT_VERSION, 5.15.0): SUBDIRS += QWidget2
}
qtHaveModule(quick): SUBDIRS += Quick
qtHaveModule(widgets): SUBDIRS += widget
qtHaveModule(quick): SUBDIRS += quick

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -23,9 +23,11 @@
*/
#include "../../framelessquickhelper.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickStyle>
#include "../../qtacrylicitem.h"
#include <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQuickControls2/qquickstyle.h>
#include <QtQuick/qquickwindow.h>
int main(int argc, char *argv[])
{
@ -46,22 +48,26 @@ int main(int argc, char *argv[])
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QGuiApplication application(argc, argv);
QQmlApplicationEngine engine;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
QQuickStyle::setStyle(QString::fromUtf8("Basic"));
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
QQuickStyle::setStyle(QStringLiteral("Basic"));
#else
QQuickStyle::setStyle(QString::fromUtf8("Default"));
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QQuickStyle::setStyle(QStringLiteral("Default"));
#endif
QQmlApplicationEngine engine;
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0, "FramelessHelper");
const QUrl mainQmlUrl(QString::fromUtf8("qrc:///qml/main.qml"));
qmlRegisterType<QtAcrylicItem>("wangwenx190.Utils", 1, 0, "AcrylicItem");
const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/main.qml"));
const QMetaObject::Connection connection = QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,

View File

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

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -24,6 +24,7 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import wangwenx190.Utils 1.0
Window {
@ -32,19 +33,37 @@ Window {
width: 800
height: 600
title: qsTr("Hello, World!")
color: "transparent"
FramelessHelper {
id: framelessHelper
}
Timer {
id: timer
interval: 500
running: true
repeat: true
onTriggered: label.text = Qt.formatTime(new Date(), "hh:mm:ss")
}
AcrylicItem {
id: acrylicItem
anchors.fill: parent
frameVisible: true
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: "white"
color: "transparent"
anchors {
top: parent.top
topMargin: 1
left: parent.left
leftMargin: 1
right: parent.right
rightMargin: 1
}
Text {
@ -64,8 +83,7 @@ Window {
MinimizeButton {
id: minimizeButton
onClicked: window.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(
minimizeButton)
Component.onCompleted: framelessHelper.addIgnoreObject(minimizeButton)
}
MaximizeButton {
@ -78,29 +96,32 @@ Window {
window.showMaximized()
}
}
Component.onCompleted: framelessHelper.addIgnoreObject(
maximizeButton)
Component.onCompleted: framelessHelper.addIgnoreObject(maximizeButton)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.addIgnoreObject(
closeButton)
Component.onCompleted: framelessHelper.addIgnoreObject(closeButton)
}
}
}
Rectangle {
id: content
color: "#f0f0f0"
anchors {
top: titleBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
Label {
id: label
anchors.centerIn: parent
font {
pointSize: 70
bold: true
}
}
Component.onCompleted: framelessHelper.removeWindowFrame(true)
Component.onCompleted: {
framelessHelper.removeWindowFrame()
framelessHelper.setAcrylicEffectEnabled(true)
}
// Re-draw the window frame.
onActiveChanged: acrylicItem.update()
onVisibilityChanged: acrylicItem.update()
}

View File

@ -1,7 +1,7 @@
TARGET = Quick
TEMPLATE = app
QT += quick quickcontrols2
HEADERS += ../../framelessquickhelper.h
SOURCES += ../../framelessquickhelper.cpp main.cpp
CONFIG(release, debug|release): CONFIG += qtquickcompiler
SOURCES += main.cpp
RESOURCES += qml.qrc
include($$PWD/../common.pri)

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -22,9 +22,8 @@
* SOFTWARE.
*/
#include <QtWidgets/qapplication.h>
#include "widget.h"
#include <QApplication>
#include <QStyleOption>
int main(int argc, char *argv[])
{
@ -45,19 +44,16 @@ int main(int argc, char *argv[])
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
Widget widget;
QStyleOption option;
option.initFrom(&widget);
const QIcon icon = widget.style()->standardIcon(QStyle::SP_ComputerIcon, &option);
widget.setWindowIcon(icon);
widget.setWindowTitle(QObject::tr("Hello, World!"));
widget.resize(800, 600);
widget.moveToDesktopCenter();
widget.show();
return QApplication::exec();
}

140
examples/widget/widget.cpp Normal file
View File

@ -0,0 +1,140 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "widget.h"
#include <QtGui/qscreen.h>
#include <QtWidgets/qlayout.h>
#include <QtWidgets/qlabel.h>
#include <QtCore/qdatetime.h>
#include <QtWidgets/qpushbutton.h>
#include <QtGui/qguiapplication.h>
#include "../../utilities.h"
#include "../../framelesswindowsmanager.h"
Widget::Widget(QWidget *parent) : QtAcrylicWidget(parent)
{
createWinId();
setupUi();
startTimer(500);
}
Widget::~Widget() = default;
void Widget::moveToDesktopCenter()
{
const QSize ss = screen()->size();
const int newX = (ss.width() - width()) / 2;
const int newY = (ss.height() - height()) / 2;
move(newX, newY);
}
void Widget::timerEvent(QTimerEvent *event)
{
QtAcrylicWidget::timerEvent(event);
m_label->setText(QTime::currentTime().toString(QStringLiteral("hh:mm:ss")));
}
void Widget::setupUi()
{
const QWindow *win = windowHandle();
const int titleBarHeight = Utilities::getSystemMetric(win, Utilities::SystemMetric::TitleBarHeight, false);
const QSize systemButtonSize = {qRound(titleBarHeight * 1.5), titleBarHeight};
//const QSizePolicy systemButtonSizePolicy = {QSizePolicy::Fixed, QSizePolicy::Fixed};
m_minimizeButton = new QPushButton(this);
m_minimizeButton->setObjectName(QStringLiteral("MinimizeButton"));
m_minimizeButton->setFixedSize(systemButtonSize);
m_minimizeButton->setIcon(QIcon{QStringLiteral(":/images/button_minimize_black.svg")});
m_minimizeButton->setIconSize(systemButtonSize);
connect(m_minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
m_maximizeButton = new QPushButton(this);
m_maximizeButton->setObjectName(QStringLiteral("MaximizeButton"));
m_maximizeButton->setFixedSize(systemButtonSize);
m_maximizeButton->setIcon(QIcon{QStringLiteral(":/images/button_maximize_black.svg")});
m_maximizeButton->setIconSize(systemButtonSize);
connect(m_maximizeButton, &QPushButton::clicked, this, [this](){
if (isMaximized()) {
showNormal();
m_maximizeButton->setIcon(QIcon{QStringLiteral(":/images/button_maximize_black.svg")});
} else {
showMaximized();
m_maximizeButton->setIcon(QIcon{QStringLiteral(":/images/button_restore_black.svg")});
}
});
m_closeButton = new QPushButton(this);
m_closeButton->setObjectName(QStringLiteral("CloseButton"));
m_closeButton->setFixedSize(systemButtonSize);
m_closeButton->setIcon(QIcon{QStringLiteral(":/images/button_close_black.svg")});
m_closeButton->setIconSize(systemButtonSize);
connect(m_closeButton, &QPushButton::clicked, this, &Widget::close);
FramelessWindowsManager::addIgnoreObject(win, m_minimizeButton);
FramelessWindowsManager::addIgnoreObject(win, m_maximizeButton);
FramelessWindowsManager::addIgnoreObject(win, m_closeButton);
const auto systemButtonLayout = new QHBoxLayout;
systemButtonLayout->setContentsMargins(0, 0, 0, 0);
systemButtonLayout->setSpacing(0);
systemButtonLayout->addStretch();
systemButtonLayout->addWidget(m_minimizeButton);
systemButtonLayout->addWidget(m_maximizeButton);
systemButtonLayout->addWidget(m_closeButton);
m_label = new QLabel(this);
QFont font = QGuiApplication::font();
font.setBold(true);
font.setPointSize(70);
m_label->setFont(font);
m_label->setFrameShape(QFrame::NoFrame);
const auto contentLayout = new QHBoxLayout;
contentLayout->addStretch();
contentLayout->addWidget(m_label);
contentLayout->addStretch();
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(1, 1, 1, 1);
mainLayout->setSpacing(0);
mainLayout->addLayout(systemButtonLayout);
mainLayout->addStretch();
mainLayout->addLayout(contentLayout);
mainLayout->addStretch();
setLayout(mainLayout);
setStyleSheet(QStringLiteral(R"(
#MinimizeButton, #MaximizeButton, #CloseButton {
border-style: none;
background-color: transparent;
}
#MinimizeButton:hover, #MaximizeButton:hover {
background-color: #80c7c7c7;
}
#MinimizeButton:pressed, #MaximizeButton:pressed {
background-color: #80808080;
}
#CloseButton:hover {
background-color: #e81123;
}
#CloseButton:pressed {
background-color: #8c0a15;
}
)"));
}

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -22,26 +22,35 @@
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
#pragma once
Button {
id: control
#include "../../qtacrylicwidget.h"
implicitWidth: 45
implicitHeight: 30
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_END_NAMESPACE
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Close")
class Widget : public QtAcrylicWidget
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Widget)
contentItem: Image {
anchors.fill: control
source: "qrc:/images/button_close_white.svg"
}
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
background: Rectangle {
visible: control.down || control.hovered
color: control.down ? "#8c0a15" : (control.hovered ? "#e81123" : "transparent")
}
}
Q_INVOKABLE void moveToDesktopCenter();
protected:
void timerEvent(QTimerEvent *event) override;
private:
void setupUi();
private:
QLabel *m_label = nullptr;
QPushButton *m_minimizeButton = nullptr;
QPushButton *m_maximizeButton = nullptr;
QPushButton *m_closeButton = nullptr;
};

View File

@ -1,4 +1,4 @@
TARGET = QWidget2
TARGET = Widget
TEMPLATE = app
QT += widgets
HEADERS += widget.h

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -37,3 +37,43 @@
#endif
#endif
#endif
#if defined(Q_OS_WIN) && !defined(Q_OS_WINDOWS)
#define Q_OS_WINDOWS
#endif
#ifndef Q_DISABLE_MOVE
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#endif
#ifndef Q_DISABLE_COPY_MOVE
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
#if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
#define qAsConst(i) std::as_const(i)
#endif
#ifndef _flh_framelessMode_flag
#define _flh_framelessMode_flag "_FRAMELESSHELPER_FRAMELESS_MODE_ENABLED"
#endif
#ifndef _flh_borderWidth_flag
#define _flh_borderWidth_flag "_FRAMELESSHELPER_WINDOW_BORDER_WIDTH"
#endif
#ifndef _flh_borderHeight_flag
#define _flh_borderHeight_flag "_FRAMELESSHELPER_WINDOW_BORDER_HEIGHT"
#endif
#ifndef _flh_titleBarHeight_flag
#define _flh_titleBarHeight_flag "_FRAMELESSHELPER_WINDOW_TITLE_BAR_HEIGHT"
#endif
#ifndef _flh_ignoredObjects_flag
#define _flh_ignoredObjects_flag "_FRAMELESSHELPER_WINDOW_TITLE_BAR_IGNORED_OBJECTS"
#endif

View File

@ -30,45 +30,14 @@
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "winnativeeventfilter.h"
#include <d2d1.h>
#include <QDebug>
#include <QGuiApplication>
#include <QLibrary>
#include <QSettings>
#include <QWindow>
#include <QtMath>
#include <qpa/qplatformwindow.h>
#include <qt_windows.h>
#include <dwmapi.h>
#include "framelesshelper_win32.h"
#include <QtCore/qdebug.h>
#include <QtCore/qvariant.h>
#include <QtCore/qcoreapplication.h>
#include <QtGui/qwindow.h>
#include <QtCore/qt_windows.h>
#include <shellapi.h>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
#include <QOperatingSystemVersion>
#else
#include <QSysInfo>
#endif
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
#include <qpa/qplatformnativeinterface.h>
#else
#include <qpa/qplatformwindow_p.h>
#endif
Q_DECLARE_METATYPE(QMargins)
#if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
#define qAsConst(i) std::as_const(i)
#endif
#ifndef USER_DEFAULT_SCREEN_DPI
// Only available since Windows Vista
#define USER_DEFAULT_SCREEN_DPI 96
#endif
#ifndef SM_CXPADDEDBORDER
// Only available since Windows Vista
#define SM_CXPADDEDBORDER 92
#endif
#include "utilities.h"
#ifndef WM_NCUAHDRAWCAPTION
// Not documented, only available since Windows Vista
@ -80,121 +49,11 @@ Q_DECLARE_METATYPE(QMargins)
#define WM_NCUAHDRAWFRAME 0x00AF
#endif
#ifndef WM_DWMCOMPOSITIONCHANGED
// Only available since Windows Vista
#define WM_DWMCOMPOSITIONCHANGED 0x031E
#endif
#ifndef WM_DPICHANGED
// Only available since Windows 8.1
#define WM_DPICHANGED 0x02E0
#endif
#ifndef ABM_GETAUTOHIDEBAREX
// Only available since Windows 8.1
#define ABM_GETAUTOHIDEBAREX 0x0000000b
#endif
namespace {
enum : WORD { DwmwaUseImmersiveDarkMode = 20, DwmwaUseImmersiveDarkModeBefore20h1 = 19 };
using WINDOWCOMPOSITIONATTRIB = enum _WINDOWCOMPOSITIONATTRIB
{
WCA_ACCENT_POLICY = 19
};
using WINDOWCOMPOSITIONATTRIBDATA = struct _WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using ACCENT_STATE = enum _ACCENT_STATE {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
ACCENT_INVALID_STATE = 5
};
using ACCENT_POLICY = struct _ACCENT_POLICY
{
ACCENT_STATE AccentState;
DWORD AccentFlags;
COLORREF GradientColor;
DWORD AnimationId;
};
using IMMERSIVE_HC_CACHE_MODE = enum _IMMERSIVE_HC_CACHE_MODE {
IHCM_USE_CACHED_VALUE,
IHCM_REFRESH
};
using PREFERRED_APP_MODE = enum _PREFERRED_APP_MODE {
Default,
AllowDark,
ForceDark,
ForceLight,
Max
};
bool isWin8OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8;
#endif
}
bool isWin8Point1OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8_1;
#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))
return QOperatingSystemVersion::current()
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, ver);
#else
Q_UNUSED(ver)
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
}
bool isAcrylicEffectAvailable()
{
if (!isWin10OrGreater()) {
return false;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
const QOperatingSystemVersion currentVersion = QOperatingSystemVersion::current();
if (currentVersion > QOperatingSystemVersion::Windows10) {
return true;
}
return ((currentVersion.microVersion() >= 16190) && (currentVersion.microVersion() < 18362));
#else
return false;
#endif
}
#ifndef IsMinimized
// Only available since Windows 2000
#define IsMinimized(h) IsIconic(h)
@ -215,108 +74,16 @@ bool isAcrylicEffectAvailable()
#define GET_Y_LPARAM(lp) ((int) (short) HIWORD(lp))
#endif
using MONITOR_DPI_TYPE = enum _MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI = 0 };
static const char envVarUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
static const char envVarPreserveWindowFrame[] = "WNEF_PRESERVE_WINDOW_FRAME";
static const char envVarForceWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
using PROCESS_DPI_AWARENESS = enum _PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
};
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, WINDOWCOMPOSITIONATTRIBDATA *);
using ShouldAppsUseDarkModePtr = BOOL(WINAPI *)();
using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL);
using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL);
using IsDarkModeAllowedForWindowPtr = BOOL(WINAPI *)(HWND);
using GetIsImmersiveColorUsingHighContrastPtr = BOOL(WINAPI *)(IMMERSIVE_HC_CACHE_MODE);
using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)();
using ShouldSystemUseDarkModePtr = BOOL(WINAPI *)();
using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE);
using IsDarkModeAllowedForAppPtr = BOOL(WINAPI *)();
using GetDpiForMonitorPtr = HRESULT(WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
using GetProcessDpiAwarenessPtr = HRESULT(WINAPI *)(HANDLE, PROCESS_DPI_AWARENESS *);
using GetSystemDpiForProcessPtr = UINT(WINAPI *)(HANDLE);
using GetDpiForWindowPtr = UINT(WINAPI *)(HWND);
using GetDpiForSystemPtr = UINT(WINAPI *)();
using GetSystemMetricsForDpiPtr = int(WINAPI *)(int, UINT);
using AdjustWindowRectExForDpiPtr = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT);
// Internal data structure.
using WNEF_CORE_DATA = struct _WNEF_CORE_DATA
{
_WNEF_CORE_DATA() { load(); }
SetWindowCompositionAttributePtr SetWindowCompositionAttributePFN = nullptr;
ShouldAppsUseDarkModePtr ShouldAppsUseDarkModePFN = nullptr;
AllowDarkModeForWindowPtr AllowDarkModeForWindowPFN = nullptr;
AllowDarkModeForAppPtr AllowDarkModeForAppPFN = nullptr;
IsDarkModeAllowedForWindowPtr IsDarkModeAllowedForWindowPFN = nullptr;
GetIsImmersiveColorUsingHighContrastPtr GetIsImmersiveColorUsingHighContrastPFN = nullptr;
RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyStatePFN = nullptr;
ShouldSystemUseDarkModePtr ShouldSystemUseDarkModePFN = nullptr;
SetPreferredAppModePtr SetPreferredAppModePFN = nullptr;
IsDarkModeAllowedForAppPtr IsDarkModeAllowedForAppPFN = nullptr;
GetDpiForMonitorPtr GetDpiForMonitorPFN = nullptr;
GetProcessDpiAwarenessPtr GetProcessDpiAwarenessPFN = nullptr;
GetSystemDpiForProcessPtr GetSystemDpiForProcessPFN = nullptr;
GetDpiForWindowPtr GetDpiForWindowPFN = nullptr;
GetDpiForSystemPtr GetDpiForSystemPFN = nullptr;
GetSystemMetricsForDpiPtr GetSystemMetricsForDpiPFN = nullptr;
AdjustWindowRectExForDpiPtr AdjustWindowRectExForDpiPFN = nullptr;
void load()
{
QLibrary User32Dll(QString::fromUtf8("User32"));
SetWindowCompositionAttributePFN = reinterpret_cast<SetWindowCompositionAttributePtr>(User32Dll.resolve("SetWindowCompositionAttribute"));
GetDpiForWindowPFN = reinterpret_cast<GetDpiForWindowPtr>(User32Dll.resolve("GetDpiForWindow"));
GetDpiForSystemPFN = reinterpret_cast<GetDpiForSystemPtr>(User32Dll.resolve("GetDpiForSystem"));
GetSystemMetricsForDpiPFN = reinterpret_cast<GetSystemMetricsForDpiPtr>(User32Dll.resolve("GetSystemMetricsForDpi"));
AdjustWindowRectExForDpiPFN = reinterpret_cast<AdjustWindowRectExForDpiPtr>(User32Dll.resolve("AdjustWindowRectExForDpi"));
GetSystemDpiForProcessPFN = reinterpret_cast<GetSystemDpiForProcessPtr>(User32Dll.resolve("GetSystemDpiForProcess"));
QLibrary UxThemeDll(QString::fromUtf8("UxTheme"));
ShouldAppsUseDarkModePFN = reinterpret_cast<ShouldAppsUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(132)));
AllowDarkModeForWindowPFN = reinterpret_cast<AllowDarkModeForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(133)));
AllowDarkModeForAppPFN = reinterpret_cast<AllowDarkModeForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
RefreshImmersiveColorPolicyStatePFN = reinterpret_cast<RefreshImmersiveColorPolicyStatePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(104)));
IsDarkModeAllowedForWindowPFN = reinterpret_cast<IsDarkModeAllowedForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(137)));
GetIsImmersiveColorUsingHighContrastPFN = reinterpret_cast<GetIsImmersiveColorUsingHighContrastPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(106)));
ShouldSystemUseDarkModePFN = reinterpret_cast<ShouldSystemUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(138)));
SetPreferredAppModePFN = reinterpret_cast<SetPreferredAppModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
IsDarkModeAllowedForAppPFN = reinterpret_cast<IsDarkModeAllowedForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(139)));
QLibrary SHCoreDll(QString::fromUtf8("SHCore"));
GetDpiForMonitorPFN = reinterpret_cast<GetDpiForMonitorPtr>(SHCoreDll.resolve("GetDpiForMonitor"));
GetProcessDpiAwarenessPFN = reinterpret_cast<GetProcessDpiAwarenessPtr>(SHCoreDll.resolve("GetProcessDpiAwareness"));
}
QScopedPointer<WinNativeEventFilter> m_instance;
};
} // namespace
Q_GLOBAL_STATIC(WNEF_CORE_DATA, coreData)
namespace {
const quint32 m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI;
const qreal m_defaultDevicePixelRatio = 1.0;
const char envVarUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
const char envVarPreserveWindowFrame[] = "WNEF_PRESERVE_WINDOW_FRAME";
const char envVarForceWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char envVarForceAcrylic[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
bool shouldUseNativeTitleBar()
static inline bool shouldUseNativeTitleBar()
{
return qEnvironmentVariableIsSet(envVarUseNativeTitleBar);
}
bool shouldHaveWindowFrame()
static inline bool shouldHaveWindowFrame()
{
if (shouldUseNativeTitleBar()) {
// We have to use the original window frame unconditionally if we
@ -332,161 +99,94 @@ bool shouldHaveWindowFrame()
if (should) {
// If you preserve the window frame on Win7~8.1,
// the window will have a terrible appearance.
return isWin10OrGreater();
return Utilities::isWin10OrGreater();
}
}
return false;
}
bool forceEnableAcrylicOnWin10()
{
return qEnvironmentVariableIsSet(envVarForceAcrylic);
}
bool isDwmCompositionEnabled()
{
// Since Win8, DWM composition is always enabled and can't be disabled.
// In other words, DwmIsCompositionEnabled will always return TRUE on
// systems newer than Win7.
BOOL enabled = FALSE;
return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled;
}
QWindow *findWindow(const HWND handle)
{
Q_ASSERT(handle);
const auto wid = reinterpret_cast<WId>(handle);
const QWindowList windows = QGuiApplication::topLevelWindows();
for (auto &&window : qAsConst(windows)) {
if (window && window->handle()) {
if (window->winId() == wid) {
return window;
}
}
}
return nullptr;
}
void triggerFrameChange(const QWindow *window)
{
Q_ASSERT(window);
SetWindowPos(reinterpret_cast<HWND>(window->winId()), nullptr, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
void updateFrameMargins(const QWindow *window, bool reset)
{
Q_ASSERT(window);
const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(window->winId()), &margins);
}
// The standard values of border width, border height and title bar height
// when DPI is 96.
const int m_defaultBorderWidth = 8, m_defaultBorderHeight = 8, m_defaultTitleBarHeight = 31;
// The thickness of an auto-hide taskbar in pixels.
const int kAutoHideTaskbarThicknessPx = 2;
const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
static const int kAutoHideTaskbarThicknessPx = 2;
static const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
const QString g_sDwmRegistryKey = QString::fromUtf8(
R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)");
const QString g_sPersonalizeRegistryKey = QString::fromUtf8(
R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
static QScopedPointer<FramelessHelperWin> g_instance;
const char m_framelessMode[] = "_WNEF_FRAMELESS_MODE_ENABLED";
const char m_borderWidth[] = "_WNEF_WINDOW_BORDER_WIDTH";
const char m_borderHeight[] = "_WNEF_WINDOW_BORDER_HEIGHT";
const char m_titleBarHeight[] = "_WNEF_TITLE_BAR_HEIGHT";
const char m_ignoredObjects[] = "_WNEF_TITLE_BAR_IGNORED_OBJECTS";
void setup()
static inline void setup()
{
if (coreData()->m_instance.isNull()) {
coreData()->m_instance.reset(new WinNativeEventFilter);
qApp->installNativeEventFilter(coreData()->m_instance.get());
if (g_instance.isNull()) {
g_instance.reset(new FramelessHelperWin);
qApp->installNativeEventFilter(g_instance.get());
}
}
void installHelper(QWindow *window, const bool enable)
static inline void installHelper(QWindow *window, const bool enable)
{
Q_ASSERT(window);
window->setProperty(m_framelessMode, enable);
const int tbh = enable ? WinNativeEventFilter::getSystemMetric(
window, WinNativeEventFilter::SystemMetric::TitleBarHeight, true, true)
: 0;
const QMargins margins = {0, -tbh, 0, 0};
const QVariant marginsVar = QVariant::fromValue(margins);
window->setProperty("_q_windowsCustomMargins", marginsVar);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPlatformWindow *platformWindow = window->handle();
if (platformWindow) {
QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow,
QString::fromUtf8(
"WindowsCustomMargins"),
marginsVar);
if (!window) {
return;
}
#else
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
window->handle());
if (platformWindow) {
platformWindow->setCustomMargins(margins);
}
#endif
updateFrameMargins(window, !enable);
triggerFrameChange(window);
window->setProperty(_flh_framelessMode_flag, enable);
Utilities::updateQtFrameMargins(window, enable);
Utilities::updateFrameMargins(window, !enable);
Utilities::triggerFrameChange(window);
}
} // namespace
FramelessHelperWin::FramelessHelperWin() = default;
WinNativeEventFilter::WinNativeEventFilter() = default;
WinNativeEventFilter::~WinNativeEventFilter()
FramelessHelperWin::~FramelessHelperWin()
{
if (!coreData()->m_instance.isNull()) {
qApp->removeNativeEventFilter(coreData()->m_instance.get());
if (!g_instance.isNull()) {
qApp->removeNativeEventFilter(g_instance.get());
}
}
void WinNativeEventFilter::addFramelessWindow(QWindow *window)
void FramelessHelperWin::addFramelessWindow(QWindow *window)
{
Q_ASSERT(window);
setup();
installHelper(window, true);
}
bool WinNativeEventFilter::isWindowFrameless(const QWindow *window)
bool FramelessHelperWin::isWindowFrameless(const QWindow *window)
{
Q_ASSERT(window);
return window->property(m_framelessMode).toBool();
if (!window) {
return false;
}
return window->property(_flh_framelessMode_flag).toBool();
}
void WinNativeEventFilter::removeFramelessWindow(QWindow *window)
void FramelessHelperWin::removeFramelessWindow(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
installHelper(window, false);
}
void WinNativeEventFilter::setIgnoredObjects(QWindow *window, const QObjectList &objects)
void FramelessHelperWin::setIgnoredObjects(QWindow *window, const QObjectList &objects)
{
Q_ASSERT(window);
window->setProperty(m_ignoredObjects, QVariant::fromValue(objects));
if (!window) {
return;
}
window->setProperty(_flh_ignoredObjects_flag, QVariant::fromValue(objects));
}
QObjectList WinNativeEventFilter::getIgnoredObjects(const QWindow *window)
QObjectList FramelessHelperWin::getIgnoredObjects(const QWindow *window)
{
Q_ASSERT(window);
return qvariant_cast<QObjectList>(window->property(m_ignoredObjects));
if (!window) {
return {};
}
return qvariant_cast<QObjectList>(window->property(_flh_ignoredObjects_flag));
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
void *message,
qintptr *result)
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
#else
bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
void *message,
long *result)
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
#endif
{
// "result" can't be null in theory and I don't see any projects check
@ -514,8 +214,8 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
// Anyway, we should skip it in this case.
return false;
}
const QWindow *window = findWindow(msg->hwnd);
if (!window || (window && !window->property(m_framelessMode).toBool())) {
const QWindow *window = Utilities::findWindow(reinterpret_cast<WId>(msg->hwnd));
if (!window || (window && !window->property(_flh_framelessMode_flag).toBool())) {
return false;
}
switch (msg->message) {
@ -625,11 +325,11 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
// The value of border width and border height should be
// identical in most cases, when the scale factor is 1.0, it
// should be eight pixels.
const int bh = getSystemMetric(window, SystemMetric::BorderHeight, true);
const int bh = getSystemMetric(window, Utilities::SystemMetric::BorderHeight, true);
clientRect->top += bh;
if (!shouldHaveWindowFrame()) {
clientRect->bottom -= bh;
const int bw = getSystemMetric(window, SystemMetric::BorderWidth, true);
const int bw = getSystemMetric(window, Utilities::SystemMetric::BorderWidth, true);
clientRect->left += bw;
clientRect->right -= bw;
}
@ -652,7 +352,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
// Due to ABM_GETAUTOHIDEBAREX only exists from Win8.1,
// we have to use another way to judge this if we are
// running on Windows 7 or Windows 8.
if (isWin8Point1OrGreater()) {
if (Utilities::isWin8Point1OrGreater()) {
MONITORINFO monitorInfo;
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
monitorInfo.cbSize = sizeof(monitorInfo);
@ -749,7 +449,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
case WM_NCPAINT: {
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
if (!isDwmCompositionEnabled() && !shouldHaveWindowFrame()) {
if (!Utilities::isBlurEffectSupported() && !shouldHaveWindowFrame()) {
// 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.
@ -763,7 +463,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
if (shouldHaveWindowFrame()) {
break;
} else {
if (isDwmCompositionEnabled()) {
if (Utilities::isBlurEffectSupported()) {
// DefWindowProc won't repaint the window border if lParam
// (normally a HRGN) is -1. See the following link's "lParam"
// section:
@ -870,6 +570,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
}
const auto mapOriginPointToWindow = [](const QObject *obj) -> QPointF {
Q_ASSERT(obj);
if (!obj) {
return {};
}
QPointF point = {obj->property("x").toReal(), obj->property("y").toReal()};
for (QObject *parent = obj->parent(); parent; parent = parent->parent()) {
point += {parent->property("x").toReal(), parent->property("y").toReal()};
@ -896,11 +599,10 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
const QPointF localMouse = {static_cast<qreal>(winLocalMouse.x),
static_cast<qreal>(winLocalMouse.y)};
const bool isInIgnoreObjects = isInSpecificObjects(globalMouse,
qvariant_cast<QObjectList>(
window->property(m_ignoredObjects)),
qvariant_cast<QObjectList>(window->property(_flh_ignoredObjects_flag)),
dpr);
const int bh = getSystemMetric(window, SystemMetric::BorderHeight, true);
const int tbh = getSystemMetric(window, SystemMetric::TitleBarHeight, true);
const int bh = getSystemMetric(window, Utilities::SystemMetric::BorderHeight, true);
const int tbh = getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, true);
const bool isTitleBar = (localMouse.y() <= tbh) && !isInIgnoreObjects;
const bool isTop = localMouse.y() <= bh;
if (shouldHaveWindowFrame()) {
@ -933,7 +635,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
GetClientRect(msg->hwnd, &clientRect);
const LONG ww = clientRect.right;
const LONG wh = clientRect.bottom;
const int bw = getSystemMetric(window, SystemMetric::BorderWidth, true);
const int bw = getSystemMetric(window, Utilities::SystemMetric::BorderWidth, true);
if (IsMaximized(msg->hwnd)) {
if (isTitleBar) {
return HTCAPTION;
@ -1006,10 +708,10 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
// Prevent Windows from drawing the default title bar by temporarily
// toggling the WS_VISIBLE style.
SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle & ~WS_VISIBLE);
triggerFrameChange(window);
Utilities::triggerFrameChange(window);
const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle);
triggerFrameChange(window);
Utilities::triggerFrameChange(window);
*result = ret;
return true;
}
@ -1019,199 +721,29 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
return false;
}
void WinNativeEventFilter::setBorderWidth(QWindow *window, const int bw)
void FramelessHelperWin::setBorderWidth(QWindow *window, const int bw)
{
Q_ASSERT(window);
window->setProperty(m_borderWidth, bw);
}
void WinNativeEventFilter::setBorderHeight(QWindow *window, const int bh)
{
Q_ASSERT(window);
window->setProperty(m_borderHeight, bh);
}
void WinNativeEventFilter::setTitleBarHeight(QWindow *window, const int tbh)
{
Q_ASSERT(window);
window->setProperty(m_titleBarHeight, tbh);
}
int WinNativeEventFilter::getSystemMetric(const QWindow *window,
const SystemMetric metric,
const bool dpiAware,
const bool forceSystemValue)
{
Q_ASSERT(window);
const qreal dpr = dpiAware ? window->devicePixelRatio() : m_defaultDevicePixelRatio;
const auto getSystemMetricsForWindow = [dpr](const int index, const bool dpiAware) -> int {
if (coreData()->GetSystemMetricsForDpiPFN) {
const quint32 dpi = dpiAware ? qRound(m_defaultDotsPerInch * dpr)
: m_defaultDotsPerInch;
return coreData()->GetSystemMetricsForDpiPFN(index, dpi);
} else {
const int value = GetSystemMetrics(index);
return dpiAware ? value : qRound(value / dpr);
}
};
int ret = 0;
switch (metric) {
case SystemMetric::BorderWidth: {
const int bw = window->property(m_borderWidth).toInt();
if ((bw > 0) && !forceSystemValue) {
ret = qRound(bw * dpr);
} else {
const int result_nondpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, false)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
const int result_dpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, true)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
const int result = dpiAware ? result_dpi : result_nondpi;
ret = result > 0 ? result : qRound(m_defaultBorderWidth * dpr);
}
} break;
case SystemMetric::BorderHeight: {
const int bh = window->property(m_borderHeight).toInt();
if ((bh > 0) && !forceSystemValue) {
ret = qRound(bh * dpr);
} else {
const int result_nondpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, false)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
const int result_dpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, true)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
const int result = dpiAware ? result_dpi : result_nondpi;
ret = result > 0 ? result : qRound(m_defaultBorderHeight * dpr);
}
} break;
case SystemMetric::TitleBarHeight: {
const int tbh = window->property(m_titleBarHeight).toInt();
if ((tbh > 0) && !forceSystemValue) {
// Special case: this is the user defined value,
// don't change it and just return it untouched.
return qRound(tbh * dpr);
} else {
const int result_nondpi = getSystemMetricsForWindow(SM_CYCAPTION, false);
const int result_dpi = getSystemMetricsForWindow(SM_CYCAPTION, true);
const int result = dpiAware ? result_dpi : result_nondpi;
ret = result > 0 ? result : qRound(m_defaultTitleBarHeight * dpr);
}
} break;
if (!window) {
return;
}
// When dpr = 1.0 (DPI = 96):
// SM_CXSIZEFRAME = SM_CYSIZEFRAME = 4px
// SM_CXPADDEDBORDER = 4px
// SM_CYCAPTION = 23px
// Border Width = Border Height = SM_C(X|Y)SIZEFRAME + SM_CXPADDEDBORDER = 8px
// Title Bar Height = Border Height + SM_CYCAPTION = 31px
// dpr = 1.25 --> Title Bar Height = 38px
// dpr = 1.5 --> Title Bar Height = 45px
// dpr = 1.75 --> Title Bar Height = 51px
ret += (metric == SystemMetric::TitleBarHeight)
? getSystemMetric(window, SystemMetric::BorderHeight, dpiAware)
: 0;
return ret;
window->setProperty(_flh_borderWidth_flag, bw);
}
bool WinNativeEventFilter::setBlurEffectEnabled(const QWindow *window,
const bool enabled,
const QColor &gradientColor)
void FramelessHelperWin::setBorderHeight(QWindow *window, const int bh)
{
Q_ASSERT(window);
const auto hwnd = reinterpret_cast<HWND>(window->winId());
// We prefer DwmEnableBlurBehindWindow on Windows 7.
if (isWin8OrGreater() && coreData()->SetWindowCompositionAttributePFN) {
ACCENT_POLICY accentPolicy;
SecureZeroMemory(&accentPolicy, sizeof(accentPolicy));
WINDOWCOMPOSITIONATTRIBDATA wcaData;
SecureZeroMemory(&wcaData, sizeof(wcaData));
wcaData.Attrib = WCA_ACCENT_POLICY;
wcaData.pvData = &accentPolicy;
wcaData.cbData = sizeof(accentPolicy);
if (enabled) {
if (isAcrylicEffectAvailable() || (isWin10OrGreater() && forceEnableAcrylicOnWin10())) {
accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
// The gradient color must be set otherwise it'll look
// like a classic blur. Use semi-transparent gradient
// color to get better appearance.
const QColor color = gradientColor.isValid() ? gradientColor : Qt::white;
accentPolicy.GradientColor = qRgba(color.blue(), color.green(), color.red(), color.alpha());
} else if (isWin10OrGreater()) {
// Windows 10
accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND;
} else {
// Windows 8 and 8.1.
accentPolicy.AccentState = ACCENT_ENABLE_TRANSPARENTGRADIENT;
}
} else {
accentPolicy.AccentState = ACCENT_DISABLED;
}
return coreData()->SetWindowCompositionAttributePFN(hwnd, &wcaData);
} else {
DWM_BLURBEHIND dwmBB;
SecureZeroMemory(&dwmBB, sizeof(dwmBB));
dwmBB.dwFlags = DWM_BB_ENABLE;
dwmBB.fEnable = enabled ? TRUE : FALSE;
return SUCCEEDED(DwmEnableBlurBehindWindow(hwnd, &dwmBB));
if (!window) {
return;
}
window->setProperty(_flh_borderHeight_flag, bh);
}
bool WinNativeEventFilter::isColorizationEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_sDwmRegistryKey, QSettings::NativeFormat);
const bool colorPrevalence
= registry.value(QString::fromUtf8("ColorPrevalence"), 0).toULongLong(&ok) != 0;
return (ok && colorPrevalence);
}
QColor WinNativeEventFilter::getColorizationColor()
{
DWORD color = 0;
BOOL opaqueBlend = FALSE;
return SUCCEEDED(DwmGetColorizationColor(&color, &opaqueBlend)) ? QColor::fromRgba(color) : Qt::darkGray;
}
bool WinNativeEventFilter::isLightThemeEnabled()
{
return !isDarkThemeEnabled();
}
bool WinNativeEventFilter::isDarkThemeEnabled()
{
return coreData()->ShouldSystemUseDarkModePFN ? coreData()->ShouldSystemUseDarkModePFN() : false;
}
bool WinNativeEventFilter::isHighContrastModeEnabled()
{
HIGHCONTRASTW hc;
SecureZeroMemory(&hc, sizeof(hc));
hc.cbSize = sizeof(hc);
return SystemParametersInfoW(SPI_GETHIGHCONTRAST, 0, &hc, 0) ? (hc.dwFlags & HCF_HIGHCONTRASTON) : false;
}
bool WinNativeEventFilter::isDarkFrameEnabled(const QWindow *window)
void FramelessHelperWin::setTitleBarHeight(QWindow *window, const int tbh)
{
Q_ASSERT(window);
if (!isWin10OrGreater(17763)) {
return false;
if (!window) {
return;
}
const auto hwnd = reinterpret_cast<HWND>(window->winId());
BOOL result = FALSE;
const bool ok = SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|| SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
return (ok && result);
}
bool WinNativeEventFilter::isTransparencyEffectEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_sPersonalizeRegistryKey, QSettings::NativeFormat);
const bool enableTransparency
= registry.value(QString::fromUtf8("EnableTransparency"), 0).toULongLong(&ok) != 0;
return (ok && enableTransparency);
window->setProperty(_flh_titleBarHeight_flag, tbh);
}

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -25,33 +25,20 @@
#pragma once
#include "framelesshelper_global.h"
#include <QAbstractNativeEventFilter>
#include <QColor>
#include <QObject>
#include <QtCore/qabstractnativeeventfilter.h>
#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class FRAMELESSHELPER_EXPORT WinNativeEventFilter : public QAbstractNativeEventFilter
class FRAMELESSHELPER_EXPORT FramelessHelperWin : public QAbstractNativeEventFilter
{
Q_DISABLE_COPY_MOVE(WinNativeEventFilter)
Q_DISABLE_COPY_MOVE(FramelessHelperWin)
public:
enum class SystemMetric { BorderWidth, BorderHeight, TitleBarHeight };
explicit WinNativeEventFilter();
~WinNativeEventFilter() override;
explicit FramelessHelperWin();
~FramelessHelperWin() override;
static void addFramelessWindow(QWindow *window);
static bool isWindowFrameless(const QWindow *window);
@ -64,38 +51,6 @@ public:
static void setBorderHeight(QWindow *window, const int bh);
static void setTitleBarHeight(QWindow *window, const int tbh);
static int getSystemMetric(const QWindow *window,
const SystemMetric metric,
const bool dpiAware,
const bool forceSystemValue = false);
// Enable or disable the blur effect for a specific window.
// On Win10 it's the Acrylic effect.
static bool setBlurEffectEnabled(const QWindow *window,
const bool enabled,
const QColor &gradientColor = Qt::white);
// Query whether colorization is enabled or not.
static bool isColorizationEnabled();
// Acquire the theme/colorization color set by the user.
static QColor getColorizationColor();
// Query whether the user is using the light theme or not.
static bool isLightThemeEnabled();
// Query whether the user is using the dark theme or not.
static bool isDarkThemeEnabled();
// Query whether the high contrast mode is enabled or not.
static bool isHighContrastModeEnabled();
// Query whether the given window is using dark frame or not.
static bool isDarkFrameEnabled(const QWindow *window);
// Query whether the transparency effect is enabled or not.
static bool isTransparencyEffectEnabled();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
#else

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -23,28 +23,17 @@
*/
#include "framelessquickhelper.h"
#include "framelesswindowsmanager.h"
#include <QQuickWindow>
#ifdef Q_OS_WINDOWS
#include "winnativeeventfilter.h"
#include <QOperatingSystemVersion>
#endif
#include <QtQuick/qquickwindow.h>
#include "utilities.h"
#ifdef Q_OS_WINDOWS
namespace {
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
} // namespace
#endif
// Windows only variables:
static const char g_preserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
static const char g_forceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent)
{
#ifdef Q_OS_WINDOWS
startTimer(500);
#endif
}
int FramelessQuickHelper::borderWidth() const
@ -91,47 +80,40 @@ void FramelessQuickHelper::setResizable(const bool val)
Q_EMIT resizableChanged(val);
}
#ifdef Q_OS_WINDOWS
bool FramelessQuickHelper::canHaveWindowFrame() const
{
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
}
bool FramelessQuickHelper::colorizationEnabled() const
{
return WinNativeEventFilter::isColorizationEnabled();
return Utilities::isColorizationEnabled();
}
QColor FramelessQuickHelper::colorizationColor() const
{
return WinNativeEventFilter::getColorizationColor();
return Utilities::getColorizationColor();
}
bool FramelessQuickHelper::lightThemeEnabled() const
{
return WinNativeEventFilter::isLightThemeEnabled();
return Utilities::isLightThemeEnabled();
}
bool FramelessQuickHelper::darkThemeEnabled() const
{
return WinNativeEventFilter::isDarkThemeEnabled();
return Utilities::isDarkThemeEnabled();
}
bool FramelessQuickHelper::highContrastModeEnabled() const
{
return WinNativeEventFilter::isHighContrastModeEnabled();
return Utilities::isHighContrastModeEnabled();
}
bool FramelessQuickHelper::darkFrameEnabled() const
{
return WinNativeEventFilter::isDarkFrameEnabled(window());
return Utilities::isDarkFrameEnabled(window());
}
bool FramelessQuickHelper::transparencyEffectEnabled() const
{
return WinNativeEventFilter::isTransparencyEffectEnabled();
return Utilities::isTransparencyEffectEnabled();
}
#endif
void FramelessQuickHelper::removeWindowFrame()
{
@ -141,10 +123,12 @@ void FramelessQuickHelper::removeWindowFrame()
void FramelessQuickHelper::addIgnoreObject(QQuickItem *val)
{
Q_ASSERT(val);
if (!val) {
return;
}
FramelessWindowsManager::addIgnoreObject(window(), val);
}
#ifdef Q_OS_WINDOWS
void FramelessQuickHelper::timerEvent(QTimerEvent *event)
{
QQuickItem::timerEvent(event);
@ -157,24 +141,12 @@ void FramelessQuickHelper::timerEvent(QTimerEvent *event)
Q_EMIT transparencyEffectEnabledChanged(transparencyEffectEnabled());
}
void FramelessQuickHelper::setWindowFrameVisible(const bool value)
{
if (value) {
qputenv(g_sPreserveWindowFrame, "1");
} else {
qunsetenv(g_sPreserveWindowFrame);
}
}
void FramelessQuickHelper::setBlurEffectEnabled(const bool enabled,
const bool forceAcrylic,
const QColor &gradientColor)
void FramelessQuickHelper::setAcrylicEffectEnabled(const bool enabled, const bool forceAcrylic, const QColor &gradientColor)
{
if (forceAcrylic) {
qputenv(g_sForceUseAcrylicEffect, "1");
qputenv(g_forceUseAcrylicEffect, "1");
} else {
qunsetenv(g_sForceUseAcrylicEffect);
qunsetenv(g_forceUseAcrylicEffect);
}
WinNativeEventFilter::setBlurEffectEnabled(window(), enabled, gradientColor);
Utilities::setAcrylicEffectEnabled(window(), enabled, gradientColor);
}
#endif

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -24,24 +24,10 @@
#pragma once
#include <QQuickItem>
#include "framelesshelper_global.h"
#include <QtQuick/qquickitem.h>
#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; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class FramelessQuickHelper : public QQuickItem
class FRAMELESSHELPER_EXPORT FramelessQuickHelper : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
@ -50,21 +36,15 @@ class FramelessQuickHelper : public QQuickItem
#endif
Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged)
Q_PROPERTY(int borderHeight READ borderHeight WRITE setBorderHeight NOTIFY borderHeightChanged)
Q_PROPERTY(
int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
Q_PROPERTY(int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged)
#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)
Q_PROPERTY(bool lightThemeEnabled READ lightThemeEnabled NOTIFY lightThemeEnabledChanged)
Q_PROPERTY(bool darkThemeEnabled READ darkThemeEnabled NOTIFY darkThemeEnabledChanged)
Q_PROPERTY(bool highContrastModeEnabled READ highContrastModeEnabled NOTIFY
highContrastModeEnabledChanged)
Q_PROPERTY(bool highContrastModeEnabled READ highContrastModeEnabled NOTIFY highContrastModeEnabledChanged)
Q_PROPERTY(bool darkFrameEnabled READ darkFrameEnabled NOTIFY darkFrameEnabledChanged)
Q_PROPERTY(bool transparencyEffectEnabled READ transparencyEffectEnabled NOTIFY
transparencyEffectEnabledChanged)
#endif
Q_PROPERTY(bool transparencyEffectEnabled READ transparencyEffectEnabled NOTIFY transparencyEffectEnabledChanged)
public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
@ -82,8 +62,6 @@ public:
bool resizable() const;
void setResizable(const bool val);
#ifdef Q_OS_WINDOWS
bool canHaveWindowFrame() const;
bool colorizationEnabled() const;
QColor colorizationColor() const;
bool lightThemeEnabled() const;
@ -91,31 +69,20 @@ public:
bool highContrastModeEnabled() const;
bool darkFrameEnabled() const;
bool transparencyEffectEnabled() const;
#endif
public Q_SLOTS:
void removeWindowFrame();
void addIgnoreObject(QQuickItem *val);
void setAcrylicEffectEnabled(const bool enabled = true, const bool forceAcrylic = false, const QColor &gradientColor = {});
#ifdef Q_OS_WINDOWS
void setWindowFrameVisible(const bool value = true);
void setBlurEffectEnabled(const bool enabled = true,
const bool forceAcrylic = false,
const QColor &gradientColor = Qt::white);
#endif
#ifdef Q_OS_WINDOWS
protected:
void timerEvent(QTimerEvent *event) override;
#endif
Q_SIGNALS:
void borderWidthChanged(int);
void borderHeightChanged(int);
void titleBarHeightChanged(int);
void resizableChanged(bool);
#ifdef Q_OS_WINDOWS
void colorizationEnabledChanged(bool);
void colorizationColorChanged(const QColor &);
void lightThemeEnabledChanged(bool);
@ -123,5 +90,4 @@ Q_SIGNALS:
void highContrastModeEnabledChanged(bool);
void darkFrameEnabledChanged(bool);
void transparencyEffectEnabledChanged(bool);
#endif
};

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -23,11 +23,10 @@
*/
#include "framelesswindowsmanager.h"
#include <QWindow>
#include <QtGui/qwindow.h>
#include "utilities.h"
#ifdef Q_OS_WINDOWS
#include "winnativeeventfilter.h"
#include "framelesshelper_win32.h"
#else
#include "framelesshelper.h"
#endif
@ -41,8 +40,11 @@ FramelessWindowsManager::FramelessWindowsManager() = default;
void FramelessWindowsManager::addWindow(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
WinNativeEventFilter::addFramelessWindow(const_cast<QWindow *>(window));
FramelessHelperWin::addFramelessWindow(const_cast<QWindow *>(window));
#else
framelessHelper()->removeWindowFrame(const_cast<QWindow *>(window));
#endif
@ -51,10 +53,13 @@ void FramelessWindowsManager::addWindow(const QWindow *window)
void FramelessWindowsManager::addIgnoreObject(const QWindow *window, QObject *object)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
QObjectList objects = WinNativeEventFilter::getIgnoredObjects(window);
QObjectList objects = FramelessHelperWin::getIgnoredObjects(window);
objects.append(object);
WinNativeEventFilter::setIgnoredObjects(const_cast<QWindow *>(window), objects);
FramelessHelperWin::setIgnoredObjects(const_cast<QWindow *>(window), objects);
#else
framelessHelper()->addIgnoreObject(window, object);
#endif
@ -64,11 +69,12 @@ int FramelessWindowsManager::getBorderWidth(const QWindow *window)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
return WinNativeEventFilter::getSystemMetric(window,
WinNativeEventFilter::SystemMetric::BorderWidth,
false);
if (!window) {
return 0;
}
return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderWidth, false);
#else
Q_UNUSED(window)
Q_UNUSED(window);
return framelessHelper()->getBorderWidth();
#endif
}
@ -77,9 +83,12 @@ void FramelessWindowsManager::setBorderWidth(const QWindow *window, const int va
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
WinNativeEventFilter::setBorderWidth(const_cast<QWindow *>(window), value);
if (!window) {
return;
}
FramelessHelperWin::setBorderWidth(const_cast<QWindow *>(window), value);
#else
Q_UNUSED(window)
Q_UNUSED(window);
framelessHelper()->setBorderWidth(value);
#endif
}
@ -88,11 +97,12 @@ int FramelessWindowsManager::getBorderHeight(const QWindow *window)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
return WinNativeEventFilter::getSystemMetric(window,
WinNativeEventFilter::SystemMetric::BorderHeight,
false);
if (!window) {
return 0;
}
return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderHeight, false);
#else
Q_UNUSED(window)
Q_UNUSED(window);
return framelessHelper()->getBorderHeight();
#endif
}
@ -101,9 +111,12 @@ void FramelessWindowsManager::setBorderHeight(const QWindow *window, const int v
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
WinNativeEventFilter::setBorderHeight(const_cast<QWindow *>(window), value);
if (!window) {
return;
}
FramelessHelperWin::setBorderHeight(const_cast<QWindow *>(window), value);
#else
Q_UNUSED(window)
Q_UNUSED(window);
framelessHelper()->setBorderHeight(value);
#endif
}
@ -112,11 +125,12 @@ int FramelessWindowsManager::getTitleBarHeight(const QWindow *window)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
return WinNativeEventFilter::getSystemMetric(window,
WinNativeEventFilter::SystemMetric::TitleBarHeight,
false);
if (!window) {
return 0;
}
return Utilities::getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, false);
#else
Q_UNUSED(window)
Q_UNUSED(window);
return framelessHelper()->getTitleBarHeight();
#endif
}
@ -125,9 +139,12 @@ void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
WinNativeEventFilter::setTitleBarHeight(const_cast<QWindow *>(window), value);
if (!window) {
return;
}
FramelessHelperWin::setTitleBarHeight(const_cast<QWindow *>(window), value);
#else
Q_UNUSED(window)
Q_UNUSED(window);
framelessHelper()->setTitleBarHeight(value);
#endif
}
@ -135,6 +152,9 @@ void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int
bool FramelessWindowsManager::getResizable(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return false;
}
#ifdef Q_OS_WINDOWS
if (window->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)) {
return false;
@ -153,6 +173,9 @@ bool FramelessWindowsManager::getResizable(const QWindow *window)
void FramelessWindowsManager::setResizable(const QWindow *window, const bool value)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
const_cast<QWindow *>(window)->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value);
#else

View File

@ -27,26 +27,11 @@
#include "framelesshelper_global.h"
#include <QRect>
#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
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class FRAMELESSHELPER_EXPORT FramelessWindowsManager
{
Q_DISABLE_COPY_MOVE(FramelessWindowsManager)

40
lib.pro
View File

@ -14,14 +14,42 @@ DEFINES += \
HEADERS += \
framelesshelper_global.h \
framelesshelper.h \
framelesswindowsmanager.h
framelesswindowsmanager.h \
utilities.h \
qtacryliceffecthelper.h
SOURCES += \
framelesshelper.cpp \
framelesswindowsmanager.cpp
framelesswindowsmanager.cpp \
utilities.cpp \
qtacryliceffecthelper.cpp
qtHaveModule(widgets) {
QT += widgets
HEADERS += qtacrylicwidget.h
SOURCES += qtacrylicwidget.cpp
}
qtHaveModule(quick) {
QT += quick
HEADERS += \
framelessquickhelper.h \
qtacrylicitem.h
SOURCES += \
framelessquickhelper.cpp \
qtacrylicitem.cpp
}
RESOURCES += qtacrylichelper.qrc
win32 {
DEFINES += WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS
HEADERS += winnativeeventfilter.h
SOURCES += winnativeeventfilter.cpp
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi -lshcore -ld2d1 -luxtheme
DEFINES += \
WIN32_LEAN_AND_MEAN \
_CRT_SECURE_NO_WARNINGS \
UNICODE \
_UNICODE
HEADERS += \
framelesshelper_win32.h \
qtacryliceffecthelper_win32.h
SOURCES += \
utilities_win32.cpp \
framelesshelper_win32.cpp \
qtacryliceffecthelper_win32.cpp
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi
RC_FILE = framelesshelper.rc
}

275
qtacryliceffecthelper.cpp Normal file
View File

@ -0,0 +1,275 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "qtacryliceffecthelper.h"
#include "utilities.h"
#include <QtGui/qpainter.h>
#include <QtCore/qdebug.h>
#include <QtGui/qwindow.h>
#include <QtCore/qcoreapplication.h>
QtAcrylicEffectHelper::QtAcrylicEffectHelper()
{
Q_INIT_RESOURCE(qtacrylichelper);
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#ifdef Q_OS_MACOS
if (Utilities::isAcrylicEffectSupported()) {
m_tintOpacity = 0.6;
}
#endif
m_frameColor = Utilities::getNativeWindowFrameColor(true);
}
void QtAcrylicEffectHelper::install(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
if (m_window != window) {
m_window = const_cast<QWindow *>(window);
}
}
void QtAcrylicEffectHelper::uninstall()
{
if (m_window) {
m_window = nullptr;
}
}
void QtAcrylicEffectHelper::clearWallpaper()
{
if (!m_bluredWallpaper.isNull()) {
m_bluredWallpaper = {};
}
}
QBrush QtAcrylicEffectHelper::getAcrylicBrush() const
{
return m_acrylicBrush;
}
QColor QtAcrylicEffectHelper::getTintColor() const
{
return m_tintColor;
}
qreal QtAcrylicEffectHelper::getTintOpacity() const
{
return m_tintOpacity;
}
qreal QtAcrylicEffectHelper::getNoiseOpacity() const
{
return m_noiseOpacity;
}
QPixmap QtAcrylicEffectHelper::getBluredWallpaper() const
{
return m_bluredWallpaper;
}
QColor QtAcrylicEffectHelper::getFrameColor() const
{
return m_frameColor;
}
void QtAcrylicEffectHelper::setTintColor(const QColor &value)
{
m_tintColor = value;
}
void QtAcrylicEffectHelper::setTintOpacity(qreal value)
{
m_tintOpacity = value;
}
void QtAcrylicEffectHelper::setNoiseOpacity(qreal value)
{
m_noiseOpacity = value;
}
void QtAcrylicEffectHelper::setFrameColor(const QColor &value)
{
m_frameColor = value;
}
QtAcrylicEffectHelper::~QtAcrylicEffectHelper() = default;
void QtAcrylicEffectHelper::paintWindowBackground(QPainter *painter, const QRegion &clip)
{
Q_ASSERT(painter);
Q_ASSERT(!clip.isEmpty());
if (!painter || clip.isEmpty() || !m_window) {
return;
}
painter->save();
painter->setClipRegion(clip);
paintBackground(painter, clip.boundingRect());
}
void QtAcrylicEffectHelper::paintWindowBackground(QPainter *painter, const QRect &rect)
{
Q_ASSERT(painter);
Q_ASSERT(rect.isValid());
if (!painter || !rect.isValid() || !m_window) {
return;
}
painter->save();
painter->setClipRegion({rect});
paintBackground(painter, rect);
}
void QtAcrylicEffectHelper::paintBackground(QPainter *painter, const QRect &rect)
{
Q_ASSERT(painter);
Q_ASSERT(rect.isValid());
if (!painter || !rect.isValid() || !m_window) {
return;
}
if (Utilities::isAcrylicEffectSupported()) {
const QPainter::CompositionMode mode = painter->compositionMode();
painter->setCompositionMode(QPainter::CompositionMode_Clear);
painter->fillRect(rect, Qt::white);
painter->setCompositionMode(mode);
} else {
// Emulate blur behind window by blurring the desktop wallpaper.
updateBehindWindowBackground();
painter->drawPixmap(QPoint{0, 0}, m_bluredWallpaper, rect);
}
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setOpacity(1);
painter->fillRect(rect, m_acrylicBrush);
painter->restore();
}
void QtAcrylicEffectHelper::paintWindowFrame(QPainter *painter, const QRect &rect)
{
Q_ASSERT(painter);
if (!painter || !m_window) {
return;
}
if (m_window->windowState() != Qt::WindowNoState) {
// We shouldn't draw the window frame when it's minimized/maximized/fullscreen.
return;
}
const int width = rect.isValid() ? rect.width() : m_window->width();
const int height = rect.isValid() ? rect.height() : m_window->height();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QList<QLine> lines = {
#else
const QVector<QLine> lines = {
#endif
{0, 0, width, 0},
{width - 1, 0, width - 1, height},
{width, height - 1, 0, height - 1},
{0, height, 0, 0}
};
const bool active = m_window->isActive();
const QColor color = (active && m_frameColor.isValid() && (m_frameColor != Qt::transparent)) ? m_frameColor : Utilities::getNativeWindowFrameColor(active);
painter->save();
painter->setPen({color, 1});
painter->drawLines(lines);
painter->restore();
}
void QtAcrylicEffectHelper::updateAcrylicBrush(const QColor &alternativeTintColor)
{
const auto getAppropriateTintColor = [&alternativeTintColor, this]() -> QColor {
if (alternativeTintColor.isValid() && (alternativeTintColor != Qt::transparent)) {
return alternativeTintColor;
}
if (m_tintColor.isValid() && (m_tintColor != Qt::transparent)) {
return m_tintColor;
}
return Qt::white;
};
static const QImage noiseTexture(QStringLiteral(":/QtAcrylicHelper/Noise.png"));
QImage acrylicTexture({64, 64}, QImage::Format_ARGB32_Premultiplied);
QColor fillColor = Qt::transparent;
#ifdef Q_OS_WINDOWS
if (!Utilities::isMSWin10AcrylicEffectAvailable()) {
// Add a soft light layer for the background.
fillColor = Qt::white;
fillColor.setAlpha(150);
}
#endif
acrylicTexture.fill(fillColor);
QPainter painter(&acrylicTexture);
painter.setOpacity(m_tintOpacity);
painter.fillRect(QRect{0, 0, acrylicTexture.width(), acrylicTexture.height()}, getAppropriateTintColor());
painter.setOpacity(m_noiseOpacity);
painter.fillRect(QRect{0, 0, acrylicTexture.width(), acrylicTexture.height()}, noiseTexture);
m_acrylicBrush = acrylicTexture;
}
void QtAcrylicEffectHelper::updateBehindWindowBackground()
{
if (!m_bluredWallpaper.isNull()) {
return;
}
const QSize size = Utilities::getScreenAvailableGeometry().size();
m_bluredWallpaper = QPixmap(size);
m_bluredWallpaper.fill(Qt::transparent);
QImage image = Utilities::getDesktopWallpaperImage();
// On some platforms we may not be able to get the desktop wallpaper, such as Linux and WebAssembly.
if (image.isNull()) {
return;
}
const Utilities::DesktopWallpaperAspectStyle aspectStyle = Utilities::getDesktopWallpaperAspectStyle();
QImage buffer(size, QImage::Format_ARGB32_Premultiplied);
#ifdef Q_OS_WINDOWS
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::Central) {
buffer.fill(Qt::black);
}
#endif
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::IgnoreRatio ||
aspectStyle == Utilities::DesktopWallpaperAspectStyle::KeepRatio ||
aspectStyle == Utilities::DesktopWallpaperAspectStyle::KeepRatioByExpanding) {
Qt::AspectRatioMode mode = Qt::KeepAspectRatioByExpanding;
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::IgnoreRatio) {
mode = Qt::IgnoreAspectRatio;
} else if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::KeepRatio) {
mode = Qt::KeepAspectRatio;
}
QSize newSize = image.size();
newSize.scale(size, mode);
image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
}
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::Tiled) {
QPainter painterBuffer(&buffer);
painterBuffer.fillRect(QRect{{0, 0}, size}, image);
} else {
QPainter painterBuffer(&buffer);
const QRect rect = Utilities::alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), {{0, 0}, size});
painterBuffer.drawImage(rect.topLeft(), image);
}
QPainter painter(&m_bluredWallpaper);
#if 1
Utilities::blurImage(&painter, buffer, 128, false, false);
#else
painter.drawImage(QPoint{0, 0}, buffer);
#endif
}

72
qtacryliceffecthelper.h Normal file
View File

@ -0,0 +1,72 @@
/*
* MIT License
*
* Copyright (C) 2021 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 <QtGui/qbrush.h>
class FRAMELESSHELPER_EXPORT QtAcrylicEffectHelper
{
Q_DISABLE_COPY_MOVE(QtAcrylicEffectHelper)
public:
explicit QtAcrylicEffectHelper();
~QtAcrylicEffectHelper();
void install(const QWindow *window);
void uninstall();
void clearWallpaper();
QBrush getAcrylicBrush() const;
QColor getTintColor() const;
qreal getTintOpacity() const;
qreal getNoiseOpacity() const;
QPixmap getBluredWallpaper() const;
QColor getFrameColor() const;
void setTintColor(const QColor &value);
void setTintOpacity(qreal value);
void setNoiseOpacity(qreal value);
void setFrameColor(const QColor &value);
void paintWindowBackground(QPainter *painter, const QRegion &clip);
void paintWindowBackground(QPainter *painter, const QRect &rect);
void paintWindowFrame(QPainter *painter, const QRect &rect = {});
void updateAcrylicBrush(const QColor &alternativeTintColor = {});
private:
void paintBackground(QPainter *painter, const QRect &rect);
void updateBehindWindowBackground();
private:
QWindow *m_window = nullptr;
QBrush m_acrylicBrush = {};
QColor m_tintColor = {};
qreal m_tintOpacity = 0.7;
qreal m_noiseOpacity = 0.04;
QPixmap m_bluredWallpaper = {};
QColor m_frameColor = {};
};

View File

@ -0,0 +1,126 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "qtacryliceffecthelper_win32.h"
#include "utilities.h"
#include <QtCore/qt_windows.h>
#include <QtCore/qcoreapplication.h>
#ifndef WM_DWMCOMPOSITIONCHANGED
// Only available since Windows Vista
#define WM_DWMCOMPOSITIONCHANGED 0x031E
#endif
#ifndef WM_DWMCOLORIZATIONCOLORCHANGED
// Only available since Windows Vista
#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
#endif
#ifndef WM_DPICHANGED
// Only available since Windows 8.1
#define WM_DPICHANGED 0x02E0
#endif
const int QtAcrylicWinUpdateEvent::QtAcrylicEffectChangeEventId = QEvent::registerEventType();
QtAcrylicWinUpdateEvent::QtAcrylicWinUpdateEvent(const bool clearWallpaper) : QEvent(static_cast<QEvent::Type>(QtAcrylicEffectChangeEventId))
{
m_shouldClearPreviousWallpaper = clearWallpaper;
}
QtAcrylicWinUpdateEvent::~QtAcrylicWinUpdateEvent() = default;
static QScopedPointer<QtAcrylicWinEventFilter> g_instance;
QtAcrylicWinEventFilter::QtAcrylicWinEventFilter() = default;
QtAcrylicWinEventFilter::~QtAcrylicWinEventFilter()
{
// FIXME
//unsetup();
}
void QtAcrylicWinEventFilter::setup()
{
if (g_instance.isNull()) {
g_instance.reset(new QtAcrylicWinEventFilter);
qApp->installNativeEventFilter(g_instance.get());
}
}
void QtAcrylicWinEventFilter::unsetup()
{
if (!g_instance.isNull()) {
qApp->removeNativeEventFilter(g_instance.get());
g_instance.reset();
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool QtAcrylicWinEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
#else
bool QtAcrylicWinEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
#endif
{
Q_ASSERT(eventType == "windows_generic_MSG");
Q_ASSERT(message);
Q_UNUSED(result); // We don't need this parameter.
if ((eventType != "windows_generic_MSG") || !message) {
return false;
}
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
const auto msg = *reinterpret_cast<MSG **>(message);
#else
const auto msg = static_cast<LPMSG>(message);
#endif
bool shouldUpdate = false;
bool shouldClearWallpaper = false;
switch (msg->message) {
case WM_SETTINGCHANGE: {
if (msg->wParam == SPI_SETDESKWALLPAPER) {
shouldClearWallpaper = true;
shouldUpdate = true;
}
if ((msg->wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(msg->lParam)) == QStringLiteral("ImmersiveColorSet"))) {
shouldUpdate = true;
}
} break;
case WM_THEMECHANGED:
case WM_DWMCOMPOSITIONCHANGED:
case WM_DWMCOLORIZATIONCOLORCHANGED:
case WM_DPICHANGED:
shouldUpdate = true;
break;
default :
break;
}
if (shouldUpdate) {
const QWindow *window = Utilities::findWindow(reinterpret_cast<WId>(msg->hwnd));
if (window) {
const QScopedPointer<QEvent> updateEvent(new QtAcrylicWinUpdateEvent(shouldClearWallpaper));
QCoreApplication::sendEvent(const_cast<QWindow *>(window), updateEvent.get());
}
}
return false;
}

View File

@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
* Copyright (C) 2021 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
@ -22,28 +22,41 @@
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
#pragma once
Button {
id: control
#include "framelesshelper_global.h"
#include <QtCore/qcoreevent.h>
#include <QtCore/qabstractnativeeventfilter.h>
implicitWidth: 45
implicitHeight: 30
class FRAMELESSHELPER_EXPORT QtAcrylicWinUpdateEvent : public QEvent
{
public:
static const int QtAcrylicEffectChangeEventId;
property bool maximized: false
explicit QtAcrylicWinUpdateEvent(const bool clearWallpaper = false);
~QtAcrylicWinUpdateEvent() override;
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize")
contentItem: Image {
anchors.fill: control
source: maximized ? "qrc:/images/button_restore_white.svg" : "qrc:/images/button_maximize_white.svg"
inline bool shouldClearPreviousWallpaper() const
{
return m_shouldClearPreviousWallpaper;
}
background: Rectangle {
visible: control.down || control.hovered
color: control.down ? "#808080" : (control.hovered ? "#c7c7c7" : "transparent")
}
}
private:
bool m_shouldClearPreviousWallpaper = false;
};
class FRAMELESSHELPER_EXPORT QtAcrylicWinEventFilter : public QAbstractNativeEventFilter
{
public:
explicit QtAcrylicWinEventFilter();
~QtAcrylicWinEventFilter() override;
static void setup();
static void unsetup();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
#else
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
#endif
};

5
qtacrylichelper.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/QtAcrylicHelper">
<file alias="Noise.png">resources/noise.png</file>
</qresource>
</RCC>

138
qtacrylicitem.cpp Normal file
View File

@ -0,0 +1,138 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "qtacrylicitem.h"
#include <QtQuick/qquickwindow.h>
#include "utilities.h"
#include <QtCore/qdebug.h>
#include <QtGui/qpainter.h>
QtAcrylicItem::QtAcrylicItem(QQuickItem *parent) : QQuickPaintedItem(parent)
{
connect(this, &QtAcrylicItem::windowChanged, this, [this](QQuickWindow *win){
m_acrylicHelper.uninstall();
if (win) {
m_acrylicHelper.install(win);
m_acrylicHelper.updateAcrylicBrush();
}
});
}
QtAcrylicItem::~QtAcrylicItem() = default;
void QtAcrylicItem::paint(QPainter *painter)
{
const QRect rect = {0, 0, qRound(width()), qRound(height())};
m_acrylicHelper.paintWindowBackground(painter, rect);
if (m_frameVisible) {
m_acrylicHelper.paintWindowFrame(painter, rect);
}
}
QColor QtAcrylicItem::tintColor() const
{
const QColor color = m_acrylicHelper.getTintColor();
if (color.isValid() && (color != Qt::transparent)) {
return color;
} else {
return /*palette().color(backgroundRole())*/Qt::white;
}
}
void QtAcrylicItem::setTintColor(const QColor &value)
{
if (!value.isValid()) {
qWarning() << "Tint color not valid.";
return;
}
if (m_acrylicHelper.getTintColor() != value) {
m_acrylicHelper.setTintColor(value);
#if 0
QPalette pal = palette();
pal.setColor(backgroundRole(), m_acrylicHelper.getTintColor());
setPalette(pal);
#endif
m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
Q_EMIT tintColorChanged();
}
}
qreal QtAcrylicItem::tintOpacity() const
{
return m_acrylicHelper.getTintOpacity();
}
void QtAcrylicItem::setTintOpacity(qreal value)
{
if (m_acrylicHelper.getTintOpacity() != value) {
m_acrylicHelper.setTintOpacity(value);
m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
Q_EMIT tintOpacityChanged();
}
}
qreal QtAcrylicItem::noiseOpacity() const
{
return m_acrylicHelper.getNoiseOpacity();
}
void QtAcrylicItem::setNoiseOpacity(qreal value)
{
if (m_acrylicHelper.getNoiseOpacity() != value) {
m_acrylicHelper.setNoiseOpacity(value);
m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
Q_EMIT noiseOpacityChanged();
}
}
bool QtAcrylicItem::frameVisible() const
{
return m_frameVisible;
}
void QtAcrylicItem::setFrameVisible(bool value)
{
if (m_frameVisible != value) {
m_frameVisible = value;
update();
Q_EMIT frameVisibleChanged();
}
}
QColor QtAcrylicItem::frameColor() const
{
return m_acrylicHelper.getFrameColor();
}
void QtAcrylicItem::setFrameColor(const QColor &value)
{
if (m_acrylicHelper.getFrameColor() != value) {
m_acrylicHelper.setFrameColor(value);
update();
Q_EMIT frameColorChanged();
}
}

75
qtacrylicitem.h Normal file
View File

@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (C) 2021 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 <QtQuick/qquickpainteditem.h>
#include "qtacryliceffecthelper.h"
class FRAMELESSHELPER_EXPORT QtAcrylicItem : public QQuickPaintedItem
{
Q_OBJECT
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
QML_NAMED_ELEMENT(AcrylicItem)
#endif
Q_DISABLE_COPY_MOVE(QtAcrylicItem)
Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged)
Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged)
Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged)
Q_PROPERTY(bool frameVisible READ frameVisible WRITE setFrameVisible NOTIFY frameVisibleChanged)
Q_PROPERTY(QColor frameColor READ frameColor WRITE setFrameColor NOTIFY frameColorChanged)
public:
explicit QtAcrylicItem(QQuickItem *parent = nullptr);
~QtAcrylicItem() override;
void paint(QPainter *painter) override;
QColor tintColor() const;
void setTintColor(const QColor &value);
qreal tintOpacity() const;
void setTintOpacity(qreal value);
qreal noiseOpacity() const;
void setNoiseOpacity(qreal value);
bool frameVisible() const;
void setFrameVisible(bool value);
QColor frameColor() const;
void setFrameColor(const QColor &value);
Q_SIGNALS:
void tintColorChanged();
void tintOpacityChanged();
void noiseOpacityChanged();
void frameVisibleChanged();
void frameColorChanged();
private:
QtAcrylicEffectHelper m_acrylicHelper;
bool m_frameVisible = true;
};

166
qtacrylicwidget.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "qtacrylicwidget.h"
#include "utilities.h"
#include "framelesswindowsmanager.h"
#include <QtCore/qdebug.h>
#include <QtGui/qevent.h>
#include <QtGui/qpainter.h>
QtAcrylicWidget::QtAcrylicWidget(QWidget *parent) : QWidget(parent)
{
setAutoFillBackground(false);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_OpaquePaintEvent);
setBackgroundRole(QPalette::Base);
}
QtAcrylicWidget::~QtAcrylicWidget() = default;
QColor QtAcrylicWidget::tintColor() const
{
const QColor color = m_acrylicHelper.getTintColor();
if (color.isValid() && (color != Qt::transparent)) {
return color;
} else {
return palette().color(backgroundRole());
}
}
void QtAcrylicWidget::setTintColor(const QColor &value)
{
if (!value.isValid()) {
qWarning() << "Tint color not valid.";
return;
}
if (m_acrylicHelper.getTintColor() != value) {
m_acrylicHelper.setTintColor(value);
QPalette pal = palette();
pal.setColor(backgroundRole(), m_acrylicHelper.getTintColor());
setPalette(pal);
//m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
Q_EMIT tintColorChanged();
}
}
qreal QtAcrylicWidget::tintOpacity() const
{
return m_acrylicHelper.getTintOpacity();
}
void QtAcrylicWidget::setTintOpacity(qreal value)
{
if (m_acrylicHelper.getTintOpacity() != value) {
m_acrylicHelper.setTintOpacity(value);
m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
Q_EMIT tintOpacityChanged();
}
}
qreal QtAcrylicWidget::noiseOpacity() const
{
return m_acrylicHelper.getNoiseOpacity();
}
void QtAcrylicWidget::setNoiseOpacity(qreal value)
{
if (m_acrylicHelper.getNoiseOpacity() != value) {
m_acrylicHelper.setNoiseOpacity(value);
m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
Q_EMIT noiseOpacityChanged();
}
}
bool QtAcrylicWidget::frameVisible() const
{
return m_frameVisible;
}
void QtAcrylicWidget::setFrameVisible(bool value)
{
if (m_frameVisible != value) {
m_frameVisible = value;
update();
Q_EMIT frameVisibleChanged();
}
}
QColor QtAcrylicWidget::frameColor() const
{
return m_acrylicHelper.getFrameColor();
}
void QtAcrylicWidget::setFrameColor(const QColor &value)
{
if (m_acrylicHelper.getFrameColor() != value) {
m_acrylicHelper.setFrameColor(value);
update();
Q_EMIT frameColorChanged();
}
}
void QtAcrylicWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
static bool inited = false;
if (!inited) {
const QWindow *win = windowHandle();
FramelessWindowsManager::addWindow(win);
Utilities::setAcrylicEffectEnabled(win, true);
m_acrylicHelper.install(win);
m_acrylicHelper.updateAcrylicBrush(tintColor());
update();
inited = true;
}
}
void QtAcrylicWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
m_acrylicHelper.paintWindowBackground(&painter, event->region());
if (frameVisible()) {
m_acrylicHelper.paintWindowFrame(&painter);
}
QWidget::paintEvent(event);
}
void QtAcrylicWidget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
switch (event->type()) {
case QEvent::PaletteChange:
m_acrylicHelper.updateAcrylicBrush(tintColor());
break;
case QEvent::ActivationChange:
case QEvent::WindowStateChange:
update();
break;
default:
break;
}
}

75
qtacrylicwidget.h Normal file
View File

@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (C) 2021 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 <QtWidgets/qwidget.h>
#include "qtacryliceffecthelper.h"
class FRAMELESSHELPER_EXPORT QtAcrylicWidget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QtAcrylicWidget)
Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged)
Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged)
Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged)
Q_PROPERTY(bool frameVisible READ frameVisible WRITE setFrameVisible NOTIFY frameVisibleChanged)
Q_PROPERTY(QColor frameColor READ frameColor WRITE setFrameColor NOTIFY frameColorChanged)
public:
explicit QtAcrylicWidget(QWidget *parent = nullptr);
~QtAcrylicWidget() override;
QColor tintColor() const;
void setTintColor(const QColor &value);
qreal tintOpacity() const;
void setTintOpacity(qreal value);
qreal noiseOpacity() const;
void setNoiseOpacity(qreal value);
bool frameVisible() const;
void setFrameVisible(bool value);
QColor frameColor() const;
void setFrameColor(const QColor &value);
Q_SIGNALS:
void tintColorChanged();
void tintOpacityChanged();
void noiseOpacityChanged();
void frameVisibleChanged();
void frameColorChanged();
protected:
void showEvent(QShowEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void changeEvent(QEvent *event) override;
private:
QtAcrylicEffectHelper m_acrylicHelper;
bool m_frameVisible = true;
};

BIN
resources/noise.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

498
utilities.cpp Normal file
View File

@ -0,0 +1,498 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#include "utilities.h"
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qscreen.h>
#include <QtGui/qpa/qplatformwindow.h>
#include <QtGui/qpainter.h>
#include <QtGui/private/qmemrotate_p.h>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
#include <QtGui/qpa/qplatformnativeinterface.h>
#else
#include <QtGui/qpa/qplatformwindow_p.h>
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
#include <QtCore/qoperatingsystemversion.h>
#else
#include <QtCore/qsysinfo.h>
#endif
Q_DECLARE_METATYPE(QMargins)
/*
* Copied from https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/effects/qpixmapfilter.cpp
* With minor modifications, most of them are format changes.
*/
#ifndef AVG
#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
#endif
#ifndef AVG16
#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
#endif
template<const int shift>
static inline int qt_static_shift(const int value)
{
if (shift == 0) {
return value;
} else if (shift > 0) {
return value << (uint(shift) & 0x1f);
} else {
return value >> (uint(-shift) & 0x1f);
}
}
template<const int aprec, const int zprec>
static inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, const int alpha)
{
QRgb *pixel = reinterpret_cast<QRgb *>(bptr);
#define Z_MASK (0xff << zprec)
const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
#undef Z_MASK
const int zR_zprec = zR >> aprec;
const int zG_zprec = zG >> aprec;
const int zB_zprec = zB >> aprec;
const int zA_zprec = zA >> aprec;
zR += alpha * (R_zprec - zR_zprec);
zG += alpha * (G_zprec - zG_zprec);
zB += alpha * (B_zprec - zB_zprec);
zA += alpha * (A_zprec - zA_zprec);
#define ZA_MASK (0xff << (zprec + aprec))
*pixel =
qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
| qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
| qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
| qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
#undef ZA_MASK
}
static const int alphaIndex = ((QSysInfo::ByteOrder == QSysInfo::BigEndian) ? 0 : 3);
template<const int aprec, const int zprec>
static inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, const int alpha)
{
const int A_zprec = int(*(bptr)) << zprec;
const int z_zprec = z >> aprec;
z += alpha * (A_zprec - z_zprec);
*(bptr) = z >> (zprec + aprec);
}
template<const int aprec, const int zprec, const bool alphaOnly>
static inline void qt_blurrow(QImage &im, const int line, const int alpha)
{
uchar *bptr = im.scanLine(line);
int zR = 0, zG = 0, zB = 0, zA = 0;
if (alphaOnly && (im.format() != QImage::Format_Indexed8)) {
bptr += alphaIndex;
}
const int stride = im.depth() >> 3;
const int im_width = im.width();
for (int index = 0; index < im_width; ++index) {
if (alphaOnly) {
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
} else {
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
}
bptr += stride;
}
bptr -= stride;
for (int index = im_width - 2; index >= 0; --index) {
bptr -= stride;
if (alphaOnly) {
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
} else {
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
}
}
}
/*
* expblur(QImage &img, const qreal radius)
*
* Based on exponential blur algorithm by Jani Huhtanen
*
* In-place blur of image 'img' with kernel
* of approximate radius 'radius'.
*
* Blurs with two sided exponential impulse
* response.
*
* aprec = precision of alpha parameter
* in fixed-point format 0.aprec
*
* zprec = precision of state parameters
* zR,zG,zB and zA in fp format 8.zprec
*/
template<const int aprec, const int zprec, const bool alphaOnly>
static inline void expblur(QImage &img, const qreal radius, const bool improvedQuality = false, const int transposed = 0)
{
qreal _radius = radius;
// halve the radius if we're using two passes
if (improvedQuality) {
_radius *= 0.5;
}
Q_ASSERT((img.format() == QImage::Format_ARGB32_Premultiplied)
|| (img.format() == QImage::Format_RGB32)
|| (img.format() == QImage::Format_Indexed8)
|| (img.format() == QImage::Format_Grayscale8));
// choose the alpha such that pixels at radius distance from a fully
// saturated pixel will have an alpha component of no greater than
// the cutOffIntensity
const qreal cutOffIntensity = 2;
const int alpha = _radius <= qreal(1e-5)
? ((1 << aprec)-1)
: qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / _radius)));
int img_height = img.height();
for (int row = 0; row < img_height; ++row) {
for (int i = 0; i <= int(improvedQuality); ++i) {
qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
}
}
QImage temp(img.height(), img.width(), img.format());
temp.setDevicePixelRatio(img.devicePixelRatio());
if (transposed >= 0) {
if (img.depth() == 8) {
qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint8*>(temp.bits()),
temp.bytesPerLine());
} else {
qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint32*>(temp.bits()),
temp.bytesPerLine());
}
} else {
if (img.depth() == 8) {
qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint8*>(temp.bits()),
temp.bytesPerLine());
} else {
qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint32*>(temp.bits()),
temp.bytesPerLine());
}
}
img_height = temp.height();
for (int row = 0; row < img_height; ++row) {
for (int i = 0; i <= int(improvedQuality); ++i) {
qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
}
}
if (transposed == 0) {
if (img.depth() == 8) {
qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
temp.width(), temp.height(), temp.bytesPerLine(),
reinterpret_cast<quint8*>(img.bits()),
img.bytesPerLine());
} else {
qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
temp.width(), temp.height(), temp.bytesPerLine(),
reinterpret_cast<quint32*>(img.bits()),
img.bytesPerLine());
}
} else {
img = temp;
}
}
static inline QImage qt_halfScaled(const QImage &source)
{
if (source.width() < 2 || source.height() < 2) {
return {};
}
QImage srcImage = source;
if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
// assumes grayscale
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
dest.setDevicePixelRatio(source.devicePixelRatio());
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
qsizetype sx = srcImage.bytesPerLine();
qsizetype sx2 = sx << 1;
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
qsizetype dx = dest.bytesPerLine();
int ww = dest.width();
int hh = dest.height();
for (int y = hh; y; --y, dst += dx, src += sx2) {
const uchar *p1 = src;
const uchar *p2 = src + sx;
uchar *q = dst;
for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2) {
*q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
}
}
return dest;
} else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
dest.setDevicePixelRatio(source.devicePixelRatio());
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
qsizetype sx = srcImage.bytesPerLine();
qsizetype sx2 = sx << 1;
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
qsizetype dx = dest.bytesPerLine();
int ww = dest.width();
int hh = dest.height();
for (int y = hh; y; --y, dst += dx, src += sx2) {
const uchar *p1 = src;
const uchar *p2 = src + sx;
uchar *q = dst;
for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
// alpha
q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
// rgb
const quint16 p16_1 = (p1[2] << 8) | p1[1];
const quint16 p16_2 = (p1[5] << 8) | p1[4];
const quint16 p16_3 = (p2[2] << 8) | p2[1];
const quint16 p16_4 = (p2[5] << 8) | p2[4];
const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
q[1] = result & 0xff;
q[2] = result >> 8;
}
}
return dest;
} else if ((source.format() != QImage::Format_ARGB32_Premultiplied) && (source.format() != QImage::Format_RGB32)) {
srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
}
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
dest.setDevicePixelRatio(source.devicePixelRatio());
const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
qsizetype sx = srcImage.bytesPerLine() >> 2;
qsizetype sx2 = sx << 1;
quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
qsizetype dx = dest.bytesPerLine() >> 2;
int ww = dest.width();
int hh = dest.height();
for (int y = hh; y; --y, dst += dx, src += sx2) {
const quint32 *p1 = src;
const quint32 *p2 = src + sx;
quint32 *q = dst;
for (int x = ww; x; --x, q++, p1 += 2, p2 += 2) {
*q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
}
}
return dest;
}
void Utilities::blurImage(QPainter *painter, QImage &blurImage, const qreal radius, const bool quality, const bool alphaOnly, const int transposed)
{
if ((blurImage.format() != QImage::Format_ARGB32_Premultiplied) && (blurImage.format() != QImage::Format_RGB32)) {
blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
}
qreal _radius = radius;
qreal scale = 1;
if ((_radius >= 4) && (blurImage.width() >= 2) && (blurImage.height() >= 2)) {
blurImage = qt_halfScaled(blurImage);
scale = 2;
_radius *= 0.5;
}
if (alphaOnly) {
expblur<12, 10, true>(blurImage, _radius, quality, transposed);
} else {
expblur<12, 10, false>(blurImage, _radius, quality, transposed);
}
if (painter) {
painter->scale(scale, scale);
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->drawImage(QRect{QPoint{0, 0}, blurImage.size() / blurImage.devicePixelRatio()}, blurImage);
}
}
void Utilities::blurImage(QImage &blurImage, const qreal radius, const bool quality, const int transposed)
{
if ((blurImage.format() == QImage::Format_Indexed8) || (blurImage.format() == QImage::Format_Grayscale8)) {
expblur<12, 10, true>(blurImage, radius, quality, transposed);
} else {
expblur<12, 10, false>(blurImage, radius, quality, transposed);
}
}
///////////////////////////////////////////////////
/*
* Copied from https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/styles/qstyle.cpp
* With minor modifications, most of them are format changes.
*/
static inline Qt::Alignment visualAlignment(const Qt::LayoutDirection direction, const Qt::Alignment alignment)
{
return QGuiApplicationPrivate::visualAlignment(direction, alignment);
}
QRect Utilities::alignedRect(const Qt::LayoutDirection direction, const Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
{
const Qt::Alignment align = visualAlignment(direction, alignment);
int x = rectangle.x();
int y = rectangle.y();
const int w = size.width();
const int h = size.height();
if ((align & Qt::AlignVCenter) == Qt::AlignVCenter) {
y += rectangle.size().height() / 2 - h / 2;
} else if ((align & Qt::AlignBottom) == Qt::AlignBottom) {
y += rectangle.size().height() - h;
}
if ((align & Qt::AlignRight) == Qt::AlignRight) {
x += rectangle.size().width() - w;
} else if ((align & Qt::AlignHCenter) == Qt::AlignHCenter) {
x += rectangle.size().width() / 2 - w / 2;
}
return {x, y, w, h};
}
///////////////////////////////////////////////////
bool Utilities::isWin7OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows7;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS7;
#endif
}
bool Utilities::isWin8OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8;
#endif
}
bool Utilities::isWin8Point1OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8_1;
#endif
}
bool Utilities::isWin10OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
#else
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
}
bool Utilities::isWin10OrGreater(const int subVer)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, subVer);
#else
Q_UNUSED(ver);
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
#endif
}
bool Utilities::isMSWin10AcrylicEffectAvailable()
{
if (!isWin10OrGreater()) {
return false;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
const QOperatingSystemVersion currentVersion = QOperatingSystemVersion::current();
if (currentVersion > QOperatingSystemVersion::Windows10) {
return true;
}
return ((currentVersion.microVersion() >= 16190) && (currentVersion.microVersion() < 18362));
#else
// TODO
return false;
#endif
}
QWindow *Utilities::findWindow(const WId winId)
{
Q_ASSERT(winId);
if (!winId) {
return nullptr;
}
const QWindowList windows = QGuiApplication::topLevelWindows();
for (auto &&window : qAsConst(windows)) {
if (window && window->handle()) {
if (window->winId() == winId) {
return window;
}
}
}
return nullptr;
}
bool Utilities::isAcrylicEffectSupported()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
return isWin10OrGreater() || (QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXYosemite);
#else
// TODO
return false;
#endif
}
void Utilities::updateQtFrameMargins(QWindow *window, const bool enable)
{
Q_ASSERT(window);
if (!window) {
return;
}
const int tbh = enable ? Utilities::getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, true, true) : 0;
const QMargins margins = {0, -tbh, 0, 0};
const QVariant marginsVar = QVariant::fromValue(margins);
window->setProperty("_q_windowsCustomMargins", marginsVar);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPlatformWindow *platformWindow = window->handle();
if (platformWindow) {
QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow, QStringLiteral("WindowsCustomMargins"), marginsVar);
}
#else
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
window->handle());
if (platformWindow) {
platformWindow->setCustomMargins(margins);
}
#endif
}
QRect Utilities::getScreenAvailableGeometry()
{
return QGuiApplication::primaryScreen()->availableGeometry();
}
QColor Utilities::getNativeWindowFrameColor(const bool isActive)
{
if (!isActive) {
return Qt::darkGray;
}
// TODO: what about Linux and macOS?
return isWin10OrGreater() ? getColorizationColor() : Qt::black;
}

80
utilities.h Normal file
View File

@ -0,0 +1,80 @@
/*
* MIT License
*
* Copyright (C) 2021 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 <QtGui/qcolor.h>
#include <QtGui/qwindow.h>
namespace Utilities {
enum class SystemMetric
{
BorderWidth,
BorderHeight,
TitleBarHeight
};
enum class DesktopWallpaperAspectStyle
{
Central,
Tiled,
IgnoreRatio,
KeepRatio,
KeepRatioByExpanding
};
FRAMELESSHELPER_EXPORT bool isWin7OrGreater();
FRAMELESSHELPER_EXPORT bool isWin8OrGreater();
FRAMELESSHELPER_EXPORT bool isWin8Point1OrGreater();
FRAMELESSHELPER_EXPORT bool isWin10OrGreater();
FRAMELESSHELPER_EXPORT bool isWin10OrGreater(const int subVer);
FRAMELESSHELPER_EXPORT bool isMSWin10AcrylicEffectAvailable();
FRAMELESSHELPER_EXPORT bool isBlurEffectSupported();
FRAMELESSHELPER_EXPORT bool isAcrylicEffectSupported();
FRAMELESSHELPER_EXPORT int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue = false);
FRAMELESSHELPER_EXPORT bool setAcrylicEffectEnabled(const QWindow *window, const bool enabled, const QColor &gradientColor = {});
FRAMELESSHELPER_EXPORT bool isColorizationEnabled();
FRAMELESSHELPER_EXPORT QColor getColorizationColor();
FRAMELESSHELPER_EXPORT bool isLightThemeEnabled();
FRAMELESSHELPER_EXPORT bool isDarkThemeEnabled();
FRAMELESSHELPER_EXPORT bool isHighContrastModeEnabled();
FRAMELESSHELPER_EXPORT bool isDarkFrameEnabled(const QWindow *window);
FRAMELESSHELPER_EXPORT bool isTransparencyEffectEnabled();
FRAMELESSHELPER_EXPORT QWindow *findWindow(const WId winId);
FRAMELESSHELPER_EXPORT void triggerFrameChange(const QWindow *window);
FRAMELESSHELPER_EXPORT void updateFrameMargins(const QWindow *window, const bool reset);
FRAMELESSHELPER_EXPORT void updateQtFrameMargins(QWindow *window, const bool enable);
FRAMELESSHELPER_EXPORT QImage getDesktopWallpaperImage(const int screen = -1);
FRAMELESSHELPER_EXPORT DesktopWallpaperAspectStyle getDesktopWallpaperAspectStyle(const int screen = -1);
FRAMELESSHELPER_EXPORT quint32 getWindowDpi(const QWindow *window);
FRAMELESSHELPER_EXPORT QMargins getWindowNativeFrameMargins(const QWindow *window);
FRAMELESSHELPER_EXPORT QRect getScreenAvailableGeometry();
FRAMELESSHELPER_EXPORT QRect alignedRect(const Qt::LayoutDirection direction, const Qt::Alignment alignment, const QSize &size, const QRect &rectangle);
FRAMELESSHELPER_EXPORT void blurImage(QImage &blurImage, const qreal radius, const bool quality, const int transposed = 0);
FRAMELESSHELPER_EXPORT void blurImage(QPainter *painter, QImage &blurImage, const qreal radius, const bool quality, const bool alphaOnly, const int transposed = 0);
FRAMELESSHELPER_EXPORT QColor getNativeWindowFrameColor(const bool isActive = true);
}

540
utilities_win32.cpp Normal file
View File

@ -0,0 +1,540 @@
/*
* MIT License
*
* Copyright (C) 2021 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.
*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "utilities.h"
#include <QtCore/qsettings.h>
#include <QtCore/qlibrary.h>
#include <QtCore/qt_windows.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <dwmapi.h>
#ifndef USER_DEFAULT_SCREEN_DPI
// Only available since Windows Vista
#define USER_DEFAULT_SCREEN_DPI 96
#endif
#ifndef SM_CXPADDEDBORDER
// Only available since Windows Vista
#define SM_CXPADDEDBORDER 92
#endif
enum : WORD
{
DwmwaUseImmersiveDarkMode = 20,
DwmwaUseImmersiveDarkModeBefore20h1 = 19
};
using WINDOWCOMPOSITIONATTRIB = enum _WINDOWCOMPOSITIONATTRIB
{
WCA_ACCENT_POLICY = 19
};
using WINDOWCOMPOSITIONATTRIBDATA = struct _WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using ACCENT_STATE = enum _ACCENT_STATE
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
ACCENT_INVALID_STATE = 5
};
using ACCENT_POLICY = struct _ACCENT_POLICY
{
ACCENT_STATE AccentState;
DWORD AccentFlags;
COLORREF GradientColor;
DWORD AnimationId;
};
using IMMERSIVE_HC_CACHE_MODE = enum _IMMERSIVE_HC_CACHE_MODE
{
IHCM_USE_CACHED_VALUE,
IHCM_REFRESH
};
using PREFERRED_APP_MODE = enum _PREFERRED_APP_MODE
{
Default,
AllowDark,
ForceDark,
ForceLight,
Max
};
static const QString g_dwmRegistryKey = QStringLiteral(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)");
static const QString g_personalizeRegistryKey = QStringLiteral(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
// The standard values of border width, border height and title bar height when DPI is 96.
static const int g_defaultBorderWidth = 8, g_defaultBorderHeight = 8, g_defaultTitleBarHeight = 31;
using MONITOR_DPI_TYPE = enum _MONITOR_DPI_TYPE
{
MDT_EFFECTIVE_DPI = 0
};
using PROCESS_DPI_AWARENESS = enum _PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
};
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, WINDOWCOMPOSITIONATTRIBDATA *);
using ShouldAppsUseDarkModePtr = BOOL(WINAPI *)();
using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL);
using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL);
using IsDarkModeAllowedForWindowPtr = BOOL(WINAPI *)(HWND);
using GetIsImmersiveColorUsingHighContrastPtr = BOOL(WINAPI *)(IMMERSIVE_HC_CACHE_MODE);
using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)();
using ShouldSystemUseDarkModePtr = BOOL(WINAPI *)();
using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE);
using IsDarkModeAllowedForAppPtr = BOOL(WINAPI *)();
using GetDpiForMonitorPtr = HRESULT(WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
using GetProcessDpiAwarenessPtr = HRESULT(WINAPI *)(HANDLE, PROCESS_DPI_AWARENESS *);
using GetSystemDpiForProcessPtr = UINT(WINAPI *)(HANDLE);
using GetDpiForWindowPtr = UINT(WINAPI *)(HWND);
using GetDpiForSystemPtr = UINT(WINAPI *)();
using GetSystemMetricsForDpiPtr = int(WINAPI *)(int, UINT);
using AdjustWindowRectExForDpiPtr = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT);
using Win32Data = struct _FLH_UTILITIES_WIN32_DATA
{
_FLH_UTILITIES_WIN32_DATA() { load(); }
SetWindowCompositionAttributePtr SetWindowCompositionAttributePFN = nullptr;
ShouldAppsUseDarkModePtr ShouldAppsUseDarkModePFN = nullptr;
AllowDarkModeForWindowPtr AllowDarkModeForWindowPFN = nullptr;
AllowDarkModeForAppPtr AllowDarkModeForAppPFN = nullptr;
IsDarkModeAllowedForWindowPtr IsDarkModeAllowedForWindowPFN = nullptr;
GetIsImmersiveColorUsingHighContrastPtr GetIsImmersiveColorUsingHighContrastPFN = nullptr;
RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyStatePFN = nullptr;
ShouldSystemUseDarkModePtr ShouldSystemUseDarkModePFN = nullptr;
SetPreferredAppModePtr SetPreferredAppModePFN = nullptr;
IsDarkModeAllowedForAppPtr IsDarkModeAllowedForAppPFN = nullptr;
GetDpiForMonitorPtr GetDpiForMonitorPFN = nullptr;
GetProcessDpiAwarenessPtr GetProcessDpiAwarenessPFN = nullptr;
GetSystemDpiForProcessPtr GetSystemDpiForProcessPFN = nullptr;
GetDpiForWindowPtr GetDpiForWindowPFN = nullptr;
GetDpiForSystemPtr GetDpiForSystemPFN = nullptr;
GetSystemMetricsForDpiPtr GetSystemMetricsForDpiPFN = nullptr;
AdjustWindowRectExForDpiPtr AdjustWindowRectExForDpiPFN = nullptr;
void load()
{
QLibrary User32Dll(QStringLiteral("User32"));
SetWindowCompositionAttributePFN = reinterpret_cast<SetWindowCompositionAttributePtr>(User32Dll.resolve("SetWindowCompositionAttribute"));
GetDpiForWindowPFN = reinterpret_cast<GetDpiForWindowPtr>(User32Dll.resolve("GetDpiForWindow"));
GetDpiForSystemPFN = reinterpret_cast<GetDpiForSystemPtr>(User32Dll.resolve("GetDpiForSystem"));
GetSystemMetricsForDpiPFN = reinterpret_cast<GetSystemMetricsForDpiPtr>(User32Dll.resolve("GetSystemMetricsForDpi"));
AdjustWindowRectExForDpiPFN = reinterpret_cast<AdjustWindowRectExForDpiPtr>(User32Dll.resolve("AdjustWindowRectExForDpi"));
GetSystemDpiForProcessPFN = reinterpret_cast<GetSystemDpiForProcessPtr>(User32Dll.resolve("GetSystemDpiForProcess"));
QLibrary UxThemeDll(QStringLiteral("UxTheme"));
ShouldAppsUseDarkModePFN = reinterpret_cast<ShouldAppsUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(132)));
AllowDarkModeForWindowPFN = reinterpret_cast<AllowDarkModeForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(133)));
AllowDarkModeForAppPFN = reinterpret_cast<AllowDarkModeForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
RefreshImmersiveColorPolicyStatePFN = reinterpret_cast<RefreshImmersiveColorPolicyStatePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(104)));
IsDarkModeAllowedForWindowPFN = reinterpret_cast<IsDarkModeAllowedForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(137)));
GetIsImmersiveColorUsingHighContrastPFN = reinterpret_cast<GetIsImmersiveColorUsingHighContrastPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(106)));
ShouldSystemUseDarkModePFN = reinterpret_cast<ShouldSystemUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(138)));
SetPreferredAppModePFN = reinterpret_cast<SetPreferredAppModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
IsDarkModeAllowedForAppPFN = reinterpret_cast<IsDarkModeAllowedForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(139)));
QLibrary SHCoreDll(QStringLiteral("SHCore"));
GetDpiForMonitorPFN = reinterpret_cast<GetDpiForMonitorPtr>(SHCoreDll.resolve("GetDpiForMonitor"));
GetProcessDpiAwarenessPFN = reinterpret_cast<GetProcessDpiAwarenessPtr>(SHCoreDll.resolve("GetProcessDpiAwareness"));
}
};
Q_GLOBAL_STATIC(Win32Data, win32Data)
static const char envVarForceAcrylic[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
static inline bool forceEnableAcrylicOnWin10()
{
return qEnvironmentVariableIsSet(envVarForceAcrylic);
}
bool Utilities::isBlurEffectSupported()
{
if (isWin8OrGreater()) {
return true;
}
BOOL enabled = FALSE;
if (FAILED(DwmIsCompositionEnabled(&enabled))) {
qWarning() << "DwmIsCompositionEnabled failed.";
return false;
}
return isWin7OrGreater() && (enabled == TRUE);
}
int Utilities::getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue)
{
Q_ASSERT(window);
if (!window) {
return 0;
}
const qreal dpr = dpiAware ? window->devicePixelRatio() : 1.0;
const auto getSystemMetricsForWindow = [dpr](const int index, const bool dpiAware) -> int {
if (win32Data()->GetSystemMetricsForDpiPFN) {
const quint32 dpi = dpiAware ? qRound(USER_DEFAULT_SCREEN_DPI * dpr) : USER_DEFAULT_SCREEN_DPI;
return win32Data()->GetSystemMetricsForDpiPFN(index, dpi);
} else {
const int value = GetSystemMetrics(index);
return dpiAware ? value : qRound(value / dpr);
}
};
int ret = 0;
switch (metric) {
case SystemMetric::BorderWidth: {
const int bw = window->property(_flh_borderWidth_flag).toInt();
if ((bw > 0) && !forceSystemValue) {
ret = qRound(bw * dpr);
} else {
const int result_nondpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, false)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
const int result_dpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, true)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
const int result = dpiAware ? result_dpi : result_nondpi;
ret = result > 0 ? result : qRound(g_defaultBorderWidth * dpr);
}
} break;
case SystemMetric::BorderHeight: {
const int bh = window->property(_flh_borderHeight_flag).toInt();
if ((bh > 0) && !forceSystemValue) {
ret = qRound(bh * dpr);
} else {
const int result_nondpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, false)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
const int result_dpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, true)
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
const int result = dpiAware ? result_dpi : result_nondpi;
ret = result > 0 ? result : qRound(g_defaultBorderHeight * dpr);
}
} break;
case SystemMetric::TitleBarHeight: {
const int tbh = window->property(_flh_titleBarHeight_flag).toInt();
if ((tbh > 0) && !forceSystemValue) {
// Special case: this is the user defined value,
// don't change it and just return it untouched.
return qRound(tbh * dpr);
} else {
const int result_nondpi = getSystemMetricsForWindow(SM_CYCAPTION, false);
const int result_dpi = getSystemMetricsForWindow(SM_CYCAPTION, true);
const int result = dpiAware ? result_dpi : result_nondpi;
ret = result > 0 ? result : qRound(g_defaultTitleBarHeight * dpr);
}
} break;
}
// When dpr = 1.0 (DPI = 96):
// SM_CXSIZEFRAME = SM_CYSIZEFRAME = 4px
// SM_CXPADDEDBORDER = 4px
// SM_CYCAPTION = 23px
// Border Width = Border Height = SM_C(X|Y)SIZEFRAME + SM_CXPADDEDBORDER = 8px
// Title Bar Height = Border Height + SM_CYCAPTION = 31px
// dpr = 1.25 --> Title Bar Height = 38px
// dpr = 1.5 --> Title Bar Height = 45px
// dpr = 1.75 --> Title Bar Height = 51px
ret += (metric == SystemMetric::TitleBarHeight) ? getSystemMetric(window, SystemMetric::BorderHeight, dpiAware) : 0;
return ret;
}
bool Utilities::setAcrylicEffectEnabled(const QWindow *window, const bool enabled, const QColor &gradientColor)
{
Q_ASSERT(window);
if (!window) {
return false;
}
bool result = false;
const auto hwnd = reinterpret_cast<HWND>(window->winId());
// We prefer DwmEnableBlurBehindWindow on Windows 7.
if (isWin8OrGreater() && win32Data()->SetWindowCompositionAttributePFN) {
ACCENT_POLICY accentPolicy;
SecureZeroMemory(&accentPolicy, sizeof(accentPolicy));
WINDOWCOMPOSITIONATTRIBDATA wcaData;
SecureZeroMemory(&wcaData, sizeof(wcaData));
wcaData.Attrib = WCA_ACCENT_POLICY;
wcaData.pvData = &accentPolicy;
wcaData.cbData = sizeof(accentPolicy);
if (enabled) {
// The gradient color must be set otherwise it'll look like a classic blur.
// Use semi-transparent gradient color to get better appearance.
if (gradientColor.isValid()) {
accentPolicy.GradientColor = qRgba(gradientColor.blue(), gradientColor.green(), gradientColor.red(), gradientColor.alpha());
} else {
const QColor colorizationColor = getColorizationColor();
accentPolicy.GradientColor =
RGB(qRound(colorizationColor.red() * (colorizationColor.alpha() / 255.0) + 255 - colorizationColor.alpha()),
qRound(colorizationColor.green() * (colorizationColor.alpha() / 255.0) + 255 - colorizationColor.alpha()),
qRound(colorizationColor.blue() * (colorizationColor.alpha() / 255.0) + 255 - colorizationColor.alpha()));
}
if (isMSWin10AcrylicEffectAvailable() || (isWin10OrGreater() && forceEnableAcrylicOnWin10())) {
accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
if (!gradientColor.isValid()) {
accentPolicy.GradientColor = 0x01FFFFFF;
}
} else {
accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND;
}
} else {
accentPolicy.AccentState = ACCENT_DISABLED;
}
result = win32Data()->SetWindowCompositionAttributePFN(hwnd, &wcaData) == TRUE;
if (!result) {
qWarning() << "SetWindowCompositionAttribute failed.";
}
} else {
DWM_BLURBEHIND dwmBB;
SecureZeroMemory(&dwmBB, sizeof(dwmBB));
dwmBB.dwFlags = DWM_BB_ENABLE;
dwmBB.fEnable = enabled ? TRUE : FALSE;
result = SUCCEEDED(DwmEnableBlurBehindWindow(hwnd, &dwmBB));
if (!result) {
qWarning() << "DwmEnableBlurBehindWindow failed.";
}
}
return result;
}
bool Utilities::isColorizationEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_dwmRegistryKey, QSettings::NativeFormat);
const bool colorPrevalence
= registry.value(QStringLiteral("ColorPrevalence"), 0).toULongLong(&ok) != 0;
return (ok && colorPrevalence);
}
QColor Utilities::getColorizationColor()
{
DWORD color = 0;
BOOL opaqueBlend = FALSE;
if (FAILED(DwmGetColorizationColor(&color, &opaqueBlend))) {
qWarning() << "DwmGetColorizationColor failed.";
return Qt::darkGray;
}
return QColor::fromRgba(color);
}
bool Utilities::isLightThemeEnabled()
{
return !isDarkThemeEnabled();
}
bool Utilities::isDarkThemeEnabled()
{
return win32Data()->ShouldSystemUseDarkModePFN ? win32Data()->ShouldSystemUseDarkModePFN() : false;
}
bool Utilities::isHighContrastModeEnabled()
{
HIGHCONTRASTW hc;
SecureZeroMemory(&hc, sizeof(hc));
hc.cbSize = sizeof(hc);
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, 0, &hc, 0) == FALSE) {
qWarning() << "SystemParametersInfoW failed.";
return false;
}
return hc.dwFlags & HCF_HIGHCONTRASTON;
}
bool Utilities::isDarkFrameEnabled(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return false;
}
if (!isWin10OrGreater(17763)) {
return false;
}
const auto hwnd = reinterpret_cast<HWND>(window->winId());
BOOL result = FALSE;
const bool ok = SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|| SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
return (ok && result);
}
bool Utilities::isTransparencyEffectEnabled()
{
if (!isWin10OrGreater()) {
return false;
}
bool ok = false;
const QSettings registry(g_personalizeRegistryKey, QSettings::NativeFormat);
const bool enableTransparency
= registry.value(QStringLiteral("EnableTransparency"), 0).toULongLong(&ok) != 0;
return (ok && enableTransparency);
}
void Utilities::triggerFrameChange(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
if (SetWindowPos(reinterpret_cast<HWND>(window->winId()), nullptr, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER) == FALSE) {
qWarning() << "SetWindowPos failed.";
}
}
void Utilities::updateFrameMargins(const QWindow *window, const bool reset)
{
Q_ASSERT(window);
if (!window) {
return;
}
const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
if (FAILED(DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(window->winId()), &margins))) {
qWarning() << "DwmExtendFrameIntoClientArea failed.";
}
}
QImage Utilities::getDesktopWallpaperImage(const int screen)
{
Q_UNUSED(screen);
WCHAR path[MAX_PATH];
if (SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, path, 0) == FALSE) {
qWarning() << "SystemParametersInfoW failed.";
return {};
}
return QImage(QString::fromWCharArray(path));
}
Utilities::DesktopWallpaperAspectStyle Utilities::getDesktopWallpaperAspectStyle(const int screen)
{
Q_UNUSED(screen);
const QSettings settings(QStringLiteral(R"(HKEY_CURRENT_USER\Control Panel\Desktop)"), QSettings::NativeFormat);
const DWORD style = settings.value(QStringLiteral("WallpaperStyle")).toULongLong();
switch (style) {
case 0: {
if (settings.value(QStringLiteral("TileWallpaper")).toBool()) {
return DesktopWallpaperAspectStyle::Tiled;
} else {
return DesktopWallpaperAspectStyle::Central;
}
}
case 2:
return DesktopWallpaperAspectStyle::IgnoreRatio;
case 6:
return DesktopWallpaperAspectStyle::KeepRatio;
default:
return DesktopWallpaperAspectStyle::KeepRatioByExpanding;
}
}
quint32 Utilities::getWindowDpi(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return USER_DEFAULT_SCREEN_DPI;
}
const auto hwnd = reinterpret_cast<HWND>(window->winId());
Q_ASSERT(hwnd);
if (!hwnd) {
return USER_DEFAULT_SCREEN_DPI;
}
if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi)) {
return USER_DEFAULT_SCREEN_DPI;
}
if (win32Data()->GetDpiForWindowPFN) {
return win32Data()->GetDpiForWindowPFN(hwnd);
} else if (win32Data()->GetSystemDpiForProcessPFN) {
return win32Data()->GetSystemDpiForProcessPFN(GetCurrentProcess());
} else if (win32Data()->GetDpiForSystemPFN) {
return win32Data()->GetDpiForSystemPFN();
} else if (win32Data()->GetDpiForMonitorPFN) {
const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor) {
UINT dpiX = USER_DEFAULT_SCREEN_DPI, dpiY = USER_DEFAULT_SCREEN_DPI;
if (SUCCEEDED(win32Data()->GetDpiForMonitorPFN(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
return dpiX;
} else {
qWarning() << "GetDpiForMonitor failed";
}
} else {
qWarning() << "MonitorFromWindow failed.";
}
}
// Using Direct2D to get DPI is deprecated.
const HDC hdc = GetDC(nullptr);
if (hdc) {
const int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
ReleaseDC(nullptr, hdc);
if (dpiX > 0) {
return dpiX;
} else {
qWarning() << "GetDeviceCaps failed.";
}
}
return USER_DEFAULT_SCREEN_DPI;
}
QMargins Utilities::getWindowNativeFrameMargins(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return {};
}
const auto hwnd = reinterpret_cast<HWND>(window->winId());
Q_ASSERT(hwnd);
if (!hwnd) {
return {};
}
RECT rect = {0, 0, 0, 0};
const LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
const LONG_PTR exStyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
if (win32Data()->AdjustWindowRectExForDpiPFN) {
if (win32Data()->AdjustWindowRectExForDpiPFN(&rect, style, FALSE, exStyle, getWindowDpi(window)) == FALSE) {
qWarning() << "AdjustWindowRectExForDpiPFN failed.";
}
} else {
if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE) {
qWarning() << "AdjustWindowRectEx failed.";
}
}
return {qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)};
}