Compare commits

...

80 Commits

Author SHA1 Message Date
zhuzichu 10580baf54
Bug fixed. 2023-07-08 19:11:21 +08:00
Mentalflow e72257bb34
Use mirror submodule. 2023-07-08 19:11:20 +08:00
Dylan Liu 43f632f261
Fix the bug of unregistered datatype QuickGlobal::SystemTheme in Qt 5. (#240) 2023-07-06 11:50:05 +08:00
Yuhang Zhao 9dce992112 cmake: update submodule 2023-07-04 15:14:49 +08:00
Yuhang Zhao d1b4aadb76 mica material: use separate thread to do time-consuming task 2023-07-02 13:48:38 +08:00
Yuhang Zhao 6b09aa9e91 bump version to 2.4.0 2023-07-02 11:56:21 +08:00
Yuhang Zhao 617a015b97 mica material: minor fixes of previous implementation 2023-07-02 11:44:54 +08:00
Yuhang Zhao 7c0d8c4b11 mica material: support reading very large image files 2023-07-01 18:16:57 +08:00
Yuhang Zhao fa5389a0fc ci: try fix ci 2023-06-30 17:31:01 +08:00
Yuhang Zhao 2a49e9fcaf cmake: support ninja multi-config
Fixes: #231
2023-06-30 16:55:26 +08:00
Yuhang Zhao 6ccb25db0a cmake: update submodule 2023-06-29 14:52:42 +08:00
Yuhang Zhao 8f321e2e54 cmake: allow produce same binary
Task-number: #238
2023-06-29 10:59:39 +08:00
Yuhang Zhao f15412cf5b cmake: update submodule 2023-06-26 13:24:56 +08:00
Yuhang Zhao a5ec49d0a2 cmake: update submodule 2023-06-26 11:16:45 +08:00
Yuhang Zhao 86a9b85d04 cmake: fix cmake error on Apple 2023-06-20 11:07:38 +08:00
Yuhang Zhao b7556d467e cmake: update 2023-06-19 18:04:04 +08:00
Yuhang Zhao 074664afc2 cmake: update submodule 2023-06-19 17:48:40 +08:00
Yuhang Zhao 6229a0db3c README: fix wrong function name 2023-06-19 17:14:31 +08:00
Yuhang Zhao 6c27125d69 win: guard some code with proper version check 2023-06-19 16:07:43 +08:00
Yuhang Zhao fdd6640d5c cmake: fix wrong compilation flags 2023-06-19 15:29:14 +08:00
Yuhang Zhao 10b661f541 cmake: update submodule 2023-06-17 12:41:55 +08:00
Yuhang Zhao c1370c4897 cmake: minor change 2023-06-17 12:34:30 +08:00
Dylan Liu af200adf63
Remove unused function in NSWindowProxy. (#236) 2023-06-15 18:48:21 +08:00
Yuhang Zhao 85be988760 ci: fix macos ci 2023-06-10 20:54:07 +08:00
Yuhang Zhao 273e3e204c update submodule 2023-06-10 20:41:46 +08:00
Yuhang Zhao 00b67ead0e win: remove unnecessary code & add some comments
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-06-04 13:28:14 +08:00
Yuhang Zhao 907fdeae67 fix ci: remove unused functions
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-06-03 15:24:04 +08:00
Yuhang Zhao 5a344f3e23 minor fixes & some refactorings
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-06-03 15:13:50 +08:00
Yuhang Zhao b28b6e8d27 update submodule
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-06-03 13:35:02 +08:00
Yuhang Zhao 1e754c1612 minor fixes 2023-06-02 14:51:12 +08:00
Yuhang Zhao f7f8fc3dd0 core: fix wrong override condition
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-28 13:55:44 +08:00
Yuhang Zhao e1cf95f208 cmake: fix link with mingw
Signed-off-by: Yuhang Zhao <zhaoyuhang@rankyee.com>
2023-05-26 11:08:08 +08:00
Yuhang Zhao 9778582d9b update README
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-25 19:35:29 +08:00
Yuhang Zhao 0424d0ee1b README: add more info
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-25 19:30:12 +08:00
Yuhang Zhao 13e93cfed5 cmake: code simplify
Signed-off-by: Yuhang Zhao <zhaoyuhang@rankyee.com>
2023-05-24 15:41:11 +08:00
zhuzichu 9376e4d6e7
fix mingw dont work (#229) 2023-05-24 15:38:51 +08:00
Yuhang Zhao 59fb61d981 cmake: fix old mingw toolchain
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-22 19:27:26 +08:00
zhuzichu e090359d9a
fix setOverrideTheme render multiple times (#228)
* fix setOverrideTheme render multiple times

* fix setOverrideTheme render multiple times

* fix encoding utf8

* fix setOverrideTheme render multiple times

---------

Co-authored-by: 朱子楚\zhuzi <zhuzichu520@outlook.com>
2023-05-22 19:15:51 +08:00
Yuhang Zhao e7da37d06a deprecated unused function & other minor tweaks
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-21 12:04:57 +08:00
Dylan Liu 3a18f36d3f
Fix the native titlebar buttons don't show below Qt 6.2.4. (#226) 2023-05-20 21:01:23 +08:00
Yuhang Zhao 9df1e9d7b5 mac: add a comment
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-20 20:07:43 +08:00
Dylan Liu e48fc7e01f
Fix the native titlebar buttons don't show in Qt 6.0.0 - Qt 6.2.4. (#225) 2023-05-20 19:57:00 +08:00
Yuhang Zhao 2b1623fbd6 cmake: hide detailed info if the user don't want it
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-20 15:32:48 +08:00
Yuhang Zhao e67a91d668 refactor: remove all mutexes
They are currently not used and slow down the general performance.
2023-05-19 13:31:20 +08:00
Dylan Liu ec93740bd7
Add manual control of darkmode for Quick. (#220)
Add the function of manual control of dark mode in Quick module.
2023-05-19 13:12:25 +08:00
Yuhang Zhao e4fdb9c260 refactor 2023-05-19 11:58:32 +08:00
Yuhang Zhao de2cad679b update submodule 2023-05-19 11:56:14 +08:00
Yuhang Zhao 1ba9c9c918 refactor 2023-05-19 11:55:41 +08:00
Yuhang Zhao 721f46307b fix cmake syntax error
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-18 20:40:46 +08:00
Yuhang Zhao 32b055a7ab cmake: dump more info
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-18 20:38:05 +08:00
Yuhang Zhao 8b7ee7a24d cmake: don't touch AppleClang flags
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-18 19:58:42 +08:00
Dylan Liu 951dd5f931
Add version check for universal build. (#219) 2023-05-18 19:54:50 +08:00
Yuhang Zhao 2c1164b834 fix win ci warning 2023-05-18 16:49:07 +08:00
Yuhang Zhao 0be45565dc update ci 2023-05-18 16:41:44 +08:00
Yuhang Zhao 2fa3140b0f update submodule 2023-05-18 16:13:48 +08:00
Yuhang Zhao 8d5ea49d52 allow user override system theme
Task-number: #216
2023-05-18 16:08:33 +08:00
Yuhang Zhao 66c2c60a09 DPI change event improve 2023-05-18 14:40:28 +08:00
Yuhang Zhao 82e29f37bb try fix macOS ci 2023-05-18 09:47:30 +08:00
Dylan Liu 84745db4d9
Add Universal build for macOS and compile error fixed. (#217)
* Add Universal build for macOS.

* Fix compile error in example quick.

* Use cmake_dependent_option instead of option.
2023-05-17 20:19:31 +08:00
Yuhang Zhao 84a9a76021 try fix ci 2023-05-17 20:05:06 +08:00
Yuhang Zhao 901aaee7e3 try fix ci 2023-05-17 19:50:11 +08:00
Yuhang Zhao a9ee1c5fff fix ci 2023-05-17 19:31:26 +08:00
Yuhang Zhao 76453b123e fix ci: 6.5.1 has not been released yet 2023-05-17 18:06:00 +08:00
Yuhang Zhao cfcf9f4178 fix ci 2023-05-17 18:04:19 +08:00
Yuhang Zhao 8ece315390 update submodule 2023-05-17 17:51:37 +08:00
Yuhang Zhao 92d850c2a9 add support for Qt 6.6's DPR change event 2023-05-17 13:22:42 +08:00
Yuhang Zhao 224302500d cmake: quick: fix build with version below 6.3
Fixes: #215
2023-05-17 09:20:53 +08:00
Yuhang Zhao cf568b6e7a cmake: use Qt's official function to get the quick plugin info 2023-05-16 18:01:06 +08:00
Yuhang Zhao a3cfa59d1d improve dpi change experience
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-05-13 11:30:54 +08:00
Yuhang Zhao 250aff0faf cmake: fix build on arm64 platforms 2023-05-04 17:37:43 +08:00
Yuhang Zhao b039a9130a win: minor refactor 2023-04-28 09:14:06 +08:00
Yuhang Zhao 4d063369fa mica material: add fallback color and fix the qtquick one
I don't understand why I didn't make QuickMicaMaterial configurable.
2023-04-27 10:36:47 +08:00
Yuhang Zhao 41e91bb39a win: fix wrong restore geometry when after DPI changes 2023-04-25 15:19:31 +08:00
Yuhang Zhao 30d2261baf bump version to 2.3.7 2023-04-24 15:43:17 +08:00
Yuhang Zhao fe724271da unix: fix wrong system menu position 2023-04-24 13:18:57 +08:00
Yuhang Zhao b2561c16ba minor refactor 2023-04-23 13:42:27 +08:00
Yuhang Zhao a203e2c3ca win32: fix restore geometry bug
The upstream fix has not been merged yet, however, it will be in 6.5.1 for sure.

Fixes: #20

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
2023-04-22 15:21:49 +08:00
Yuhang Zhao 8930ea128f minor tweaks
Signed-off-by: Yuhang Zhao <zhaoyuhang@rankyee.com>
2023-04-17 11:16:50 +08:00
Yuhang Zhao 3b8571401f minor refactor 2023-04-11 17:09:58 +08:00
Yuhang Zhao 99417bf916 fix coordinate calculation in multi-monitor scenarios 2023-04-11 15:12:33 +08:00
58 changed files with 1799 additions and 1345 deletions

View File

@ -18,19 +18,25 @@ jobs:
name: Build
strategy:
matrix:
qt-version: [5.15.2, 6.5.0]
qt-version: [5.15.2, 6.5.1]
library-type: [shared, static]
platform: [windows-latest, ubuntu-latest, macos-latest]
include:
- platform: windows-latest
CC: cl
CXX: cl
LD: link
EXTRA_FLAGS: -DFRAMELESSHELPER_ENABLE_SPECTRE=ON -DFRAMELESSHELPER_ENABLE_EHCONTGUARD=ON -DFRAMELESSHELPER_ENABLE_INTELCET=ON -DFRAMELESSHELPER_ENABLE_CFGUARD=ON
- platform: ubuntu-latest
CC: gcc
CXX: g++
LD: ld
EXTRA_FLAGS: -DFRAMELESSHELPER_ENABLE_SPECTRE=ON -DFRAMELESSHELPER_ENABLE_INTELCET=ON -DFRAMELESSHELPER_ENABLE_CFGUARD=ON
- platform: macos-latest
CC: clang
CXX: clang++
CC: /usr/local/opt/llvm/bin/clang
CXX: /usr/local/opt/llvm/bin/clang++
LD: /usr/local/opt/llvm/bin/ld64.lld
EXTRA_FLAGS: -DFRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD=OFF
- library-type: shared
lib_type_flag: -DFRAMELESSHELPER_BUILD_STATIC=OFF
- library-type: static
@ -64,10 +70,16 @@ jobs:
run: |
sudo apt install -y libgl1-mesa-dev libxcb1-dev libgtk-3-dev
- name: Install macOS dependencies
if: ${{ matrix.platform == 'macos-latest' }}
run: |
brew install llvm
export PATH="/usr/local/opt/llvm/bin:$PATH"
- name: Build library with CMake
run: |
mkdir ci
cd ci
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_C_COMPILER=${{ matrix.CC }} -DCMAKE_CXX_COMPILER=${{ matrix.CXX }} -DCMAKE_INSTALL_PREFIX=../../install -DCMAKE_BUILD_TYPE=Release -DFRAMELESSHELPER_BUILD_EXAMPLES=ON ${{ matrix.lib_type_flag }} -DFRAMELESSHELPER_ENABLE_SPECTRE=ON -DFRAMELESSHELPER_ENABLE_INTELCET=ON -DFRAMELESSHELPER_ENABLE_INTELJCC=ON -DFRAMELESSHELPER_ENABLE_CFGUARD=ON -GNinja ..
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_C_COMPILER=${{ matrix.CC }} -DCMAKE_CXX_COMPILER=${{ matrix.CXX }} -DCMAKE_LINKER=${{ matrix.LD }} -DCMAKE_INSTALL_PREFIX=../../install -DCMAKE_BUILD_TYPE=Release -DFRAMELESSHELPER_BUILD_EXAMPLES=ON -DFRAMELESSHELPER_NO_SUMMARY=OFF ${{ matrix.lib_type_flag }} ${{ matrix.EXTRA_FLAGS }} -GNinja ..
cmake --build . --target all --config Release --parallel
cmake --install . --config Release --strip

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "cmake"]
path = cmake
url = https://github.com/wangwenx190/cmake-utils.git
url = https://git.ourdocs.cn/github_mirror/cmake-utils.git

View File

@ -25,11 +25,14 @@
cmake_minimum_required(VERSION 3.20)
project(FramelessHelper
VERSION "2.3.6"
VERSION "2.4.0"
DESCRIPTION "Cross-platform window customization framework for Qt Widgets and Qt Quick."
HOMEPAGE_URL "https://github.com/wangwenx190/framelesshelper/"
)
include(CMakeDependentOption)
# TODO: Use add_feature_info() for every option below? Is it worth doing?
option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF)
option(FRAMELESSHELPER_BUILD_WIDGETS "Build FramelessHelper's Widgets module." ON)
option(FRAMELESSHELPER_BUILD_QUICK "Build FramelessHelper's Quick module." ON)
@ -45,10 +48,29 @@ option(FRAMELESSHELPER_NO_INSTALL "Don't install any files." OFF)
option(FRAMELESSHELPER_NO_SUMMARY "Don't show CMake configure summary." OFF)
option(FRAMELESSHELPER_ENABLE_SPECTRE "Mitigate Spectre security vulnerabilities." OFF)
option(FRAMELESSHELPER_ENABLE_EHCONTGUARD "MSVC only: Enable EH Continuation (EHCONT) Metadata." OFF)
option(FRAMELESSHELPER_ENABLE_INTELCET "Enable Intel CET." ON)
option(FRAMELESSHELPER_ENABLE_INTELJCC "Enable Intel JCC." ON)
option(FRAMELESSHELPER_ENABLE_CFGUARD "Enable Control Flow Guard (CFG)." ON)
option(FRAMELESSHELPER_ENABLE_INTELCET "Enable Intel CET." OFF)
option(FRAMELESSHELPER_ENABLE_INTELJCC "Enable Intel JCC." OFF)
option(FRAMELESSHELPER_ENABLE_CFGUARD "Enable Control Flow Guard (CFG)." OFF)
option(FRAMELESSHELPER_EXAMPLES_STANDALONE "Build the demo projects as standalone CMake projects." OFF)
cmake_dependent_option(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD "macOS only: build universal library/example for Mac." ON APPLE OFF)
option(FRAMELESSHELPER_FORCE_LTO "Force enable LTO/LTCG even when building static libraries." OFF)
option(FRAMELESSHELPER_REPRODUCIBLE_OUTPUT "Don't update the build commit and date dynamically." OFF)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui)
find_package(QT NAMES Qt6 Qt5 QUIET COMPONENTS Widgets Quick)
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS Widgets Quick)
include(cmake/utils.cmake)
if(NOT APPLE AND FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
message(WARNING "Current OS is not macOS, universal build will be disabled.")
set(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD OFF)
elseif(APPLE AND ((QT_VERSION VERSION_LESS "6.2" AND QT_VERSION VERSION_GREATER_EQUAL "6.0") OR (QT_VERSION VERSION_LESS "5.15.9")))
message(WARNING "Your Qt version ${QT_VERSION} doesn't support universal build, it will be disabled.")
set(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD OFF)
endif()
if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
message(WARNING "Nothing will be embeded into the FramelessHelper library, the chrome buttons will have no icon.")
@ -58,8 +80,6 @@ if(FRAMELESSHELPER_ENABLE_VCLTL AND NOT MSVC)
message(WARNING "VC-LTL is only available for the MSVC toolchain.")
endif()
include(cmake/utils.cmake)
set(__extra_flags)
if(NOT FRAMELESSHELPER_BUILD_STATIC)
list(APPEND __extra_flags ENABLE_LTO)
@ -76,14 +96,13 @@ setup_project(
unset(__extra_flags)
set(PROJECT_VERSION_COMMIT "UNKNOWN")
get_commit_hash(RESULT PROJECT_VERSION_COMMIT)
set(PROJECT_COMPILE_DATETIME "UNKNOWN")
string(TIMESTAMP PROJECT_COMPILE_DATETIME UTC)
if(NOT FRAMELESSHELPER_REPRODUCIBLE_OUTPUT)
get_commit_hash(RESULT PROJECT_VERSION_COMMIT)
string(TIMESTAMP PROJECT_COMPILE_DATETIME UTC)
endif()
if(MINGW AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(WARNING "Your current toolchain is not officially supported by FramelessHelper.\n"
"Only LLVM-MinGW (https://github.com/mstorsjo/llvm-mingw) has partial support.")
set(FRAMELESSHELPER_ENABLE_SPECTRE OFF)
set(FRAMELESSHELPER_ENABLE_EHCONTGUARD OFF)
set(FRAMELESSHELPER_ENABLE_INTELCET OFF)
@ -95,10 +114,17 @@ if(MSVC)
if(FRAMELESSHELPER_ENABLE_VCLTL)
include(cmake/VC-LTL.cmake)
if("x${SupportLTL}" STREQUAL "xtrue")
# Make sure we will always overwrite the previous settings.
unset(CMAKE_MSVC_RUNTIME_LIBRARY)
unset(CMAKE_MSVC_RUNTIME_LIBRARY CACHE)
#unset(CMAKE_MSVC_RUNTIME_LIBRARY PARENT_SCOPE)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
endif()
endif()
if(FRAMELESSHELPER_ENABLE_YYTHUNKS)
unset(YYTHUNKS_TARGET_OS)
unset(YYTHUNKS_TARGET_OS CACHE)
#unset(YYTHUNKS_TARGET_OS PARENT_SCOPE)
set(YYTHUNKS_TARGET_OS "WinXP" CACHE STRING "" FORCE)
include(cmake/YY-Thunks.cmake)
endif()
@ -115,12 +141,6 @@ prepare_package_export(
)
unset(__extra_flags)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui)
find_package(QT NAMES Qt6 Qt5 QUIET COMPONENTS Widgets Quick)
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS Widgets Quick)
if(FRAMELESSHELPER_BUILD_QUICK AND NOT TARGET Qt${QT_VERSION_MAJOR}::Quick)
message(WARNING "Can't find the QtQuick module. FramelessHelper's QtQuick implementation and the QtQuick demo won't be built.")
set(FRAMELESSHELPER_BUILD_QUICK OFF)
@ -147,11 +167,31 @@ if(NOT FRAMELESSHELPER_NO_SUMMARY)
message("CMake version: ${CMAKE_VERSION} (${CMAKE_COMMAND})")
message("Host system: ${CMAKE_HOST_SYSTEM}")
message("Host processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
#message("C compiler: ${CMAKE_C_COMPILER_ID} (${CMAKE_C_COMPILER})") # Currently we are not using any C compilers.
#message("C compiler version: ${CMAKE_C_COMPILER_VERSION}")
#[[message("C compiler: ${CMAKE_C_COMPILER_ID} (${CMAKE_C_COMPILER})") # Currently we are not using any C compilers.
message("C compiler version: ${CMAKE_C_COMPILER_VERSION}")
message("C common flags: ${CMAKE_C_FLAGS}")
message("C debug flags: ${CMAKE_C_FLAGS_DEBUG}")
message("C release flags: ${CMAKE_C_FLAGS_RELEASE}")
message("C minsizerel flags: ${CMAKE_C_FLAGS_MINSIZEREL}")
message("C relwithdebinfo flags: ${CMAKE_C_FLAGS_RELWITHDEBINFO}")]]
message("C++ compiler: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER})")
message("C++ compiler version: ${CMAKE_CXX_COMPILER_VERSION}")
message("C++ common flags: ${CMAKE_CXX_FLAGS}")
message("C++ debug flags: ${CMAKE_CXX_FLAGS_DEBUG}")
message("C++ release flags: ${CMAKE_CXX_FLAGS_RELEASE}")
message("C++ minsizerel flags: ${CMAKE_CXX_FLAGS_MINSIZEREL}")
message("C++ relwithdebinfo flags: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
message("Linker: ${CMAKE_LINKER}")
message("Linker exe common flags: ${CMAKE_EXE_LINKER_FLAGS}")
message("Linker exe debug flags: ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
message("Linker exe release flags: ${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
message("Linker exe minsizerel flags: ${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}")
message("Linker exe relwithdebinfo flags: ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
message("Linker dll common flags: ${CMAKE_SHARED_LINKER_FLAGS}")
message("Linker dll debug flags: ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
message("Linker dll release flags: ${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
message("Linker dll minsizerel flags: ${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL}")
message("Linker dll relwithdebinfo flags: ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}")
message("Make program: ${CMAKE_MAKE_PROGRAM}")
message("Generator: ${CMAKE_GENERATOR}")
message("Build type: ${CMAKE_BUILD_TYPE}")
@ -160,25 +200,15 @@ if(NOT FRAMELESSHELPER_NO_SUMMARY)
message("Prefix paths: ${CMAKE_PREFIX_PATH}")
message("Toolchain file: ${CMAKE_TOOLCHAIN_FILE}")
message("------------------------------ Qt -------------------------------")
set(__qt_inst_dir)
if(DEFINED Qt6_DIR)
set(__qt_inst_dir "${Qt6_DIR}")
else()
set(__qt_inst_dir "${Qt5_DIR}")
endif()
# /whatever/Qt/6.4.0/gcc_64/lib/cmake/Qt6
cmake_path(GET __qt_inst_dir PARENT_PATH __qt_inst_dir)
cmake_path(GET __qt_inst_dir PARENT_PATH __qt_inst_dir)
cmake_path(GET __qt_inst_dir PARENT_PATH __qt_inst_dir)
query_qt_paths(SDK_DIR __qt_inst_dir)
query_qt_library_info(STATIC __qt_static_lib)
message("Qt SDK installation directory: ${__qt_inst_dir}")
message("Qt SDK version: ${QT_VERSION}")
get_target_property(__qt_type Qt${QT_VERSION_MAJOR}::Core TYPE)
if(__qt_type STREQUAL "STATIC_LIBRARY")
set(__qt_type static)
if(__qt_static_lib)
message("Qt SDK library type: static")
else()
set(__qt_type shared)
message("Qt SDK library type: shared")
endif()
message("Qt SDK library type: ${__qt_type}")
message("------------------------ FramelessHelper ------------------------")
message("FramelessHelper version: ${PROJECT_VERSION}")
message("FramelessHelper commit hash: ${PROJECT_VERSION_COMMIT}")
@ -201,5 +231,8 @@ if(NOT FRAMELESSHELPER_NO_SUMMARY)
message("Enable Intel JCC: ${FRAMELESSHELPER_ENABLE_INTELJCC}")
message("Enable Control Flow Guard (CFG): ${FRAMELESSHELPER_ENABLE_CFGUARD}")
message("Build standalone demo projects: ${FRAMELESSHELPER_EXAMPLES_STANDALONE}")
message("[macOS]: Build universal library/example: ${FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD}")
message("Force enable LTO: ${FRAMELESSHELPER_FORCE_LTO}")
message("Make output reproducible: ${FRAMELESSHELPER_REPRODUCIBLE_OUTPUT}")
message("-----------------------------------------------------------------")
endif()

View File

@ -28,12 +28,7 @@ set(_@PROJECT_NAME@_supported_components Core Widgets Quick)
foreach(_comp ${@PROJECT_NAME@_FIND_COMPONENTS})
if(_comp IN_LIST _@PROJECT_NAME@_supported_components)
set(__proj_name "@PROJECT_NAME@${_comp}")
cmake_path(GET CMAKE_CURRENT_LIST_FILE PARENT_PATH __imp_prefix)
cmake_path(GET __imp_prefix PARENT_PATH __imp_prefix)
include("${__imp_prefix}/${__proj_name}/${__proj_name}Config.cmake")
unset(__imp_prefix)
unset(__proj_name)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@${_comp}Config.cmake")
else()
set(@PROJECT_NAME@_FOUND FALSE)
set(@PROJECT_NAME@_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
@ -52,3 +47,9 @@ if(NOT DEFINED @PROJECT_NAME@_FOUND)
set(@PROJECT_NAME@_COMMIT "@PROJECT_VERSION_COMMIT@")
set(@PROJECT_NAME@_COMPILE_DATETIME "@PROJECT_COMPILE_DATETIME@")
endif()
include(FeatureSummary)
set_package_properties(@PROJECT_NAME@ PROPERTIES
DESCRIPTION "@PROJECT_DESCRIPTION@"
URL "@PROJECT_HOMEPAGE_URL@"
)

View File

@ -1,59 +0,0 @@
#[[
MIT License
Copyright (C) 2021-2023 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.
]]
if(NOT DEFINED @SUB_PROJ_NAME@_FOUND)
set(@SUB_PROJ_NAME@_FOUND TRUE)
endif()
if(@SUB_PROJ_NAME@_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/@SUB_PROJ_NAME@Targets.cmake")
endif()
if(TARGET @PROJECT_NAME@::@SUB_MOD_NAME@)
set(@SUB_PROJ_NAME@_LIBRARIES @PROJECT_NAME@::@SUB_MOD_NAME@)
get_target_property(@SUB_PROJ_NAME@_VERSION @PROJECT_NAME@::@SUB_MOD_NAME@ VERSION)
if(NOT @SUB_PROJ_NAME@_VERSION)
set(@SUB_PROJ_NAME@_VERSION "")
endif()
get_target_property(@SUB_PROJ_NAME@_INCLUDE_DIRS @PROJECT_NAME@::@SUB_MOD_NAME@ INTERFACE_INCLUDE_DIRECTORIES)
if(NOT @SUB_PROJ_NAME@_INCLUDE_DIRS)
set(@SUB_PROJ_NAME@_INCLUDE_DIRS "")
endif()
get_target_property(@SUB_PROJ_NAME@_DEFINITIONS @PROJECT_NAME@::@SUB_MOD_NAME@ INTERFACE_COMPILE_DEFINITIONS)
if(NOT @SUB_PROJ_NAME@_DEFINITIONS)
set(@SUB_PROJ_NAME@_DEFINITIONS "")
else()
list(TRANSFORM @SUB_PROJ_NAME@_DEFINITIONS PREPEND "-D")
endif()
get_target_property(@SUB_PROJ_NAME@_COMPILE_DEFINITIONS @PROJECT_NAME@::@SUB_MOD_NAME@ INTERFACE_COMPILE_DEFINITIONS)
if(NOT @SUB_PROJ_NAME@_COMPILE_DEFINITIONS)
set(@SUB_PROJ_NAME@_COMPILE_DEFINITIONS "")
endif()
list(REMOVE_DUPLICATES @SUB_PROJ_NAME@_INCLUDE_DIRS)
list(REMOVE_DUPLICATES @SUB_PROJ_NAME@_DEFINITIONS)
list(REMOVE_DUPLICATES @SUB_PROJ_NAME@_COMPILE_DEFINITIONS)
else()
set(@SUB_PROJ_NAME@_FOUND FALSE)
set(@SUB_PROJ_NAME@_NOT_FOUND_MESSAGE "Target \"@PROJECT_NAME@::@SUB_MOD_NAME@\" was not found.")
endif()

View File

@ -1,47 +0,0 @@
#[[
MIT License
Copyright (C) 2021-2023 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.
]]
if(NOT TARGET @PROJECT_NAME@::@SUB_MOD_NAME@)
cmake_path(GET CMAKE_CURRENT_LIST_FILE PARENT_PATH __import_prefix)
cmake_path(GET __import_prefix PARENT_PATH __import_prefix)
cmake_path(GET __import_prefix PARENT_PATH __import_prefix)
cmake_path(GET __import_prefix PARENT_PATH __import_prefix)
if(__import_prefix STREQUAL "/")
set(__import_prefix "")
endif()
add_library(@SUB_PROJ_NAME@ @SUB_MOD_LIB_TYPE@ IMPORTED)
add_library(@PROJECT_NAME@::@SUB_MOD_NAME@ ALIAS @SUB_PROJ_NAME@)
set_target_properties(@SUB_PROJ_NAME@ PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${__import_prefix}/include;${__import_prefix}/include/@PROJECT_NAME@;${__import_prefix}/include/@SUB_PROJ_PATH@;${__import_prefix}/include/@SUB_PROJ_PATH@/private"
INTERFACE_COMPILE_DEFINITIONS "@SUB_MOD_DEFS@"
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${__import_prefix}/@SUB_MOD_LIB_DIR@/@SUB_MOD_FILE_NAME@"
IMPORTED_IMPLIB "${__import_prefix}/lib/@SUB_MOD_FILE_BASENAME@.lib"
VERSION "@PROJECT_VERSION@"
SOVERSION "@PROJECT_VERSION_MAJOR@"
__COMMIT "@PROJECT_VERSION_COMMIT@"
__COMPILE_DATETIME "@PROJECT_COMPILE_DATETIME@"
)
unset(__import_prefix)
endif()

View File

@ -28,6 +28,11 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate
- Widgets: Nested frameless windows are supported now!
- Linux: There have been many improvements to the Linux/X11 implementation! Most of them won't be directly visible to the user, but the code quality has been greatly improved.
- macOS: The frameless windows will now use native window frame and buttons, only the title bar itself is hidden, which also means the window will have round corners as all other native windows on macOS.
- Mica Material: It is now possible to load wallpaper images with very large file size or resolution, for example, 4K pictures. However, if the images have larger resolution than 1920x1080, they will be shrinked to reduce memory usage, and this process will also lower the image quality and break the aspect ratio of them.
- Mica Material: FramelessHelper will now use a seperate thread to load and apply special effects to the wallpaper image, to speed up application startup performance and avoid such process block the main thread.
- Window management: It is now possible to close the window (the dtor is executed) and show it again without breaking the frameless functionalities.
- Theme: It is now possible to force a desired theme instead of always respecting the system theme.
- Build system: The [**Ninja Multi-Config**](https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html) generator is fully supported now, finally!
- Routine bug fixes and internal refactorings.
## Highlights v2.3
@ -126,28 +131,70 @@ There are some additional restrictions for each platform, please refer to the _P
## Build
```bash
git clone --recursive https://github.com/wangwenx190/framelesshelper.git
mkdir A_TEMP_DIR
cd A_TEMP_DIR
git clone --recursive https://github.com/wangwenx190/framelesshelper.git # "--recursive" is necessary to clone the submodules.
mkdir build # Please change to your own build directory!
cd build
cmake -DCMAKE_PREFIX_PATH=<YOUR_QT_SDK_DIR_PATH> -DCMAKE_INSTALL_PREFIX=<WHERE_YOU_WANT_TO_INSTALL> -DCMAKE_BUILD_TYPE=Release -GNinja <PATH_TO_THE_REPOSITORY>
cmake --build . --config Release --target all --parallel
cmake --install . --config Release --strip
cmake --install . --config Release --strip # Don't add "--strip" for MSVC/Clang-CL/Intel-CL toolchains!
# YOUR_QT_SDK_DIR_PATH: the Qt SDK directory, something like "C:/Qt/6.5.1/msvc2019_64" or "/opt/Qt/6.5.1/gcc_64". Please change to your own path!
# WHERE_YOU_WANT_TO_INSTALL: the install directory of FramelessHelper, something like "../install". You can ignore this setting if you don't need to install the CMake package. Please change to your own path!
# PATH_TO_THE_REPOSITORY: the source code directory of FramelessHelper, something like "../framelesshelper". Please change to your own path!
```
You can also use `Qt6_DIR` or `Qt5_DIR` to replace `CMAKE_PREFIX_PATH`:
```bash
cmake -DQt6_DIR=C:/Qt/6.5.1/msvc2019_64/lib/cmake/Qt6 [other parameters ...]
# Or
cmake -DQt5_DIR=C:/Qt/5.15.2/msvc2019_64/lib/cmake/Qt5 [other parameters ...]
```
If there are any errors when cloning the submodules, try run `git submodule update --init --recursive --remote` in the project directory, that command will download & update all the submodules. If it fails again, try execute it multiple times until it finally succeeds.
Once the compilation and installation is done, you will be able to use the `find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick)` command to find and link to the FramelessHelper library. But before doing that, please make sure CMake knows where to find FramelessHelper, by passing the `CMAKE_PREFIX_PATH` variable to it. For example: `-DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc...`. Build FramelessHelper as a sub-directory of your CMake project is of course also supported. The supported FramelessHelper target names are `FramelessHelper::Core`, `FramelessHelper::Widgets` and `FramelessHelper::Quick`.
Once the compilation and installation is done, you will be able to use the `find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick)` command to find and link to the FramelessHelper library. But before doing that, please make sure CMake knows where to find FramelessHelper, by passing the `CMAKE_PREFIX_PATH` or `FramelessHelper_DIR` variable to it. For example: `-DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc...` or `-DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper`. Build FramelessHelper as a sub-directory of your CMake project is of course also supported. The supported FramelessHelper target names are `FramelessHelper::Core`, `FramelessHelper::Widgets` and `FramelessHelper::Quick`. Example code:
**IMPORTANT NOTE**: Currently *Ninja Multi-Config* is known to be **NOT** supported, you can only build one single configuration at a time, however, I'm planning to support it as soon as possible, in a future version.
```cmake
# Find Qt:
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
# Find FramelessHelper:
find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets)
# Create your target:
add_executable(demo)
# Add your source code:
target_sources(demo PRIVATE main.cpp)
# Link to Qt and FramelessHelper:
target_link_libraries(demo PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
FramelessHelper::Core
FramelessHelper::Widgets
)
```
If you need the syntax highlighting of FramelessHelper's Quick module, please set up the `QML_IMPORT_PATH` variable. Example code:
```cmake
# This is the path where you want FramelessHelper's Quick plugin (it only contains the QML meta
# info and an optional dummy library, for QtCreator's QML tooling purpose, it's not the Quick
# module) to place. Please change to your own path!
# If you are using add_subdirectory() to include FramelessHelper directly, you can change it to
# "${PROJECT_BINARY_DIR}/imports" instead of the install location.
set(FRAMELESSHELPER_IMPORT_DIR "C:/packages/FramelessHelper/qml")
list(APPEND QML_IMPORT_PATH "${FRAMELESSHELPER_IMPORT_DIR}")
list(REMOVE_DUPLICATES QML_IMPORT_PATH)
# Force cache refresh:
set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "Qt Creator extra QML import paths" FORCE)
```
## Use
### Qt Widgets
To customize the window frame of a QWidget, you need to instantiate a `FramelessWidgetsHelper` object and then attach it to the widget's top level widget, and then `FramelessWidgetsHelper` will do all the rest work for you: the window frame will be removed automatically once it has been attached to the top level widget successfully. In theory you can instantiate multiple `FramelessWidgetsHelper` objects for a same widget, in this case there will be only one object that keeps functional, all other objects will become a wrapper of that one. But to make sure everything goes smoothly and normally, you should not do that in any case. The simplest way to instantiate a `FramelessWidgetsHelper`
object is to call the static method `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)`. It will return the handle of the previously instantiated object if any, or it will instantiate a new object if it can't find one. It's safe to call this method multiple times for a same widget, it won't instantiate any new objects if there is one already. It also does not matter when and where you call that function as long as the top level widget is the same. The internally created objects will always be parented to the top level widget. Once you get the handle of the `FramelessWidgetsHelper` object, you can call `void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(true)` to let it hide the default title bar provided by the operating system. In order to make sure `FramelessWidgetsHelper` can find the correct top level widget, you should call the `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)` function on a widget which has a complete parent-chain whose root parent is the top level widget. To make the frameless window draggable, you should provide a homemade title bar widget yourself, the title bar widget doesn't need to be in rectangular shape, it also doesn't need to be placed on the first row of the window. Call `void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)` to let `FramelessHelper` know what's your title bar widget. By default, all the widgets in the title bar area won't be responsible to any mouse and keyboard events due to they have been intercepted by FramelessHelper. To make them recover the responsible state, you should make them visible to hit test. Call `void FramelessWidgetsHelper::setHitTestVisible(QWidget* )` to do that. You can of course call it on a widget that is not inside the title bar at all, it won't have any effect though. Due to Qt's own limitations, you need to make sure your widget has a complete parent-chain whose root parent is the top level widget. Do not ever try to delete the `FramelessWidgetsHelper` object, it may still be monitoring and controlling your widget, and Qt will delete it for you automatically. No need to worry about memory leaks.
object is to call the static method `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)`. It will return the handle of the previously instantiated object if any, or it will instantiate a new object if it can't find one. It's safe to call this method multiple times for a same widget, it won't instantiate any new objects if there is one already. It also does not matter when and where you call that function as long as the top level widget is the same. The internally created objects will always be parented to the top level widget. Once you get the handle of the `FramelessWidgetsHelper` object, you can call `void FramelessWidgetsHelper::extendsContentIntoTitleBar()` to let it hide the default title bar provided by the operating system. In order to make sure `FramelessWidgetsHelper` can find the correct top level widget, you should call the `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)` function on a widget which has a complete parent-chain whose root parent is the top level widget. To make the frameless window draggable, you should provide a homemade title bar widget yourself, the title bar widget doesn't need to be in rectangular shape, it also doesn't need to be placed on the first row of the window. Call `void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)` to let `FramelessHelper` know what's your title bar widget. By default, all the widgets in the title bar area won't be responsible to any mouse and keyboard events due to they have been intercepted by FramelessHelper. To make them recover the responsible state, you should make them visible to hit test. Call `void FramelessWidgetsHelper::setHitTestVisible(QWidget* )` to do that. You can of course call it on a widget that is not inside the title bar at all, it won't have any effect though. Due to Qt's own limitations, you need to make sure your widget has a complete parent-chain whose root parent is the top level widget. Do not ever try to delete the `FramelessWidgetsHelper` object, it may still be monitoring and controlling your widget, and Qt will delete it for you automatically. No need to worry about memory leaks.
There are also two classes called `FramelessWidget` and `FramelessMainWindow`, they are only simple wrappers of `FramelessWidgetsHelper`, which just saves the call of the `void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(true)` function for you. You can absolutely use plain `QWidget` instead.
There are also two classes called `FramelessWidget` and `FramelessMainWindow`, they are only simple wrappers of `FramelessWidgetsHelper`, which just saves the call of the `void FramelessWidgetsHelper::extendsContentIntoTitleBar()` function for you. You can absolutely use plain `QWidget` instead.
#### Code Snippet
@ -167,7 +214,7 @@ Then hide the standard title bar provided by the OS:
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get(this)->setContentExtendedIntoTitleBar(true);
FramelessWidgetsHelper::get(this)->extendsContentIntoTitleBar();
// ...
}
```

2
cmake

@ -1 +1 @@
Subproject commit 5b496224b13f31a5cc07cb48b56aed8d5e7e29de
Subproject commit 4b4b901807771eda16fb07f36a5cb40505f64087

View File

@ -24,6 +24,10 @@
set(DEMO_NAME FramelessHelperDemo-Dialog)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
if(FRAMELESSHELPER_EXAMPLES_STANDALONE)
cmake_minimum_required(VERSION 3.20)
project(${DEMO_NAME} VERSION 1.0)
@ -102,3 +106,5 @@ if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
endif()
deploy_qt_runtime(TARGET ${DEMO_NAME} ${__extra_flags})
endif()
#dump_target_info(TARGETS ${DEMO_NAME})

View File

@ -39,16 +39,23 @@ Dialog::~Dialog() = default;
void Dialog::closeEvent(QCloseEvent *event)
{
if (!parent()) {
Settings::set({}, kGeometry, geometry());
Settings::set({}, kDevicePixelRatio, devicePixelRatioF());
const QString id = objectName();
Settings::set(id, kGeometry, geometry());
Settings::set(id, kDevicePixelRatio, devicePixelRatioF());
}
FramelessDialog::closeEvent(event);
}
void Dialog::setupUi()
{
setWindowTitle(tr("Qt Dialog demo"));
setWindowTitle(tr("FramelessHelper demo application - QDialog"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
connect(this, &Dialog::objectNameChanged, this, [this](const QString &name){
if (name.isEmpty()) {
return;
}
setWindowTitle(windowTitle() + FRAMELESSHELPER_STRING_LITERAL(" [%1]").arg(name));
});
titleBar = new StandardTitleBar(this);
titleBar->setWindowIconVisible(true);
@ -142,9 +149,10 @@ void Dialog::waitReady()
{
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->waitForReady();
const auto savedGeometry = Settings::get<QRect>({}, kGeometry);
const QString id = objectName();
const auto savedGeometry = Settings::get<QRect>(id, kGeometry);
if (savedGeometry.isValid() && !parent()) {
const auto savedDpr = Settings::get<qreal>({}, kDevicePixelRatio);
const auto savedDpr = Settings::get<qreal>(id, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (devicePixelRatioF() / oldDpr);

View File

@ -29,6 +29,12 @@
FRAMELESSHELPER_USE_NAMESPACE
#define CREATE_WINDOW(Name) \
const auto Name = std::make_unique<Dialog>(); \
Name->setObjectName(FRAMELESSHELPER_STRING_LITERAL(#Name)); \
Name->waitReady(); \
Name->show();
int main(int argc, char *argv[])
{
Log::setup(FRAMELESSHELPER_STRING_LITERAL("dialog"));
@ -51,11 +57,11 @@ int main(int argc, char *argv[])
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
//FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto dialog = std::make_unique<Dialog>();
dialog->waitReady();
dialog->show();
CREATE_WINDOW(dialog1)
CREATE_WINDOW(dialog2)
CREATE_WINDOW(dialog3)
return QCoreApplication::exec();
}

View File

@ -24,6 +24,10 @@
set(DEMO_NAME FramelessHelperDemo-MainWindow)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
if(FRAMELESSHELPER_EXAMPLES_STANDALONE)
cmake_minimum_required(VERSION 3.20)
project(${DEMO_NAME} VERSION 1.0)
@ -107,3 +111,5 @@ if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
endif()
deploy_qt_runtime(TARGET ${DEMO_NAME} ${__extra_flags})
endif()
#dump_target_info(TARGETS ${DEMO_NAME})

View File

@ -29,6 +29,12 @@
FRAMELESSHELPER_USE_NAMESPACE
#define CREATE_WINDOW(Name) \
const auto Name = std::make_unique<MainWindow>(); \
Name->setObjectName(FRAMELESSHELPER_STRING_LITERAL(#Name)); \
Name->waitReady(); \
Name->show();
int main(int argc, char *argv[])
{
Log::setup(FRAMELESSHELPER_STRING_LITERAL("mainwindow"));
@ -51,11 +57,11 @@ int main(int argc, char *argv[])
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
//FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto mainWindow = std::make_unique<MainWindow>();
mainWindow->waitReady();
mainWindow->show();
CREATE_WINDOW(mainWindow1)
CREATE_WINDOW(mainWindow2)
CREATE_WINDOW(mainWindow3)
return QCoreApplication::exec();
}

View File

@ -60,9 +60,10 @@ MainWindow::~MainWindow() = default;
void MainWindow::closeEvent(QCloseEvent *event)
{
if (!parent()) {
Settings::set({}, kGeometry, geometry());
Settings::set({}, kState, saveState());
Settings::set({}, kDevicePixelRatio, devicePixelRatioF());
const QString id = objectName();
Settings::set(id, kGeometry, geometry());
Settings::set(id, kState, saveState());
Settings::set(id, kDevicePixelRatio, devicePixelRatioF());
}
FramelessMainWindow::closeEvent(event);
}
@ -108,8 +109,14 @@ QMenuBar::item:pressed {
#endif // Q_OS_MACOS
helper->setHitTestVisible(mb); // IMPORTANT!
setWindowTitle(tr("FramelessHelper demo application - Qt MainWindow"));
setWindowTitle(tr("FramelessHelper demo application - QMainWindow"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
connect(this, &MainWindow::objectNameChanged, this, [this](const QString &name){
if (name.isEmpty()) {
return;
}
setWindowTitle(windowTitle() + FRAMELESSHELPER_STRING_LITERAL(" [%1]").arg(name));
});
connect(m_mainWindow->pushButton, &QPushButton::clicked, this, [this]{
const auto dialog = new Dialog(this);
dialog->waitReady();
@ -128,9 +135,10 @@ void MainWindow::waitReady()
{
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->waitForReady();
const auto savedGeometry = Settings::get<QRect>({}, kGeometry);
const QString id = objectName();
const auto savedGeometry = Settings::get<QRect>(id, kGeometry);
if (savedGeometry.isValid() && !parent()) {
const auto savedDpr = Settings::get<qreal>({}, kDevicePixelRatio);
const auto savedDpr = Settings::get<qreal>(id, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (devicePixelRatioF() / oldDpr);
@ -138,7 +146,7 @@ void MainWindow::waitReady()
} else {
helper->moveWindowToDesktopCenter();
}
const QByteArray savedState = Settings::get<QByteArray>({}, kState);
const QByteArray savedState = Settings::get<QByteArray>(id, kState);
if (!savedState.isEmpty() && !parent()) {
restoreState(savedState);
}

View File

@ -24,6 +24,10 @@
set(DEMO_NAME FramelessHelperDemo-OpenGLWidget)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
if(FRAMELESSHELPER_EXAMPLES_STANDALONE)
cmake_minimum_required(VERSION 3.20)
project(${DEMO_NAME} VERSION 1.0)
@ -117,3 +121,5 @@ if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
endif()
deploy_qt_runtime(TARGET ${DEMO_NAME} ${__extra_flags})
endif()
#dump_target_info(TARGETS ${DEMO_NAME})

View File

@ -24,6 +24,10 @@
set(DEMO_NAME FramelessHelperDemo-Quick)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
if(FRAMELESSHELPER_EXAMPLES_STANDALONE)
cmake_minimum_required(VERSION 3.20)
project(${DEMO_NAME} VERSION 1.0)
@ -138,3 +142,5 @@ if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
${__extra_flags}
)
endif()
#dump_target_info(TARGETS ${DEMO_NAME})

View File

@ -64,7 +64,7 @@ int main(int argc, char *argv[])
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
//FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
// Enable QtRHI debug output if not explicitly requested by the user.
if (!qEnvironmentVariableIsSet("QSG_INFO")) {
@ -129,14 +129,14 @@ int main(int argc, char *argv[])
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QObject::connect(engine.get(), &QQmlApplicationEngine::objectCreationFailed, qApp,
QObject::connect(engine.get(), &QQmlApplicationEngine::objectCreationFailed, application.get(),
[](const QUrl &url){
qCritical() << "The QML engine failed to create component:" << url;
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
#elif !QMLTC_ENABLED
const QMetaObject::Connection connection = QObject::connect(
engine.get(), &QQmlApplicationEngine::objectCreated, &application,
engine.get(), &QQmlApplicationEngine::objectCreated, application.get(),
[&mainUrl, &connection](QObject *object, const QUrl &url) {
if (url != mainUrl) {
return;

View File

@ -24,6 +24,10 @@
set(DEMO_NAME FramelessHelperDemo-Widget)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
if(FRAMELESSHELPER_EXAMPLES_STANDALONE)
cmake_minimum_required(VERSION 3.20)
project(${DEMO_NAME} VERSION 1.0)
@ -102,3 +106,5 @@ if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
endif()
deploy_qt_runtime(TARGET ${DEMO_NAME} ${__extra_flags})
endif()
#dump_target_info(TARGETS ${DEMO_NAME})

View File

@ -29,6 +29,12 @@
FRAMELESSHELPER_USE_NAMESPACE
#define CREATE_WINDOW(Name) \
const auto Name = std::make_unique<Widget>(); \
Name->setObjectName(FRAMELESSHELPER_STRING_LITERAL(#Name)); \
Name->waitReady(); \
Name->show();
int main(int argc, char *argv[])
{
Log::setup(FRAMELESSHELPER_STRING_LITERAL("widget"));
@ -51,17 +57,11 @@ int main(int argc, char *argv[])
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
//FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto window1 = std::make_unique<Widget>();
window1->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window1"));
window1->waitReady();
window1->show();
const auto window2 = std::make_unique<Widget>();
window2->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window2"));
window2->waitReady();
window2->show();
CREATE_WINDOW(widget1)
CREATE_WINDOW(widget2)
CREATE_WINDOW(widget3)
return QCoreApplication::exec();
}

View File

@ -33,7 +33,6 @@
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qfileiconprovider.h>
#include <FramelessHelper/Core/framelessmanager.h>
#include <FramelessHelper/Core/utils.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
@ -81,7 +80,7 @@ void Widget::closeEvent(QCloseEvent *event)
void Widget::initialize()
{
setWindowTitle(tr("FramelessHelper demo application - Qt Widgets"));
setWindowTitle(tr("FramelessHelper demo application - QWidget"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
resize(800, 600);
m_titleBar = new StandardTitleBar(this);
@ -144,7 +143,7 @@ void Widget::initialize()
void Widget::updateStyleSheet()
{
const bool dark = Utils::shouldAppsUseDarkMode();
const bool dark = (FramelessManager::instance()->systemTheme() == SystemTheme::Dark);
const QColor clockLabelTextColor = (dark ? kDefaultWhiteColor : kDefaultBlackColor);
m_clockLabel->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent; color: %1;")
.arg(clockLabelTextColor.name()));

View File

@ -138,6 +138,14 @@
# define IsMaximized(hwnd) (IsZoomed(hwnd) != FALSE)
#endif
#ifndef RECT_WIDTH
# define RECT_WIDTH(rect) ((rect).right - (rect).left)
#endif
#ifndef RECT_HEIGHT
# define RECT_HEIGHT(rect) ((rect).bottom - (rect).top)
#endif
#ifndef MMSYSERR_NOERROR
# define MMSYSERR_NOERROR (0)
#endif

View File

@ -259,145 +259,147 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API)
[[maybe_unused]] inline const QByteArray kSysMenuDisableRestoreVar
= FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_SYSTEM_MENU_DISABLE_RESTORE");
enum class Option
enum class Option : quint8
{
UseCrossPlatformQtImplementation = 0,
ForceHideWindowFrameBorder = 1,
ForceShowWindowFrameBorder = 2,
DisableWindowsSnapLayout = 3,
WindowUseRoundCorners = 4,
CenterWindowBeforeShow = 5,
EnableBlurBehindWindow = 6,
ForceNonNativeBackgroundBlur = 7,
DisableLazyInitializationForMicaMaterial = 8,
ForceNativeBackgroundBlur = 9
UseCrossPlatformQtImplementation,
ForceHideWindowFrameBorder,
ForceShowWindowFrameBorder,
DisableWindowsSnapLayout,
WindowUseRoundCorners,
CenterWindowBeforeShow,
EnableBlurBehindWindow,
ForceNonNativeBackgroundBlur,
DisableLazyInitializationForMicaMaterial,
ForceNativeBackgroundBlur
};
Q_ENUM_NS(Option)
enum class SystemTheme
enum class SystemTheme : quint8
{
Unknown = -1,
Light = 0,
Dark = 1,
HighContrast = 2
Unknown,
Light,
Dark,
HighContrast
};
Q_ENUM_NS(SystemTheme)
enum class SystemButtonType
enum class SystemButtonType : quint8
{
Unknown = -1,
WindowIcon = 0,
Help = 1,
Minimize = 2,
Maximize = 3,
Restore = 4,
Close = 5
Unknown,
WindowIcon,
Help,
Minimize,
Maximize,
Restore,
Close
};
Q_ENUM_NS(SystemButtonType)
#ifdef Q_OS_WINDOWS
enum class DwmColorizationArea
enum class DwmColorizationArea : quint8
{
None = 0,
StartMenu_TaskBar_ActionCenter = 1,
TitleBar_WindowBorder = 2,
All = 3
None,
StartMenu_TaskBar_ActionCenter,
TitleBar_WindowBorder,
All
};
Q_ENUM_NS(DwmColorizationArea)
#endif // Q_OS_WINDOWS
enum class ButtonState
enum class ButtonState : quint8
{
Unspecified = -1,
Hovered = 0,
Pressed = 1,
Clicked = 2
Normal,
Hovered,
Pressed,
Released
};
Q_ENUM_NS(ButtonState)
#ifdef Q_OS_WINDOWS
enum class WindowsVersion
enum class WindowsVersion : quint8
{
_2000 = 0,
_XP = 1,
_XP_64 = 2,
_2000,
_XP,
_XP_64,
_Vista,
_Vista_SP1,
_Vista_SP2,
_7,
_7_SP1,
_8,
_8_1,
_8_1_Update1,
_10_1507,
_10_1511,
_10_1607,
_10_1703,
_10_1709,
_10_1803,
_10_1809,
_10_1903,
_10_1909,
_10_2004,
_10_20H2,
_10_21H1,
_10_21H2,
_10_22H2,
_11_21H2,
_11_22H2,
_WS_03 = _XP_64, // Windows Server 2003
_Vista = 3,
_Vista_SP1 = 4,
_Vista_SP2 = 5,
_7 = 6,
_7_SP1 = 7,
_8 = 8,
_8_1 = 9,
_8_1_Update1 = 10,
_10_1507 = 11,
_10_1511 = 12,
_10_1607 = 13,
_10_1703 = 14,
_10_1709 = 15,
_10_1803 = 16,
_10_1809 = 17,
_10_1903 = 18,
_10_1909 = 19,
_10_2004 = 20,
_10_20H2 = 21,
_10_21H1 = 22,
_10_21H2 = 23,
_10_22H2 = 24,
_10 = _10_1507,
_11_21H2 = 25,
_11_22H2 = 26,
_11 = _11_21H2,
Latest = _11_22H2
};
Q_ENUM_NS(WindowsVersion)
#endif // Q_OS_WINDOWS
enum class BlurMode
enum class BlurMode : quint8
{
Disable = 0, // Do not enable blur behind window
Default = 1, // Use platform default blur mode
Windows_Aero = 2, // Windows only, use the traditional DWM blur
Windows_Acrylic = 3, // Windows only, use the Acrylic blur
Windows_Mica = 4, // Windows only, use the Mica material
Windows_MicaAlt = 5 // Windows only, use the Mica Alt material
Disable, // Do not enable blur behind window
Default, // Use platform default blur mode
Windows_Aero, // Windows only, use the traditional DWM blur
Windows_Acrylic, // Windows only, use the Acrylic blur
Windows_Mica, // Windows only, use the Mica material
Windows_MicaAlt // Windows only, use the Mica Alt material
};
Q_ENUM_NS(BlurMode)
enum class WallpaperAspectStyle
enum class WallpaperAspectStyle : quint8
{
Fill = 0, // Keep aspect ratio to fill, expand/crop if necessary.
Fit = 1, // Keep aspect ratio to fill, but don't expand/crop.
Stretch = 2, // Ignore aspect ratio to fill.
Tile = 3,
Center = 4,
Span = 5 // ???
Fill, // Keep aspect ratio to fill, expand/crop if necessary.
Fit, // Keep aspect ratio to fill, but don't expand/crop.
Stretch, // Ignore aspect ratio to fill.
Tile,
Center,
Span // ???
};
Q_ENUM_NS(WallpaperAspectStyle)
#ifdef Q_OS_WINDOWS
enum class RegistryRootKey
enum class RegistryRootKey : quint8
{
ClassesRoot = 0,
CurrentUser = 1,
LocalMachine = 2,
Users = 3,
PerformanceData = 4,
CurrentConfig = 5,
DynData = 6,
CurrentUserLocalSettings = 7,
PerformanceText = 8,
PerformanceNlsText = 9
ClassesRoot,
CurrentUser,
LocalMachine,
Users,
PerformanceData,
CurrentConfig,
DynData,
CurrentUserLocalSettings,
PerformanceText,
PerformanceNlsText
};
Q_ENUM_NS(RegistryRootKey)
#endif // Q_OS_WINDOWS
enum class WindowEdge : quint32
enum class WindowEdge : quint8
{
Left = 0x00000001,
Top = 0x00000002,
Right = 0x00000004,
Bottom = 0x00000008
Left = 1 << 0,
Top = 1 << 1,
Right = 1 << 2,
Bottom = 1 << 3
};
Q_ENUM_NS(WindowEdge)
Q_DECLARE_FLAGS(WindowEdges, WindowEdge)
@ -405,23 +407,23 @@ Q_FLAG_NS(WindowEdges)
Q_DECLARE_OPERATORS_FOR_FLAGS(WindowEdges)
#ifdef Q_OS_WINDOWS
enum class DpiAwareness
enum class DpiAwareness : quint8
{
Unknown = -1,
Unaware = 0,
System = 1,
PerMonitor = 2,
PerMonitorVersion2 = 3,
Unaware_GdiScaled = 4
Unknown,
Unaware,
System,
PerMonitor,
PerMonitorVersion2,
Unaware_GdiScaled
};
Q_ENUM_NS(DpiAwareness)
#endif // Q_OS_WINDOWS
enum class WindowCornerStyle
enum class WindowCornerStyle : quint8
{
Default = 0,
Square = 1,
Round = 2
Default,
Square,
Round
};
Q_ENUM_NS(WindowCornerStyle)
@ -440,6 +442,16 @@ struct Dpi
{
quint32 x = 0;
quint32 y = 0;
[[nodiscard]] friend constexpr bool operator==(const Dpi &lhs, const Dpi &rhs) noexcept
{
return ((lhs.x == rhs.x) && (lhs.y == rhs.y));
}
[[nodiscard]] friend constexpr bool operator!=(const Dpi &lhs, const Dpi &rhs) noexcept
{
return !operator==(lhs, rhs);
}
};
} // namespace Global

View File

@ -36,7 +36,7 @@ class FRAMELESSHELPER_CORE_API FramelessManager : public QObject
Q_OBJECT
Q_DECLARE_PRIVATE(FramelessManager)
Q_DISABLE_COPY_MOVE(FramelessManager)
Q_PROPERTY(Global::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL)
Q_PROPERTY(Global::SystemTheme systemTheme READ systemTheme WRITE setOverrideTheme NOTIFY systemThemeChanged FINAL)
Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemThemeChanged FINAL)
Q_PROPERTY(QString wallpaper READ wallpaper NOTIFY wallpaperChanged FINAL)
Q_PROPERTY(Global::WallpaperAspectStyle wallpaperAspectStyle READ wallpaperAspectStyle NOTIFY wallpaperChanged FINAL)
@ -55,6 +55,7 @@ public:
public Q_SLOTS:
void addWindow(const SystemParameters *params);
void removeWindow(const WId windowId);
void setOverrideTheme(const Global::SystemTheme theme);
Q_SIGNALS:
void systemThemeChanged();

View File

@ -38,7 +38,9 @@ class FRAMELESSHELPER_CORE_API MicaMaterial : public QObject
Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged FINAL)
Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged FINAL)
Q_PROPERTY(QColor fallbackColor READ fallbackColor WRITE setFallbackColor NOTIFY fallbackColorChanged FINAL)
Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged FINAL)
Q_PROPERTY(bool fallbackEnabled READ isFallbackEnabled WRITE setFallbackEnabled NOTIFY fallbackEnabledChanged FINAL)
public:
explicit MicaMaterial(QObject *parent = nullptr);
@ -50,16 +52,24 @@ public:
Q_NODISCARD qreal tintOpacity() const;
void setTintOpacity(const qreal value);
Q_NODISCARD QColor fallbackColor() const;
void setFallbackColor(const QColor &value);
Q_NODISCARD qreal noiseOpacity() const;
void setNoiseOpacity(const qreal value);
Q_NODISCARD bool isFallbackEnabled() const;
void setFallbackEnabled(const bool value);
public Q_SLOTS:
void paint(QPainter *painter, const QSize &size, const QPoint &pos);
void paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active = true);
Q_SIGNALS:
void tintColorChanged();
void tintOpacityChanged();
void fallbackColorChanged();
void noiseOpacityChanged();
void fallbackEnabledChanged();
void shouldRedraw();
private:

View File

@ -61,6 +61,7 @@ using GetPropertyCallback = std::function<QVariant(const QByteArray &, const QVa
using SetCursorCallback = std::function<void(const QCursor &)>;
using UnsetCursorCallback = std::function<void()>;
using GetWidgetHandleCallback = std::function<QObject *()>;
using ForceChildrenRepaintCallback = std::function<void(const int)>;
struct SystemParameters
{
@ -90,6 +91,7 @@ struct SystemParameters
SetCursorCallback setCursor = nullptr;
UnsetCursorCallback unsetCursor = nullptr;
GetWidgetHandleCallback getWidgetHandle = nullptr;
ForceChildrenRepaintCallback forceChildrenRepaint = nullptr;
};
using FramelessParams = SystemParameters *;

View File

@ -60,12 +60,16 @@ public:
Q_NODISCARD static bool usePureQtImplementation();
void setOverrideTheme(const Global::SystemTheme theme);
Q_NODISCARD bool isThemeOverrided() const;
private:
void initialize();
private:
FramelessManager *q_ptr = nullptr;
Global::SystemTheme m_systemTheme = Global::SystemTheme::Unknown;
std::optional<Global::SystemTheme> m_overrideTheme = std::nullopt;
QColor m_accentColor = {};
#ifdef Q_OS_WINDOWS
Global::DwmColorizationArea m_colorizationArea = Global::DwmColorizationArea::None;

View File

@ -31,6 +31,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
class MicaMaterial;
using Transform = struct Transform
{
qreal Horizontal = 0;
qreal Vertical = 0;
};
class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject
{
Q_OBJECT
@ -44,10 +50,12 @@ public:
Q_NODISCARD static MicaMaterialPrivate *get(MicaMaterial *q);
Q_NODISCARD static const MicaMaterialPrivate *get(const MicaMaterial *q);
Q_NODISCARD static QColor systemFallbackColor();
public Q_SLOTS:
void maybeGenerateBlurredWallpaper(const bool force = false);
void updateMaterialBrush();
void paint(QPainter *painter, const QSize &size, const QPoint &pos);
void paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active = true);
private:
void initialize();
@ -57,9 +65,12 @@ private:
MicaMaterial *q_ptr = nullptr;
QColor tintColor = {};
qreal tintOpacity = 0.0;
QColor fallbackColor = {};
qreal noiseOpacity = 0.0;
bool fallbackEnabled = true;
QBrush micaBrush = {};
bool initialized = false;
Transform transform = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -52,7 +52,6 @@ FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges
[[nodiscard]] FRAMELESSHELPER_CORE_API QWindow *findWindow(const WId windowId);
FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
const SystemParameters *params, const bool considerTaskBar);
[[nodiscard]] FRAMELESSHELPER_CORE_API Global::SystemTheme getSystemTheme();
[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::WindowState windowStatesToWindowState(
const Qt::WindowStates states);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isThemeChangeEvent(const QEvent * const event);
@ -81,6 +80,11 @@ FRAMELESSHELPER_CORE_API void registerThemeChangeNotification();
[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point);
[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint fromNativeGlobalPosition(const QWindow *window, const QPoint &point);
[[nodiscard]] FRAMELESSHELPER_CORE_API int horizontalAdvance(const QFontMetrics &fm, const QString &str);
[[nodiscard]] FRAMELESSHELPER_CORE_API qreal getRelativeScaleFactor(const quint32 oldDpi, const quint32 newDpi);
[[nodiscard]] FRAMELESSHELPER_CORE_API QSize rescaleSize(const QSize &oldSize, const quint32 oldDpi, const quint32 newDpi);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isValidGeometry(const QRect &rect);
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getAccentColor();
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 defaultScreenDpi();
#ifdef Q_OS_WINDOWS
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version);
@ -122,8 +126,8 @@ FRAMELESSHELPER_CORE_API void setAeroSnappingEnabled(const WId windowId, const b
FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel();
FRAMELESSHELPER_CORE_API void updateGlobalWin32ControlsTheme(const WId windowId, const bool dark);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_windows();
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getAccentColor_windows();
FRAMELESSHELPER_CORE_API void setCornerStyleForWindow(const WId windowId, const Global::WindowCornerStyle style);
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getDwmAccentColor();
FRAMELESSHELPER_CORE_API void hideOriginalTitleBarElements
(const WId windowId, const bool disable = true);
FRAMELESSHELPER_CORE_API void setQtDarkModeAwareEnabled(const bool enable);
@ -135,6 +139,10 @@ FRAMELESSHELPER_CORE_API void fixupChildWindowsDpiMessage(const WId windowId);
FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling();
FRAMELESSHELPER_CORE_API void setDarkModeAllowedForApp(const bool allow = true);
FRAMELESSHELPER_CORE_API void bringWindowToFront(const WId windowId);
[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint getWindowPlacementOffset(const WId windowId);
[[nodiscard]] FRAMELESSHELPER_CORE_API QRect getWindowRestoreGeometry(const WId windowId);
FRAMELESSHELPER_CORE_API void removeMicaWindow(const WId windowId);
FRAMELESSHELPER_CORE_API void removeSysMenuHook(const WId windowId);
#endif // Q_OS_WINDOWS
#ifdef Q_OS_LINUX
@ -161,7 +169,7 @@ FRAMELESSHELPER_CORE_API void clearWindowProperty(const WId windowId, const xcb_
[[nodiscard]] FRAMELESSHELPER_CORE_API bool tryHideSystemTitleBar(const WId windowId, const bool hide = true);
FRAMELESSHELPER_CORE_API void openSystemMenu(const WId windowId, const QPoint &globalPos);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_linux();
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getWmThemeColor();
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getAccentColor_linux();
FRAMELESSHELPER_CORE_API void sendMoveResizeMessage
(const WId windowId, const uint32_t action, const QPoint &globalPos, const Qt::MouseButton button = Qt::LeftButton);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isCustomDecorationSupported();
@ -171,8 +179,8 @@ FRAMELESSHELPER_CORE_API void sendMoveResizeMessage
#ifdef Q_OS_MACOS
[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_macos();
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getAccentColor_macos();
FRAMELESSHELPER_CORE_API void setSystemTitleBarVisible(const WId windowId, const bool visible);
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getControlsAccentColor();
FRAMELESSHELPER_CORE_API void removeWindowProxy(const WId windowId);
#endif // Q_OS_MACOS
} // namespace Utils

View File

@ -90,7 +90,7 @@ public:
explicit QuickGlobal(QObject *parent = nullptr);
~QuickGlobal() override;
enum class SystemTheme
enum class SystemTheme : quint8
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Unknown)
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Light)
@ -99,7 +99,7 @@ public:
};
Q_ENUM(SystemTheme)
enum class SystemButtonType
enum class SystemButtonType : quint8
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Unknown)
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, WindowIcon)
@ -111,16 +111,16 @@ public:
};
Q_ENUM(SystemButtonType)
enum class ButtonState
enum class ButtonState : quint8
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Unspecified)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Normal)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Hovered)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Pressed)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Clicked)
FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Released)
};
Q_ENUM(ButtonState)
enum class BlurMode
enum class BlurMode : quint8
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Disable)
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Default)
@ -131,7 +131,7 @@ public:
};
Q_ENUM(BlurMode)
enum class WindowEdge : quint32
enum class WindowEdge : quint8
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Left)
FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Top)

View File

@ -47,7 +47,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject, public QQm
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL)
Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL)
Q_PROPERTY(QuickGlobal::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL)
Q_PROPERTY(QuickGlobal::SystemTheme systemTheme READ systemTheme WRITE setOverrideTheme NOTIFY systemThemeChanged FINAL)
Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL)
Q_PROPERTY(bool titleBarColorized READ titleBarColorized NOTIFY titleBarColorizedChanged FINAL)
Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL)
@ -65,6 +65,7 @@ public:
Q_NODISCARD bool frameBorderVisible() const;
Q_NODISCARD qreal frameBorderThickness() const;
Q_NODISCARD QuickGlobal::SystemTheme systemTheme() const;
void setOverrideTheme(const QuickGlobal::SystemTheme theme);
Q_NODISCARD QColor systemAccentColor() const;
Q_NODISCARD bool titleBarColorized() const;
Q_NODISCARD QColor defaultSystemLightColor() const;

View File

@ -88,6 +88,8 @@ public:
Q_NODISCARD bool isReady() const;
void waitForReady();
void repaintAllChildren(const int delay = 0) const;
private:
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const;

View File

@ -26,8 +26,13 @@
#include <FramelessHelper/Quick/framelesshelperquick_global.h>
QT_BEGIN_NAMESPACE
class QQuickRectangle;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class MicaMaterial;
class QuickMicaMaterial;
class WallpaperImageNode;
@ -48,15 +53,21 @@ public Q_SLOTS:
void rebindWindow();
void forceRegenerateWallpaperImageCache();
void appendNode(WallpaperImageNode *node);
void updateFallbackColor();
private:
void initialize();
private:
friend class WallpaperImageNode;
QuickMicaMaterial *q_ptr = nullptr;
QMetaObject::Connection m_rootWindowXChangedConnection = {};
QMetaObject::Connection m_rootWindowYChangedConnection = {};
QMetaObject::Connection m_rootWindowActiveChangedConnection = {};
QList<QPointer<WallpaperImageNode>> m_nodes = {};
QQuickRectangle *m_fallbackColorItem = nullptr;
MicaMaterial *m_micaMaterial = nullptr;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -40,10 +40,38 @@ class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickItem
Q_DISABLE_COPY_MOVE(QuickMicaMaterial)
Q_DECLARE_PRIVATE(QuickMicaMaterial)
Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged FINAL)
Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged FINAL)
Q_PROPERTY(QColor fallbackColor READ fallbackColor WRITE setFallbackColor NOTIFY fallbackColorChanged FINAL)
Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged FINAL)
Q_PROPERTY(bool fallbackEnabled READ isFallbackEnabled WRITE setFallbackEnabled NOTIFY fallbackEnabledChanged FINAL)
public:
explicit QuickMicaMaterial(QQuickItem *parent = nullptr);
~QuickMicaMaterial() override;
Q_NODISCARD QColor tintColor() const;
void setTintColor(const QColor &value);
Q_NODISCARD qreal tintOpacity() const;
void setTintOpacity(const qreal value);
Q_NODISCARD QColor fallbackColor() const;
void setFallbackColor(const QColor &value);
Q_NODISCARD qreal noiseOpacity() const;
void setNoiseOpacity(const qreal value);
Q_NODISCARD bool isFallbackEnabled() const;
void setFallbackEnabled(const bool value);
Q_SIGNALS:
void tintColorChanged();
void tintOpacityChanged();
void fallbackColorChanged();
void noiseOpacityChanged();
void fallbackEnabledChanged();
protected:
void itemChange(const ItemChange change, const ItemChangeData &value) override;
[[nodiscard]] QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) override;

View File

@ -26,6 +26,7 @@
#include <FramelessHelper/Widgets/framelesshelperwidgets_global.h>
#include <QtCore/qvariant.h>
#include <QtWidgets/qsizepolicy.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -89,6 +90,8 @@ public:
Q_NODISCARD bool isReady() const;
void waitForReady();
void repaintAllChildren(const int delay = 0) const;
private:
Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const;
@ -106,6 +109,7 @@ private:
QPointer<QWidget> m_window = nullptr;
bool m_destroying = false;
bool m_qpaReady = false;
QSizePolicy m_savedSizePolicy = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LibraryPath>$(MSBuildThisFileDirectory)lib;$(LibraryPath)</LibraryPath>
<LibraryPath>$(MSBuildThisFileDirectory)lib64;$(MSBuildThisFileDirectory)lib64\debug;$(MSBuildThisFileDirectory)lib64\release;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Label="QtSettings">
<QtHeaderSearchPath>$(MSBuildThisFileDirectory)include;$(MSBuildThisFileDirectory)include\FramelessHelper;$(MSBuildThisFileDirectory)include\FramelessHelper\Core;$(MSBuildThisFileDirectory)include\FramelessHelper\Core\private;$(MSBuildThisFileDirectory)include\FramelessHelper\Widgets;$(MSBuildThisFileDirectory)include\FramelessHelper\Widgets\private;$(MSBuildThisFileDirectory)include\FramelessHelper\Quick;$(MSBuildThisFileDirectory)include\FramelessHelper\Quick\private;$(QtHeaderSearchPath)</QtHeaderSearchPath>

View File

@ -34,10 +34,10 @@
#define _FRAMELESSHELPER_VERSION_DEFINED_
[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MAJOR = 2;
[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MINOR = 3;
[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_PATCH = 6;
[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MINOR = 4;
[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_PATCH = 0;
//[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_TWEAK = 0;
[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_VERSION_STR[] = "2.3.6";
[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_VERSION_STR[] = "2.4.0";
[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_COMMIT_STR[] = "UNKNOWN";
[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_COMPILE_DATETIME_STR[] = "UNKNOWN";

View File

@ -24,6 +24,10 @@
include(GNUInstallDirs)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
if(UNIX AND NOT APPLE)
if(FRAMELESSHELPER_NO_PRIVATE)
# Qt X11Extras is only available in Qt5.
@ -48,11 +52,11 @@ if(UNIX AND NOT APPLE)
endif()
endif()
set(SUB_MOD_NAME Core)
set(SUB_PROJ_NAME ${PROJECT_NAME}${SUB_MOD_NAME})
set(SUB_PROJ_PATH ${PROJECT_NAME}/${SUB_MOD_NAME})
set(SUB_MODULE Core)
set(SUB_MODULE_FULL_NAME ${PROJECT_NAME}${SUB_MODULE})
set(SUB_MODULE_PATH ${PROJECT_NAME}/${SUB_MODULE})
set(INCLUDE_PREFIX ../../include/${SUB_PROJ_PATH})
set(INCLUDE_PREFIX ../../include/${SUB_MODULE_PATH})
configure_file(framelesshelper.version.in
${CMAKE_CURRENT_BINARY_DIR}/framelesshelper.version @ONLY)
@ -138,18 +142,20 @@ elseif(UNIX)
endif()
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
set(__rc_path "${CMAKE_CURRENT_BINARY_DIR}/${SUB_PROJ_NAME}.rc")
set(__rc_path "${CMAKE_CURRENT_BINARY_DIR}/${SUB_MODULE_FULL_NAME}.rc")
if(NOT EXISTS "${__rc_path}")
generate_win32_rc_file(
PATH "${__rc_path}"
VERSION "${PROJECT_VERSION}"
COMPANY "wangwenx190"
DESCRIPTION "${PROJECT_NAME} ${SUB_MOD_NAME} Module"
DESCRIPTION "${PROJECT_NAME} ${SUB_MODULE} Module"
COPYRIGHT "MIT License"
ORIGINAL_FILENAME "${PROJECT_NAME}${SUB_MOD_NAME}.dll"
ORIGINAL_FILENAME "${PROJECT_NAME}${SUB_MODULE}.dll"
PRODUCT "${PROJECT_NAME}"
COMMENTS "Built from commit ${PROJECT_VERSION_COMMIT} on ${PROJECT_COMPILE_DATETIME} (UTC)."
LIBRARY
)
endif()
list(APPEND SOURCES "${__rc_path}")
endif()
@ -160,56 +166,22 @@ if(FRAMELESSHELPER_BUILD_STATIC)
else()
set(SUB_MOD_LIB_TYPE "SHARED")
endif()
add_library(${SUB_PROJ_NAME} ${SUB_MOD_LIB_TYPE} ${ALL_SOURCES})
add_library(${PROJECT_NAME}::${SUB_PROJ_NAME} ALIAS ${SUB_PROJ_NAME})
add_library(${PROJECT_NAME}::${SUB_MOD_NAME} ALIAS ${SUB_PROJ_NAME})
add_library(${SUB_MODULE} ${SUB_MOD_LIB_TYPE} ${ALL_SOURCES})
add_library(${SUB_MODULE_FULL_NAME} ALIAS ${SUB_MODULE})
add_library(${PROJECT_NAME}::${SUB_MODULE} ALIAS ${SUB_MODULE})
add_library(${PROJECT_NAME}::${SUB_MODULE_FULL_NAME} ALIAS ${SUB_MODULE})
set_target_properties(${SUB_PROJ_NAME} PROPERTIES
set_target_properties(${SUB_MODULE} PROPERTIES
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION_MAJOR}"
OUTPUT_NAME "${SUB_MODULE_FULL_NAME}"
)
set(SUB_MOD_TARGETS ${SUB_PROJ_NAME})
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
set(SUB_MOD_LIB_DIR "${CMAKE_INSTALL_BINDIR}")
else()
set(SUB_MOD_LIB_DIR "${CMAKE_INSTALL_LIBDIR}")
endif()
set(__prefix "")
if(NOT WIN32)
set(__prefix "lib")
endif()
set(__suffix "")
if(FRAMELESSHELPER_BUILD_STATIC)
if(MSVC)
set(__suffix "lib")
else()
set(__suffix "a")
endif()
else()
if(WIN32)
set(__suffix "dll")
elseif(APPLE)
set(__suffix "dylib")
elseif(UNIX)
set(__suffix "so")
endif()
endif()
set(SUB_MOD_FILE_PREFIX "${__prefix}")
set(SUB_MOD_FILE_SUFFIX "${__suffix}")
set(SUB_MOD_FILE_BASENAME "${SUB_MOD_FILE_PREFIX}${SUB_PROJ_NAME}")
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
string(APPEND SUB_MOD_FILE_BASENAME "${CMAKE_DEBUG_POSTFIX}")
endif()
set(SUB_MOD_FILE_NAME "${SUB_MOD_FILE_BASENAME}.${SUB_MOD_FILE_SUFFIX}")
unset(__suffix)
unset(__prefix)
set(__export_targets ${SUB_MODULE})
if(NOT FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
qt_add_resources(${SUB_PROJ_NAME} framelesshelpercore
qt_add_resources(${SUB_MODULE} framelesshelpercore
PREFIX
"/org.wangwenx190.${PROJECT_NAME}"
FILES
@ -218,115 +190,103 @@ if(NOT FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
OUTPUT_TARGETS __qrc_targets
)
if(__qrc_targets)
foreach(__target ${__qrc_targets})
list(APPEND SUB_MOD_TARGETS ${__target})
list(APPEND __export_targets ${__qrc_targets})
if(FRAMELESSHELPER_BUILD_STATIC)
target_sources(${SUB_PROJ_NAME} PRIVATE
foreach(__target ${__qrc_targets})
target_sources(${SUB_MODULE} PRIVATE
$<TARGET_OBJECTS:${__target}>
)
endif()
endforeach()
endif()
endif()
else()
target_sources(${SUB_PROJ_NAME} PRIVATE
target_sources(${SUB_MODULE} PRIVATE
framelesshelpercore.qrc
)
endif()
endif()
if(FRAMELESSHELPER_BUILD_STATIC)
set(__def FRAMELESSHELPER_CORE_STATIC)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_CORE_STATIC)
endif()
if(FRAMELESSHELPER_NO_DEBUG_OUTPUT)
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT
)
endif()
if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
set(__def FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE)
endif()
if(FRAMELESSHELPER_NO_PRIVATE)
set(__def FRAMELESSHELPER_CORE_NO_PRIVATE)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_CORE_NO_PRIVATE)
endif()
if(DEFINED FRAMELESSHELPER_NAMESPACE)
if("x${FRAMELESSHELPER_NAMESPACE}" STREQUAL "x")
message(FATAL_ERROR "FRAMELESSHELPER_NAMESPACE can't be empty!")
endif()
set(__def FRAMELESSHELPER_NAMESPACE=${FRAMELESSHELPER_NAMESPACE})
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_NAMESPACE=${FRAMELESSHELPER_NAMESPACE})
endif()
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
FRAMELESSHELPER_CORE_LIBRARY
)
if(APPLE)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
"-framework Foundation"
"-framework Cocoa"
"-framework AppKit"
)
elseif(UNIX)
if(TARGET X11::xcb)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
X11::xcb
)
endif()
if(TARGET PkgConfig::GTK3)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
PkgConfig::GTK3
)
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6
)
endif()
endif()
if(FRAMELESSHELPER_NO_PRIVATE)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
)
# Qt X11Extras was first introduced in 5.1 and got removed in 6.0
# But it was again brought back as a private feature of QtGui in 6.2
if(TARGET Qt5::X11Extras)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
Qt5::X11Extras
)
endif()
else()
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
Qt${QT_VERSION_MAJOR}::CorePrivate
Qt${QT_VERSION_MAJOR}::GuiPrivate
)
endif()
target_include_directories(${SUB_PROJ_NAME} PUBLIC
target_include_directories(${SUB_MODULE} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}/../..>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}/private>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_MODULE_PATH}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_MODULE_PATH}/private>"
)
setup_qt_stuff(TARGETS ${SUB_PROJ_NAME})
setup_qt_stuff(TARGETS ${SUB_MODULE})
set(__extra_flags)
if(NOT FRAMELESSHELPER_NO_PERMISSIVE_CHECKS)
list(APPEND __extra_flags PERMISSIVE)
@ -346,31 +306,22 @@ endif()
if(FRAMELESSHELPER_ENABLE_CFGUARD)
list(APPEND __extra_flags CFGUARD)
endif()
setup_compile_params(TARGETS ${SUB_PROJ_NAME} ${__extra_flags})
if(FRAMELESSHELPER_FORCE_LTO)
list(APPEND __extra_flags FORCE_LTO)
endif()
setup_compile_params(TARGETS ${SUB_MODULE} ${__extra_flags})
if(NOT FRAMELESSHELPER_NO_INSTALL)
set(__cmake_dir "${CMAKE_CURRENT_BINARY_DIR}/cmake")
set(__config_file "${__cmake_dir}/${SUB_PROJ_NAME}Config.cmake")
configure_file(../../FramelessHelperModuleConfig.cmake.in ${__config_file} @ONLY)
set(__targets_file "${__cmake_dir}/${SUB_PROJ_NAME}Targets.cmake")
configure_file(../../FramelessHelperModuleTargets.cmake.in ${__targets_file} @ONLY)
install(
FILES "${__config_file}" "${__targets_file}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${SUB_PROJ_NAME}"
)
set(__inc_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}")
install(
FILES ${PUBLIC_HEADERS} ${PUBLIC_HEADERS_ALIAS}
DESTINATION "${__inc_dir}"
)
install(
FILES ${PRIVATE_HEADERS}
DESTINATION "${__inc_dir}/private"
)
install(
TARGETS ${SUB_MOD_TARGETS}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
INCLUDES DESTINATION "${__inc_dir}"
setup_package_export(
TARGETS ${__export_targets}
NAMESPACE ${PROJECT_NAME}
PACKAGE_NAME ${PROJECT_NAME}
COMPONENT ${SUB_MODULE}
PUBLIC_HEADERS ${PUBLIC_HEADERS}
ALIAS_HEADERS ${PUBLIC_HEADERS_ALIAS}
PRIVATE_HEADERS ${PRIVATE_HEADERS}
)
endif()
if(NOT FRAMELESSHELPER_NO_SUMMARY)
dump_target_info(TARGETS ${SUB_MODULE})
endif()

View File

@ -81,18 +81,10 @@ const ChromePalettePrivate *ChromePalettePrivate::get(const ChromePalette *q)
void ChromePalettePrivate::refresh()
{
const bool colorized = Utils::isTitleBarColorized();
const bool dark = Utils::shouldAppsUseDarkMode();
const bool dark = (FramelessManager::instance()->systemTheme() == SystemTheme::Dark);
titleBarActiveBackgroundColor_sys = [colorized, dark]() -> QColor {
if (colorized) {
#ifdef Q_OS_WINDOWS
return Utils::getDwmAccentColor();
#elif defined(Q_OS_LINUX)
return Utils::getWmThemeColor();
#elif defined(Q_OS_MACOS)
return Utils::getControlsAccentColor();
#else
return {};
#endif
return Utils::getAccentColor();
} else {
return (dark ? kDefaultBlackColor : kDefaultWhiteColor);
}

View File

@ -23,7 +23,6 @@
*/
#include "framelessconfig_p.h"
#include <QtCore/qmutex.h>
#include <QtCore/qdir.h>
#include <QtCore/qsettings.h>
#include <QtCore/qcoreapplication.h>
@ -81,7 +80,6 @@ static constexpr const auto OptionCount = std::size(OptionsTable);
struct ConfigData
{
QMutex mutex;
bool loaded = false;
bool options[OptionCount] = {};
bool disableEnvVar = false;
@ -134,7 +132,6 @@ FramelessConfig *FramelessConfig::instance()
void FramelessConfig::reload(const bool force)
{
const QMutexLocker locker(&g_data()->mutex);
if (g_data()->loaded && !force) {
return;
}
@ -160,25 +157,21 @@ void FramelessConfig::reload(const bool force)
void FramelessConfig::set(const Option option, const bool on)
{
const QMutexLocker locker(&g_data()->mutex);
g_data()->options[static_cast<int>(option)] = on;
}
bool FramelessConfig::isSet(const Option option) const
{
const QMutexLocker locker(&g_data()->mutex);
return g_data()->options[static_cast<int>(option)];
}
void FramelessConfig::setLoadFromEnvironmentVariablesDisabled(const bool on)
{
const QMutexLocker locker(&g_data()->mutex);
g_data()->disableEnvVar = on;
}
void FramelessConfig::setLoadFromConfigurationFileDisabled(const bool on)
{
const QMutexLocker locker(&g_data()->mutex);
g_data()->disableCfgFile = on;
}

View File

@ -28,7 +28,6 @@
#include "framelessconfig_p.h"
#include "framelesshelpercore_global_p.h"
#include "utils.h"
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/qevent.h>
#include <QtGui/qwindow.h>
@ -61,7 +60,6 @@ struct QtHelperData
struct QtHelper
{
QMutex mutex;
QHash<WId, QtHelperData> data = {};
};
@ -78,18 +76,15 @@ void FramelessHelperQt::addWindow(FramelessParamsConst params)
return;
}
const WId windowId = params->getWindowId();
g_qtHelper()->mutex.lock();
if (g_qtHelper()->data.contains(windowId)) {
g_qtHelper()->mutex.unlock();
return;
}
QtHelperData data = {};
data.params = *params;
QWindow *window = params->getWindowHandle();
// Give it a parent so that it can be deleted even if we forget to do so.
// Give it a parent so that it can be automatically deleted by Qt.
data.eventFilter = new FramelessHelperQt(window);
g_qtHelper()->data.insert(windowId, data);
g_qtHelper()->mutex.unlock();
const auto shouldApplyFramelessFlag = []() -> bool {
#ifdef Q_OS_MACOS
return false;
@ -121,16 +116,9 @@ void FramelessHelperQt::removeWindow(const WId windowId)
if (!windowId) {
return;
}
const QMutexLocker locker(&g_qtHelper()->mutex);
if (!g_qtHelper()->data.contains(windowId)) {
return;
}
if (const auto eventFilter = g_qtHelper()->data.value(windowId).eventFilter) {
if (QWindow * const window = Utils::findWindow(windowId)) {
window->removeEventFilter(eventFilter);
}
delete eventFilter;
}
g_qtHelper()->data.remove(windowId);
#ifdef Q_OS_MACOS
Utils::removeWindowProxy(windowId);
@ -162,20 +150,32 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
return QObject::eventFilter(object, event);
}
const QEvent::Type type = event->type();
// We are only interested in some specific mouse events.
// We are only interested in some specific mouse events (plus DPR change event).
if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseButtonRelease)
&& (type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseMove)) {
&& (type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseMove)
#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
&& (type != QEvent::DevicePixelRatioChange)
#else // QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
&& (type != QEvent::ScreenChangeInternal) // Qt's internal event to notify screen change and DPR change.
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
) {
return QObject::eventFilter(object, event);
}
const auto window = qobject_cast<QWindow *>(object);
const WId windowId = window->winId();
g_qtHelper()->mutex.lock();
if (!g_qtHelper()->data.contains(windowId)) {
g_qtHelper()->mutex.unlock();
return QObject::eventFilter(object, event);
}
const QtHelperData data = g_qtHelper()->data.value(windowId);
g_qtHelper()->mutex.unlock();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
if (type == QEvent::DevicePixelRatioChange)
#else // QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
if (type == QEvent::ScreenChangeInternal)
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
{
data.params.forceChildrenRepaint(500);
return QObject::eventFilter(object, event);
}
const auto mouseEvent = static_cast<QMouseEvent *>(event);
const Qt::MouseButton button = mouseEvent->button();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -193,9 +193,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
switch (type) {
case QEvent::MouseButtonPress: {
if (button == Qt::LeftButton) {
g_qtHelper()->mutex.lock();
g_qtHelper()->data[windowId].leftButtonPressed = true;
g_qtHelper()->mutex.unlock();
if (!windowFixedSize) {
const Qt::Edges edges = Utils::calculateWindowEdges(window, scenePos);
if (edges != Qt::Edges{}) {
@ -208,12 +206,11 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
} break;
case QEvent::MouseButtonRelease: {
if (button == Qt::LeftButton) {
const QMutexLocker locker(&g_qtHelper()->mutex);
g_qtHelper()->data[windowId].leftButtonPressed = false;
}
if (button == Qt::RightButton) {
if (!ignoreThisEvent && insideTitleBar) {
data.params.showSystemMenu(scenePos);
data.params.showSystemMenu(globalPos);
event->accept();
return true;
}
@ -236,12 +233,10 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
if (cs == Qt::ArrowCursor) {
if (data.cursorShapeChanged) {
data.params.unsetCursor();
const QMutexLocker locker(&g_qtHelper()->mutex);
g_qtHelper()->data[windowId].cursorShapeChanged = false;
}
} else {
data.params.setCursor(cs);
const QMutexLocker locker(&g_qtHelper()->mutex);
g_qtHelper()->data[windowId].cursorShapeChanged = true;
}
}

View File

@ -31,7 +31,6 @@
#include "framelesshelper_windows.h"
#include "framelesshelpercore_global_p.h"
#include <QtCore/qhash.h>
#include <QtCore/qmutex.h>
#include <QtCore/qvariant.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qtimer.h>
@ -84,6 +83,8 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW)
FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow)
FRAMELESSHELPER_STRING_CONSTANT(GetWindowPlacement)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowPlacement)
[[maybe_unused]] static constexpr const char kFallbackTitleBarErrorMessage[] =
"FramelessHelper is unable to create the fallback title bar window, and thus the snap layout feature will be disabled"
" unconditionally. You can ignore this error and continue running your application, nothing else will be affected, "
@ -98,11 +99,13 @@ struct Win32HelperData
bool trackingMouse = false;
WId fallbackTitleBarWindowId = 0;
Dpi dpi = {};
#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
QRect restoreGeometry = {};
#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
};
struct Win32Helper
{
QMutex mutex;
std::unique_ptr<FramelessHelperWin> nativeEventFilter = nullptr;
QHash<WId, Win32HelperData> data = {};
QHash<WId, WId> fallbackTitleBarToParentWindowMapping = {};
@ -110,18 +113,16 @@ struct Win32Helper
Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
[[nodiscard]] static inline QString hwnd2str(const WId windowId)
{
// NULL handle is allowed here.
return FRAMELESSHELPER_STRING_LITERAL("0x")
+ QString::number(windowId, 16).toUpper();
}
[[nodiscard]] extern bool operator==(const RECT &lhs, const RECT &rhs) noexcept;
[[nodiscard]] extern bool operator!=(const RECT &lhs, const RECT &rhs) noexcept;
[[nodiscard]] static inline QString hwnd2str(const HWND hwnd)
{
// NULL handle is allowed here.
return hwnd2str(reinterpret_cast<WId>(hwnd));
}
[[nodiscard]] extern QRect rect2qrect(const RECT &rect);
[[nodiscard]] extern RECT qrect2rect(const QRect &qrect);
[[nodiscard]] extern QString hwnd2str(const WId windowId);
[[nodiscard]] extern QString hwnd2str(const HWND hwnd);
[[nodiscard]] extern std::optional<MONITORINFOEXW> getMonitorForWindow(const HWND hwnd);
[[nodiscard]] static inline LRESULT CALLBACK FallbackTitleBarWindowProc
(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
@ -141,25 +142,21 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
const auto windowId = reinterpret_cast<WId>(hWnd);
g_win32Helper()->mutex.lock();
if (!g_win32Helper()->fallbackTitleBarToParentWindowMapping.contains(windowId)) {
g_win32Helper()->mutex.unlock();
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
const WId parentWindowId = g_win32Helper()->fallbackTitleBarToParentWindowMapping.value(windowId);
if (!g_win32Helper()->data.contains(parentWindowId)) {
g_win32Helper()->mutex.unlock();
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
const Win32HelperData data = g_win32Helper()->data.value(parentWindowId);
g_win32Helper()->mutex.unlock();
const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId);
// All mouse events: client area mouse events + non-client area mouse events.
// Hit-testing event should not be considered as a mouse event.
const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) ||
((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK)));
const auto releaseButtons = [&data](const std::optional<SystemButtonType> exclude) -> void {
static constexpr const auto defaultButtonState = ButtonState::Unspecified;
static constexpr const auto defaultButtonState = ButtonState::Normal;
const SystemButtonType button = exclude.value_or(SystemButtonType::Unknown);
if (button != SystemButtonType::WindowIcon) {
data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState);
@ -190,7 +187,7 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
};
const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
releaseButtons(button);
data.params.setSystemButtonState(button, ButtonState::Clicked);
data.params.setSystemButtonState(button, ButtonState::Released);
};
switch (uMsg) {
case WM_NCHITTEST: {
@ -207,8 +204,6 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
SystemButtonType buttonType = SystemButtonType::Unknown;
if (data.params.isInsideSystemButtons(qtScenePos, &buttonType)) {
switch (buttonType) {
case SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(HTNOWHERE);
case SystemButtonType::WindowIcon:
return HTSYSMENU;
case SystemButtonType::Help:
@ -220,6 +215,8 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
return HTZOOM;
case SystemButtonType::Close:
return HTCLOSE;
case SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(HTNOWHERE);
}
}
// Returns "HTTRANSPARENT" to let the mouse event pass through this invisible
@ -284,7 +281,6 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
WARNING << Utils::getSystemErrorMessage(kTrackMouseEvent);
break;
}
const QMutexLocker locker(&g_win32Helper()->mutex);
g_win32Helper()->data[parentWindowId].trackingMouse = true;
}
} break;
@ -292,7 +288,6 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
case WM_MOUSELEAVE: {
// When the mouse leaves the drag rect, make sure to dismiss any hover.
releaseButtons(std::nullopt);
const QMutexLocker locker(&g_win32Helper()->mutex);
g_win32Helper()->data[parentWindowId].trackingMouse = false;
} break;
// NB: *Shouldn't be forwarding these* when they're not over the caption
@ -406,7 +401,7 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
// snap layout feature introduced in Windows 11. So you may wonder, why not just
// limit it to the area of the three system buttons, instead of covering the
// whole title bar area? Well, I've tried that solution already and unfortunately
// it doesn't work. And according to my experiment, it won't work either even if we
// it doesn't work. And according to my experiments, it won't work either even if we
// only reduce the window width for some pixels. So we have to make it expand to the
// full width of the parent window to let it occupy the whole top area, and this time
// it finally works. Since our current solution works well, I have no interest in digging
@ -419,6 +414,22 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
return true;
}
static inline void cleanupFallbackWindow()
{
const HINSTANCE instance = GetModuleHandleW(nullptr);
if (!instance) {
//WARNING << Utils::getSystemErrorMessage(kGetModuleHandleW);
return;
}
// According to MSDN, if the window class is registered from a shared library,
// we will need to unregister it manually. Only the code which is directly
// linked into the executable doesn't need manual unregistration, because
// Windows will take care of it for us.
if (UnregisterClassW(kFallbackTitleBarWindowClassName, instance) == FALSE) {
//WARNING << Utils::getSystemErrorMessage(kUnregisterClassW);
}
}
[[nodiscard]] static inline bool createFallbackTitleBarWindow(const WId parentWindowId, const bool hide)
{
Q_ASSERT(parentWindowId);
@ -453,16 +464,7 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
wcex.lpfnWndProc = FallbackTitleBarWindowProc;
wcex.hInstance = instance;
if (RegisterClassExW(&wcex) != INVALID_ATOM) {
registerUninitializeHook([](){
const HINSTANCE instance = GetModuleHandleW(nullptr);
if (!instance) {
//WARNING << Utils::getSystemErrorMessage(kGetModuleHandleW);
return;
}
if (UnregisterClassW(kFallbackTitleBarWindowClassName, instance) == FALSE) {
//WARNING << Utils::getSystemErrorMessage(kUnregisterClassW);
}
});
qAddPostRoutine(cleanupFallbackWindow);
return true;
}
WARNING << Utils::getSystemErrorMessage(kRegisterClassExW);
@ -499,7 +501,6 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
WARNING << "Failed to re-position the fallback title bar window.";
return false;
}
const QMutexLocker locker(&g_win32Helper()->mutex);
g_win32Helper()->data[parentWindowId].fallbackTitleBarWindowId = fallbackTitleBarWindowId;
g_win32Helper()->fallbackTitleBarToParentWindowMapping.insert(fallbackTitleBarWindowId, parentWindowId);
return true;
@ -516,9 +517,7 @@ void FramelessHelperWin::addWindow(FramelessParamsConst params)
return;
}
const WId windowId = params->getWindowId();
g_win32Helper()->mutex.lock();
if (g_win32Helper()->data.contains(windowId)) {
g_win32Helper()->mutex.unlock();
return;
}
Win32HelperData data = {};
@ -529,7 +528,6 @@ void FramelessHelperWin::addWindow(FramelessParamsConst params)
g_win32Helper()->nativeEventFilter = std::make_unique<FramelessHelperWin>();
qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.get());
}
g_win32Helper()->mutex.unlock();
DEBUG.noquote() << "The DPI of window" << hwnd2str(windowId) << "is" << data.dpi;
#if 0
params->setWindowFlags(params->getWindowFlags() | Qt::FramelessWindowHint);
@ -557,7 +555,7 @@ void FramelessHelperWin::addWindow(FramelessParamsConst params)
// Tell DWM we may need dark theme non-client area (title bar & frame border).
FramelessHelper::Core::setApplicationOSThemeAware();
if (WindowsVersionHelper::isWin10RS5OrGreater()) {
const bool dark = Utils::shouldAppsUseDarkMode();
const bool dark = (FramelessManager::instance()->systemTheme() == SystemTheme::Dark);
const auto isWidget = [params]() -> bool {
const auto widget = params->getWidgetHandle();
return (widget && widget->isWidgetType());
@ -587,9 +585,7 @@ void FramelessHelperWin::removeWindow(const WId windowId)
if (!windowId) {
return;
}
g_win32Helper()->mutex.lock();
if (!g_win32Helper()->data.contains(windowId)) {
g_win32Helper()->mutex.unlock();
return;
}
g_win32Helper()->data.remove(windowId);
@ -599,21 +595,14 @@ void FramelessHelperWin::removeWindow(const WId windowId)
g_win32Helper()->nativeEventFilter.reset();
}
}
HWND hwnd = nullptr;
auto it = g_win32Helper()->fallbackTitleBarToParentWindowMapping.constBegin();
while (it != g_win32Helper()->fallbackTitleBarToParentWindowMapping.constEnd()) {
if (it.value() == windowId) {
const WId key = it.key();
hwnd = reinterpret_cast<HWND>(key);
g_win32Helper()->fallbackTitleBarToParentWindowMapping.remove(key);
g_win32Helper()->fallbackTitleBarToParentWindowMapping.remove(it.key());
break;
}
++it;
}
g_win32Helper()->mutex.unlock();
if (DestroyWindow(reinterpret_cast<HWND>(hwnd)) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kDestroyWindow);
}
}
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result)
@ -645,16 +634,31 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
return false;
}
const auto windowId = reinterpret_cast<WId>(hWnd);
g_win32Helper()->mutex.lock();
if (!g_win32Helper()->data.contains(windowId)) {
g_win32Helper()->mutex.unlock();
return false;
}
const Win32HelperData data = g_win32Helper()->data.value(windowId);
g_win32Helper()->mutex.unlock();
const bool frameBorderVisible = Utils::isWindowFrameBorderVisible();
const WPARAM wParam = msg->wParam;
const LPARAM lParam = msg->lParam;
#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
const auto updateRestoreGeometry = [windowId, &data](const bool ignoreWindowState) -> void {
if (!ignoreWindowState && !Utils::isWindowNoState(windowId)) {
return;
}
const QRect rect = Utils::getWindowRestoreGeometry(windowId);
if (!Utils::isValidGeometry(rect)) {
WARNING << "The calculated restore geometry is invalid.";
return;
}
if (Utils::isValidGeometry(data.restoreGeometry) && (data.restoreGeometry == rect)) {
return;
}
g_win32Helper()->data[windowId].restoreGeometry = rect;
};
#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
switch (uMsg) {
#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) // Qt has done this for us since 5.9.0
case WM_NCCREATE: {
@ -819,28 +823,22 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// we have to use another way to judge this if we are running
// on Windows 7 or Windows 8.
if (WindowsVersionHelper::isWin8Point1OrGreater()) {
MONITORINFOEXW monitorInfo;
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
monitorInfo.cbSize = sizeof(monitorInfo);
const HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
if (!monitor) {
WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow);
break;
}
if (GetMonitorInfoW(monitor, &monitorInfo) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kGetMonitorInfoW);
const std::optional<MONITORINFOEXW> monitorInfo = getMonitorForWindow(hWnd);
if (!monitorInfo.has_value()) {
WARNING << "Failed to retrieve the window's monitor.";
break;
}
const RECT monitorRect = monitorInfo.value().rcMonitor;
// This helper can be used to determine if there's a
// auto-hide taskbar on the given edge of the monitor
// we're currently on.
const auto hasAutohideTaskbar = [&monitorInfo](const UINT edge) -> bool {
APPBARDATA _abd;
SecureZeroMemory(&_abd, sizeof(_abd));
_abd.cbSize = sizeof(_abd);
_abd.uEdge = edge;
_abd.rc = monitorInfo.rcMonitor;
const auto hTaskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAREX, &_abd));
const auto hasAutohideTaskbar = [monitorRect](const UINT edge) -> bool {
APPBARDATA abd2;
SecureZeroMemory(&abd2, sizeof(abd2));
abd2.cbSize = sizeof(abd2);
abd2.uEdge = edge;
abd2.rc = monitorRect;
const auto hTaskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAREX, &abd2));
return (hTaskbar != nullptr);
};
top = hasAutohideTaskbar(ABE_TOP);
@ -849,24 +847,24 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
right = hasAutohideTaskbar(ABE_RIGHT);
} else {
int edge = -1;
APPBARDATA _abd;
SecureZeroMemory(&_abd, sizeof(_abd));
_abd.cbSize = sizeof(_abd);
_abd.hWnd = FindWindowW(L"Shell_TrayWnd", nullptr);
if (_abd.hWnd) {
APPBARDATA abd2;
SecureZeroMemory(&abd2, sizeof(abd2));
abd2.cbSize = sizeof(abd2);
abd2.hWnd = FindWindowW(L"Shell_TrayWnd", nullptr);
if (abd2.hWnd) {
const HMONITOR windowMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
if (!windowMonitor) {
WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow);
break;
}
const HMONITOR taskbarMonitor = MonitorFromWindow(_abd.hWnd, MONITOR_DEFAULTTOPRIMARY);
const HMONITOR taskbarMonitor = MonitorFromWindow(abd2.hWnd, MONITOR_DEFAULTTOPRIMARY);
if (!taskbarMonitor) {
WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow);
break;
}
if (taskbarMonitor == windowMonitor) {
SHAppBarMessage(ABM_GETTASKBARPOS, &_abd);
edge = _abd.uEdge;
SHAppBarMessage(ABM_GETTASKBARPOS, &abd2);
edge = abd2.uEdge;
}
} else {
WARNING << Utils::getSystemErrorMessage(kFindWindowW);
@ -1149,16 +1147,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
*result = FALSE; // Use the default linear DPI scaling provided by Windows.
return true; // Jump over Qt's wrong handling logic.
}
const QSizeF oldSize = {qreal(clientRect.right - clientRect.left), qreal(clientRect.bottom - clientRect.top)};
static constexpr const auto defaultDpi = qreal(USER_DEFAULT_SCREEN_DPI);
// We need to round the scale factor according to Qt's rounding policy.
const qreal oldDpr = Utils::roundScaleFactor(qreal(data.dpi.x) / defaultDpi);
const auto newDpi = UINT(wParam);
const qreal newDpr = Utils::roundScaleFactor(qreal(newDpi) / defaultDpi);
const QSizeF newSize = (oldSize / oldDpr * newDpr);
const QSize oldSize = {RECT_WIDTH(clientRect), RECT_HEIGHT(clientRect)};
const QSize newSize = Utils::rescaleSize(oldSize, data.dpi.x, newDpi);
const auto suggestedSize = reinterpret_cast<LPSIZE>(lParam);
suggestedSize->cx = std::round(newSize.width());
suggestedSize->cy = std::round(newSize.height());
suggestedSize->cx = newSize.width();
suggestedSize->cy = newSize.height();
// If the window frame is visible, we need to expand the suggested size, currently
// it's pure client size, we need to add the frame size to it. Windows expects a
// full window size, including the window frame.
@ -1176,25 +1170,59 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
}
#endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2))
case WM_DPICHANGED: {
const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))};
DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << dpi;
g_win32Helper()->mutex.lock();
g_win32Helper()->data[windowId].dpi = dpi;
g_win32Helper()->mutex.unlock();
#if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2))
// We need to wait until Qt has handled this message, otherwise everything
// we have done here will always be overwritten.
QTimer::singleShot(0, qApp, [data](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use them.
// Sync the internal window frame margins with the latest DPI, otherwise
// we will get wrong window sizes after the DPI change.
Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true);
});
#endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2))
const Dpi oldDpi = data.dpi;
const Dpi newDpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))};
if (Q_UNLIKELY(newDpi == oldDpi)) {
WARNING << "Wrong WM_DPICHANGED received: same DPI.";
break;
}
DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd)
<< "is" << newDpi << "(was" << oldDpi << ").";
g_win32Helper()->data[windowId].dpi = newDpi;
#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
if (Utils::isValidGeometry(data.restoreGeometry)) {
// Update the window size only. The position should not be changed.
g_win32Helper()->data[windowId].restoreGeometry.setSize(
Utils::rescaleSize(data.restoreGeometry.size(), oldDpi.x, newDpi.x));
}
#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
data.params.forceChildrenRepaint(500);
} break;
case WM_DWMCOMPOSITIONCHANGED: {
// Re-apply the custom window frame if recovered from the basic theme.
Utils::updateWindowFrameMargins(windowId, false);
} break;
#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
case WM_ENTERSIZEMOVE: // Sent to a window when the user drags the title bar or the resize border.
case WM_EXITSIZEMOVE: // Sent to a window when the user releases the mouse button (from dragging the title bar or the resize border).
updateRestoreGeometry(false);
break;
case WM_SIZE: {
if (wParam != SIZE_MAXIMIZED) {
break;
}
if (!Utils::isValidGeometry(data.restoreGeometry)) {
updateRestoreGeometry(true);
break;
}
WINDOWPLACEMENT wp;
SecureZeroMemory(&wp, sizeof(wp));
wp.length = sizeof(wp);
if (GetWindowPlacement(hWnd, &wp) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kGetWindowPlacement);
break;
}
// The restore geometry is correct, no need to bother.
if (rect2qrect(wp.rcNormalPosition) == data.restoreGeometry) {
break;
}
// OK, the restore geometry is wrong, let's correct it then :)
wp.rcNormalPosition = qrect2rect(data.restoreGeometry);
if (SetWindowPlacement(hWnd, &wp) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kSetWindowPlacement);
}
} break;
#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1))
default:
break;
}
@ -1294,7 +1322,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
&& (std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), kThemeSettingChangeEventName) == 0)) {
systemThemeChanged = true;
if (WindowsVersionHelper::isWin10RS5OrGreater()) {
const bool dark = Utils::shouldAppsUseDarkMode();
const bool dark = (FramelessManager::instance()->systemTheme() == SystemTheme::Dark);
const auto isWidget = [&data]() -> bool {
const auto widget = data.params.getWidgetHandle();
return (widget && widget->isWidgetType());

View File

@ -26,7 +26,6 @@
#include "framelesshelpercore_global_p.h"
#include "versionnumber_p.h"
#include "utils.h"
#include <QtCore/qmutex.h>
#include <QtCore/qiodevice.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qloggingcategory.h>
@ -87,7 +86,7 @@ QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Ver
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &dpi)
{
const QDebugStateSaver saver(d);
const qreal scaleFactor = (qreal(dpi.x) / qreal(96));
const qreal scaleFactor = (qreal(dpi.x) / qreal(FRAMELESSHELPER_PREPEND_NAMESPACE(Utils)::defaultScreenDpi()));
d.nospace().noquote() << "Dpi("
<< "x: " << dpi.x << ", "
<< "y: " << dpi.y << ", "
@ -141,33 +140,16 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT(xcb)
[[maybe_unused]] static constexpr const char kNoLogoEnvVar[] = "FRAMELESSHELPER_NO_LOGO";
struct CoreData
{
QMutex mutex;
QList<InitializeHookCallback> initHooks = {};
QList<UninitializeHookCallback> uninitHooks = {};
};
Q_GLOBAL_STATIC(CoreData, coreData)
void registerInitializeHook(const InitializeHookCallback &cb)
{
Q_ASSERT(cb);
if (!cb) {
return;
}
const QMutexLocker locker(&coreData()->mutex);
coreData()->initHooks.append(cb);
Q_UNUSED(cb);
WARNING << "registerInitializeHook: This function is deprecated and will be removed in a future version. Please consider using Qt's official Q_COREAPP_STARTUP_FUNCTION() macro instead.";
}
void registerUninitializeHook(const UninitializeHookCallback &cb)
{
Q_ASSERT(cb);
if (!cb) {
return;
}
const QMutexLocker locker(&coreData()->mutex);
coreData()->uninitHooks.append(cb);
Q_UNUSED(cb);
WARNING << "registerUninitializeHook: This function is deprecated and will be removed in a future version. Please consider using Qt's official qAddPostRoutine() function instead.";
}
namespace FramelessHelper::Core
@ -226,17 +208,6 @@ void initialize()
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
const QMutexLocker locker(&coreData()->mutex);
if (!coreData()->initHooks.isEmpty()) {
for (auto &&hook : std::as_const(coreData()->initHooks)) {
Q_ASSERT(hook);
if (!hook) {
continue;
}
hook();
}
}
}
void uninitialize()
@ -246,18 +217,6 @@ void uninitialize()
return;
}
uninited = true;
const QMutexLocker locker(&coreData()->mutex);
if (coreData()->uninitHooks.isEmpty()) {
return;
}
for (auto &&hook : std::as_const(coreData()->uninitHooks)) {
Q_ASSERT(hook);
if (!hook) {
continue;
}
hook();
}
}
VersionInfo version()

View File

@ -32,11 +32,11 @@
# include "framelesshelper_win.h"
# include "winverhelper_p.h"
#endif
#include <QtCore/qmutex.h>
#include <QtCore/qvariant.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/qfontdatabase.h>
#include <QtGui/qwindow.h>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
# include <QtGui/qguiapplication.h>
# include <QtGui/qstylehints.h>
@ -62,7 +62,6 @@ using namespace Global;
struct FramelessManagerHelper
{
QMutex mutex;
QList<WId> windowIds = {};
};
@ -168,25 +167,25 @@ QFont FramelessManagerPrivate::getIconFont()
SystemTheme FramelessManagerPrivate::systemTheme() const
{
const QMutexLocker locker(&g_helper()->mutex);
// The user's choice has top priority.
if (isThemeOverrided()) {
return m_overrideTheme.value();
}
return m_systemTheme;
}
QColor FramelessManagerPrivate::systemAccentColor() const
{
const QMutexLocker locker(&g_helper()->mutex);
return m_accentColor;
}
QString FramelessManagerPrivate::wallpaper() const
{
const QMutexLocker locker(&g_helper()->mutex);
return m_wallpaper;
}
WallpaperAspectStyle FramelessManagerPrivate::wallpaperAspectStyle() const
{
const QMutexLocker locker(&g_helper()->mutex);
return m_wallpaperAspectStyle;
}
@ -197,13 +196,10 @@ void FramelessManagerPrivate::addWindow(FramelessParamsConst params)
return;
}
const WId windowId = params->getWindowId();
g_helper()->mutex.lock();
if (g_helper()->windowIds.contains(windowId)) {
g_helper()->mutex.unlock();
return;
}
g_helper()->windowIds.append(windowId);
g_helper()->mutex.unlock();
static const bool pureQt = usePureQtImplementation();
if (pureQt) {
FramelessHelperQt::addWindow(params);
@ -214,6 +210,7 @@ void FramelessManagerPrivate::addWindow(FramelessParamsConst params)
}
Utils::installSystemMenuHook(windowId, params);
#endif
connect(params->getWindowHandle(), &QWindow::destroyed, FramelessManager::instance(), [windowId](){ removeWindow(windowId); });
}
void FramelessManagerPrivate::removeWindow(const WId windowId)
@ -222,13 +219,10 @@ void FramelessManagerPrivate::removeWindow(const WId windowId)
if (!windowId) {
return;
}
g_helper()->mutex.lock();
if (!g_helper()->windowIds.contains(windowId)) {
g_helper()->mutex.unlock();
return;
}
g_helper()->windowIds.removeAll(windowId);
g_helper()->mutex.unlock();
static const bool pureQt = usePureQtImplementation();
if (pureQt) {
FramelessHelperQt::removeWindow(windowId);
@ -237,23 +231,17 @@ void FramelessManagerPrivate::removeWindow(const WId windowId)
if (!pureQt) {
FramelessHelperWin::removeWindow(windowId);
}
Utils::uninstallSystemMenuHook(windowId);
Utils::removeSysMenuHook(windowId);
Utils::removeMicaWindow(windowId);
#endif
}
void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot()
{
const QMutexLocker locker(&g_helper()->mutex);
const SystemTheme currentSystemTheme = Utils::getSystemTheme();
const SystemTheme currentSystemTheme = (Utils::shouldAppsUseDarkMode() ? SystemTheme::Dark : SystemTheme::Light);
const QColor currentAccentColor = Utils::getAccentColor();
#ifdef Q_OS_WINDOWS
const DwmColorizationArea currentColorizationArea = Utils::getDwmColorizationArea();
const QColor currentAccentColor = Utils::getDwmAccentColor();
#endif
#ifdef Q_OS_LINUX
const QColor currentAccentColor = Utils::getWmThemeColor();
#endif
#ifdef Q_OS_MACOS
const QColor currentAccentColor = Utils::getControlsAccentColor();
#endif
bool notify = false;
if (m_systemTheme != currentSystemTheme) {
@ -270,7 +258,8 @@ void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot()
notify = true;
}
#endif
if (notify) {
// Don't emit the signal if the user has overrided the global theme.
if (notify && !isThemeOverrided()) {
Q_Q(FramelessManager);
Q_EMIT q->systemThemeChanged();
DEBUG.nospace() << "System theme changed. Current theme: " << m_systemTheme
@ -284,7 +273,6 @@ void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot()
void FramelessManagerPrivate::notifyWallpaperHasChangedOrNot()
{
const QMutexLocker locker(&g_helper()->mutex);
const QString currentWallpaper = Utils::getWallpaperFilePath();
const WallpaperAspectStyle currentWallpaperAspectStyle = Utils::getWallpaperAspectStyle();
bool notify = false;
@ -316,19 +304,32 @@ bool FramelessManagerPrivate::usePureQtImplementation()
return result;
}
void FramelessManagerPrivate::setOverrideTheme(const SystemTheme theme)
{
if ((!m_overrideTheme.has_value() && (theme == SystemTheme::Unknown))
|| (m_overrideTheme.has_value() && (m_overrideTheme.value() == theme))) {
return;
}
if (theme == SystemTheme::Unknown) {
m_overrideTheme = std::nullopt;
} else {
m_overrideTheme = theme;
}
Q_Q(FramelessManager);
Q_EMIT q->systemThemeChanged();
}
bool FramelessManagerPrivate::isThemeOverrided() const
{
return (m_overrideTheme.value_or(SystemTheme::Unknown) != SystemTheme::Unknown);
}
void FramelessManagerPrivate::initialize()
{
const QMutexLocker locker(&g_helper()->mutex);
m_systemTheme = Utils::getSystemTheme();
m_systemTheme = (Utils::shouldAppsUseDarkMode() ? SystemTheme::Dark : SystemTheme::Light);
m_accentColor = Utils::getAccentColor();
#ifdef Q_OS_WINDOWS
m_colorizationArea = Utils::getDwmColorizationArea();
m_accentColor = Utils::getDwmAccentColor();
#endif
#ifdef Q_OS_LINUX
m_accentColor = Utils::getWmThemeColor();
#endif
#ifdef Q_OS_MACOS
m_accentColor = Utils::getControlsAccentColor();
#endif
m_wallpaper = Utils::getWallpaperFilePath();
m_wallpaperAspectStyle = Utils::getWallpaperAspectStyle();
@ -409,4 +410,10 @@ void FramelessManager::removeWindow(const WId windowId)
d->removeWindow(windowId);
}
void FramelessManager::setOverrideTheme(const SystemTheme theme)
{
Q_D(FramelessManager);
d->setOverrideTheme(theme);
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -28,10 +28,12 @@
#include "utils.h"
#include "framelessconfig_p.h"
#include <QtCore/qsysinfo.h>
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
#include <QtGui/qpixmap.h>
#include <QtGui/qimage.h>
#include <QtGui/qimagereader.h>
#include <QtGui/qpainter.h>
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
@ -57,25 +59,51 @@ static Q_LOGGING_CATEGORY(lcMicaMaterial, "wangwenx190.framelesshelper.core.mica
using namespace Global;
[[maybe_unused]] static constexpr const QSize kMaximumPictureSize = { 1920, 1080 };
[[maybe_unused]] static constexpr const QImage::Format kDefaultImageFormat = QImage::Format_ARGB32_Premultiplied;
[[maybe_unused]] static constexpr const qreal kDefaultTintOpacity = 0.7;
[[maybe_unused]] static constexpr const qreal kDefaultNoiseOpacity = 0.04;
[[maybe_unused]] static constexpr const qreal kDefaultBlurRadius = 128.0;
[[maybe_unused]] static Q_COLOR_CONSTEXPR const QColor kDefaultSystemLightColor2 = {243, 243, 243}; // #F3F3F3
[[maybe_unused]] static Q_COLOR_CONSTEXPR const QColor kDefaultFallbackColorDark = {44, 44, 44}; // #2C2C2C
[[maybe_unused]] static Q_COLOR_CONSTEXPR const QColor kDefaultFallbackColorLight = {249, 249, 249}; // #F9F9F9
#ifndef FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE
FRAMELESSHELPER_STRING_CONSTANT2(NoiseImageFilePath, ":/org.wangwenx190.FramelessHelper/resources/images/noise.png")
#endif // FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE
struct MicaMaterialData
{
QMutex mutex;
QPixmap blurredWallpaper = {};
bool graphicsResourcesReady = false;
QMutex mutex{};
};
Q_GLOBAL_STATIC(MicaMaterialData, g_micaMaterialData)
[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator>(const QSize &lhs, const QSize &rhs) noexcept
{
return ((lhs.width() * lhs.height()) > (rhs.width() * rhs.height()));
}
[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator>=(const QSize &lhs, const QSize &rhs) noexcept
{
return (operator>(lhs, rhs) || operator==(lhs, rhs));
}
[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator<(const QSize &lhs, const QSize &rhs) noexcept
{
return (operator!=(lhs, rhs) && !operator>(lhs, rhs));
}
[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator<=(const QSize &lhs, const QSize &rhs) noexcept
{
return (operator<(lhs, rhs) || operator==(lhs, rhs));
}
#ifndef FRAMELESSHELPER_CORE_NO_PRIVATE
template<const int shift>
[[nodiscard]] static inline constexpr int qt_static_shift(const int value)
@ -191,11 +219,11 @@ static inline void qt_blurrow(QImage &im, const int line, const int alpha)
template<const int aprec, const int zprec, const bool alphaOnly>
static inline void expblur(QImage &img, qreal radius, const bool improvedQuality = false, const int transposed = 0)
{
Q_ASSERT((img.format() == QImage::Format_ARGB32_Premultiplied)
Q_ASSERT((img.format() == kDefaultImageFormat)
|| (img.format() == QImage::Format_RGB32)
|| (img.format() == QImage::Format_Indexed8)
|| (img.format() == QImage::Format_Grayscale8));
if ((img.format() != QImage::Format_ARGB32_Premultiplied)
if ((img.format() != kDefaultImageFormat)
&& (img.format() != QImage::Format_RGB32)
&& (img.format() != QImage::Format_Indexed8)
&& (img.format() != QImage::Format_Grayscale8)) {
@ -344,9 +372,9 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality
return dest;
}
if ((source.format() != QImage::Format_ARGB32_Premultiplied)
if ((source.format() != kDefaultImageFormat)
&& (source.format() != QImage::Format_RGB32)) {
srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
srcImage = source.convertToFormat(kDefaultImageFormat);
}
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
@ -376,9 +404,9 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality
[[maybe_unused]] static inline void qt_blurImage(QPainter *p, QImage &blurImage,
qreal radius, const bool quality, const bool alphaOnly, const int transposed = 0)
{
if ((blurImage.format() != QImage::Format_ARGB32_Premultiplied)
if ((blurImage.format() != kDefaultImageFormat)
&& (blurImage.format() != QImage::Format_RGB32)) {
blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
blurImage = blurImage.convertToFormat(kDefaultImageFormat);
}
qreal scale = 1.0;
@ -472,6 +500,130 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality
return {x, y, w, h};
}
class WallpaperThread : public QThread
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(WallpaperThread)
public:
explicit WallpaperThread(QObject *parent = nullptr) : QThread(parent) {}
~WallpaperThread() override = default;
Q_SIGNALS:
void imageUpdated(const Transform &);
protected:
void run() override
{
Transform transform = {};
// ### FIXME: Ideally, we should not use virtual desktop size here.
QSize monitorSize = QGuiApplication::primaryScreen()->virtualSize();
if (monitorSize.isEmpty()) {
WARNING << "Failed to retrieve the monitor size. Using default size (1920x1080) instead ...";
monitorSize = kMaximumPictureSize;
}
const QSize imageSize = (monitorSize > kMaximumPictureSize ? kMaximumPictureSize : monitorSize);
if (imageSize != monitorSize) {
transform.Horizontal = (qreal(imageSize.width()) / qreal(monitorSize.width()));
transform.Vertical = (qreal(imageSize.height()) / qreal(monitorSize.height()));
}
const QString wallpaperFilePath = Utils::getWallpaperFilePath();
if (wallpaperFilePath.isEmpty()) {
WARNING << "Failed to retrieve the wallpaper file path.";
return;
}
QImageReader reader(wallpaperFilePath);
if (!reader.canRead()) {
WARNING << "Qt can't read the wallpaper file:" << reader.errorString();
return;
}
const QSize actualSize = reader.size();
if (actualSize.isEmpty()) {
WARNING << "The wallpaper picture size is invalid.";
return;
}
const QSize correctedSize = (actualSize > kMaximumPictureSize ? kMaximumPictureSize : actualSize);
if (correctedSize != actualSize) {
DEBUG << "The wallpaper picture size is greater than 1920x1080, it will be shrinked to reduce memory consumption.";
reader.setScaledSize(correctedSize);
}
QImage image(correctedSize, kDefaultImageFormat);
if (!reader.read(&image)) {
WARNING << "Failed to read the wallpaper image:" << reader.errorString();
return;
}
if (image.isNull()) {
WARNING << "The obtained image data is null.";
return;
}
WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle();
QImage buffer(imageSize, kDefaultImageFormat);
#ifdef Q_OS_WINDOWS
if (aspectStyle == WallpaperAspectStyle::Center) {
buffer.fill(kDefaultBlackColor);
}
#endif
if ((aspectStyle == WallpaperAspectStyle::Stretch)
|| (aspectStyle == WallpaperAspectStyle::Fit)
|| (aspectStyle == WallpaperAspectStyle::Fill)) {
Qt::AspectRatioMode mode = Qt::KeepAspectRatioByExpanding;
if (aspectStyle == WallpaperAspectStyle::Stretch) {
mode = Qt::IgnoreAspectRatio;
} else if (aspectStyle == WallpaperAspectStyle::Fit) {
mode = Qt::KeepAspectRatio;
}
QSize newSize = image.size();
newSize.scale(imageSize, mode);
image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
static constexpr const QPoint desktopOriginPoint = {0, 0};
const QRect desktopRect = {desktopOriginPoint, imageSize};
if (aspectStyle == WallpaperAspectStyle::Tile) {
QPainter bufferPainter(&buffer);
bufferPainter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
bufferPainter.fillRect(desktopRect, QBrush(image));
} else {
QPainter bufferPainter(&buffer);
bufferPainter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
const QRect rect = alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), desktopRect);
bufferPainter.drawImage(rect.topLeft(), image);
}
g_micaMaterialData()->mutex.lock();
g_micaMaterialData()->blurredWallpaper = QPixmap(imageSize);
g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor);
QPainter painter(&g_micaMaterialData()->blurredWallpaper);
painter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
#ifdef FRAMELESSHELPER_CORE_NO_PRIVATE
painter.drawImage(desktopOriginPoint, buffer);
#else // !FRAMELESSHELPER_CORE_NO_PRIVATE
qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false);
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
g_micaMaterialData()->mutex.unlock();
Q_EMIT imageUpdated(transform);
}
};
struct ThreadData
{
std::unique_ptr<WallpaperThread> thread = nullptr;
QMutex mutex{};
};
Q_GLOBAL_STATIC(ThreadData, g_threadData)
static inline void threadCleaner()
{
const QMutexLocker locker(&g_threadData()->mutex);
if (g_threadData()->thread && g_threadData()->thread->isRunning()) {
g_threadData()->thread->requestInterruption();
g_threadData()->thread->quit();
g_threadData()->thread->wait();
}
}
MicaMaterialPrivate::MicaMaterialPrivate(MicaMaterial *q) : QObject(q)
{
Q_ASSERT(q);
@ -510,67 +662,13 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
return;
}
g_micaMaterialData()->mutex.unlock();
const QSize size = QGuiApplication::primaryScreen()->virtualSize();
g_micaMaterialData()->mutex.lock();
g_micaMaterialData()->blurredWallpaper = QPixmap(size);
g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor);
g_micaMaterialData()->mutex.unlock();
const QString wallpaperFilePath = Utils::getWallpaperFilePath();
if (wallpaperFilePath.isEmpty()) {
WARNING << "Failed to retrieve the wallpaper file path.";
return;
const QMutexLocker locker(&g_threadData()->mutex);
if (g_threadData()->thread->isRunning()) {
g_threadData()->thread->requestInterruption();
g_threadData()->thread->quit();
g_threadData()->thread->wait();
}
QImage image(wallpaperFilePath);
if (image.isNull()) {
WARNING << "QImage doesn't support this kind of file:" << wallpaperFilePath;
return;
}
WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle();
QImage buffer(size, QImage::Format_ARGB32_Premultiplied);
#ifdef Q_OS_WINDOWS
if (aspectStyle == WallpaperAspectStyle::Center) {
buffer.fill(kDefaultBlackColor);
}
#endif
if ((aspectStyle == WallpaperAspectStyle::Stretch)
|| (aspectStyle == WallpaperAspectStyle::Fit)
|| (aspectStyle == WallpaperAspectStyle::Fill)) {
Qt::AspectRatioMode mode = Qt::KeepAspectRatioByExpanding;
if (aspectStyle == WallpaperAspectStyle::Stretch) {
mode = Qt::IgnoreAspectRatio;
} else if (aspectStyle == WallpaperAspectStyle::Fit) {
mode = Qt::KeepAspectRatio;
}
QSize newSize = image.size();
newSize.scale(size, mode);
image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
static constexpr const QPoint desktopOriginPoint = {0, 0};
const QRect desktopRect = {desktopOriginPoint, size};
if (aspectStyle == WallpaperAspectStyle::Tile) {
QPainter bufferPainter(&buffer);
bufferPainter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
bufferPainter.fillRect(desktopRect, QBrush(image));
} else {
QPainter bufferPainter(&buffer);
bufferPainter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
const QRect rect = alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), desktopRect);
bufferPainter.drawImage(rect.topLeft(), image);
}
g_micaMaterialData()->mutex.lock();
QPainter painter(&g_micaMaterialData()->blurredWallpaper);
painter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
#ifdef FRAMELESSHELPER_CORE_NO_PRIVATE
painter.drawImage(desktopOriginPoint, buffer);
#else // !FRAMELESSHELPER_CORE_NO_PRIVATE
qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false);
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
g_micaMaterialData()->mutex.unlock();
Q_Q(MicaMaterial);
Q_EMIT q->shouldRedraw();
g_threadData()->thread->start(QThread::LowPriority);
}
void MicaMaterialPrivate::updateMaterialBrush()
@ -579,8 +677,8 @@ void MicaMaterialPrivate::updateMaterialBrush()
framelesshelpercore_initResource();
static const QImage noiseTexture = QImage(kNoiseImageFilePath);
#endif // FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE
QImage micaTexture = QImage(QSize(64, 64), QImage::Format_ARGB32_Premultiplied);
QColor fillColor = (Utils::shouldAppsUseDarkMode() ? kDefaultSystemDarkColor : kDefaultSystemLightColor2);
QImage micaTexture = QImage(QSize(64, 64), kDefaultImageFormat);
QColor fillColor = ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultSystemDarkColor : kDefaultSystemLightColor2);
fillColor.setAlphaF(0.9f);
micaTexture.fill(fillColor);
QPainter painter(&micaTexture);
@ -600,30 +698,68 @@ void MicaMaterialPrivate::updateMaterialBrush()
}
}
void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoint &pos)
void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active)
{
Q_ASSERT(painter);
if (!painter) {
Q_ASSERT(!size.isEmpty());
if (!painter || size.isEmpty()) {
return;
}
prepareGraphicsResources();
static constexpr const QPoint originPoint = {0, 0};
static constexpr const QPointF originPoint = {0, 0};
QPointF correctedPos = pos;
QSizeF correctedSize = size;
if (!qFuzzyIsNull(transform.Horizontal) && (transform.Horizontal > qreal(0))
&& !qFuzzyCompare(transform.Horizontal, qreal(1))) {
correctedPos.setX(correctedPos.x() * transform.Horizontal);
correctedSize.setWidth(correctedSize.width() * transform.Horizontal);
}
if (!qFuzzyIsNull(transform.Vertical) && (transform.Vertical > qreal(0))
&& !qFuzzyCompare(transform.Vertical, qreal(1))) {
correctedPos.setY(correctedPos.y() * transform.Vertical);
correctedSize.setHeight(correctedSize.height() * transform.Vertical);
}
painter->save();
painter->setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
g_micaMaterialData()->mutex.lock();
painter->drawPixmap(originPoint, g_micaMaterialData()->blurredWallpaper, QRect(pos, size));
g_micaMaterialData()->mutex.unlock();
if (active) {
const QMutexLocker locker(&g_micaMaterialData()->mutex);
painter->drawPixmap(originPoint, g_micaMaterialData()->blurredWallpaper, QRectF{correctedPos, correctedSize});
}
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setOpacity(1.0);
painter->fillRect(QRect(originPoint, size), micaBrush);
painter->setOpacity(qreal(1));
painter->fillRect(QRectF{originPoint, correctedSize}, [this, active]() -> QBrush {
if (!fallbackEnabled || active) {
return micaBrush;
}
if (fallbackColor.isValid()) {
return fallbackColor;
}
return systemFallbackColor();
}());
painter->restore();
}
void MicaMaterialPrivate::initialize()
{
g_threadData()->mutex.lock();
if (!g_threadData()->thread) {
g_threadData()->thread = std::make_unique<WallpaperThread>();
qAddPostRoutine(threadCleaner);
}
connect(g_threadData()->thread.get(), &WallpaperThread::imageUpdated, this, [this](const Transform &t){
transform = t;
if (initialized) {
Q_Q(MicaMaterial);
Q_EMIT q->shouldRedraw();
}
});
g_threadData()->mutex.unlock();
tintColor = kDefaultTransparentColor;
tintOpacity = kDefaultTintOpacity;
// Leave fallbackColor invalid, we need to use this state to judge
// whether we should use the system color instead.
noiseOpacity = kDefaultNoiseOpacity;
updateMaterialBrush();
@ -654,6 +790,11 @@ void MicaMaterialPrivate::prepareGraphicsResources()
maybeGenerateBlurredWallpaper();
}
QColor MicaMaterialPrivate::systemFallbackColor()
{
return ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultFallbackColorDark : kDefaultFallbackColorLight);
}
MicaMaterial::MicaMaterial(QObject *parent)
: QObject(parent), d_ptr(new MicaMaterialPrivate(this))
{
@ -701,6 +842,28 @@ void MicaMaterial::setTintOpacity(const qreal value)
Q_EMIT tintOpacityChanged();
}
QColor MicaMaterial::fallbackColor() const
{
Q_D(const MicaMaterial);
return d->fallbackColor;
}
void MicaMaterial::setFallbackColor(const QColor &value)
{
Q_ASSERT(value.isValid());
if (!value.isValid()) {
return;
}
Q_D(MicaMaterial);
if (d->fallbackColor == value) {
return;
}
d->prepareGraphicsResources();
d->fallbackColor = value;
d->updateMaterialBrush();
Q_EMIT fallbackColorChanged();
}
qreal MicaMaterial::noiseOpacity() const
{
Q_D(const MicaMaterial);
@ -719,10 +882,30 @@ void MicaMaterial::setNoiseOpacity(const qreal value)
Q_EMIT noiseOpacityChanged();
}
void MicaMaterial::paint(QPainter *painter, const QSize &size, const QPoint &pos)
bool MicaMaterial::isFallbackEnabled() const
{
Q_D(const MicaMaterial);
return d->fallbackEnabled;
}
void MicaMaterial::setFallbackEnabled(const bool value)
{
Q_D(MicaMaterial);
d->paint(painter, size, pos);
if (d->fallbackEnabled == value) {
return;
}
d->prepareGraphicsResources();
d->fallbackEnabled = value;
d->updateMaterialBrush();
Q_EMIT fallbackEnabledChanged();
}
void MicaMaterial::paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active)
{
Q_D(MicaMaterial);
d->paint(painter, size, pos, active);
}
FRAMELESSHELPER_END_NAMESPACE
#include "micamaterial.moc"

View File

@ -45,7 +45,6 @@
#endif // SYSAPILOADER_QLIBRARY
#include <QtCore/qhash.h>
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qdir.h>
#include <QtCore/qvarlengtharray.h>
@ -74,7 +73,6 @@ static Q_LOGGING_CATEGORY(lcSysApiLoader, "wangwenx190.framelesshelper.core.sysa
struct SysApiLoaderData
{
QMutex mutex;
QHash<QString, QFunctionPointer> functionCache = {};
};
@ -197,7 +195,6 @@ bool SysApiLoader::isAvailable(const QString &library, const QString &function)
return false;
}
const QString key = generateUniqueKey(library, function);
const QMutexLocker locker(&g_loaderData()->mutex);
if (g_loaderData()->functionCache.contains(key)) {
if (LoaderDebugFlag) {
DEBUG << Q_FUNC_INFO << "Function cache found:" << key;
@ -227,7 +224,6 @@ QFunctionPointer SysApiLoader::get(const QString &library, const QString &functi
return nullptr;
}
const QString key = generateUniqueKey(library, function);
const QMutexLocker locker(&g_loaderData()->mutex);
if (g_loaderData()->functionCache.contains(key)) {
if (LoaderDebugFlag) {
DEBUG << Q_FUNC_INFO << "Function cache found:" << key;

View File

@ -32,6 +32,7 @@
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qfontmetrics.h>
#include <QtGui/qpalette.h>
#ifndef FRAMELESSHELPER_CORE_NO_PRIVATE
# include <QtGui/private/qhighdpiscaling_p.h>
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
@ -92,7 +93,7 @@ static const QHash<int, FONT_ICON> g_fontIconsTable = {
if (!screen) {
return {};
}
return screen->virtualGeometry().topLeft();
return screen->geometry().topLeft();
}
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
@ -228,8 +229,8 @@ void Utils::moveWindowToDesktopCenter(FramelessParamsConst params, const bool co
if (!screen) {
return;
}
const QSize screenSize = (considerTaskBar ? screen->availableVirtualSize() : screen->virtualSize());
const QPoint offset = (considerTaskBar ? screen->availableVirtualGeometry().topLeft() : QPoint(0, 0));
const QSize screenSize = (considerTaskBar ? screen->availableSize() : screen->size());
const QPoint offset = (considerTaskBar ? screen->availableGeometry().topLeft() : QPoint(0, 0));
const int newX = std::round(qreal(screenSize.width() - windowSize.width()) / 2.0);
const int newY = std::round(qreal(screenSize.height() - windowSize.height()) / 2.0);
params->setWindowPosition(QPoint(newX + offset.x(), newY + offset.y()));
@ -265,7 +266,7 @@ bool Utils::isThemeChangeEvent(const QEvent * const event)
QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button, const ButtonState state)
{
if (state == ButtonState::Unspecified) {
if (state == ButtonState::Normal) {
return kDefaultTransparentColor;
}
const bool isClose = (button == SystemButtonType::Close);
@ -276,15 +277,7 @@ QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button
return kDefaultSystemCloseButtonBackgroundColor;
}
if (isTitleColor) {
#ifdef Q_OS_WINDOWS
return getDwmAccentColor();
#endif
#ifdef Q_OS_LINUX
return getWmThemeColor();
#endif
#ifdef Q_OS_MACOS
return getControlsAccentColor();
#endif
return getAccentColor();
}
return kDefaultSystemButtonBackgroundColor;
}();
@ -522,4 +515,70 @@ int Utils::horizontalAdvance(const QFontMetrics &fm, const QString &str)
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
}
qreal Utils::getRelativeScaleFactor(const quint32 oldDpi, const quint32 newDpi)
{
if (newDpi == oldDpi) {
return qreal(1);
}
static const quint32 defaultDpi = defaultScreenDpi();
if ((oldDpi < defaultDpi) || (newDpi < defaultDpi)) {
return qreal(1);
}
// We need to round the scale factor according to Qt's rounding policy.
const qreal oldDpr = roundScaleFactor(qreal(oldDpi) / qreal(defaultDpi));
const qreal newDpr = roundScaleFactor(qreal(newDpi) / qreal(defaultDpi));
return qreal(newDpr / oldDpr);
}
QSize Utils::rescaleSize(const QSize &oldSize, const quint32 oldDpi, const quint32 newDpi)
{
if (oldSize.isEmpty()) {
return {};
}
if (newDpi == oldDpi) {
return oldSize;
}
const qreal scaleFactor = getRelativeScaleFactor(oldDpi, newDpi);
if (qFuzzyIsNull(scaleFactor)) {
return {};
}
if (qFuzzyCompare(scaleFactor, qreal(1))) {
return oldSize;
}
const QSizeF newSize = QSizeF(oldSize) * scaleFactor;
return newSize.toSize(); // The numbers will be rounded to the nearest integer.
}
bool Utils::isValidGeometry(const QRect &rect)
{
// The position of the rectangle is not relevant.
return ((rect.right() > rect.left()) && (rect.bottom() > rect.top()));
}
quint32 Utils::defaultScreenDpi()
{
#ifdef Q_OS_MACOS
return 72;
#else // !Q_OS_MACOS
return 96;
#endif // Q_OS_MACOS
}
QColor Utils::getAccentColor()
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
return QGuiApplication::palette().color(QPalette::AccentColor);
#else // (QT_VERSION < QT_VERSION_CHECK(6, 6, 0))
# ifdef Q_OS_WINDOWS
return getAccentColor_windows();
# elif defined(Q_OS_LINUX)
return getAccentColor_linux();
# elif defined(Q_OS_MACOS)
return getAccentColor_macos();
# else
return QGuiApplication::palette().color(QPalette::Highlight);
# endif
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0))
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -344,12 +344,6 @@ xcb_connection_t *Utils::x11_connection()
#endif // FRAMELESSHELPER_HAS_X11EXTRAS
}
SystemTheme Utils::getSystemTheme()
{
// ### TODO: how to detect high contrast mode on Linux?
return (shouldAppsUseDarkMode() ? SystemTheme::Dark : SystemTheme::Light);
}
void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
{
Q_ASSERT(window);
@ -390,7 +384,7 @@ bool Utils::isTitleBarColorized()
return false;
}
QColor Utils::getWmThemeColor()
QColor Utils::getAccentColor_linux()
{
// ### TODO
return QGuiApplication::palette().color(QPalette::Highlight);
@ -577,7 +571,7 @@ void Utils::registerThemeChangeNotification()
QColor Utils::getFrameBorderColor(const bool active)
{
return (active ? getWmThemeColor() : kDefaultDarkGrayColor);
return (active ? getAccentColor() : kDefaultDarkGrayColor);
}
xcb_atom_t Utils::internAtom(const char *name)

View File

@ -28,7 +28,6 @@
#include "framelessconfig_p.h"
#include "framelesshelpercore_global_p.h"
#include <QtCore/qhash.h>
#include <QtCore/qmutex.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qloggingcategory.h>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
@ -234,7 +233,6 @@ public:
qwindow = qtWindow;
nswindow = macWindow;
instances.insert(macWindow, this);
saveState();
if (!windowClass) {
windowClass = [nswindow class];
Q_ASSERT(windowClass);
@ -246,41 +244,12 @@ public:
{
instances.remove(nswindow);
if (instances.count() <= 0) {
restoreImplementations();
windowClass = nil;
}
restoreState();
nswindow = nil;
}
public Q_SLOTS:
void saveState()
{
oldStyleMask = nswindow.styleMask;
oldTitlebarAppearsTransparent = nswindow.titlebarAppearsTransparent;
oldTitleVisibility = nswindow.titleVisibility;
oldHasShadow = nswindow.hasShadow;
oldShowsToolbarButton = nswindow.showsToolbarButton;
oldMovableByWindowBackground = nswindow.movableByWindowBackground;
oldMovable = nswindow.movable;
oldCloseButtonVisible = ![nswindow standardWindowButton:NSWindowCloseButton].hidden;
oldMiniaturizeButtonVisible = ![nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden;
oldZoomButtonVisible = ![nswindow standardWindowButton:NSWindowZoomButton].hidden;
}
void restoreState()
{
nswindow.styleMask = oldStyleMask;
nswindow.titlebarAppearsTransparent = oldTitlebarAppearsTransparent;
nswindow.titleVisibility = oldTitleVisibility;
nswindow.hasShadow = oldHasShadow;
nswindow.showsToolbarButton = oldShowsToolbarButton;
nswindow.movableByWindowBackground = oldMovableByWindowBackground;
nswindow.movable = oldMovable;
[nswindow standardWindowButton:NSWindowCloseButton].hidden = !oldCloseButtonVisible;
[nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = !oldMiniaturizeButtonVisible;
[nswindow standardWindowButton:NSWindowZoomButton].hidden = !oldZoomButtonVisible;
}
void replaceImplementations()
{
@ -362,9 +331,12 @@ public Q_SLOTS:
nswindow.showsToolbarButton = NO;
nswindow.movableByWindowBackground = NO;
nswindow.movable = NO;
// For some unknown reason, we don't need the following hack in Qt versions below or equal to 6.2.4.
#if (QT_VERSION > QT_VERSION_CHECK(6, 2, 4))
[nswindow standardWindowButton:NSWindowCloseButton].hidden = (visible ? NO : YES);
[nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = (visible ? NO : YES);
[nswindow standardWindowButton:NSWindowZoomButton].hidden = (visible ? NO : YES);
#endif
}
void setBlurBehindWindowEnabled(const bool enable)
@ -431,7 +403,7 @@ public Q_SLOTS:
return;
}
const auto view = static_cast<NSVisualEffectView *>(blurEffect);
if (Utils::shouldAppsUseDarkMode()) {
if (FramelessManager::instance()->systemTheme() == SystemTheme::Dark) {
view.appearance = [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantDark"];
} else {
view.appearance = [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantLight"];
@ -514,17 +486,6 @@ private:
//NSEvent *lastMouseDownEvent = nil;
NSView *blurEffect = nil;
NSWindowStyleMask oldStyleMask = 0;
BOOL oldTitlebarAppearsTransparent = NO;
BOOL oldHasShadow = NO;
BOOL oldShowsToolbarButton = NO;
BOOL oldMovableByWindowBackground = NO;
BOOL oldMovable = NO;
BOOL oldCloseButtonVisible = NO;
BOOL oldMiniaturizeButtonVisible = NO;
BOOL oldZoomButtonVisible = NO;
NSWindowTitleVisibility oldTitleVisibility = NSWindowTitleVisible;
QMetaObject::Connection widthChangeConnection = {};
QMetaObject::Connection heightChangeConnection = {};
QMetaObject::Connection themeChangeConnection = {};
@ -551,7 +512,6 @@ private:
struct MacUtilsData
{
QMutex mutex;
QHash<WId, NSWindowProxy *> hash = {};
};
@ -571,13 +531,27 @@ Q_GLOBAL_STATIC(MacUtilsData, g_macUtilsData);
return [nsview window];
}
static inline void cleanupProxy()
{
if (g_macUtilsData()->hash.isEmpty()) {
return;
}
for (auto &&proxy : std::as_const(g_macUtilsData()->hash)) {
Q_ASSERT(proxy);
if (!proxy) {
continue;
}
delete proxy;
}
g_macUtilsData()->hash.clear();
}
[[nodiscard]] static inline NSWindowProxy *ensureWindowProxy(const WId windowId)
{
Q_ASSERT(windowId);
if (!windowId) {
return nil;
}
const QMutexLocker locker(&g_macUtilsData()->mutex);
if (!g_macUtilsData()->hash.contains(windowId)) {
QWindow * const qwindow = Utils::findWindow(windowId);
Q_ASSERT(qwindow);
@ -592,33 +566,14 @@ Q_GLOBAL_STATIC(MacUtilsData, g_macUtilsData);
const auto proxy = new NSWindowProxy(qwindow, nswindow);
g_macUtilsData()->hash.insert(windowId, proxy);
}
volatile static const auto hook = []() -> int {
registerUninitializeHook([](){
const QMutexLocker locker(&g_macUtilsData()->mutex);
if (g_macUtilsData()->hash.isEmpty()) {
return;
static bool cleanerInstalled = false;
if (!cleanerInstalled) {
cleanerInstalled = true;
qAddPostRoutine(cleanupProxy);
}
for (auto &&proxy : std::as_const(g_macUtilsData()->hash)) {
Q_ASSERT(proxy);
if (!proxy) {
continue;
}
delete proxy;
}
g_macUtilsData()->hash.clear();
});
return 0;
}();
Q_UNUSED(hook);
return g_macUtilsData()->hash.value(windowId);
}
SystemTheme Utils::getSystemTheme()
{
// ### TODO: how to detect high contrast mode on macOS?
return (shouldAppsUseDarkMode() ? SystemTheme::Dark : SystemTheme::Light);
}
void Utils::setSystemTitleBarVisible(const WId windowId, const bool visible)
{
Q_ASSERT(windowId);
@ -676,7 +631,7 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
#endif
}
QColor Utils::getControlsAccentColor()
QColor Utils::getAccentColor_macos()
{
return qt_mac_toQColor([NSColor controlAccentColor]);
}
@ -778,7 +733,6 @@ void Utils::removeWindowProxy(const WId windowId)
if (!windowId) {
return;
}
const QMutexLocker locker(&g_macUtilsData()->mutex);
if (!g_macUtilsData()->hash.contains(windowId)) {
return;
}
@ -792,7 +746,7 @@ void Utils::removeWindowProxy(const WId windowId)
QColor Utils::getFrameBorderColor(const bool active)
{
return (active ? getControlsAccentColor() : kDefaultDarkGrayColor);
return (active ? getAccentColor() : kDefaultDarkGrayColor);
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -32,7 +32,6 @@
#include "framelesshelpercore_global_p.h"
#include "versionnumber_p.h"
#include "scopeguard_p.h"
#include <QtCore/qmutex.h>
#include <QtCore/qhash.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/qwindow.h>
@ -186,6 +185,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SendMessageTimeoutW)
FRAMELESSHELPER_STRING_CONSTANT(AttachThreadInput)
FRAMELESSHELPER_STRING_CONSTANT(BringWindowToTop)
FRAMELESSHELPER_STRING_CONSTANT(SetActiveWindow)
FRAMELESSHELPER_STRING_CONSTANT(RedrawWindow)
struct Win32UtilsHelperData
{
@ -195,20 +195,12 @@ struct Win32UtilsHelperData
struct Win32UtilsHelper
{
QMutex mutex;
QHash<WId, Win32UtilsHelperData> data = {};
QList<WId> micaWindowIds = {};
};
Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper)
struct MicaWindowData
{
QMutex mutex;
QList<WId> windowIds = {};
};
Q_GLOBAL_STATIC(MicaWindowData, g_micaData)
struct SYSTEM_METRIC
{
int DPI_96 = 0; // 100%. The scale factor for the device is 1x.
@ -236,6 +228,62 @@ struct SYSTEM_METRIC
{SM_CXPADDEDBORDER, { 4, 5, 5, 6, 6, 6, 7, 7, 8, 9, 10, 12, 14, 16, 18, 20}}
};
[[nodiscard]] bool operator==(const RECT &lhs, const RECT &rhs) noexcept
{
return ((lhs.left == rhs.left) && (lhs.top == rhs.top)
&& (lhs.right == rhs.right) && (lhs.bottom == rhs.bottom));
}
[[nodiscard]] bool operator!=(const RECT &lhs, const RECT &rhs) noexcept
{
return !operator==(lhs, rhs);
}
[[nodiscard]] QRect rect2qrect(const RECT &rect)
{
return QRect{QPoint{rect.left, rect.top}, QSize{RECT_WIDTH(rect), RECT_HEIGHT(rect)}};
}
[[nodiscard]] RECT qrect2rect(const QRect &qrect)
{
return {qrect.left(), qrect.top(), qrect.right(), qrect.bottom()};
}
[[nodiscard]] QString hwnd2str(const WId windowId)
{
// NULL handle is allowed here.
return FRAMELESSHELPER_STRING_LITERAL("0x") + QString::number(windowId, 16).toUpper().rightJustified(8, u'0');
}
[[nodiscard]] QString hwnd2str(const HWND hwnd)
{
// NULL handle is allowed here.
return hwnd2str(reinterpret_cast<WId>(hwnd));
}
[[nodiscard]] std::optional<MONITORINFOEXW> getMonitorForWindow(const HWND hwnd)
{
Q_ASSERT(hwnd);
if (!hwnd) {
return std::nullopt;
}
// Use "MONITOR_DEFAULTTONEAREST" here so that we can still get the correct
// monitor even if the window is minimized.
const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (!monitor) {
WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow);
return std::nullopt;
}
MONITORINFOEXW monitorInfo;
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
monitorInfo.cbSize = sizeof(monitorInfo);
if (GetMonitorInfoW(monitor, &monitorInfo) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kGetMonitorInfoW);
return std::nullopt;
}
return monitorInfo;
};
[[nodiscard]] static inline QString dwmRegistryKey()
{
static const QString key = QString::fromWCharArray(kDwmRegistryKey);
@ -296,7 +344,7 @@ struct SYSTEM_METRIC
return (VerifyVersionInfoW(&osvi, (VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER), dwlConditionMask) != FALSE);
}
[[nodiscard]] static inline QString __getSystemErrorMessage(const QString &function, const DWORD code)
[[nodiscard]] static inline QString getSystemErrorMessageImpl(const QString &function, const DWORD code)
{
Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) {
@ -321,7 +369,7 @@ struct SYSTEM_METRIC
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
}
[[nodiscard]] static inline QString __getSystemErrorMessage(const QString &function, const HRESULT hr)
[[nodiscard]] static inline QString getSystemErrorMessageImpl(const QString &function, const HRESULT hr)
{
Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) {
@ -331,36 +379,9 @@ struct SYSTEM_METRIC
return kSuccessMessageText;
}
const DWORD dwError = HRESULT_CODE(hr);
return __getSystemErrorMessage(function, dwError);
return getSystemErrorMessageImpl(function, dwError);
}
[[nodiscard]] static inline bool operator==(const RECT &lhs, const RECT &rhs) noexcept
{
return ((lhs.left == rhs.left) && (lhs.top == rhs.top)
&& (lhs.right == rhs.right) && (lhs.bottom == rhs.bottom));
}
[[nodiscard]] static inline std::optional<MONITORINFOEXW> getMonitorForWindow(const HWND hwnd)
{
Q_ASSERT(hwnd);
if (!hwnd) {
return std::nullopt;
}
const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (!monitor) {
WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow);
return std::nullopt;
}
MONITORINFOEXW monitorInfo;
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
monitorInfo.cbSize = sizeof(monitorInfo);
if (GetMonitorInfoW(monitor, &monitorInfo) == FALSE) {
WARNING << Utils::getSystemErrorMessage(kGetMonitorInfoW);
return std::nullopt;
}
return monitorInfo;
};
static inline void moveWindowToMonitor(const HWND hwnd, const MONITORINFOEXW &activeMonitor)
{
Q_ASSERT(hwnd);
@ -468,13 +489,10 @@ static inline void moveWindowToMonitor(const HWND hwnd, const MONITORINFOEXW &ac
return 0;
}
const auto windowId = reinterpret_cast<WId>(hWnd);
g_utilsHelper()->mutex.lock();
if (!g_utilsHelper()->data.contains(windowId)) {
g_utilsHelper()->mutex.unlock();
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
const Win32UtilsHelperData data = g_utilsHelper()->data.value(windowId);
g_utilsHelper()->mutex.unlock();
const auto getNativePosFromMouse = [lParam]() -> QPoint {
return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
};
@ -594,7 +612,7 @@ bool Utils::isDwmCompositionEnabled()
BOOL enabled = FALSE;
const HRESULT hr = API_CALL_FUNCTION(dwmapi, DwmIsCompositionEnabled, &enabled);
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmIsCompositionEnabled, hr);
WARNING << getSystemErrorMessageImpl(kDwmIsCompositionEnabled, hr);
return resultFromRegistry();
}
return (enabled != FALSE);
@ -607,11 +625,18 @@ void Utils::triggerFrameChange(const WId windowId)
return;
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
static constexpr const UINT flags =
static constexpr const UINT swpFlags =
(SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE
| SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
if (SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, flags) == FALSE) {
if (SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, swpFlags) == FALSE) {
WARNING << getSystemErrorMessage(kSetWindowPos);
return;
}
static constexpr const UINT rdwFlags =
(RDW_ERASE | RDW_FRAME | RDW_INVALIDATE
| RDW_UPDATENOW | RDW_ALLCHILDREN);
if (RedrawWindow(hwnd, nullptr, nullptr, rdwFlags) == FALSE) {
WARNING << getSystemErrorMessage(kRedrawWindow);
}
}
@ -629,9 +654,7 @@ void Utils::updateWindowFrameMargins(const WId windowId, const bool reset)
if (!API_DWM_AVAILABLE(DwmExtendFrameIntoClientArea)) {
return;
}
g_micaData()->mutex.lock();
const bool micaEnabled = g_micaData()->windowIds.contains(windowId);
g_micaData()->mutex.unlock();
const bool micaEnabled = g_utilsHelper()->micaWindowIds.contains(windowId);
const auto margins = [micaEnabled, reset]() -> MARGINS {
// To make Mica/Mica Alt work for normal Win32 windows, we have to
// let the window frame extend to the whole window (or disable the
@ -649,7 +672,7 @@ void Utils::updateWindowFrameMargins(const WId windowId, const bool reset)
const auto hwnd = reinterpret_cast<HWND>(windowId);
const HRESULT hr = API_CALL_FUNCTION(dwmapi, DwmExtendFrameIntoClientArea, hwnd, &margins);
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr);
WARNING << getSystemErrorMessageImpl(kDwmExtendFrameIntoClientArea, hr);
return;
}
triggerFrameChange(windowId);
@ -712,7 +735,7 @@ QString Utils::getSystemErrorMessage(const QString &function)
if (code == ERROR_SUCCESS) {
return {};
}
return __getSystemErrorMessage(function, code);
return getSystemErrorMessageImpl(function, code);
}
QColor Utils::getDwmColorizationColor()
@ -735,7 +758,7 @@ QColor Utils::getDwmColorizationColor()
BOOL opaque = FALSE;
const HRESULT hr = API_CALL_FUNCTION(dwmapi, DwmGetColorizationColor, &color, &opaque);
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmGetColorizationColor, hr);
WARNING << getSystemErrorMessageImpl(kDwmGetColorizationColor, hr);
return resultFromRegistry();
}
return QColor::fromRgba(color);
@ -959,7 +982,7 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal)
if (SUCCEEDED(hr) && (dpiX > 0) && (dpiY > 0)) {
return (horizontal ? dpiX : dpiY);
} else {
WARNING << __getSystemErrorMessage(kGetDpiForMonitor, hr);
WARNING << getSystemErrorMessageImpl(kGetDpiForMonitor, hr);
}
}
// GetScaleFactorForMonitor() is only available on Windows 8 and onwards.
@ -969,7 +992,7 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal)
if (SUCCEEDED(hr) && (factor != _DEVICE_SCALE_FACTOR_INVALID)) {
return quint32(std::round(qreal(USER_DEFAULT_SCREEN_DPI) * qreal(factor) / qreal(100)));
} else {
WARNING << __getSystemErrorMessage(kGetScaleFactorForMonitor, hr);
WARNING << getSystemErrorMessageImpl(kGetScaleFactorForMonitor, hr);
}
}
// This solution is supported on Windows 2000 and onwards.
@ -1028,10 +1051,10 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal)
WARNING << "GetDesktopDpi() failed.";
}
} else {
WARNING << __getSystemErrorMessage(kReloadSystemMetrics, hr);
WARNING << getSystemErrorMessageImpl(kReloadSystemMetrics, hr);
}
} else {
WARNING << __getSystemErrorMessage(kD2D1CreateFactory, hr);
WARNING << getSystemErrorMessageImpl(kD2D1CreateFactory, hr);
}
if (d2dFactory) {
d2dFactory->Release();
@ -1239,10 +1262,10 @@ QColor Utils::getFrameBorderColor(const bool active)
if (!WindowsVersionHelper::isWin10OrGreater()) {
return (active ? kDefaultBlackColor : kDefaultDarkGrayColor);
}
const bool dark = shouldAppsUseDarkMode();
const bool dark = (FramelessManager::instance()->systemTheme() == SystemTheme::Dark);
if (active) {
if (isFrameBorderColorized()) {
return getDwmAccentColor();
return getAccentColor();
}
return (dark ? kDefaultFrameBorderActiveColor : kDefaultTransparentColor);
} else {
@ -1394,7 +1417,6 @@ void Utils::installSystemMenuHook(const WId windowId, FramelessParamsConst param
if (!windowId || !params) {
return;
}
const QMutexLocker locker(&g_utilsHelper()->mutex);
if (g_utilsHelper()->data.contains(windowId)) {
return;
}
@ -1424,7 +1446,6 @@ void Utils::uninstallSystemMenuHook(const WId windowId)
if (!windowId) {
return;
}
const QMutexLocker locker(&g_utilsHelper()->mutex);
if (!g_utilsHelper()->data.contains(windowId)) {
return;
}
@ -1499,7 +1520,7 @@ void Utils::tryToEnableHighestDpiAwarenessLevel()
DEBUG << kDpiNoAccessErrorMessage;
return true;
}
WARNING << __getSystemErrorMessage(kSetProcessDpiAwarenessContext, dwError);
WARNING << getSystemErrorMessageImpl(kSetProcessDpiAwarenessContext, dwError);
return false;
};
if (currentAwareness == DpiAwareness::PerMonitorVersion2) {
@ -1540,7 +1561,7 @@ void Utils::tryToEnableHighestDpiAwarenessLevel()
DEBUG << kDpiNoAccessErrorMessage;
return true;
}
WARNING << __getSystemErrorMessage(kSetProcessDpiAwareness, hr);
WARNING << getSystemErrorMessageImpl(kSetProcessDpiAwareness, hr);
return false;
};
if (currentAwareness == DpiAwareness::PerMonitorVersion2) {
@ -1580,17 +1601,6 @@ void Utils::tryToEnableHighestDpiAwarenessLevel()
}
}
SystemTheme Utils::getSystemTheme()
{
if (isHighContrastModeEnabled()) {
return SystemTheme::HighContrast;
}
if (WindowsVersionHelper::isWin10RS1OrGreater() && shouldAppsUseDarkMode()) {
return SystemTheme::Dark;
}
return SystemTheme::Light;
}
void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
{
Q_ASSERT(windowId);
@ -1608,14 +1618,14 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
const HRESULT hr = API_CALL_FUNCTION(uxtheme, SetWindowTheme, hwnd,
(dark ? kSystemDarkThemeResourceName : kSystemLightThemeResourceName), nullptr);
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kSetWindowTheme, hr);
WARNING << getSystemErrorMessageImpl(kSetWindowTheme, hr);
}
}
bool Utils::shouldAppsUseDarkMode_windows()
{
// The global dark mode was first introduced in Windows 10 1607.
if (!WindowsVersionHelper::isWin10RS1OrGreater()) {
if (!WindowsVersionHelper::isWin10RS1OrGreater() || isHighContrastModeEnabled()) {
return false;
}
#ifndef FRAMELESSHELPER_CORE_NO_PRIVATE
@ -1688,7 +1698,7 @@ void Utils::setCornerStyleForWindow(const WId windowId, const WindowCornerStyle
const HRESULT hr = API_CALL_FUNCTION(dwmapi, DwmSetWindowAttribute,
hwnd, _DWMWA_WINDOW_CORNER_PREFERENCE, &wcp, sizeof(wcp));
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
}
@ -1706,11 +1716,9 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
return false;
}
const auto restoreWindowFrameMargins = [windowId]() -> void {
g_micaData()->mutex.lock();
if (g_micaData()->windowIds.contains(windowId)) {
g_micaData()->windowIds.removeAll(windowId);
if (g_utilsHelper()->micaWindowIds.contains(windowId)) {
g_utilsHelper()->micaWindowIds.removeAll(windowId);
}
g_micaData()->mutex.unlock();
updateWindowFrameMargins(windowId, false);
};
const bool preferMicaAlt = (qEnvironmentVariableIntValue("FRAMELESSHELPER_PREFER_MICA_ALT") != 0);
@ -1750,7 +1758,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
if (FAILED(hr)) {
result = false;
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
} else if (WindowsVersionHelper::isWin11OrGreater()) {
const BOOL enable = FALSE;
@ -1758,7 +1766,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
if (FAILED(hr)) {
result = false;
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
} else {
ACCENT_POLICY policy;
@ -1781,11 +1789,9 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
return result;
} else {
if ((blurMode == BlurMode::Windows_Mica) || (blurMode == BlurMode::Windows_MicaAlt)) {
g_micaData()->mutex.lock();
if (!g_micaData()->windowIds.contains(windowId)) {
g_micaData()->windowIds.append(windowId);
if (!g_utilsHelper()->micaWindowIds.contains(windowId)) {
g_utilsHelper()->micaWindowIds.append(windowId);
}
g_micaData()->mutex.unlock();
// By giving a negative value, DWM will extend the window frame into the whole
// client area. We need this step because the Mica material can only be applied
// to the non-client area of a window. Without this step, you'll get a window
@ -1813,7 +1819,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
if (SUCCEEDED(hr)) {
return true;
} else {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
} else {
const BOOL enable = TRUE;
@ -1822,11 +1828,11 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
if (SUCCEEDED(hr)) {
return true;
} else {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
}
} else {
WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr);
WARNING << getSystemErrorMessageImpl(kDwmExtendFrameIntoClientArea, hr);
}
restoreWindowFrameMargins();
} else {
@ -1839,7 +1845,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
if (color.isValid()) {
return color;
}
QColor clr = (shouldAppsUseDarkMode() ? kDefaultSystemDarkColor : kDefaultSystemLightColor);
QColor clr = ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultSystemDarkColor : kDefaultSystemLightColor);
clr.setAlphaF(0.9f);
return clr;
}();
@ -1895,14 +1901,14 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
if (SUCCEEDED(hr)) {
return true;
}
WARNING << __getSystemErrorMessage(kDwmEnableBlurBehindWindow, hr);
WARNING << getSystemErrorMessageImpl(kDwmEnableBlurBehindWindow, hr);
}
return false;
}
QColor Utils::getDwmAccentColor()
QColor Utils::getAccentColor_windows()
{
// According to my test, this AccentColor will be exactly the same with
// According to my experiments, this AccentColor will be exactly the same with
// ColorizationColor, what's the meaning of it? But Microsoft products
// usually read this setting instead of using DwmGetColorizationColor(),
// so we'd better also do the same thing.
@ -1918,7 +1924,7 @@ QColor Utils::getDwmAccentColor()
return alternative;
}
// The retrieved value is in the #AABBGGRR format, we need to
// convert it to the #AARRGGBB format that Qt accepts.
// convert it to the #AARRGGBB format which Qt expects.
const QColor abgr = QColor::fromRgba(qvariant_cast<DWORD>(value));
if (!abgr.isValid()) {
return alternative;
@ -1990,7 +1996,7 @@ void Utils::hideOriginalTitleBarElements(const WId windowId, const bool disable)
const DWORD mask = (disable ? validBits : 0);
const HRESULT hr = _SetWindowThemeNonClientAttributes(hwnd, mask, mask);
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kSetWindowThemeAttribute, hr);
WARNING << getSystemErrorMessageImpl(kSetWindowThemeAttribute, hr);
}
}
@ -2086,7 +2092,7 @@ void Utils::refreshWin32ThemeResources(const WId windowId, const bool dark)
}
const HRESULT hr = API_CALL_FUNCTION(dwmapi, DwmSetWindowAttribute, hWnd, borderFlag, &darkFlag, sizeof(darkFlag));
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
SetLastError(ERROR_SUCCESS);
_FlushMenuThemes();
@ -2114,7 +2120,7 @@ void Utils::refreshWin32ThemeResources(const WId windowId, const bool dark)
}
const HRESULT hr = API_CALL_FUNCTION(dwmapi, DwmSetWindowAttribute, hWnd, borderFlag, &darkFlag, sizeof(darkFlag));
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
WARNING << getSystemErrorMessageImpl(kDwmSetWindowAttribute, hr);
}
SetLastError(ERROR_SUCCESS);
_FlushMenuThemes();
@ -2217,7 +2223,7 @@ DpiAwareness Utils::getDpiAwarenessForCurrentProcess(bool *highest)
_PROCESS_DPI_AWARENESS pda = _PROCESS_DPI_UNAWARE;
const HRESULT hr = API_CALL_FUNCTION4(shcore, GetProcessDpiAwareness, nullptr, &pda);
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kGetProcessDpiAwareness, hr);
WARNING << getSystemErrorMessageImpl(kGetProcessDpiAwareness, hr);
return DpiAwareness::Unknown;
}
auto result = DpiAwareness::Unknown;
@ -2390,4 +2396,72 @@ void Utils::bringWindowToFront(const WId windowId)
moveWindowToMonitor(hwnd, activeMonitor.value());
}
QPoint Utils::getWindowPlacementOffset(const WId windowId)
{
Q_ASSERT(windowId);
if (!windowId) {
return {};
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS);
const auto exStyle = static_cast<DWORD>(GetWindowLongPtrW(hwnd, GWL_EXSTYLE));
if (exStyle == 0) {
WARNING << getSystemErrorMessage(kGetWindowLongPtrW);
return {};
}
// Tool windows are special and they don't need any offset.
if (exStyle & WS_EX_TOOLWINDOW) {
return {};
}
const std::optional<MONITORINFOEXW> mi = getMonitorForWindow(hwnd);
if (!mi.has_value()) {
WARNING << "Failed to retrieve the window's monitor.";
return {};
}
const RECT work = mi.value().rcWork;
const RECT total = mi.value().rcMonitor;
return {work.left - total.left, work.top - total.top};
}
QRect Utils::getWindowRestoreGeometry(const WId windowId)
{
Q_ASSERT(windowId);
if (!windowId) {
return {};
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
WINDOWPLACEMENT wp;
SecureZeroMemory(&wp, sizeof(wp));
wp.length = sizeof(wp);
if (GetWindowPlacement(hwnd, &wp) == FALSE) {
WARNING << getSystemErrorMessage(kGetWindowPlacement);
return {};
}
return rect2qrect(wp.rcNormalPosition).translated(getWindowPlacementOffset(windowId));
}
void Utils::removeMicaWindow(const WId windowId)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
if (!g_utilsHelper()->micaWindowIds.contains(windowId)) {
return;
}
g_utilsHelper()->micaWindowIds.removeAll(windowId);
}
void Utils::removeSysMenuHook(const WId windowId)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
if (!g_utilsHelper()->data.contains(windowId)) {
return;
}
g_utilsHelper()->data.remove(windowId);
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -112,20 +112,15 @@ void WindowBorderPainterPrivate::paint(QPainter *painter, const QSize &size, con
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
QList<QLine> lines = {};
QList<QLineF> lines = {};
#else
QVector<QLine> lines = {};
QVector<QLineF> lines = {};
#endif
const QPoint leftTop = {0, 0};
// In fact, we should use "size.width() - 1" here in theory but we can't
// because Qt's drawing system has some rounding errors internally and if
// we minus one here we'll get a one pixel gap, so sad. But drawing a line
// with a little extra pixels won't hurt anyway.
const QPoint rightTop = {size.width(), 0};
// Same here as above: we should use "size.height() - 1" ideally but we
// can't, sadly.
const QPoint rightBottom = {size.width(), size.height()};
const QPoint leftBottom = {0, size.height()};
static constexpr const auto gap = qreal(0.5);
const QPointF leftTop = {gap, gap};
const QPointF rightTop = {qreal(size.width()) - gap, gap};
const QPointF rightBottom = {qreal(size.width()) - gap, qreal(size.height()) - gap};
const QPointF leftBottom = {gap, qreal(size.height()) - gap};
const WindowEdges edges = m_edges.value_or(getNativeBorderEdges());
if (edges & WindowEdge::Left) {
lines.append({leftBottom, leftTop});
@ -143,10 +138,7 @@ void WindowBorderPainterPrivate::paint(QPainter *painter, const QSize &size, con
return;
}
painter->save();
painter->setRenderHints(QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
// We can't enable antialiasing here, because the border is too thin and antialiasing
// will break it's painting.
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
QPen pen = {};
pen.setColor([active, this]() -> QColor {
QColor color = {};

View File

@ -24,14 +24,18 @@
include(GNUInstallDirs)
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS QuickTemplates2 QuickControls2)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS QuickTemplates2 QuickControls2)
set(SUB_MOD_NAME Quick)
set(SUB_PROJ_NAME ${PROJECT_NAME}${SUB_MOD_NAME})
set(SUB_PROJ_PATH ${PROJECT_NAME}/${SUB_MOD_NAME})
set(SUB_MODULE Quick)
set(SUB_MODULE_FULL_NAME ${PROJECT_NAME}${SUB_MODULE})
set(SUB_MODULE_PATH ${PROJECT_NAME}/${SUB_MODULE})
set(INCLUDE_PREFIX ../../include/${SUB_PROJ_PATH})
set(INCLUDE_PREFIX ../../include/${SUB_MODULE_PATH})
set(PUBLIC_HEADERS
${INCLUDE_PREFIX}/framelesshelperquick_global.h
@ -84,18 +88,20 @@ set(SOURCES
)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
set(__rc_path "${CMAKE_CURRENT_BINARY_DIR}/${SUB_PROJ_NAME}.rc")
set(__rc_path "${CMAKE_CURRENT_BINARY_DIR}/${SUB_MODULE_FULL_NAME}.rc")
if(NOT EXISTS "${__rc_path}")
generate_win32_rc_file(
PATH "${__rc_path}"
VERSION "${PROJECT_VERSION}"
COMPANY "wangwenx190"
DESCRIPTION "${PROJECT_NAME} ${SUB_MOD_NAME} Module"
DESCRIPTION "${PROJECT_NAME} ${SUB_MODULE} Module"
COPYRIGHT "MIT License"
ORIGINAL_FILENAME "${PROJECT_NAME}${SUB_MOD_NAME}.dll"
ORIGINAL_FILENAME "${PROJECT_NAME}${SUB_MODULE}.dll"
PRODUCT "${PROJECT_NAME}"
COMMENTS "Built from commit ${PROJECT_VERSION_COMMIT} on ${PROJECT_COMPILE_DATETIME} (UTC)."
LIBRARY
)
endif()
list(APPEND SOURCES "${__rc_path}")
endif()
@ -106,65 +112,34 @@ if(FRAMELESSHELPER_BUILD_STATIC)
else()
set(SUB_MOD_LIB_TYPE "SHARED")
endif()
add_library(${SUB_PROJ_NAME} ${SUB_MOD_LIB_TYPE} ${ALL_SOURCES})
add_library(${PROJECT_NAME}::${SUB_PROJ_NAME} ALIAS ${SUB_PROJ_NAME})
add_library(${PROJECT_NAME}::${SUB_MOD_NAME} ALIAS ${SUB_PROJ_NAME})
add_library(${SUB_MODULE} ${SUB_MOD_LIB_TYPE} ${ALL_SOURCES})
add_library(${SUB_MODULE_FULL_NAME} ALIAS ${SUB_MODULE})
add_library(${PROJECT_NAME}::${SUB_MODULE} ALIAS ${SUB_MODULE})
add_library(${PROJECT_NAME}::${SUB_MODULE_FULL_NAME} ALIAS ${SUB_MODULE})
set_target_properties(${SUB_PROJ_NAME} PROPERTIES
set_target_properties(${SUB_MODULE} PROPERTIES
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION_MAJOR}"
OUTPUT_NAME "${SUB_MODULE_FULL_NAME}"
)
set(SUB_MOD_TARGETS ${SUB_PROJ_NAME})
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
set(SUB_MOD_LIB_DIR "${CMAKE_INSTALL_BINDIR}")
else()
set(SUB_MOD_LIB_DIR "${CMAKE_INSTALL_LIBDIR}")
endif()
set(__prefix "")
if(NOT WIN32)
set(__prefix "lib")
endif()
set(__suffix "")
if(FRAMELESSHELPER_BUILD_STATIC)
if(MSVC)
set(__suffix "lib")
else()
set(__suffix "a")
endif()
else()
if(WIN32)
set(__suffix "dll")
elseif(APPLE)
set(__suffix "dylib")
elseif(UNIX)
set(__suffix "so")
endif()
endif()
set(SUB_MOD_FILE_PREFIX "${__prefix}")
set(SUB_MOD_FILE_SUFFIX "${__suffix}")
set(SUB_MOD_FILE_BASENAME "${SUB_MOD_FILE_PREFIX}${SUB_PROJ_NAME}")
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
string(APPEND SUB_MOD_FILE_BASENAME "${CMAKE_DEBUG_POSTFIX}")
endif()
set(SUB_MOD_FILE_NAME "${SUB_MOD_FILE_BASENAME}.${SUB_MOD_FILE_SUFFIX}")
unset(__suffix)
unset(__prefix)
set(__export_targets ${SUB_MODULE})
set(__import_base_dir "${PROJECT_BINARY_DIR}/imports")
if(DEFINED FRAMELESSHELPER_IMPORT_DIR)
set(__import_base_dir "${FRAMELESSHELPER_IMPORT_DIR}")
endif()
set(__import_uri "org/wangwenx190/${PROJECT_NAME}")
set(__import_dir "${__import_base_dir}/${__import_uri}")
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
qt_add_qml_module(${SUB_PROJ_NAME}
# qt_add_qml_module() was introduced in Qt 6.2 but qt_query_qml_module()
# was introduced in 6.3, to simplify the CMake code, I decide to limit the
# version check to 6.3, otherwise we'll need a lot of code to query and
# calculate the generated files, which will also break Ninja Multi-Config
# builds.
if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
qt_add_qml_module(${SUB_MODULE}
URI "org.wangwenx190.${PROJECT_NAME}"
VERSION "1.0"
OUTPUT_DIRECTORY "${__import_dir}"
OUTPUT_DIRECTORY "${__import_base_dir}/org/wangwenx190/${PROJECT_NAME}"
RESOURCE_PREFIX "/"
#NO_RESOURCE_TARGET_PATH # Can't be used for non-executables.
OUTPUT_TARGETS __qml_targets
@ -174,114 +149,127 @@ if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
QtQuick.Controls.Basic/auto
)
if(__qml_targets)
foreach(__target ${__qml_targets})
list(APPEND SUB_MOD_TARGETS ${__target})
list(APPEND __export_targets ${__qml_targets})
# We have some resources bundled into the library,
# however, for static builds, the object files will
# not be packed into our final static library file,
# and thus it causes linker errors for our users,
# so we need this hack here.
if(FRAMELESSHELPER_BUILD_STATIC)
target_sources(${SUB_PROJ_NAME} PRIVATE
foreach(__target ${__qml_targets})
target_sources(${SUB_MODULE} PRIVATE
$<TARGET_OBJECTS:${__target}>
)
endif()
endforeach()
endif()
endif()
if(NOT FRAMELESSHELPER_NO_INSTALL)
set(__lib_prefix)
if(UNIX)
set(__lib_prefix lib)
endif()
set(__lib_suffix "$<$<CONFIG:Debug>:${CMAKE_DEBUG_POSTFIX}>")
set(__lib_ext)
if(FRAMELESSHELPER_BUILD_STATIC)
if(MSVC)
set(__lib_ext lib)
else()
set(__lib_ext a)
endif()
else()
if(WIN32)
set(__lib_ext dll)
elseif(APPLE)
set(__lib_ext dylib)
elseif(UNIX)
set(__lib_ext so)
endif()
endif()
install(
FILES
"${__import_dir}/qmldir"
"${__import_dir}/${SUB_PROJ_NAME}.qmltypes"
"${__import_dir}/${__lib_prefix}${SUB_PROJ_NAME}plugin${__lib_suffix}.${__lib_ext}"
DESTINATION "qml/${__import_uri}"
qt_query_qml_module(${SUB_MODULE}
URI module_uri
VERSION module_version
PLUGIN_TARGET module_plugin_target
TARGET_PATH module_target_path
QMLDIR module_qmldir
TYPEINFO module_typeinfo
#QML_FILES module_qml_files
#QML_FILES_DEPLOY_PATHS module_qml_files_deploy_paths
#RESOURCES module_resources
#RESOURCES_DEPLOY_PATHS module_resources_deploy_paths
)
if(module_target_path)
set(__qml_plugin_dir "qml/${module_target_path}")
if(module_plugin_target)
install(TARGETS ${module_plugin_target}
RUNTIME DESTINATION "${__qml_plugin_dir}"
LIBRARY DESTINATION "${__qml_plugin_dir}"
ARCHIVE DESTINATION "${__qml_plugin_dir}"
)
endif()
if(module_qmldir)
install(FILES "${module_qmldir}" DESTINATION "${__qml_plugin_dir}")
endif()
if(module_typeinfo)
install(FILES "${module_typeinfo}" DESTINATION "${__qml_plugin_dir}")
endif()
if(module_qml_files)
list(LENGTH module_qml_files num_files)
math(EXPR last_index "${num_files} - 1")
foreach(i RANGE 0 ${last_index})
list(GET module_qml_files ${i} src_file)
list(GET module_qml_files_deploy_paths ${i} deploy_path)
get_filename_component(dest_dir "${deploy_path}" DIRECTORY)
install(FILES "${src_file}" DESTINATION "${__qml_plugin_dir}/${dest_dir}")
endforeach()
endif()
if(module_resources)
list(LENGTH module_resources num_files)
math(EXPR last_index "${num_files} - 1")
foreach(i RANGE 0 ${last_index})
list(GET module_resources ${i} src_file)
list(GET module_resources_deploy_paths ${i} deploy_path)
get_filename_component(dest_dir "${deploy_path}" DIRECTORY)
install(FILES "${src_file}" DESTINATION "${__qml_plugin_dir}/${dest_dir}")
endforeach()
endif()
endif()
endif()
endif()
if(FRAMELESSHELPER_BUILD_STATIC)
set(__def FRAMELESSHELPER_QUICK_STATIC)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_QUICK_STATIC)
endif()
if(FRAMELESSHELPER_NO_DEBUG_OUTPUT)
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
FRAMELESSHELPER_QUICK_NO_DEBUG_OUTPUT
)
endif()
if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
set(__def FRAMELESSHELPER_QUICK_NO_BUNDLE_RESOURCE)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_QUICK_NO_BUNDLE_RESOURCE)
endif()
if(FRAMELESSHELPER_NO_PRIVATE)
set(__def FRAMELESSHELPER_QUICK_NO_PRIVATE)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_QUICK_NO_PRIVATE)
endif()
if(DEFINED FRAMELESSHELPER_NAMESPACE)
if("x${FRAMELESSHELPER_NAMESPACE}" STREQUAL "x")
message(FATAL_ERROR "FRAMELESSHELPER_NAMESPACE can't be empty!")
endif()
set(__def FRAMELESSHELPER_NAMESPACE=${FRAMELESSHELPER_NAMESPACE})
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_NAMESPACE=${FRAMELESSHELPER_NAMESPACE})
endif()
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
FRAMELESSHELPER_QUICK_LIBRARY
)
if(FRAMELESSHELPER_NO_PRIVATE)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
)
else()
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
Qt${QT_VERSION_MAJOR}::QuickPrivate
Qt${QT_VERSION_MAJOR}::QuickTemplates2Private
Qt${QT_VERSION_MAJOR}::QuickControls2Private
)
endif()
target_link_libraries(${SUB_PROJ_NAME} PUBLIC
target_link_libraries(${SUB_MODULE} PUBLIC
${PROJECT_NAME}::Core
)
target_include_directories(${SUB_PROJ_NAME} PUBLIC
target_include_directories(${SUB_MODULE} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}/../..>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}/private>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_MODULE_PATH}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_MODULE_PATH}/private>"
)
setup_qt_stuff(TARGETS ${SUB_PROJ_NAME} ALLOW_KEYWORD)
setup_qt_stuff(TARGETS ${SUB_MODULE} ALLOW_KEYWORD)
set(__extra_flags)
if(NOT FRAMELESSHELPER_NO_PERMISSIVE_CHECKS)
list(APPEND __extra_flags PERMISSIVE)
@ -301,31 +289,22 @@ endif()
if(FRAMELESSHELPER_ENABLE_CFGUARD)
list(APPEND __extra_flags CFGUARD)
endif()
setup_compile_params(TARGETS ${SUB_PROJ_NAME} ${__extra_flags})
if(FRAMELESSHELPER_FORCE_LTO)
list(APPEND __extra_flags FORCE_LTO)
endif()
setup_compile_params(TARGETS ${SUB_MODULE} ${__extra_flags})
if(NOT FRAMELESSHELPER_NO_INSTALL)
set(__cmake_dir "${CMAKE_CURRENT_BINARY_DIR}/cmake")
set(__config_file "${__cmake_dir}/${SUB_PROJ_NAME}Config.cmake")
configure_file(../../FramelessHelperModuleConfig.cmake.in ${__config_file} @ONLY)
set(__targets_file "${__cmake_dir}/${SUB_PROJ_NAME}Targets.cmake")
configure_file(../../FramelessHelperModuleTargets.cmake.in ${__targets_file} @ONLY)
install(
FILES "${__config_file}" "${__targets_file}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${SUB_PROJ_NAME}"
)
set(__inc_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}")
install(
FILES ${PUBLIC_HEADERS} ${PUBLIC_HEADERS_ALIAS}
DESTINATION "${__inc_dir}"
)
install(
FILES ${PRIVATE_HEADERS}
DESTINATION "${__inc_dir}/private"
)
install(
TARGETS ${SUB_MOD_TARGETS}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
INCLUDES DESTINATION "${__inc_dir}"
setup_package_export(
TARGETS ${__export_targets}
NAMESPACE ${PROJECT_NAME}
PACKAGE_NAME ${PROJECT_NAME}
COMPONENT ${SUB_MODULE}
PUBLIC_HEADERS ${PUBLIC_HEADERS}
ALIAS_HEADERS ${PUBLIC_HEADERS_ALIAS}
PRIVATE_HEADERS ${PRIVATE_HEADERS}
)
endif()
if(NOT FRAMELESSHELPER_NO_SUMMARY)
dump_target_info(TARGETS ${SUB_MODULE})
endif()

View File

@ -33,7 +33,6 @@
#ifdef Q_OS_WINDOWS
# include <FramelessHelper/Core/private/winverhelper_p.h>
#endif // Q_OS_WINDOWS
#include <QtCore/qmutex.h>
#include <QtCore/qtimer.h>
#include <QtCore/qeventloop.h>
#include <QtCore/qloggingcategory.h>
@ -86,7 +85,6 @@ struct QuickHelperData
struct QuickHelper
{
QMutex mutex;
QHash<WId, QuickHelperData> data = {};
};
@ -160,7 +158,6 @@ void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value)
if (!value) {
return;
}
const QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
if (!data) {
return;
@ -181,13 +178,10 @@ void FramelessQuickHelperPrivate::attach()
return;
}
g_quickHelper()->mutex.lock();
QuickHelperData * const data = getWindowDataMutable();
if (!data || data->ready) {
g_quickHelper()->mutex.unlock();
return;
}
g_quickHelper()->mutex.unlock();
SystemParameters params = {};
params.getWindowId = [window]() -> WId { return window->winId(); };
@ -224,13 +218,12 @@ void FramelessQuickHelperPrivate::attach()
params.setCursor = [window](const QCursor &cursor) -> void { window->setCursor(cursor); };
params.unsetCursor = [window]() -> void { window->unsetCursor(); };
params.getWidgetHandle = []() -> QObject * { return nullptr; };
params.forceChildrenRepaint = [this](const int delay) -> void { repaintAllChildren(delay); };
FramelessManager::instance()->addWindow(&params);
g_quickHelper()->mutex.lock();
data->params = params;
data->ready = true;
g_quickHelper()->mutex.unlock();
// We have to wait for a little time before moving the top level window
// , because the platform window may not finish initializing by the time
@ -257,7 +250,6 @@ void FramelessQuickHelperPrivate::detach()
return;
}
const WId windowId = w->winId();
const QMutexLocker locker(&g_quickHelper()->mutex);
if (!g_quickHelper()->data.contains(windowId)) {
return;
}
@ -272,14 +264,11 @@ void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickG
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
return;
}
const QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
if (!data) {
return;
}
switch (buttonType) {
case QuickGlobal::SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(static_cast<void>(0));
case QuickGlobal::SystemButtonType::WindowIcon:
data->windowIconButton = item;
break;
@ -296,6 +285,8 @@ void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickG
case QuickGlobal::SystemButtonType::Close:
data->closeButton = item;
break;
case QuickGlobal::SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(static_cast<void>(0));
}
}
@ -305,7 +296,6 @@ void FramelessQuickHelperPrivate::setHitTestVisible(QQuickItem *item, const bool
if (!item) {
return;
}
const QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
if (!data) {
return;
@ -325,7 +315,6 @@ void FramelessQuickHelperPrivate::setHitTestVisible(const QRect &rect, const boo
if (!rect.isValid()) {
return;
}
const QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
if (!data) {
return;
@ -685,6 +674,38 @@ void FramelessQuickHelperPrivate::waitForReady()
#endif
}
void FramelessQuickHelperPrivate::repaintAllChildren(const int delay) const
{
Q_Q(const FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
const auto update = [window]() -> void {
window->requestUpdate();
#ifdef Q_OS_WINDOWS
// Sync the internal window frame margins with the latest DPI, otherwise
// we will get wrong window sizes after the DPI change.
Utils::updateInternalWindowFrameMargins(window, true);
#endif // Q_OS_WINDOWS
const QList<QQuickItem *> items = window->findChildren<QQuickItem *>();
if (items.isEmpty()) {
return;
}
for (auto &&item : std::as_const(items)) {
// Only items with the "QQuickItem::ItemHasContents" flag enabled are allowed to call "update()".
if (item->flags() & QQuickItem::ItemHasContents) {
item->update();
}
}
};
if (delay > 0) {
QTimer::singleShot(delay, this, update);
} else {
update();
}
}
QRect FramelessQuickHelperPrivate::mapItemGeometryToScene(const QQuickItem * const item) const
{
Q_ASSERT(item);
@ -827,8 +848,6 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System
const QuickHelperData data = getWindowData();
QQuickAbstractButton *quickButton = nullptr;
switch (button) {
case QuickGlobal::SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0));
case QuickGlobal::SystemButtonType::WindowIcon:
if (data.windowIconButton) {
if (const auto btn = qobject_cast<QQuickAbstractButton *>(data.windowIconButton)) {
@ -865,15 +884,19 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System
}
}
break;
case QuickGlobal::SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0));
}
if (!quickButton) {
return;
}
if (quickButton) {
const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void {
Q_ASSERT(btn);
if (!btn) {
return;
}
switch (state) {
case QuickGlobal::ButtonState::Unspecified: {
case QuickGlobal::ButtonState::Normal: {
btn->setPressed(false);
btn->setHovered(false);
} break;
@ -885,7 +908,7 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System
btn->setHovered(true);
btn->setPressed(true);
} break;
case QuickGlobal::ButtonState::Clicked: {
case QuickGlobal::ButtonState::Released: {
// Clicked: pressed --> released, so behave like hovered.
btn->setPressed(false);
btn->setHovered(true);
@ -894,7 +917,6 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System
}
};
updateButtonState(quickButton);
}
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
}
@ -907,7 +929,6 @@ QuickHelperData FramelessQuickHelperPrivate::getWindowData() const
return {};
}
const WId windowId = window->winId();
const QMutexLocker locker(&g_quickHelper()->mutex);
if (!g_quickHelper()->data.contains(windowId)) {
g_quickHelper()->data.insert(windowId, {});
}

View File

@ -91,7 +91,13 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
return new FramelessQuickUtils;
});
qmlRegisterAnonymousType<QuickChromePalette>(QUICK_URI_SHORT);
#if (QT_VERSION <= QT_VERSION_CHECK(5, 16, 0))
qRegisterMetaType<QuickGlobal::SystemTheme>("QuickGlobal::SystemTheme");
qRegisterMetaType<QuickGlobal::SystemButtonType>("QuickGlobal::SystemButtonType");
qRegisterMetaType<QuickGlobal::ButtonState>("QuickGlobal::ButtonState");
qRegisterMetaType<QuickGlobal::BlurMode>("QuickGlobal::BlurMode");
qRegisterMetaType<QuickGlobal::WindowEdge>("QuickGlobal::WindowEdge");
#endif
qmlRegisterType<FramelessQuickHelper>(QUICK_URI_EXPAND("FramelessHelper"));
qmlRegisterType<QuickMicaMaterial>(QUICK_URI_EXPAND("MicaMaterial"));
qmlRegisterType<QuickImageItem>(QUICK_URI_EXPAND("ImageItem"));

View File

@ -84,20 +84,17 @@ qreal FramelessQuickUtils::frameBorderThickness() const
QuickGlobal::SystemTheme FramelessQuickUtils::systemTheme() const
{
return FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemTheme, Utils::getSystemTheme());
return FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemTheme, FramelessManager::instance()->systemTheme());
}
void FramelessQuickUtils::setOverrideTheme(const QuickGlobal::SystemTheme theme)
{
FramelessManager::instance()->setOverrideTheme(FRAMELESSHELPER_ENUM_QUICK_TO_CORE(SystemTheme, theme));
}
QColor FramelessQuickUtils::systemAccentColor() const
{
#ifdef Q_OS_WINDOWS
return Utils::getDwmAccentColor();
#elif defined(Q_OS_LINUX)
return Utils::getWmThemeColor();
#elif defined(Q_OS_MACOS)
return Utils::getControlsAccentColor();
#else
return {};
#endif
return Utils::getAccentColor();
}
bool FramelessQuickUtils::titleBarColorized() const

View File

@ -25,7 +25,8 @@
#include "quickmicamaterial.h"
#include "quickmicamaterial_p.h"
#include <FramelessHelper/Core/micamaterial.h>
#include <QtCore/qmutex.h>
#include <FramelessHelper/Core/framelessmanager.h>
#include <FramelessHelper/Core/private/micamaterial_p.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/qscreen.h>
#include <QtGui/qpainter.h>
@ -34,6 +35,8 @@
#include <QtQuick/qsgsimpletexturenode.h>
#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE
# include <QtQuick/private/qquickitem_p.h>
# include <QtQuick/private/qquickrectangle_p.h>
# include <QtQuick/private/qquickanchors_p.h>
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -54,13 +57,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
struct QuickMicaData
{
QMutex mutex;
};
Q_GLOBAL_STATIC(QuickMicaData, g_data)
class WallpaperImageNode : public QObject, public QSGTransformNode
{
Q_OBJECT
@ -82,7 +78,6 @@ private:
QPointer<QuickMicaMaterial> m_item = nullptr;
QSGSimpleTextureNode *m_node = nullptr;
QPixmap m_pixmapCache = {};
MicaMaterial *m_micaMaterial = nullptr;
};
WallpaperImageNode::WallpaperImageNode(QuickMicaMaterial *item)
@ -95,28 +90,29 @@ WallpaperImageNode::WallpaperImageNode(QuickMicaMaterial *item)
initialize();
}
WallpaperImageNode::~WallpaperImageNode() = default;
WallpaperImageNode::~WallpaperImageNode(){
if (m_texture) {
delete m_texture;
m_texture = nullptr;
}
if (m_node) {
delete m_node;
m_node = nullptr;
}
};
void WallpaperImageNode::initialize()
{
g_data()->mutex.lock();
QQuickWindow * const window = m_item->window();
m_micaMaterial = new MicaMaterial(this);
m_node = new QSGSimpleTextureNode;
m_node->setFiltering(QSGTexture::Linear);
g_data()->mutex.unlock();
maybeGenerateWallpaperImageCache();
maybeUpdateWallpaperImageClipRect();
appendChildNode(m_node);
connect(m_micaMaterial, &MicaMaterial::shouldRedraw, this, [this](){
maybeGenerateWallpaperImageCache(true);
});
connect(window, &QQuickWindow::beforeRendering, this,
&WallpaperImageNode::maybeUpdateWallpaperImageClipRect, Qt::DirectConnection);
@ -125,7 +121,6 @@ void WallpaperImageNode::initialize()
void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force)
{
const QMutexLocker locker(&g_data()->mutex);
if (!m_pixmapCache.isNull() && !force) {
return;
}
@ -134,7 +129,10 @@ void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force)
m_pixmapCache = QPixmap(desktopSize);
m_pixmapCache.fill(kDefaultTransparentColor);
QPainter painter(&m_pixmapCache);
m_micaMaterial->paint(&painter, desktopSize, originPoint);
MicaMaterial * const mica = QuickMicaMaterialPrivate::get(m_item)->m_micaMaterial;
Q_ASSERT(mica);
// We need the real wallpaper image here, so always use "active" state.
mica->paint(&painter, desktopSize, originPoint, true);
if (m_texture) {
delete m_texture;
m_texture = nullptr;
@ -145,7 +143,6 @@ void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force)
void WallpaperImageNode::maybeUpdateWallpaperImageClipRect()
{
const QMutexLocker locker(&g_data()->mutex);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
const QSizeF itemSize = m_item->size();
#else
@ -188,10 +185,30 @@ const QuickMicaMaterialPrivate *QuickMicaMaterialPrivate::get(const QuickMicaMat
void QuickMicaMaterialPrivate::initialize()
{
Q_Q(QuickMicaMaterial);
q->setFlag(QuickMicaMaterial::ItemHasContents);
q->setSmooth(true);
q->setAntialiasing(true);
q->setClip(true);
m_micaMaterial = new MicaMaterial(this);
connect(m_micaMaterial, &MicaMaterial::tintColorChanged, q, &QuickMicaMaterial::tintColorChanged);
connect(m_micaMaterial, &MicaMaterial::tintOpacityChanged, q, &QuickMicaMaterial::tintOpacityChanged);
connect(m_micaMaterial, &MicaMaterial::fallbackColorChanged, q, &QuickMicaMaterial::fallbackColorChanged);
connect(m_micaMaterial, &MicaMaterial::noiseOpacityChanged, q, &QuickMicaMaterial::noiseOpacityChanged);
connect(m_micaMaterial, &MicaMaterial::fallbackEnabledChanged, q, &QuickMicaMaterial::fallbackEnabledChanged);
connect(m_micaMaterial, &MicaMaterial::shouldRedraw, this, &QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache);
#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE
m_fallbackColorItem = new QQuickRectangle(q);
QQuickItemPrivate::get(m_fallbackColorItem)->anchors()->setFill(q);
QQuickPen * const border = m_fallbackColorItem->border();
border->setColor(kDefaultTransparentColor);
border->setWidth(0);
updateFallbackColor();
m_fallbackColorItem->setVisible(false);
connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &QuickMicaMaterialPrivate::updateFallbackColor);
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
}
void QuickMicaMaterialPrivate::rebindWindow()
@ -218,6 +235,19 @@ void QuickMicaMaterialPrivate::rebindWindow()
}
m_rootWindowXChangedConnection = connect(window, &QQuickWindow::xChanged, q, [q](){ q->update(); });
m_rootWindowYChangedConnection = connect(window, &QQuickWindow::yChanged, q, [q](){ q->update(); });
#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE
if (m_rootWindowActiveChangedConnection) {
disconnect(m_rootWindowActiveChangedConnection);
m_rootWindowActiveChangedConnection = {};
}
m_rootWindowActiveChangedConnection = connect(window, &QQuickWindow::activeChanged, q, [this, window](){
if (m_micaMaterial->isFallbackEnabled()) {
m_fallbackColorItem->setVisible(!window->isActive());
} else {
m_fallbackColorItem->setVisible(false);
}
});
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
}
void QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache()
@ -244,6 +274,21 @@ void QuickMicaMaterialPrivate::appendNode(WallpaperImageNode *node)
m_nodes.append(node);
}
void QuickMicaMaterialPrivate::updateFallbackColor()
{
#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE
if (!m_fallbackColorItem || !m_micaMaterial) {
return;
}
const QColor color = m_micaMaterial->fallbackColor();
if (color.isValid()) {
m_fallbackColorItem->setColor(color);
return;
}
m_fallbackColorItem->setColor(MicaMaterialPrivate::systemFallbackColor());
#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE
}
QuickMicaMaterial::QuickMicaMaterial(QQuickItem *parent)
: QQuickItem(parent), d_ptr(new QuickMicaMaterialPrivate(this))
{
@ -251,6 +296,66 @@ QuickMicaMaterial::QuickMicaMaterial(QQuickItem *parent)
QuickMicaMaterial::~QuickMicaMaterial() = default;
QColor QuickMicaMaterial::tintColor() const
{
Q_D(const QuickMicaMaterial);
return d->m_micaMaterial->tintColor();
}
void QuickMicaMaterial::setTintColor(const QColor &value)
{
Q_D(QuickMicaMaterial);
d->m_micaMaterial->setTintColor(value);
}
qreal QuickMicaMaterial::tintOpacity() const
{
Q_D(const QuickMicaMaterial);
return d->m_micaMaterial->tintOpacity();
}
void QuickMicaMaterial::setTintOpacity(const qreal value)
{
Q_D(QuickMicaMaterial);
d->m_micaMaterial->setTintOpacity(value);
}
QColor QuickMicaMaterial::fallbackColor() const
{
Q_D(const QuickMicaMaterial);
return d->m_micaMaterial->fallbackColor();
}
void QuickMicaMaterial::setFallbackColor(const QColor &value)
{
Q_D(QuickMicaMaterial);
d->m_micaMaterial->setFallbackColor(value);
}
qreal QuickMicaMaterial::noiseOpacity() const
{
Q_D(const QuickMicaMaterial);
return d->m_micaMaterial->noiseOpacity();
}
void QuickMicaMaterial::setNoiseOpacity(const qreal value)
{
Q_D(QuickMicaMaterial);
d->m_micaMaterial->setNoiseOpacity(value);
}
bool QuickMicaMaterial::isFallbackEnabled() const
{
Q_D(const QuickMicaMaterial);
return d->m_micaMaterial->isFallbackEnabled();
}
void QuickMicaMaterial::setFallbackEnabled(const bool value)
{
Q_D(QuickMicaMaterial);
d->m_micaMaterial->setFallbackEnabled(value);
}
void QuickMicaMaterial::itemChange(const ItemChange change, const ItemChangeData &value)
{
QQuickItem::itemChange(change, value);

View File

@ -24,11 +24,15 @@
include(GNUInstallDirs)
set(SUB_MOD_NAME Widgets)
set(SUB_PROJ_NAME ${PROJECT_NAME}${SUB_MOD_NAME})
set(SUB_PROJ_PATH ${PROJECT_NAME}/${SUB_MOD_NAME})
if(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
set(INCLUDE_PREFIX ../../include/${SUB_PROJ_PATH})
set(SUB_MODULE Widgets)
set(SUB_MODULE_FULL_NAME ${PROJECT_NAME}${SUB_MODULE})
set(SUB_MODULE_PATH ${PROJECT_NAME}/${SUB_MODULE})
set(INCLUDE_PREFIX ../../include/${SUB_MODULE_PATH})
set(PUBLIC_HEADERS
${INCLUDE_PREFIX}/framelesshelperwidgets_global.h
@ -72,18 +76,20 @@ set(SOURCES
)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
set(__rc_path "${CMAKE_CURRENT_BINARY_DIR}/${SUB_PROJ_NAME}.rc")
set(__rc_path "${CMAKE_CURRENT_BINARY_DIR}/${SUB_MODULE_FULL_NAME}.rc")
if(NOT EXISTS "${__rc_path}")
generate_win32_rc_file(
PATH "${__rc_path}"
VERSION "${PROJECT_VERSION}"
COMPANY "wangwenx190"
DESCRIPTION "${PROJECT_NAME} ${SUB_MOD_NAME} Module"
DESCRIPTION "${PROJECT_NAME} ${SUB_MODULE} Module"
COPYRIGHT "MIT License"
ORIGINAL_FILENAME "${PROJECT_NAME}${SUB_MOD_NAME}.dll"
ORIGINAL_FILENAME "${PROJECT_NAME}${SUB_MODULE}.dll"
PRODUCT "${PROJECT_NAME}"
COMMENTS "Built from commit ${PROJECT_VERSION_COMMIT} on ${PROJECT_COMPILE_DATETIME} (UTC)."
LIBRARY
)
endif()
list(APPEND SOURCES "${__rc_path}")
endif()
@ -94,110 +100,64 @@ if(FRAMELESSHELPER_BUILD_STATIC)
else()
set(SUB_MOD_LIB_TYPE "SHARED")
endif()
add_library(${SUB_PROJ_NAME} ${SUB_MOD_LIB_TYPE} ${ALL_SOURCES})
add_library(${PROJECT_NAME}::${SUB_PROJ_NAME} ALIAS ${SUB_PROJ_NAME})
add_library(${PROJECT_NAME}::${SUB_MOD_NAME} ALIAS ${SUB_PROJ_NAME})
add_library(${SUB_MODULE} ${SUB_MOD_LIB_TYPE} ${ALL_SOURCES})
add_library(${SUB_MODULE_FULL_NAME} ALIAS ${SUB_MODULE})
add_library(${PROJECT_NAME}::${SUB_MODULE} ALIAS ${SUB_MODULE})
add_library(${PROJECT_NAME}::${SUB_MODULE_FULL_NAME} ALIAS ${SUB_MODULE})
set_target_properties(${SUB_PROJ_NAME} PROPERTIES
set_target_properties(${SUB_MODULE} PROPERTIES
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION_MAJOR}"
OUTPUT_NAME "${SUB_MODULE_FULL_NAME}"
)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
set(SUB_MOD_LIB_DIR "${CMAKE_INSTALL_BINDIR}")
else()
set(SUB_MOD_LIB_DIR "${CMAKE_INSTALL_LIBDIR}")
endif()
set(__prefix "")
if(NOT WIN32)
set(__prefix "lib")
endif()
set(__suffix "")
if(FRAMELESSHELPER_BUILD_STATIC)
if(MSVC)
set(__suffix "lib")
else()
set(__suffix "a")
endif()
else()
if(WIN32)
set(__suffix "dll")
elseif(APPLE)
set(__suffix "dylib")
elseif(UNIX)
set(__suffix "so")
endif()
endif()
set(SUB_MOD_FILE_PREFIX "${__prefix}")
set(SUB_MOD_FILE_SUFFIX "${__suffix}")
set(SUB_MOD_FILE_BASENAME "${SUB_MOD_FILE_PREFIX}${SUB_PROJ_NAME}")
if("x${CMAKE_BUILD_TYPE}" STREQUAL "xDebug")
string(APPEND SUB_MOD_FILE_BASENAME "${CMAKE_DEBUG_POSTFIX}")
endif()
set(SUB_MOD_FILE_NAME "${SUB_MOD_FILE_BASENAME}.${SUB_MOD_FILE_SUFFIX}")
unset(__suffix)
unset(__prefix)
if(FRAMELESSHELPER_BUILD_STATIC)
set(__def FRAMELESSHELPER_WIDGETS_STATIC)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_WIDGETS_STATIC)
endif()
if(FRAMELESSHELPER_NO_DEBUG_OUTPUT)
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
FRAMELESSHELPER_WIDGETS_NO_DEBUG_OUTPUT
)
endif()
if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
set(__def FRAMELESSHELPER_WIDGETS_NO_BUNDLE_RESOURCE)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_WIDGETS_NO_BUNDLE_RESOURCE)
endif()
if(FRAMELESSHELPER_NO_PRIVATE)
set(__def FRAMELESSHELPER_WIDGETS_NO_PRIVATE)
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_WIDGETS_NO_PRIVATE)
endif()
if(DEFINED FRAMELESSHELPER_NAMESPACE)
if("x${FRAMELESSHELPER_NAMESPACE}" STREQUAL "x")
message(FATAL_ERROR "FRAMELESSHELPER_NAMESPACE can't be empty!")
endif()
set(__def FRAMELESSHELPER_NAMESPACE=${FRAMELESSHELPER_NAMESPACE})
target_compile_definitions(${SUB_PROJ_NAME} PUBLIC ${__def})
list(APPEND SUB_MOD_DEFS ${__def})
unset(__def)
target_compile_definitions(${SUB_MODULE} PUBLIC FRAMELESSHELPER_NAMESPACE=${FRAMELESSHELPER_NAMESPACE})
endif()
target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
target_compile_definitions(${SUB_MODULE} PRIVATE
FRAMELESSHELPER_WIDGETS_LIBRARY
)
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
target_link_libraries(${SUB_MODULE} PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
)
target_link_libraries(${SUB_PROJ_NAME} PUBLIC
target_link_libraries(${SUB_MODULE} PUBLIC
${PROJECT_NAME}::Core
)
target_include_directories(${SUB_PROJ_NAME} PUBLIC
target_include_directories(${SUB_MODULE} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}/../..>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${INCLUDE_PREFIX}/private>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_MODULE_PATH}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_MODULE_PATH}/private>"
)
setup_qt_stuff(TARGETS ${SUB_PROJ_NAME})
setup_qt_stuff(TARGETS ${SUB_MODULE})
set(__extra_flags)
if(NOT FRAMELESSHELPER_NO_PERMISSIVE_CHECKS)
list(APPEND __extra_flags PERMISSIVE)
@ -217,31 +177,22 @@ endif()
if(FRAMELESSHELPER_ENABLE_CFGUARD)
list(APPEND __extra_flags CFGUARD)
endif()
setup_compile_params(TARGETS ${SUB_PROJ_NAME} ${__extra_flags})
if(FRAMELESSHELPER_FORCE_LTO)
list(APPEND __extra_flags FORCE_LTO)
endif()
setup_compile_params(TARGETS ${SUB_MODULE} ${__extra_flags})
if(NOT FRAMELESSHELPER_NO_INSTALL)
set(__cmake_dir "${CMAKE_CURRENT_BINARY_DIR}/cmake")
set(__config_file "${__cmake_dir}/${SUB_PROJ_NAME}Config.cmake")
configure_file(../../FramelessHelperModuleConfig.cmake.in ${__config_file} @ONLY)
set(__targets_file "${__cmake_dir}/${SUB_PROJ_NAME}Targets.cmake")
configure_file(../../FramelessHelperModuleTargets.cmake.in ${__targets_file} @ONLY)
install(
FILES "${__config_file}" "${__targets_file}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${SUB_PROJ_NAME}"
)
set(__inc_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}")
install(
FILES ${PUBLIC_HEADERS} ${PUBLIC_HEADERS_ALIAS}
DESTINATION "${__inc_dir}"
)
install(
FILES ${PRIVATE_HEADERS}
DESTINATION "${__inc_dir}/private"
)
install(
TARGETS ${SUB_PROJ_NAME}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
INCLUDES DESTINATION "${__inc_dir}"
setup_package_export(
TARGETS ${SUB_MODULE}
NAMESPACE ${PROJECT_NAME}
PACKAGE_NAME ${PROJECT_NAME}
COMPONENT ${SUB_MODULE}
PUBLIC_HEADERS ${PUBLIC_HEADERS}
ALIAS_HEADERS ${PUBLIC_HEADERS_ALIAS}
PRIVATE_HEADERS ${PRIVATE_HEADERS}
)
endif()
if(NOT FRAMELESSHELPER_NO_SUMMARY)
dump_target_info(TARGETS ${SUB_MODULE})
endif()

View File

@ -35,7 +35,6 @@
#include <FramelessHelper/Core/utils.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <FramelessHelper/Core/private/framelesshelpercore_global_p.h>
#include <QtCore/qmutex.h>
#include <QtCore/qhash.h>
#include <QtCore/qtimer.h>
#include <QtCore/qeventloop.h>
@ -82,12 +81,81 @@ struct WidgetsHelperData
struct WidgetsHelper
{
QMutex mutex;
QHash<WId, WidgetsHelperData> data = {};
};
Q_GLOBAL_STATIC(WidgetsHelper, g_widgetsHelper)
[[nodiscard]] static inline bool isWidgetFixedSize(const QWidget * const widget)
{
Q_ASSERT(widget);
if (!widget) {
return false;
}
// "Qt::MSWindowsFixedSizeDialogHint" is used cross-platform actually.
if (widget->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) {
return true;
}
// Caused by setFixedWidth/Height/Size().
const QSize minSize = widget->minimumSize();
const QSize maxSize = widget->maximumSize();
if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) {
return true;
}
// Usually set by the user.
const QSizePolicy sizePolicy = widget->sizePolicy();
if ((sizePolicy.horizontalPolicy() == QSizePolicy::Fixed)
&& (sizePolicy.verticalPolicy() == QSizePolicy::Fixed)) {
return true;
}
return false;
}
static inline void forceWidgetRepaint(QWidget * const widget)
{
Q_ASSERT(widget);
if (!widget) {
return;
}
// Tell the widget to repaint itself, but it may not happen due to QWidget's
// internal painting optimizations.
widget->update();
// Try to force the widget to repaint itself, in case:
// (1) It's a child widget;
// (2) It's a top level window but not minimized/maximized/fullscreen.
if (!widget->isWindow() || !(widget->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen))) {
// A widget will most likely repaint itself if it's size is changed.
if (!isWidgetFixedSize(widget)) {
const QSize originalSize = widget->size();
static constexpr const auto margins = QMargins{10, 10, 10, 10};
widget->resize(originalSize.shrunkBy(margins));
widget->resize(originalSize.grownBy(margins));
widget->resize(originalSize);
}
// However, some widgets won't repaint themselves unless their position is changed.
const QPoint originalPosition = widget->pos();
static constexpr const auto offset = QPoint{10, 10};
widget->move(originalPosition - offset);
widget->move(originalPosition + offset);
widget->move(originalPosition);
}
#ifdef Q_OS_WINDOWS
// There's some additional things to do for top level windows on Windows.
if (widget->isWindow()) {
// Don't crash if the QWindow instance has not been created yet.
if (QWindow * const window = widget->windowHandle()) {
// Sync the internal window frame margins with the latest DPI, otherwise
// we will get wrong window sizes after the DPI change.
Utils::updateInternalWindowFrameMargins(window, true);
}
}
#endif // Q_OS_WINDOWS
// Let's try again with the ordinary way.
widget->update();
// ### TODO: I observed the font size is often wrong after DPI changes,
// do we need to refresh the font settings here as well?
}
FramelessWidgetsHelperPrivate::FramelessWidgetsHelperPrivate(FramelessWidgetsHelper *q) : QObject(q)
{
Q_ASSERT(q);
@ -126,18 +194,7 @@ bool FramelessWidgetsHelperPrivate::isWindowFixedSize() const
if (!m_window) {
return false;
}
if (m_window->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) {
return true;
}
const QSize minSize = m_window->minimumSize();
const QSize maxSize = m_window->maximumSize();
if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) {
return true;
}
if (m_window->sizePolicy() == QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)) {
return true;
}
return false;
return isWidgetFixedSize(m_window);
}
void FramelessWidgetsHelperPrivate::setWindowFixedSize(const bool value)
@ -149,8 +206,11 @@ void FramelessWidgetsHelperPrivate::setWindowFixedSize(const bool value)
return;
}
if (value) {
m_savedSizePolicy = m_window->sizePolicy();
m_window->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_window->setFixedSize(m_window->size());
} else {
m_window->setSizePolicy(m_savedSizePolicy);
m_window->setMinimumSize(kDefaultWindowSize);
m_window->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
}
@ -345,6 +405,28 @@ void FramelessWidgetsHelperPrivate::waitForReady()
#endif
}
void FramelessWidgetsHelperPrivate::repaintAllChildren(const int delay) const
{
if (!m_window) {
return;
}
const auto update = [this]() -> void {
forceWidgetRepaint(m_window);
const QList<QWidget *> widgets = m_window->findChildren<QWidget *>();
if (widgets.isEmpty()) {
return;
}
for (auto &&widget : std::as_const(widgets)) {
forceWidgetRepaint(widget);
}
};
if (delay > 0) {
QTimer::singleShot(delay, this, update);
} else {
update();
}
}
bool FramelessWidgetsHelperPrivate::isContentExtendedIntoTitleBar() const
{
return getWindowData().ready;
@ -356,7 +438,6 @@ void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget)
if (!widget) {
return;
}
const QMutexLocker locker(&g_widgetsHelper()->mutex);
WidgetsHelperData *data = getWindowDataMutable();
if (!data) {
return;
@ -379,7 +460,6 @@ void FramelessWidgetsHelperPrivate::setHitTestVisible(QWidget *widget, const boo
if (!widget) {
return;
}
const QMutexLocker locker(&g_widgetsHelper()->mutex);
WidgetsHelperData *data = getWindowDataMutable();
if (!data) {
return;
@ -399,7 +479,6 @@ void FramelessWidgetsHelperPrivate::setHitTestVisible(const QRect &rect, const b
if (!rect.isValid()) {
return;
}
const QMutexLocker locker(&g_widgetsHelper()->mutex);
WidgetsHelperData *data = getWindowDataMutable();
if (!data) {
return;
@ -446,13 +525,10 @@ void FramelessWidgetsHelperPrivate::attach()
window->setAttribute(Qt::WA_NativeWindow);
}
g_widgetsHelper()->mutex.lock();
WidgetsHelperData * const data = getWindowDataMutable();
if (!data || data->ready) {
g_widgetsHelper()->mutex.unlock();
return;
}
g_widgetsHelper()->mutex.unlock();
SystemParameters params = {};
params.getWindowId = [window]() -> WId { return window->winId(); };
@ -487,13 +563,12 @@ void FramelessWidgetsHelperPrivate::attach()
params.setCursor = [window](const QCursor &cursor) -> void { window->setCursor(cursor); };
params.unsetCursor = [window]() -> void { window->unsetCursor(); };
params.getWidgetHandle = [window]() -> QObject * { return window; };
params.forceChildrenRepaint = [this](const int delay) -> void { repaintAllChildren(delay); };
FramelessManager::instance()->addWindow(&params);
g_widgetsHelper()->mutex.lock();
data->params = params;
data->ready = true;
g_widgetsHelper()->mutex.unlock();
// We have to wait for a little time before moving the top level window
// , because the platform window may not finish initializing by the time
@ -519,7 +594,6 @@ void FramelessWidgetsHelperPrivate::detach()
return;
}
const WId windowId = m_window->winId();
const QMutexLocker locker(&g_widgetsHelper()->mutex);
if (!g_widgetsHelper()->data.contains(windowId)) {
return;
}
@ -564,7 +638,6 @@ WidgetsHelperData FramelessWidgetsHelperPrivate::getWindowData() const
return {};
}
const WId windowId = m_window->winId();
const QMutexLocker locker(&g_widgetsHelper()->mutex);
if (!g_widgetsHelper()->data.contains(windowId)) {
g_widgetsHelper()->data.insert(windowId, {});
}
@ -716,8 +789,6 @@ void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType
const WidgetsHelperData data = getWindowData();
QWidget *widgetButton = nullptr;
switch (button) {
case SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0));
case SystemButtonType::WindowIcon:
if (data.windowIconButton) {
widgetButton = data.windowIconButton;
@ -744,15 +815,19 @@ void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType
widgetButton = data.closeButton;
}
break;
case SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0));
}
if (!widgetButton) {
return;
}
if (widgetButton) {
const auto updateButtonState = [state](QWidget *btn) -> void {
Q_ASSERT(btn);
if (!btn) {
return;
}
switch (state) {
case ButtonState::Unspecified: {
case ButtonState::Normal: {
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false));
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, false));
} break;
@ -764,7 +839,7 @@ void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true));
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, true));
} break;
case ButtonState::Clicked: {
case ButtonState::Released: {
// Clicked: pressed --> released, so behave like hovered.
QMetaObject::invokeMethod(btn, "setPressed", Q_ARG(bool, false));
QMetaObject::invokeMethod(btn, "setHovered", Q_ARG(bool, true));
@ -781,7 +856,6 @@ void FramelessWidgetsHelperPrivate::setSystemButtonState(const SystemButtonType
updateButtonState(widgetButton);
}
}
}
}
void FramelessWidgetsHelperPrivate::moveWindowToDesktopCenter()
@ -856,14 +930,11 @@ void FramelessWidgetsHelperPrivate::setSystemButton(QWidget *widget, const Syste
if (!widget || (buttonType == SystemButtonType::Unknown)) {
return;
}
const QMutexLocker locker(&g_widgetsHelper()->mutex);
WidgetsHelperData *data = getWindowDataMutable();
if (!data) {
return;
}
switch (buttonType) {
case SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0));
case SystemButtonType::WindowIcon:
data->windowIconButton = widget;
break;
@ -880,6 +951,8 @@ void FramelessWidgetsHelperPrivate::setSystemButton(QWidget *widget, const Syste
case SystemButtonType::Close:
data->closeButton = widget;
break;
case SystemButtonType::Unknown:
Q_UNREACHABLE_RETURN(void(0));
}
}

View File

@ -173,9 +173,7 @@ bool WidgetsSharedHelper::eventFilter(QObject *object, QEvent *event)
break;
case QEvent::Move:
case QEvent::Resize:
if (m_micaEnabled) {
m_targetWidget->update();
}
break;
default:
break;
@ -189,7 +187,8 @@ void WidgetsSharedHelper::repaintMica()
return;
}
QPainter painter(m_targetWidget);
m_micaMaterial->paint(&painter, m_targetWidget->size(), m_targetWidget->mapToGlobal(QPoint(0, 0)));
m_micaMaterial->paint(&painter, m_targetWidget->size(),
m_targetWidget->mapToGlobal(QPoint(0, 0)), m_targetWidget->isActiveWindow());
}
void WidgetsSharedHelper::repaintBorder()