├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs ├── framelesshelper-related.md ├── images │ ├── linux.png │ ├── mac.png │ ├── vs-guide-1.png │ ├── vs-guide-2.png │ ├── vs-guide-3.png │ ├── win10.png │ └── win11.png └── visual-studio-guide.md ├── examples ├── CMakeLists.txt ├── mainwindow │ ├── CMakeLists.txt │ ├── dark-style.qss │ ├── light-style.qss │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── mainwindow.qrc ├── qml │ ├── CMakeLists.txt │ ├── FramelessWindow.qml │ ├── QWKButton.qml │ ├── main.cpp │ ├── main.qml │ └── qml.qrc └── shared │ ├── CMakeLists.txt │ ├── resources │ ├── app │ │ ├── example.icns │ │ ├── example.ico │ │ └── example.png │ ├── shared.qrc │ └── window-bar │ │ ├── close.svg │ │ ├── fullscreen.svg │ │ ├── maximize.svg │ │ ├── minimize.svg │ │ ├── more-line.svg │ │ ├── pin-fill.svg │ │ ├── pin.svg │ │ └── restore.svg │ └── widgetframe │ ├── CMakeLists.txt │ ├── windowbar.cpp │ ├── windowbar.h │ ├── windowbar_p.h │ ├── windowbutton.cpp │ ├── windowbutton.h │ └── windowbutton_p.h ├── share ├── install.cmake ├── msbuild │ └── QWindowKit.props.in └── qmake │ ├── QWKCore.pri.in │ ├── QWKQuick.pri.in │ └── QWKWidgets.pri.in └── src ├── CMakeLists.txt ├── QWindowKitConfig.cmake.in ├── core ├── CMakeLists.txt ├── contexts │ ├── abstractwindowcontext.cpp │ ├── abstractwindowcontext_p.h │ ├── cocoawindowcontext.mm │ ├── cocoawindowcontext_p.h │ ├── qtwindowcontext.cpp │ ├── qtwindowcontext_p.h │ ├── win32windowcontext.cpp │ └── win32windowcontext_p.h ├── kernel │ ├── nativeeventfilter.cpp │ ├── nativeeventfilter_p.h │ ├── sharedeventfilter.cpp │ ├── sharedeventfilter_p.h │ ├── winidchangeeventfilter.cpp │ └── winidchangeeventfilter_p.h ├── qwindowkit_linux.h ├── qwindowkit_windows.cpp ├── qwindowkit_windows.h ├── qwkglobal.cpp ├── qwkglobal.h ├── qwkglobal_p.h ├── shared │ ├── qwkwindowsextra_p.h │ ├── systemwindow_p.h │ └── windows10borderhandler_p.h ├── style │ ├── styleagent.cpp │ ├── styleagent.h │ ├── styleagent_linux.cpp │ ├── styleagent_mac.mm │ ├── styleagent_p.h │ └── styleagent_win.cpp ├── windowagentbase.cpp ├── windowagentbase.h ├── windowagentbase_p.h ├── windowitemdelegate.cpp └── windowitemdelegate_p.h ├── quick ├── CMakeLists.txt ├── quickitemdelegate.cpp ├── quickitemdelegate_p.h ├── quickwindowagent.cpp ├── quickwindowagent.h ├── quickwindowagent_mac.cpp ├── quickwindowagent_p.h ├── quickwindowagent_win.cpp ├── qwkquickglobal.cpp └── qwkquickglobal.h └── widgets ├── CMakeLists.txt ├── qwkwidgetsglobal.h ├── widgetitemdelegate.cpp ├── widgetitemdelegate_p.h ├── widgetwindowagent.cpp ├── widgetwindowagent.h ├── widgetwindowagent_mac.cpp ├── widgetwindowagent_p.h └── widgetwindowagent_win.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | # References: 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | # https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format 4 | 5 | BasedOnStyle: LLVM 6 | 7 | Standard: c++17 8 | 9 | # 指针和引用的对齐方式。 10 | # 可能的值有: 11 | # PAS_Left (在配置中: Left) 指针左对齐。 12 | # PAS_Right (在配置中: Right) 指针右对齐。 13 | # PAS_Middle (在配置中: Middle) 指针中间对齐。 14 | PointerAlignment: Right 15 | 16 | # public/protected/private 等访问修饰符偏移量 17 | AccessModifierOffset: -4 18 | 19 | # 缩进长度 20 | IndentWidth: 4 21 | 22 | # 连续空行的最大数 23 | MaxEmptyLinesToKeep: 999 24 | 25 | # 在OC中的@property后面添加一个空格。例如:使用“@property (readonly)”而不是“@property(readonly)” 26 | ObjCSpaceAfterProperty: true 27 | 28 | # OC块中所拍的字符数 29 | ObjCBlockIndentWidth: 4 30 | 31 | # 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。 32 | # 可能的值有: 33 | # SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。 34 | # SFS_Empty (在配置中: Empty) 仅合并空的函数。 35 | # SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”. 36 | # SFS_All (在配置中: All) 合并所有的方法适应单行. 37 | AllowShortFunctionsOnASingleLine: None 38 | 39 | # 如果为真(true), 语句“if (a) return;” 能被放到单行。 40 | AllowShortIfStatementsOnASingleLine: false 41 | 42 | # 如果为真(true), 对齐注释。 43 | AlignTrailingComments: true 44 | 45 | # 如果为真,对齐连续的宏定义 46 | AlignConsecutiveMacros: true 47 | 48 | # 如果为真(true),将会在“[”之后和“]”之前插入空格。 49 | SpacesInSquareBrackets: false 50 | 51 | # 如果为真(true), 将会在“(”之后和“)”之前插入空格。 52 | SpacesInParentheses : false 53 | 54 | # 如果为真(true), 校准连续的声明。 55 | # 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式: 56 | # int aaaa = 12; 57 | # float b = 23; 58 | # std::string ccc = 23; 59 | AlignConsecutiveDeclarations: false 60 | 61 | # 如果为真(true),连续调整多行 62 | # 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式: 63 | # int aaaa = 12; 64 | # int b = 23; 65 | # int ccc = 23; 66 | AlignConsecutiveAssignments: false 67 | 68 | # 如果为假(false),移除分配操作符(=)前空格。 69 | SpaceBeforeAssignmentOperators: true 70 | 71 | # 如果为真(true), 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。 72 | SpacesInContainerLiterals: false 73 | 74 | # 缩进case标签 75 | IndentCaseLabels: true 76 | 77 | # 如果表达式中包含函数调用,并且函数调用因为表达式太长被放到了下一行,是否缩进 78 | IndentWrappedFunctionNames: true 79 | 80 | # 如果为真(true), 保持块的起始空行。 81 | # true: false: 82 | # if (foo) { vs. if (foo) { 83 | # bar(); 84 | # bar(); } 85 | # } 86 | KeepEmptyLinesAtTheStartOfBlocks: true 87 | 88 | # 允许所有参数都被放在下一行 89 | AllowAllParametersOfDeclarationOnNextLine: false 90 | 91 | # 使用C风格强制类型转换后,是否在中间添加一个空格 92 | SpaceAfterCStyleCast: true 93 | 94 | # 在模板定义后换行 95 | AlwaysBreakTemplateDeclarations: Yes 96 | 97 | # Tab长度 98 | TabWidth: 4 99 | 100 | # 是否使用Tab 101 | UseTab: Never 102 | 103 | # 在括号后对齐参数 104 | # someLongFunction(argument1, 105 | # argument2); 106 | AlignAfterOpenBracket: Align 107 | 108 | # 名字空间内部缩进 109 | NamespaceIndentation: All 110 | 111 | # 一行最长列数 112 | ColumnLimit: 100 113 | 114 | # 按层次缩进宏定义 115 | IndentPPDirectives: AfterHash 116 | 117 | # 预处理语句缩进为 2 118 | PPIndentWidth: 2 119 | 120 | # 数组元素对齐 121 | AlignArrayOfStructures: Left 122 | 123 | # 不对头文件排序 124 | SortIncludes: Never 125 | 126 | FixNamespaceComments: false 127 | 128 | StatementMacros: ['__qas_attr__', '__qas_exclude__', '__qas_include__'] 129 | 130 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.log 6 | *.autosave 7 | *.a 8 | *.core 9 | *.moc 10 | *.o 11 | *.obj 12 | *.orig 13 | *.rej 14 | *.so 15 | *.so.* 16 | *_pch.h.cpp 17 | *_resource.rc 18 | *.qm 19 | .#* 20 | *.*# 21 | core 22 | !core/ 23 | tags 24 | .DS_Store 25 | .directory 26 | *.debug 27 | Makefile* 28 | *.prl 29 | *.app 30 | moc_*.cpp 31 | ui_*.h 32 | qrc_*.cpp 33 | Thumbs.db 34 | # *.res 35 | # *.rc 36 | /.qmake.cache 37 | /.qmake.stash 38 | 39 | # qtcreator generated files 40 | *.pro.user* 41 | 42 | # xemacs temporary files 43 | *.flc 44 | 45 | # Vim temporary files 46 | .*.swp 47 | 48 | # Visual Studio generated files 49 | *.ib_pdb_index 50 | *.idb 51 | *.ilk 52 | *.pdb 53 | *.sln 54 | *.suo 55 | *.vcproj 56 | *vcproj.*.*.user 57 | *.ncb 58 | *.sdf 59 | *.opensdf 60 | *.vcxproj 61 | *vcxproj.* 62 | 63 | # MinGW generated files 64 | *.Debug 65 | *.Release 66 | 67 | # Python byte code 68 | __pycache__ 69 | *.pyc 70 | 71 | # Binaries 72 | # -------- 73 | *.dll 74 | *.exe 75 | 76 | .DS_Store 77 | */.DS_Store 78 | 79 | build-* 80 | build/ 81 | cmake-build* 82 | *.user 83 | *.lnk 84 | _workingDir* 85 | .vscode 86 | .idea 87 | .cache 88 | cache 89 | .vs 90 | out/ 91 | CMakeSettings.json 92 | # /vcpkg 93 | /data 94 | /*.natvis 95 | 96 | *.sublime-* 97 | setup-vcpkg.json 98 | setup-vcpkg-temp* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "qmsetup"] 2 | path = qmsetup 3 | url = ../../stdware/qmsetup.git 4 | branch = main -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | project(QWindowKit 4 | VERSION 1.4.1.0 5 | LANGUAGES CXX 6 | HOMEPAGE_URL "https://github.com/stdware/qwindowkit" 7 | DESCRIPTION "Cross-platform window customization framework" 8 | ) 9 | 10 | # ---------------------------------- 11 | # Build Options 12 | # ---------------------------------- 13 | option(QWINDOWKIT_BUILD_STATIC "Build static libraries" OFF) 14 | option(QWINDOWKIT_BUILD_WIDGETS "Build widgets module" ON) 15 | option(QWINDOWKIT_BUILD_QUICK "Build quick module" OFF) 16 | option(QWINDOWKIT_BUILD_EXAMPLES "Build examples" OFF) 17 | option(QWINDOWKIT_BUILD_DOCUMENTATIONS "Build documentations" OFF) 18 | option(QWINDOWKIT_INSTALL "Install library" ON) 19 | 20 | option(QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT "Enable Qt Window Context anyway" OFF) 21 | option(QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS "Enable system borders on Windows" ON) 22 | option(QWINDOWKIT_ENABLE_STYLE_AGENT "Enable building style agent" ON) 23 | 24 | #[[ 25 | 26 | Detailed Introcuction to Configure Options: 27 | 28 | `QWINDOWKIT_BUILD_DOCUMENTATIONS` 29 | - If you have installed `Doxygen`, you can ENABLE this option so that the documentations 30 | will also be built and installed. 31 | - If not, you can read the comments in `qdoc` style in `cpp` files to get detailed usages 32 | of the public APIs. 33 | 34 | `QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS` 35 | - If you don't want the system borders on Windows 10/11, you can DISABLE this option. 36 | - If so, the Windows 10 top border issue will disappear. However, part of the client edge 37 | area will be occupied as the resizing margins. 38 | 39 | `QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT` 40 | - If you want to use pure Qt emulated frameless implementation, you can ENABLE this option. 41 | - If so, all system native features will be lost. 42 | 43 | `QWINDOWKIT_ENABLE_STYLE_AGENT` 44 | - Select whether to exclude the style component by DISABLING this option according to your 45 | requirements and your Qt version. 46 | 47 | #]] 48 | 49 | # ---------------------------------- 50 | # CMake Settings 51 | # ---------------------------------- 52 | if(MSVC) 53 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no") 54 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /manifest:no") 55 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no") 56 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") 57 | 58 | if(NOT DEFINED CMAKE_DEBUG_POSTFIX) 59 | set(CMAKE_DEBUG_POSTFIX "d") 60 | endif() 61 | elseif(MINGW) 62 | set(CMAKE_STATIC_LIBRARY_PREFIX "") 63 | set(CMAKE_SHARED_LIBRARY_PREFIX "") 64 | endif() 65 | 66 | if(QWINDOWKIT_INSTALL) 67 | include(GNUInstallDirs) 68 | include(CMakePackageConfigHelpers) 69 | endif() 70 | 71 | # ---------------------------------- 72 | # Project Variables 73 | # ---------------------------------- 74 | set(QWINDOWKIT_VERSION ${PROJECT_VERSION}) 75 | set(QWINDOWKIT_INSTALL_NAME ${PROJECT_NAME}) 76 | 77 | string(TIMESTAMP _QACTIONKIT_CURRENT_YEAR "%Y") 78 | set(QACTIONKIT_COPYRIGHT "Copyright 2023-${_QACTIONKIT_CURRENT_YEAR} Stdware Collections") 79 | set(QACTIONKIT_DESCRIPTION ${PROJECT_DESCRIPTION}) 80 | 81 | # ---------------------------------- 82 | # Find basic dependencies 83 | # ---------------------------------- 84 | find_package(qmsetup QUIET) 85 | 86 | if(NOT TARGET qmsetup::library) 87 | # Modify this variable according to your project structure 88 | set(_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/qmsetup) 89 | 90 | # Import install function 91 | include("${_source_dir}/cmake/modules/private/InstallPackage.cmake") 92 | 93 | # Install package in place 94 | set(_package_path) 95 | qm_install_package(qmsetup 96 | SOURCE_DIR ${_source_dir} 97 | BUILD_TYPE Release 98 | RESULT_PATH _package_path 99 | ) 100 | 101 | # Find package again 102 | find_package(qmsetup REQUIRED PATHS ${_package_path}) 103 | 104 | # Update import path 105 | set(qmsetup_DIR ${_package_path} CACHE PATH "" FORCE) 106 | endif() 107 | 108 | qm_import(Filesystem) 109 | qm_init_directories() 110 | 111 | # ---------------------------------- 112 | # Add source modules 113 | # ---------------------------------- 114 | add_subdirectory(src) 115 | 116 | if(QWINDOWKIT_BUILD_EXAMPLES) 117 | add_subdirectory(examples) 118 | endif() -------------------------------------------------------------------------------- /docs/framelesshelper-related.md: -------------------------------------------------------------------------------- 1 | # FramelessHelper 2.x 2 | 3 | Cross-platform window customization framework for Qt Widgets and Qt Quick. Supports Windows, Linux and macOS. 4 | 5 | ## Join with Us :triangular_flag_on_post: 6 | 7 | You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate with us. You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps! 8 | 9 | ## More 10 | 11 | ### Title Bar Design Guidance 12 | 13 | - Microsoft: 14 | - KDE: 15 | - GNOME: 16 | - Apple: 17 | 18 | ## Platform Notes 19 | 20 | ### Windows 21 | 22 | - If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square if you re-enable DWM composition. 23 | - There's an OpenGL driver bug which will cause some frameless windows have a strange black bar right on top of your homemade title bar, and it also makes the controls in your windows shifted to the bottom-right corner for some pixels. It's a bug of your graphics card driver, specifically, your OpenGL driver, not FramelessHelper. There are some solutions provided by our users but some of them may not work in all conditions, you can pick one from them: 24 | 25 | Solution | Principle 26 | -------- | --------- 27 | Upgrade the graphics driver | Try to use a newer driver which may ship with the fix 28 | Change the system theme to "Basic" (in contrary to "Windows Aero") | Let Windows use pure software rendering 29 | If there are multiple graphics cards, use another one instead | Try to use a different driver which may don't have such bug at all 30 | Upgrade the system to at least Windows 11 | Windows 11 redesigned the windowing system so the bug can no longer be triggered 31 | Remove the `WS_THICKFRAME` and `WS_OVERLAPPED` styles from the window, and maybe also add the `WS_POPUP` style at the same time, and don't do anything inside the `WM_NCCALCSIZE` block (just return `false` directly or remove/comment out the whole block) | Try to mirror Qt's `FramelessWindowHint`'s behavior 32 | Use `Qt::FramelessWindowHint` instead of doing the `WM_NCCALCSIZE` trick | Qt's rendering code path is totally different between these two solutions 33 | Force Qt to use the ANGLE backend instead of the Desktop OpenGL | ANGLE will translate OpenGL directives into D3D ones 34 | Force Qt to use pure software rendering instead of rendering through OpenGL | Qt is not using OpenGL at all 35 | Force Qt to use the Mesa 3D libraries instead of normal OpenGL | Try to use a different OpenGL implementation 36 | Use Direct3D/Vulkan/Metal instead of OpenGL | Just don't use the buggy OpenGL 37 | 38 | If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. **But I can't guarantee the issue can 100% be fixed.** 39 | - Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least **no older than Windows 10 1809**. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11. 40 | - To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow: 41 | - **Add a manifest file to your application. In the manifest file, you need to claim your application supports Windows 11 explicitly. This step is VERY VERY IMPORTANT. Without this step, the snap layout feature can't be enabled.** 42 | - Call `setSystemButton()` for each button (it can be any *QWidget* or *QQuickItem*) to let FramelessHelper know which is the minimize/maximize/close button. 43 | 44 | ### Linux 45 | 46 | - FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland. 47 | - The resize area is inside of the window. 48 | 49 | ### macOS 50 | 51 | - Some users reported that the window is not resizable on some old macOS versions. 52 | 53 | ## Special Thanks 54 | 55 | *Ordered by first contribution time (it may not be very accurate, sorry)* 56 | 57 | - [Yuhang Zhao](https://github.com/wangwenx190): Help me create this project. This project is mainly based on his code. 58 | - [Julien](https://github.com/JulienMaille): Help me test this library on many various environments and help me fix the bugs we found. Contributed many code to improve this library. The MainWindow example is mostly based on his code. 59 | - [Altair Wei](https://github.com/altairwei): Help me fix quite some small bugs and give me many important suggestions, the 2.x version is also inspired by his idea during our discussions. 60 | - [Kenji Mouri](https://github.com/MouriNaruto): Give me a lot of help on Win32 native developing. 61 | - [Dylan Liu](https://github.com/mentalfl0w): Help me improve the build process on macOS. 62 | - [SineStriker](https://github.com/SineStriker): Spent over a whole week helping me improve the Snap Layout implementation, fixing potential bugs and also give me a lot of professional and useful suggestions. Without his great effort, the new implementation may never come. 63 | - And also thanks to other contributors not listed here! Without their valuable help, this library wouldn't have such good quality and user experience! 64 | 65 | ## License 66 | 67 | ```text 68 | MIT License 69 | 70 | Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao) 71 | 72 | Permission is hereby granted, free of charge, to any person obtaining a copy 73 | of this software and associated documentation files (the "Software"), to deal 74 | in the Software without restriction, including without limitation the rights 75 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 76 | copies of the Software, and to permit persons to whom the Software is 77 | furnished to do so, subject to the following conditions: 78 | 79 | The above copyright notice and this permission notice shall be included in all 80 | copies or substantial portions of the Software. 81 | 82 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 83 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 84 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 85 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 86 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 87 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 88 | SOFTWARE. 89 | ``` -------------------------------------------------------------------------------- /docs/images/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/linux.png -------------------------------------------------------------------------------- /docs/images/mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/mac.png -------------------------------------------------------------------------------- /docs/images/vs-guide-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/vs-guide-1.png -------------------------------------------------------------------------------- /docs/images/vs-guide-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/vs-guide-2.png -------------------------------------------------------------------------------- /docs/images/vs-guide-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/vs-guide-3.png -------------------------------------------------------------------------------- /docs/images/win10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/win10.png -------------------------------------------------------------------------------- /docs/images/win11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/docs/images/win11.png -------------------------------------------------------------------------------- /docs/visual-studio-guide.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Guide 2 | 3 | ![Visual Studio Guide - Step 1](./images/vs-guide-1.png) 4 | 5 | First, click the "View" menu, find "Other Windows", click "Property Manager". 6 | 7 | ![Visual Studio Guide - Step 2](./images/vs-guide-2.png) 8 | 9 | Then, right click the project name item and pop up a context menu, click "Add 10 | Existing Property Sheet...". 11 | 12 | ![Visual Studio Guide - Step 3](./images/vs-guide-3.png) 13 | 14 | Finally, find the "QWindowKit.props" in the "[Build output folder of 15 | QWindowKit]\share\QWindowKit" folder, click "Open" button. -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(QWK_EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 2 | 3 | macro(qwk_add_example _target) 4 | set(CMAKE_AUTOMOC ON) 5 | set(CMAKE_AUTOUIC ON) 6 | set(CMAKE_AUTORCC ON) 7 | 8 | add_executable(${_target}) 9 | qm_configure_target(${_target} ${ARGN}) 10 | qm_add_win_rc(${_target} ICON ${QWK_EXAMPLES_DIR}/shared/resources/app/example.ico) 11 | qm_add_win_manifest(${_target}) 12 | qm_add_mac_bundle(${_target} ICON ${QWK_EXAMPLES_DIR}/shared/resources/app/example.icns) 13 | endmacro() 14 | 15 | add_subdirectory(shared) 16 | 17 | if(QWINDOWKIT_BUILD_WIDGETS) 18 | add_subdirectory(mainwindow) 19 | endif() 20 | 21 | if(QWINDOWKIT_BUILD_QUICK) 22 | add_subdirectory(qml) 23 | endif() -------------------------------------------------------------------------------- /examples/mainwindow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(QWKExample_MainWindow) 2 | 3 | file(GLOB _src *.h *.cpp) 4 | 5 | qwk_add_example(${PROJECT_NAME} 6 | FEATURES cxx_std_17 7 | SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc 8 | QT_LINKS Core Gui Widgets # MultimediaWidgets 9 | LINKS QWKWidgets WidgetFrame 10 | ) -------------------------------------------------------------------------------- /examples/mainwindow/dark-style.qss: -------------------------------------------------------------------------------- 1 | /* Window bar */ 2 | 3 | QWK--WindowBar[bar-active=true] { 4 | /*background-color: #3C3C3C;*/ 5 | background-color: transparent; 6 | } 7 | 8 | QWK--WindowBar[bar-active=false] { 9 | /*background-color: #505050;*/ 10 | background-color: transparent; 11 | } 12 | 13 | 14 | /* Title label */ 15 | 16 | QWK--WindowBar>QLabel#win-title-label { 17 | padding: 0; 18 | border: none; 19 | color: #ECECEC; 20 | background-color: transparent; 21 | min-height: 28px; 22 | } 23 | 24 | 25 | /* System buttons */ 26 | 27 | QWK--WindowBar>QAbstractButton[system-button=true] { 28 | qproperty-iconSize: 12px 12px; 29 | min-width: 50px; 30 | border: none; 31 | padding: 0; 32 | background-color: transparent; 33 | } 34 | 35 | QWK--WindowBar>QAbstractButton#pin-button { 36 | qproperty-iconNormal: url(":/window-bar/pin.svg"); 37 | qproperty-iconChecked: url(":/window-bar/pin-fill.svg"); 38 | qproperty-iconSize: 15px 15px; 39 | } 40 | 41 | QWK--WindowBar>QAbstractButton#pin-button:hover, 42 | QWK--WindowBar>QAbstractButton#pin-button:pressed { 43 | background-color: rgba(255, 255, 255, 15%); 44 | } 45 | 46 | QWK--WindowBar>QAbstractButton#min-button { 47 | qproperty-iconNormal: url(":/window-bar/minimize.svg"); 48 | } 49 | 50 | QWK--WindowBar>QAbstractButton#min-button:hover, 51 | QWK--WindowBar>QAbstractButton#min-button:pressed { 52 | background-color: rgba(255, 255, 255, 15%); 53 | } 54 | 55 | QWK--WindowBar>QAbstractButton#max-button { 56 | qproperty-iconNormal: url(":/window-bar/maximize.svg"); 57 | qproperty-iconChecked: url(":/window-bar/restore.svg"); 58 | } 59 | 60 | QWK--WindowBar>QAbstractButton#max-button:hover, 61 | QWK--WindowBar>QAbstractButton#max-button:pressed { 62 | background-color: rgba(255, 255, 255, 15%); 63 | } 64 | 65 | QWK--WindowBar>QAbstractButton#close-button { 66 | qproperty-iconNormal: url(":/window-bar/close.svg"); 67 | } 68 | 69 | QWK--WindowBar>QAbstractButton#close-button:hover, 70 | QWK--WindowBar>QAbstractButton#close-button:pressed { 71 | background-color: #e81123; 72 | } 73 | 74 | 75 | /* Icon button */ 76 | 77 | QWK--WindowBar>QAbstractButton#icon-button { 78 | qproperty-iconNormal: url(":/app/example.png"); 79 | qproperty-iconSize: 18px 18px; 80 | min-width: 40px; 81 | border: none; 82 | padding: 0; 83 | background-color: transparent; 84 | } 85 | 86 | 87 | /* Menu Bar */ 88 | 89 | QMenuBar { 90 | background-color: transparent; 91 | border: none; 92 | } 93 | 94 | QMenuBar>QToolButton#qt_menubar_ext_button { 95 | qproperty-icon: url(":/window-bar/more-line.svg"); 96 | } 97 | 98 | QMenuBar>QToolButton#qt_menubar_ext_button:hover, 99 | QMenuBar>QToolButton#qt_menubar_ext_button:pressed { 100 | background-color: rgba(255, 255, 255, 10%); 101 | } 102 | 103 | QMenuBar::item { 104 | color: #CCCCCC; 105 | border: none; 106 | padding: 8px 12px; 107 | } 108 | 109 | QMenuBar::item:selected { 110 | background-color: rgba(255, 255, 255, 10%); 111 | } 112 | 113 | 114 | /* Menu */ 115 | 116 | QMenu { 117 | padding: 4px; 118 | background: #303030; 119 | border: 1px solid transparent; 120 | } 121 | 122 | QMenu::indicator { 123 | left: 6px; 124 | width: 20px; 125 | height: 20px; 126 | } 127 | 128 | QMenu::icon { 129 | left: 6px; 130 | } 131 | 132 | QMenu::item { 133 | background: transparent; 134 | color: #CCCCCC; 135 | padding: 6px 24px; 136 | } 137 | 138 | QMenu::item:selected { 139 | color: white; 140 | background-color: #0060C0; 141 | } 142 | 143 | QMenu::item:disabled { 144 | color: #666666; 145 | background-color: transparent; 146 | } 147 | 148 | QMenu::separator { 149 | height: 2px; 150 | background-color: #5B5B5B; 151 | margin: 6px 0; 152 | } 153 | 154 | 155 | /* Window */ 156 | 157 | MainWindow { 158 | background-color: #1E1E1E; 159 | } 160 | 161 | MainWindow[custom-style=true] { 162 | background-color: transparent; 163 | } 164 | 165 | QWidget#clock-widget { 166 | font-size: 75px; 167 | color: #FEFEFE; 168 | font-weight: bold; 169 | background-color: transparent; 170 | } -------------------------------------------------------------------------------- /examples/mainwindow/light-style.qss: -------------------------------------------------------------------------------- 1 | /* Window bar */ 2 | 3 | QWK--WindowBar[bar-active=true] { 4 | background-color: #195ABE; 5 | /* background-color: transparent; */ 6 | } 7 | 8 | QWK--WindowBar[bar-active=false] { 9 | background-color: #195ABE; 10 | /* background-color: transparent; */ 11 | } 12 | 13 | 14 | /* Title label */ 15 | 16 | QWK--WindowBar>QLabel#win-title-label { 17 | padding: 0; 18 | border: none; 19 | color: #ECECEC; 20 | background-color: transparent; 21 | min-height: 28px; 22 | } 23 | 24 | 25 | /* System buttons */ 26 | 27 | QWK--WindowBar>QAbstractButton[system-button=true] { 28 | qproperty-iconSize: 12px 12px; 29 | min-width: 50px; 30 | border: none; 31 | padding: 0; 32 | background-color: transparent; 33 | } 34 | 35 | QWK--WindowBar>QAbstractButton#pin-button { 36 | qproperty-iconNormal: url(":/window-bar/pin.svg"); 37 | qproperty-iconChecked: url(":/window-bar/pin-fill.svg"); 38 | qproperty-iconSize: 15px 15px; 39 | } 40 | 41 | QWK--WindowBar>QAbstractButton#pin-button:hover, 42 | QWK--WindowBar>QAbstractButton#pin-button:pressed { 43 | background-color: rgba(0, 0, 0, 15%); 44 | } 45 | 46 | QWK--WindowBar>QAbstractButton#min-button { 47 | qproperty-iconNormal: url(":/window-bar/minimize.svg"); 48 | } 49 | 50 | QWK--WindowBar>QAbstractButton#min-button:hover, 51 | QWK--WindowBar>QAbstractButton#min-button:pressed { 52 | background-color: rgba(0, 0, 0, 15%); 53 | } 54 | 55 | QWK--WindowBar>QAbstractButton#max-button { 56 | qproperty-iconNormal: url(":/window-bar/maximize.svg"); 57 | qproperty-iconChecked: url(":/window-bar/restore.svg"); 58 | } 59 | 60 | QWK--WindowBar>QAbstractButton#max-button:hover, 61 | QWK--WindowBar>QAbstractButton#max-button:pressed { 62 | background-color: rgba(0, 0, 0, 15%); 63 | } 64 | 65 | QWK--WindowBar>QAbstractButton#close-button { 66 | qproperty-iconNormal: url(":/window-bar/close.svg"); 67 | } 68 | 69 | QWK--WindowBar>QAbstractButton#close-button:hover, 70 | QWK--WindowBar>QAbstractButton#close-button:pressed { 71 | background-color: #e81123; 72 | } 73 | 74 | 75 | /* Icon button */ 76 | 77 | QWK--WindowBar>QAbstractButton#icon-button { 78 | qproperty-iconNormal: url(":/app/example.png"); 79 | qproperty-iconSize: 18px 18px; 80 | min-width: 40px; 81 | border: none; 82 | padding: 0; 83 | background-color: transparent; 84 | } 85 | 86 | 87 | /* Menu Bar */ 88 | 89 | QMenuBar { 90 | background-color: transparent; 91 | border: none; 92 | } 93 | 94 | QMenuBar>QToolButton#qt_menubar_ext_button { 95 | qproperty-icon: url(":/window-bar/more-line.svg"); 96 | } 97 | 98 | QMenuBar>QToolButton#qt_menubar_ext_button:hover, 99 | QMenuBar>QToolButton#qt_menubar_ext_button:pressed { 100 | background-color: rgba(255, 255, 255, 10%); 101 | } 102 | 103 | QMenuBar::item { 104 | color: #EEEEEE; 105 | border: none; 106 | padding: 8px 12px; 107 | } 108 | 109 | QMenuBar::item:selected { 110 | background-color: rgba(255, 255, 255, 10%); 111 | } 112 | 113 | 114 | /* Menu */ 115 | 116 | QMenu { 117 | padding: 4px; 118 | background: white; 119 | border: 1px solid #E0E0E0; 120 | } 121 | 122 | QMenu::indicator { 123 | left: 6px; 124 | width: 20px; 125 | height: 20px; 126 | } 127 | 128 | QMenu::icon { 129 | left: 6px; 130 | } 131 | 132 | QMenu::item { 133 | background: transparent; 134 | color: #333333; 135 | padding: 6px 24px; 136 | } 137 | 138 | QMenu::item:selected { 139 | background-color: rgba(0, 0, 0, 10%); 140 | } 141 | 142 | QMenu::item:disabled { 143 | color: #CCCCCC; 144 | } 145 | 146 | QMenu::separator { 147 | height: 2px; 148 | background-color: #CCCCCC; 149 | margin: 6px 0; 150 | } 151 | 152 | 153 | /* Window */ 154 | 155 | MainWindow { 156 | background-color: #F3F3F3; 157 | } 158 | 159 | MainWindow[custom-style=true] { 160 | background-color: transparent; 161 | } 162 | 163 | QWidget#clock-widget { 164 | font-size: 75px; 165 | color: #333333; 166 | font-weight: bold; 167 | background-color: transparent; 168 | } -------------------------------------------------------------------------------- /examples/mainwindow/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include 6 | 7 | #include "mainwindow.h" 8 | 9 | int main(int argc, char *argv[]) { 10 | qputenv("QT_WIN_DEBUG_CONSOLE", "attach"); 11 | qputenv("QSG_INFO", "1"); 12 | //qputenv("QT_WIDGETS_HIGHDPI_DOWNSCALE", "1"); 13 | //qputenv("QT_WIDGETS_RHI", "1"); 14 | //qputenv("QSG_RHI_BACKEND", "d3d12"); 15 | //qputenv("QSG_RHI_HDR", "scrgb"); 16 | //qputenv("QT_QPA_DISABLE_REDIRECTION_SURFACE", "1"); 17 | 18 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 19 | QGuiApplication::setHighDpiScaleFactorRoundingPolicy( 20 | Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); 21 | #endif 22 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 23 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 24 | QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 25 | #endif 26 | 27 | QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); 28 | QApplication a(argc, argv); 29 | 30 | MainWindow w; 31 | w.show(); 32 | 33 | return a.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /examples/mainwindow/mainwindow.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef MAINWINDOW_H 6 | #define MAINWINDOW_H 7 | 8 | #include 9 | 10 | namespace QWK { 11 | class WidgetWindowAgent; 12 | class StyleAgent; 13 | } 14 | 15 | class MainWindow : public QMainWindow { 16 | Q_OBJECT 17 | public: 18 | explicit MainWindow(QWidget *parent = nullptr); 19 | ~MainWindow() override; 20 | 21 | enum Theme { 22 | Dark, 23 | Light, 24 | }; 25 | Q_ENUM(Theme) 26 | 27 | Q_SIGNALS: 28 | void themeChanged(); 29 | 30 | protected: 31 | bool event(QEvent *event) override; 32 | 33 | private: 34 | void installWindowAgent(); 35 | void loadStyleSheet(Theme theme); 36 | 37 | Theme currentTheme{}; 38 | 39 | QWK::WidgetWindowAgent *windowAgent; 40 | }; 41 | 42 | #endif // MAINWINDOW_H 43 | -------------------------------------------------------------------------------- /examples/mainwindow/mainwindow.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | dark-style.qss 4 | light-style.qss 5 | 6 | -------------------------------------------------------------------------------- /examples/qml/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(QWKExample_QML) 2 | 3 | file(GLOB _src *.h *.cpp *.qrc) 4 | 5 | qwk_add_example(${PROJECT_NAME} 6 | FEATURES cxx_std_17 7 | SOURCES ${_src} ../shared/resources/shared.qrc 8 | QT_LINKS Core Gui Qml Quick 9 | LINKS QWKQuick 10 | ) -------------------------------------------------------------------------------- /examples/qml/FramelessWindow.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Window 2.15 3 | import QtQuick.Controls 2.15 4 | import Qt.labs.platform 1.1 5 | import QWindowKit 1.0 6 | 7 | Window { 8 | property bool showWhenReady: true 9 | 10 | id: window 11 | width: 800 12 | height: 600 13 | color: darkStyle.windowBackgroundColor 14 | title: qsTr("QWindowKit QtQuick Demo") 15 | Component.onCompleted: { 16 | windowAgent.setup(window) 17 | windowAgent.setWindowAttribute("dark-mode", true) 18 | if (window.showWhenReady) { 19 | window.visible = true 20 | } 21 | } 22 | 23 | QtObject { 24 | id: lightStyle 25 | } 26 | 27 | QtObject { 28 | id: darkStyle 29 | readonly property color windowBackgroundColor: "#1E1E1E" 30 | } 31 | 32 | Timer { 33 | interval: 100 34 | running: true 35 | repeat: true 36 | onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") 37 | } 38 | 39 | WindowAgent { 40 | id: windowAgent 41 | } 42 | 43 | TapHandler { 44 | acceptedButtons: Qt.RightButton 45 | onTapped: contextMenu.open() 46 | } 47 | 48 | Rectangle { 49 | id: titleBar 50 | anchors { 51 | top: parent.top 52 | left: parent.left 53 | right: parent.right 54 | } 55 | height: 32 56 | //color: window.active ? "#3C3C3C" : "#505050" 57 | color: "transparent" 58 | Component.onCompleted: windowAgent.setTitleBar(titleBar) 59 | 60 | Image { 61 | id: iconButton 62 | anchors { 63 | verticalCenter: parent.verticalCenter 64 | left: parent.left 65 | leftMargin: 10 66 | } 67 | width: 18 68 | height: 18 69 | mipmap: true 70 | source: "qrc:///app/example.png" 71 | fillMode: Image.PreserveAspectFit 72 | Component.onCompleted: windowAgent.setSystemButton(WindowAgent.WindowIcon, iconButton) 73 | } 74 | 75 | Text { 76 | anchors { 77 | verticalCenter: parent.verticalCenter 78 | left: iconButton.right 79 | leftMargin: 10 80 | } 81 | horizontalAlignment: Text.AlignHCenter 82 | verticalAlignment: Text.AlignVCenter 83 | text: window.title 84 | font.pixelSize: 14 85 | color: "#ECECEC" 86 | } 87 | 88 | Row { 89 | anchors { 90 | top: parent.top 91 | right: parent.right 92 | } 93 | height: parent.height 94 | 95 | QWKButton { 96 | id: minButton 97 | height: parent.height 98 | source: "qrc:///window-bar/minimize.svg" 99 | onClicked: window.showMinimized() 100 | Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Minimize, minButton) 101 | } 102 | 103 | QWKButton { 104 | id: maxButton 105 | height: parent.height 106 | source: window.visibility === Window.Maximized ? "qrc:///window-bar/restore.svg" : "qrc:///window-bar/maximize.svg" 107 | onClicked: { 108 | if (window.visibility === Window.Maximized) { 109 | window.showNormal() 110 | } else { 111 | window.showMaximized() 112 | } 113 | } 114 | Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Maximize, maxButton) 115 | } 116 | 117 | QWKButton { 118 | id: closeButton 119 | height: parent.height 120 | source: "qrc:///window-bar/close.svg" 121 | background: Rectangle { 122 | color: { 123 | if (!closeButton.enabled) { 124 | return "gray"; 125 | } 126 | if (closeButton.pressed) { 127 | return "#e81123"; 128 | } 129 | if (closeButton.hovered) { 130 | return "#e81123"; 131 | } 132 | return "transparent"; 133 | } 134 | } 135 | onClicked: window.close() 136 | Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Close, closeButton) 137 | } 138 | } 139 | } 140 | 141 | Label { 142 | id: timeLabel 143 | anchors.centerIn: parent 144 | font { 145 | pointSize: 75 146 | bold: true 147 | } 148 | color: "#FEFEFE" 149 | Component.onCompleted: { 150 | if ($curveRenderingAvailable) { 151 | console.log("Curve rendering for text is available.") 152 | timeLabel.renderType = Text.CurveRendering 153 | } 154 | } 155 | } 156 | 157 | Menu { 158 | id: contextMenu 159 | 160 | Menu { 161 | id: themeMenu 162 | title: qsTr("Theme") 163 | 164 | MenuItemGroup { 165 | id: themeMenuGroup 166 | items: themeMenu.items 167 | } 168 | 169 | MenuItem { 170 | text: qsTr("Light") 171 | checkable: true 172 | onTriggered: windowAgent.setWindowAttribute("dark-mode", false) 173 | } 174 | 175 | MenuItem { 176 | text: qsTr("Dark") 177 | checkable: true 178 | checked: true 179 | onTriggered: windowAgent.setWindowAttribute("dark-mode", true) 180 | } 181 | } 182 | 183 | Menu { 184 | id: specialEffectMenu 185 | title: qsTr("Special effect") 186 | 187 | MenuItemGroup { 188 | id: specialEffectMenuGroup 189 | items: specialEffectMenu.items 190 | } 191 | 192 | MenuItem { 193 | enabled: Qt.platform.os === "windows" 194 | text: qsTr("None") 195 | checkable: true 196 | checked: true 197 | onTriggered: { 198 | window.color = darkStyle.windowBackgroundColor 199 | windowAgent.setWindowAttribute("dwm-blur", false) 200 | windowAgent.setWindowAttribute("acrylic-material", false) 201 | windowAgent.setWindowAttribute("mica", false) 202 | windowAgent.setWindowAttribute("mica-alt", false) 203 | } 204 | } 205 | 206 | MenuItem { 207 | enabled: Qt.platform.os === "windows" 208 | text: qsTr("DWM blur") 209 | checkable: true 210 | onTriggered: { 211 | window.color = "transparent" 212 | windowAgent.setWindowAttribute("acrylic-material", false) 213 | windowAgent.setWindowAttribute("mica", false) 214 | windowAgent.setWindowAttribute("mica-alt", false) 215 | windowAgent.setWindowAttribute("dwm-blur", true) 216 | } 217 | } 218 | 219 | MenuItem { 220 | enabled: Qt.platform.os === "windows" 221 | text: qsTr("Acrylic material") 222 | checkable: true 223 | onTriggered: { 224 | window.color = "transparent" 225 | windowAgent.setWindowAttribute("dwm-blur", false) 226 | windowAgent.setWindowAttribute("mica", false) 227 | windowAgent.setWindowAttribute("mica-alt", false) 228 | windowAgent.setWindowAttribute("acrylic-material", true) 229 | } 230 | } 231 | 232 | MenuItem { 233 | enabled: Qt.platform.os === "windows" 234 | text: qsTr("Mica") 235 | checkable: true 236 | onTriggered: { 237 | window.color = "transparent" 238 | windowAgent.setWindowAttribute("dwm-blur", false) 239 | windowAgent.setWindowAttribute("acrylic-material", false) 240 | windowAgent.setWindowAttribute("mica-alt", false) 241 | windowAgent.setWindowAttribute("mica", true) 242 | } 243 | } 244 | 245 | MenuItem { 246 | enabled: Qt.platform.os === "windows" 247 | text: qsTr("Mica Alt") 248 | checkable: true 249 | onTriggered: { 250 | window.color = "transparent" 251 | windowAgent.setWindowAttribute("dwm-blur", false) 252 | windowAgent.setWindowAttribute("acrylic-material", false) 253 | windowAgent.setWindowAttribute("mica", false) 254 | windowAgent.setWindowAttribute("mica-alt", true) 255 | } 256 | } 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /examples/qml/QWKButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | 4 | Button { 5 | id: root 6 | width: height * 1.5 7 | leftPadding: 0 8 | topPadding: 0 9 | rightPadding: 0 10 | bottomPadding: 0 11 | leftInset: 0 12 | topInset: 0 13 | rightInset: 0 14 | bottomInset: 0 15 | property alias source: image.source 16 | contentItem: Item { 17 | Image { 18 | id: image 19 | anchors.centerIn: parent 20 | mipmap: true 21 | width: 12 22 | height: 12 23 | fillMode: Image.PreserveAspectFit 24 | } 25 | } 26 | background: Rectangle { 27 | color: { 28 | if (!root.enabled) { 29 | return "gray"; 30 | } 31 | if (root.pressed) { 32 | return Qt.rgba(0, 0, 0, 0.15); 33 | } 34 | if (root.hovered) { 35 | return Qt.rgba(0, 0, 0, 0.15); 36 | } 37 | return "transparent"; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /examples/qml/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #ifdef Q_OS_WIN 13 | // Indicates to hybrid graphics systems to prefer the discrete part by default. 14 | extern "C" { 15 | Q_DECL_EXPORT unsigned long NvOptimusEnablement = 0x00000001; 16 | Q_DECL_EXPORT int AmdPowerXpressRequestHighPerformance = 1; 17 | } 18 | #endif 19 | 20 | int main(int argc, char *argv[]) { 21 | qputenv("QT_WIN_DEBUG_CONSOLE", "attach"); // or "new": create a separate console window 22 | qputenv("QSG_INFO", "1"); 23 | qputenv("QSG_NO_VSYNC", "1"); 24 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 25 | qputenv("QT_QUICK_CONTROLS_STYLE", "Basic"); 26 | #else 27 | qputenv("QT_QUICK_CONTROLS_STYLE", "Default"); 28 | #endif 29 | //qputenv("QSG_RHI_BACKEND", "opengl"); // other options: d3d11, d3d12, vulkan 30 | //qputenv("QSG_RHI_HDR", "scrgb"); // other options: hdr10, p3 31 | //qputenv("QT_QPA_DISABLE_REDIRECTION_SURFACE", "1"); 32 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 33 | QGuiApplication::setHighDpiScaleFactorRoundingPolicy( 34 | Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); 35 | #endif 36 | QGuiApplication application(argc, argv); 37 | // Make sure alpha channel is requested, our special effects on Windows depends on it. 38 | QQuickWindow::setDefaultAlphaBuffer(true); 39 | QQmlApplicationEngine engine; 40 | #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) 41 | const bool curveRenderingAvailable = true; 42 | #else 43 | const bool curveRenderingAvailable = false; 44 | #endif 45 | engine.rootContext()->setContextProperty(QStringLiteral("$curveRenderingAvailable"), QVariant(curveRenderingAvailable)); 46 | QWK::registerTypes(&engine); 47 | engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); 48 | return application.exec(); 49 | } -------------------------------------------------------------------------------- /examples/qml/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Window 2.15 3 | import QtQuick.Controls 2.15 4 | 5 | FramelessWindow { 6 | property FramelessWindow childWindow: FramelessWindow { 7 | showWhenReady: false 8 | } 9 | 10 | Button { 11 | anchors { 12 | horizontalCenter: parent.horizontalCenter 13 | bottom: parent.bottom 14 | bottomMargin: 20 15 | } 16 | text: qsTr("Open Child Window") 17 | onClicked: childWindow.visible = true 18 | } 19 | } -------------------------------------------------------------------------------- /examples/qml/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | QWKButton.qml 5 | FramelessWindow.qml 6 | 7 | -------------------------------------------------------------------------------- /examples/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(widgetframe) -------------------------------------------------------------------------------- /examples/shared/resources/app/example.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/examples/shared/resources/app/example.icns -------------------------------------------------------------------------------- /examples/shared/resources/app/example.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/examples/shared/resources/app/example.ico -------------------------------------------------------------------------------- /examples/shared/resources/app/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stdware/qwindowkit/a4d0b98d40d0f740742e393db774ee53302ec64c/examples/shared/resources/app/example.png -------------------------------------------------------------------------------- /examples/shared/resources/shared.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | window-bar/close.svg 4 | window-bar/fullscreen.svg 5 | window-bar/maximize.svg 6 | window-bar/minimize.svg 7 | window-bar/restore.svg 8 | window-bar/more-line.svg 9 | window-bar/pin.svg 10 | window-bar/pin-fill.svg 11 | app/example.png 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/close.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/maximize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/minimize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/more-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/pin-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /examples/shared/resources/window-bar/restore.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/shared/widgetframe/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(WidgetFrame) 2 | 3 | set(CMAKE_AUTOMOC ON) 4 | set(CMAKE_AUTOUIC ON) 5 | set(CMAKE_AUTORCC ON) 6 | 7 | file(GLOB _src *.h *.cpp) 8 | 9 | add_library(${PROJECT_NAME} STATIC) 10 | 11 | qm_configure_target(${PROJECT_NAME} 12 | FEATURES cxx_std_17 13 | SOURCES ${_src} 14 | QT_LINKS Core Gui Widgets 15 | ) 16 | 17 | target_include_directories(${PROJECT_NAME} PUBLIC . ..) -------------------------------------------------------------------------------- /examples/shared/widgetframe/windowbar.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWBAR_H 6 | #define WINDOWBAR_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace QWK { 14 | 15 | class WindowBarPrivate; 16 | 17 | class WindowBar : public QFrame { 18 | Q_OBJECT 19 | Q_DECLARE_PRIVATE(WindowBar) 20 | public: 21 | explicit WindowBar(QWidget *parent = nullptr); 22 | ~WindowBar(); 23 | 24 | public: 25 | QMenuBar *menuBar() const; 26 | QLabel *titleLabel() const; 27 | QAbstractButton *iconButton() const; 28 | QAbstractButton *pinButton() const; 29 | QAbstractButton *minButton() const; 30 | QAbstractButton *maxButton() const; 31 | QAbstractButton *closeButton() const; 32 | 33 | void setMenuBar(QMenuBar *menuBar); 34 | void setTitleLabel(QLabel *label); 35 | void setIconButton(QAbstractButton *btn); 36 | void setPinButton(QAbstractButton *btn); 37 | void setMinButton(QAbstractButton *btn); 38 | void setMaxButton(QAbstractButton *btn); 39 | void setCloseButton(QAbstractButton *btn); 40 | 41 | QMenuBar *takeMenuBar(); 42 | QLabel *takeTitleLabel(); 43 | QAbstractButton *takeIconButton(); 44 | QAbstractButton *takePinButton(); 45 | QAbstractButton *takeMinButton(); 46 | QAbstractButton *takeMaxButton(); 47 | QAbstractButton *takeCloseButton(); 48 | 49 | QWidget *hostWidget() const; 50 | void setHostWidget(QWidget *w); 51 | 52 | bool titleFollowWindow() const; 53 | void setTitleFollowWindow(bool value); 54 | 55 | bool iconFollowWindow() const; 56 | void setIconFollowWindow(bool value); 57 | 58 | Q_SIGNALS: 59 | void pinRequested(bool pin = false); 60 | void minimizeRequested(); 61 | void maximizeRequested(bool max = false); 62 | void closeRequested(); 63 | 64 | protected: 65 | bool eventFilter(QObject *obj, QEvent *event) override; 66 | 67 | virtual void titleChanged(const QString &text); 68 | virtual void iconChanged(const QIcon &icon); 69 | 70 | protected: 71 | WindowBar(WindowBarPrivate &d, QWidget *parent = nullptr); 72 | 73 | QScopedPointer d_ptr; 74 | }; 75 | 76 | } 77 | 78 | #endif // WINDOWBAR_H -------------------------------------------------------------------------------- /examples/shared/widgetframe/windowbar_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWBARPRIVATE_H 6 | #define WINDOWBARPRIVATE_H 7 | 8 | #include 9 | 10 | #include "windowbar.h" 11 | 12 | namespace QWK { 13 | 14 | class WindowBarPrivate { 15 | Q_DECLARE_PUBLIC(WindowBar) 16 | public: 17 | WindowBarPrivate(); 18 | virtual ~WindowBarPrivate(); 19 | 20 | void init(); 21 | 22 | WindowBar *q_ptr; 23 | 24 | QWidget *w; 25 | bool autoTitle; 26 | bool autoIcon; 27 | 28 | enum WindowBarItem { 29 | IconButton, 30 | MenuWidget, 31 | TitleLabel, 32 | PinButton, 33 | MinimizeButton, 34 | MaximizeButton, 35 | CloseButton, 36 | }; 37 | 38 | QHBoxLayout *layout; 39 | 40 | inline QWidget *widgetAt(int index) const { 41 | return layout->itemAt(index)->widget(); 42 | } 43 | 44 | void setWidgetAt(int index, QWidget *widget); 45 | 46 | QWidget *takeWidgetAt(int index); 47 | 48 | inline void insertDefaultSpace(int index) { 49 | layout->insertSpacerItem(index, new QSpacerItem(0, 0)); 50 | } 51 | 52 | private: 53 | Q_DISABLE_COPY(WindowBarPrivate) 54 | }; 55 | 56 | } 57 | 58 | #endif // WINDOWBARPRIVATE_H -------------------------------------------------------------------------------- /examples/shared/widgetframe/windowbutton.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "windowbutton.h" 6 | #include "windowbutton_p.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace QWK { 12 | 13 | WindowButtonPrivate::WindowButtonPrivate() = default; 14 | 15 | WindowButtonPrivate::~WindowButtonPrivate() = default; 16 | 17 | void WindowButtonPrivate::init() { 18 | } 19 | 20 | void WindowButtonPrivate::reloadIcon() { 21 | Q_Q(WindowButton); 22 | 23 | if (!q->isEnabled() && !iconDisabled.isNull()) { 24 | q->setIcon(iconDisabled); 25 | return; 26 | } 27 | 28 | if (q->isChecked() && !iconChecked.isNull()) { 29 | q->setIcon(iconChecked); 30 | return; 31 | } 32 | 33 | if (!iconNormal.isNull()) { 34 | q->setIcon(iconNormal); 35 | } 36 | } 37 | 38 | WindowButton::WindowButton(QWidget *parent) : WindowButton(*new WindowButtonPrivate(), parent) { 39 | } 40 | 41 | WindowButton::~WindowButton() = default; 42 | 43 | QIcon WindowButton::iconNormal() const { 44 | Q_D(const WindowButton); 45 | return d->iconNormal; 46 | } 47 | 48 | void WindowButton::setIconNormal(const QIcon &icon) { 49 | Q_D(WindowButton); 50 | d->iconNormal = icon; 51 | d->reloadIcon(); 52 | } 53 | 54 | QIcon WindowButton::iconChecked() const { 55 | Q_D(const WindowButton); 56 | return d->iconChecked; 57 | } 58 | 59 | void WindowButton::setIconChecked(const QIcon &icon) { 60 | Q_D(WindowButton); 61 | d->iconChecked = icon; 62 | d->reloadIcon(); 63 | } 64 | 65 | QIcon WindowButton::iconDisabled() const { 66 | Q_D(const WindowButton); 67 | return d->iconDisabled; 68 | } 69 | 70 | void WindowButton::setIconDisabled(const QIcon &icon) { 71 | Q_D(WindowButton); 72 | d->iconDisabled = icon; 73 | d->reloadIcon(); 74 | } 75 | 76 | void WindowButton::checkStateSet() { 77 | Q_D(WindowButton); 78 | d->reloadIcon(); 79 | } 80 | 81 | void WindowButton::mouseDoubleClickEvent(QMouseEvent *event) { 82 | if (event->button() == Qt::LeftButton) { 83 | Q_EMIT doubleClicked(); 84 | } 85 | } 86 | 87 | WindowButton::WindowButton(WindowButtonPrivate &d, QWidget *parent) 88 | : QPushButton(parent), d_ptr(&d) { 89 | d.q_ptr = this; 90 | 91 | d.init(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /examples/shared/widgetframe/windowbutton.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWBUTTON_H 6 | #define WINDOWBUTTON_H 7 | 8 | #include 9 | 10 | namespace QWK { 11 | 12 | class WindowButtonPrivate; 13 | 14 | class WindowButton : public QPushButton { 15 | Q_OBJECT 16 | Q_DECLARE_PRIVATE(WindowButton) 17 | Q_PROPERTY(QIcon iconNormal READ iconNormal WRITE setIconNormal FINAL) 18 | Q_PROPERTY(QIcon iconChecked READ iconChecked WRITE setIconChecked FINAL) 19 | Q_PROPERTY(QIcon iconDisabled READ iconDisabled WRITE setIconDisabled FINAL) 20 | public: 21 | explicit WindowButton(QWidget *parent = nullptr); 22 | ~WindowButton(); 23 | 24 | public: 25 | QIcon iconNormal() const; 26 | void setIconNormal(const QIcon &icon); 27 | 28 | QIcon iconChecked() const; 29 | void setIconChecked(const QIcon &icon); 30 | 31 | QIcon iconDisabled() const; 32 | void setIconDisabled(const QIcon &icon); 33 | 34 | Q_SIGNALS: 35 | void doubleClicked(); 36 | 37 | protected: 38 | void checkStateSet() override; 39 | 40 | void mouseDoubleClickEvent(QMouseEvent *event) override; 41 | 42 | protected: 43 | WindowButton(WindowButtonPrivate &d, QWidget *parent = nullptr); 44 | 45 | QScopedPointer d_ptr; 46 | }; 47 | 48 | } 49 | 50 | #endif // WINDOWBUTTON_H -------------------------------------------------------------------------------- /examples/shared/widgetframe/windowbutton_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWBUTTONPRIVATE_H 6 | #define WINDOWBUTTONPRIVATE_H 7 | 8 | #include "windowbutton.h" 9 | 10 | namespace QWK { 11 | 12 | class WindowButtonPrivate { 13 | Q_DECLARE_PUBLIC(WindowButton) 14 | public: 15 | WindowButtonPrivate(); 16 | virtual ~WindowButtonPrivate(); 17 | 18 | void init(); 19 | 20 | WindowButton *q_ptr; 21 | 22 | QIcon iconNormal; 23 | QIcon iconChecked; 24 | QIcon iconDisabled; 25 | 26 | void reloadIcon(); 27 | }; 28 | 29 | } 30 | 31 | #endif // WINDOWBUTTONPRIVATE_H -------------------------------------------------------------------------------- /share/install.cmake: -------------------------------------------------------------------------------- 1 | set(_build_data_dir ${CMAKE_CURRENT_BINARY_DIR}/../etc/share) 2 | 3 | # Install qmake files 4 | if(TRUE) 5 | set(_qmake_install_dir "${CMAKE_INSTALL_DATADIR}/QWindowKit/qmake") 6 | set(_qmake_build_dir ${_build_data_dir}/${_qmake_install_dir}) 7 | file(RELATIVE_PATH _qmake_install_prefix 8 | "${CMAKE_INSTALL_PREFIX}/${_qmake_install_dir}" 9 | "${CMAKE_INSTALL_PREFIX}" 10 | ) 11 | string(REGEX REPLACE "/$" "" _qmake_install_prefix "${_qmake_install_prefix}") 12 | 13 | set(QMAKE_QWK_INSTALL_PREFIX "\$\$PWD/${_qmake_install_prefix}") 14 | set(QMAKE_QWK_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR}) 15 | set(QMAKE_QWK_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) 16 | set(QMAKE_QWK_INSTALL_INCDIR ${CMAKE_INSTALL_INCLUDEDIR}) 17 | 18 | set(QMAKE_QWK_CORE_NAME_RELEASE QWKCore) 19 | set(QMAKE_QWK_WIDGETS_NAME_RELEASE QWKWidgets) 20 | set(QMAKE_QWK_QUICK_NAME_RELEASE QWKQuick) 21 | 22 | set(QMAKE_QWK_CORE_NAME_DEBUG QWKCore${CMAKE_DEBUG_POSTFIX}) 23 | set(QMAKE_QWK_WIDGETS_NAME_DEBUG QWKWidgets${CMAKE_DEBUG_POSTFIX}) 24 | set(QMAKE_QWK_QUICK_NAME_DEBUG QWKQuick${CMAKE_DEBUG_POSTFIX}) 25 | 26 | if(QWINDOWKIT_BUILD_STATIC) 27 | set(QMAKE_QWK_CORE_STATIC_MACRO "DEFINES += QWK_CORE_STATIC") 28 | set(QMAKE_QWK_WIDGETS_STATIC_MACRO "DEFINES += QWK_WIDGETS_STATIC") 29 | set(QMAKE_QWK_QUICK_STATIC_MACRO "DEFINES += QWK_QUICK_STATIC") 30 | endif() 31 | 32 | file(GLOB _qmake_components "${CMAKE_CURRENT_LIST_DIR}/qmake/*.pri.in") 33 | 34 | foreach(_item IN LISTS _qmake_components) 35 | get_filename_component(_name ${_item} NAME_WLE) 36 | configure_file(${_item} ${_build_data_dir}/qmake/${_name} @ONLY) 37 | endforeach() 38 | 39 | install(DIRECTORY ${_build_data_dir}/qmake/ 40 | DESTINATION ${_qmake_install_dir} 41 | ) 42 | endif() 43 | 44 | # Install msbuild files 45 | if(MSVC) 46 | macro(to_dos_separator _var) 47 | string(REPLACE "/" "\\" ${_var} ${${_var}}) 48 | endmacro() 49 | 50 | set(_msbuild_install_dir "${CMAKE_INSTALL_DATADIR}/QWindowKit/msbuild") 51 | set(_msbuild_build_dir ${_build_data_dir}/${_msbuild_install_dir}) 52 | file(RELATIVE_PATH _msbuild_install_prefix 53 | "${CMAKE_INSTALL_PREFIX}/${_msbuild_install_dir}" 54 | "${CMAKE_INSTALL_PREFIX}" 55 | ) 56 | string(REGEX REPLACE "/$" "" _msbuild_install_prefix "${_msbuild_install_prefix}") 57 | 58 | set(MSBUILD_QWK_INSTALL_PREFIX "\$(MSBuildThisFileDirectory)${_msbuild_install_prefix}") 59 | set(MSBUILD_QWK_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR}) 60 | set(MSBUILD_QWK_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) 61 | set(MSBUILD_QWK_INSTALL_INCDIR ${CMAKE_INSTALL_INCLUDEDIR}) 62 | 63 | set(MSBUILD_QWK_LIBRARY_LIST_DEBUG 64 | QWKCore${CMAKE_DEBUG_POSTFIX}.lib 65 | QWKWidgets${CMAKE_DEBUG_POSTFIX}.lib 66 | QWKQuick${CMAKE_DEBUG_POSTFIX}.lib 67 | ) 68 | 69 | set(MSBUILD_QWK_LIBRARY_LIST_RELEASE 70 | QWKCore.lib 71 | QWKWidgets.lib 72 | QWKQuick.lib 73 | ) 74 | 75 | to_dos_separator(MSBUILD_QWK_INSTALL_PREFIX) 76 | to_dos_separator(MSBUILD_QWK_INSTALL_BINDIR) 77 | to_dos_separator(MSBUILD_QWK_INSTALL_LIBDIR) 78 | to_dos_separator(MSBUILD_QWK_INSTALL_INCDIR) 79 | 80 | if(QWINDOWKIT_BUILD_STATIC) 81 | set(MSBUILD_QWK_STATIC_MACRO 82 | "QWK_CORE_STATIC;QWK_WIDGETS_STATIC;QWK_QUICK_STATIC" 83 | ) 84 | endif() 85 | 86 | configure_file(${CMAKE_CURRENT_LIST_DIR}/msbuild/QWindowKit.props.in 87 | ${_build_data_dir}/msbuild/QWindowKit.props 88 | @ONLY 89 | ) 90 | 91 | install(DIRECTORY ${_build_data_dir}/msbuild/ 92 | DESTINATION ${_msbuild_install_dir} 93 | ) 94 | endif() -------------------------------------------------------------------------------- /share/msbuild/QWindowKit.props.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @MSBUILD_QWK_INSTALL_PREFIX@\@MSBUILD_QWK_INSTALL_LIBDIR@;$(LibraryPath) 5 | 6 | 7 | @MSBUILD_QWK_INSTALL_PREFIX@\@MSBUILD_QWK_INSTALL_INCDIR@\QWindowKit;$(QtHeaderSearchPath) 8 | 9 | 10 | 11 | %(PreprocessorDefinitions) 12 | 13 | 14 | @MSBUILD_QWK_LIBRARY_LIST_DEBUG@;%(AdditionalDependencies); 15 | @MSBUILD_QWK_LIBRARY_LIST_RELEASE@;%(AdditionalDependencies); 16 | 17 | 18 | 19 | @MSBUILD_QWK_STATIC_MACRO@ 20 | 21 | 22 | -------------------------------------------------------------------------------- /share/qmake/QWKCore.pri.in: -------------------------------------------------------------------------------- 1 | !defined(QMAKE_QWK_CORE_INCLUDED, var) { 2 | QMAKE_QWK_CORE_INCLUDED = 1 3 | 4 | # CMake variables 5 | QMAKE_QWK_INSTALL_PREFIX = @QMAKE_QWK_INSTALL_PREFIX@ 6 | QMAKE_QWK_INSTALL_BINDIR = @QMAKE_QWK_INSTALL_BINDIR@ 7 | QMAKE_QWK_INSTALL_LIBDIR = @QMAKE_QWK_INSTALL_LIBDIR@ 8 | QMAKE_QWK_INSTALL_INCDIR = @QMAKE_QWK_INSTALL_INCDIR@ 9 | 10 | # Shared link directory 11 | QMAKE_QWK_LINK_PATH = "-L$$QMAKE_QWK_INSTALL_PREFIX/$$QMAKE_QWK_INSTALL_LIBDIR" 12 | 13 | # Include directory 14 | INCLUDEPATH += \ 15 | $$QMAKE_QWK_INSTALL_PREFIX/$$QMAKE_QWK_INSTALL_INCDIR/QWindowKit 16 | 17 | CONFIG(debug, debug|release) { 18 | LIBS = $$QMAKE_QWK_LINK_PATH -l@QMAKE_QWK_CORE_NAME_DEBUG@ $$LIBS 19 | } else { 20 | LIBS = $$QMAKE_QWK_LINK_PATH -l@QMAKE_QWK_CORE_NAME_RELEASE@ $$LIBS 21 | } 22 | 23 | @QMAKE_QWK_CORE_STATIC_MACRO@ 24 | } -------------------------------------------------------------------------------- /share/qmake/QWKQuick.pri.in: -------------------------------------------------------------------------------- 1 | !defined(QMAKE_QWK_QUICK_INCLUDED, var) { 2 | QMAKE_QWK_QUICK_INCLUDED = 1 3 | 4 | include($$PWD/QWKCore.pri) 5 | 6 | CONFIG(debug, debug|release) { 7 | LIBS = $$QMAKE_QWK_LINK_PATH -l@QMAKE_QWK_QUICK_NAME_DEBUG@ $$LIBS 8 | } else { 9 | LIBS = $$QMAKE_QWK_LINK_PATH -l@QMAKE_QWK_QUICK_NAME_RELEASE@ $$LIBS 10 | } 11 | 12 | @QMAKE_QWK_QUICK_STATIC_MACRO@ 13 | } -------------------------------------------------------------------------------- /share/qmake/QWKWidgets.pri.in: -------------------------------------------------------------------------------- 1 | !defined(QMAKE_QWK_WIDGETS_INCLUDED, var) { 2 | QMAKE_QWK_WIDGETS_INCLUDED = 1 3 | 4 | include($$PWD/QWKCore.pri) 5 | 6 | CONFIG(debug, debug|release) { 7 | LIBS = $$QMAKE_QWK_LINK_PATH -l@QMAKE_QWK_WIDGETS_NAME_DEBUG@ $$LIBS 8 | } else { 9 | LIBS = $$QMAKE_QWK_LINK_PATH -l@QMAKE_QWK_WIDGETS_NAME_RELEASE@ $$LIBS 10 | } 11 | 12 | @QMAKE_QWK_WIDGETS_STATIC_MACRO@ 13 | } -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qm_import(Preprocess) 2 | 3 | set(QWINDOWKIT_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/../include) 4 | set(QWINDOWKIT_BUILD_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/../etc/include) 5 | 6 | set(QWINDOWKIT_ENABLED_TARGETS) 7 | set(QWINDOWKIT_ENABLED_SUBDIRECTORIES) 8 | 9 | # ---------------------------------- 10 | # Configurations 11 | # ---------------------------------- 12 | set(QMSETUP_DEFINITION_SCOPE DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 13 | set(QMSETUP_DEFINITION_NUMERICAL on) 14 | 15 | qm_add_definition(QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT 16 | CONDITION QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT 17 | ) 18 | qm_add_definition(QWINDOWKIT_ENABLE_STYLE_AGENT 19 | CONDITION QWINDOWKIT_ENABLE_STYLE_AGENT 20 | ) 21 | qm_add_definition(QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS 22 | CONDITION QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS 23 | ) 24 | 25 | qm_generate_config(${QWINDOWKIT_BUILD_INCLUDE_DIR}/QWKCore/qwkconfig.h) 26 | 27 | if(QWINDOWKIT_INSTALL) 28 | install(FILES ${QWINDOWKIT_BUILD_INCLUDE_DIR}/QWKCore/qwkconfig.h 29 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${QWINDOWKIT_INSTALL_NAME}/QWKCore 30 | ) 31 | endif() 32 | 33 | # ---------------------------------- 34 | # CMake API 35 | # ---------------------------------- 36 | macro(qwk_add_library _target) 37 | set(options AUTOGEN NO_SYNC_INCLUDE NO_WIN_RC) 38 | set(oneValueArgs SYNC_INCLUDE_PREFIX PREFIX) 39 | set(multiValueArgs SYNC_INCLUDE_OPTIONS) 40 | cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 41 | 42 | if(FUNC_AUTOGEN) 43 | set(CMAKE_AUTOMOC ON) 44 | set(CMAKE_AUTOUIC ON) 45 | set(CMAKE_AUTORCC ON) 46 | endif() 47 | 48 | if(QWINDOWKIT_BUILD_STATIC) 49 | set(_type STATIC) 50 | else() 51 | set(_type SHARED) 52 | endif() 53 | 54 | add_library(${_target} ${_type}) 55 | 56 | if(WIN32 AND NOT FUNC_NO_WIN_RC AND(${_type} STREQUAL "SHARED")) 57 | qm_add_win_rc(${_target} 58 | NAME ${QWINDOWKIT_INSTALL_NAME} 59 | DESCRIPTION ${QWINDOWKIT_DESCRIPTION} 60 | COPYRIGHT ${QACTIONKIT_COPYRIGHT} 61 | ) 62 | endif() 63 | 64 | if(FUNC_PREFIX) 65 | set(_prefix_option PREFIX ${FUNC_PREFIX}) 66 | else() 67 | set(_prefix_option) 68 | endif() 69 | 70 | # Set global definitions 71 | qm_export_defines(${_target} ${_prefix_option}) 72 | 73 | # Configure target 74 | qm_configure_target(${_target} ${FUNC_UNPARSED_ARGUMENTS}) 75 | 76 | # Add include directories 77 | target_include_directories(${_target} PRIVATE ${QWINDOWKIT_BUILD_INCLUDE_DIR}) 78 | target_include_directories(${_target} PRIVATE .) 79 | 80 | # Library name 81 | if(${_target} MATCHES "^QWK(.+)") 82 | set(_name ${CMAKE_MATCH_1}) 83 | set_target_properties(${_target} PROPERTIES EXPORT_NAME ${_name}) 84 | else() 85 | set(_name ${_target}) 86 | endif() 87 | 88 | add_library(${QWINDOWKIT_INSTALL_NAME}::${_name} ALIAS ${_target}) 89 | 90 | if(FUNC_SYNC_INCLUDE_PREFIX) 91 | set(_inc_name ${FUNC_SYNC_INCLUDE_PREFIX}) 92 | else() 93 | set(_inc_name ${_target}) 94 | endif() 95 | 96 | set(_install_options) 97 | 98 | if(QWINDOWKIT_INSTALL) 99 | install(TARGETS ${_target} 100 | EXPORT ${QWINDOWKIT_INSTALL_NAME}Targets 101 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL 102 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL 103 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL 104 | ) 105 | 106 | target_include_directories(${_target} PUBLIC 107 | "$" 108 | ) 109 | 110 | set(_install_options 111 | INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${QWINDOWKIT_INSTALL_NAME}/${_inc_name}" 112 | ) 113 | endif() 114 | 115 | if(NOT FUNC_NO_SYNC_INCLUDE) 116 | # Generate a standard include directory in build directory 117 | qm_sync_include(. "${QWINDOWKIT_GENERATED_INCLUDE_DIR}/${_inc_name}" ${_install_options} 118 | ${FUNC_SYNC_INCLUDE_OPTIONS} FORCE 119 | ) 120 | target_include_directories(${_target} PUBLIC 121 | "$" 122 | ) 123 | endif() 124 | endmacro() 125 | 126 | # ---------------------------------- 127 | # Main Project 128 | # ---------------------------------- 129 | add_subdirectory(core) 130 | 131 | if(QWINDOWKIT_BUILD_WIDGETS) 132 | add_subdirectory(widgets) 133 | endif() 134 | 135 | if(QWINDOWKIT_BUILD_QUICK) 136 | add_subdirectory(quick) 137 | endif() 138 | 139 | # ---------------------------------- 140 | # Documentation 141 | # ---------------------------------- 142 | if(QWINDOWKIT_BUILD_DOCUMENTATIONS) 143 | if(NOT DOXYGEN_EXECUTABLE) 144 | find_package(Doxygen REQUIRED) 145 | endif() 146 | 147 | set(_install_options) 148 | 149 | if(QWINDOWKIT_INSTALL) 150 | set(_install_options INSTALL_DIR share/doc/${QWINDOWKIT_INSTALL_NAME}) 151 | endif() 152 | 153 | set(_doc_targets ${QWINDOWKIT_ENABLED_TARGETS}) 154 | 155 | set(QWINDOWKIT_DOXYGEN_TAGFILE 156 | ${CMAKE_BUILD_SHARE_DIR}/doc/${QWINDOWKIT_INSTALL_NAME}/${QWINDOWKIT_INSTALL_NAME}_tagfile.xml 157 | CACHE FILEPATH "QWindowKit doxygen tag file path" FORCE 158 | ) 159 | 160 | qm_import(Doxygen) 161 | qm_setup_doxygen(QWindowKit_RunDoxygen 162 | NAME "QWindowKit" 163 | DESCRIPTION "${QWINDOWKIT_DESCRIPTION}" 164 | MDFILE ../README.md 165 | OUTPUT_DIR ${CMAKE_BUILD_SHARE_DIR}/doc/${QWINDOWKIT_INSTALL_NAME} 166 | INPUT ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} 167 | TARGETS ${_doc_targets} 168 | DEPENDS ${_doc_targets} 169 | NO_EXPAND_MACROS Q_OBJECT Q_GADGET Q_DECLARE_TR_FUNCTIONS 170 | COMPILE_DEFINITIONS Q_SIGNALS=Q_SIGNALS Q_SLOTS=Q_SLOTS 171 | GENERATE_TAGFILE "${QWINDOWKIT_DOXYGEN_TAGFILE}" 172 | ${_install_options} 173 | ) 174 | endif() 175 | 176 | # ---------------------------------- 177 | # Install 178 | # ---------------------------------- 179 | if(QWINDOWKIT_INSTALL) 180 | qm_basic_install( 181 | NAME ${QWINDOWKIT_INSTALL_NAME} 182 | VERSION ${QWINDOWKIT_VERSION} 183 | INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${QWINDOWKIT_INSTALL_NAME} 184 | CONFIG_TEMPLATE "${QWINDOWKIT_INSTALL_NAME}Config.cmake.in" 185 | NAMESPACE ${QWINDOWKIT_INSTALL_NAME}:: 186 | EXPORT ${QWINDOWKIT_INSTALL_NAME}Targets 187 | WRITE_CONFIG_OPTIONS NO_CHECK_REQUIRED_COMPONENTS_MACRO 188 | ) 189 | 190 | # Install shared files 191 | include("../share/install.cmake") 192 | endif() -------------------------------------------------------------------------------- /src/QWindowKitConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | find_dependency(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED) 6 | find_dependency(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED) 7 | 8 | if ("Widgets" IN_LIST QWindowKit_FIND_COMPONENTS) 9 | find_dependency(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED) 10 | find_dependency(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) 11 | endif() 12 | 13 | if ("Quick" IN_LIST QWindowKit_FIND_COMPONENTS) 14 | find_dependency(QT NAMES Qt6 Qt5 COMPONENTS Quick REQUIRED) 15 | find_dependency(Qt${QT_VERSION_MAJOR} COMPONENTS Quick REQUIRED) 16 | endif() 17 | 18 | include("${CMAKE_CURRENT_LIST_DIR}/QWindowKitTargets.cmake") -------------------------------------------------------------------------------- /src/core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(QWKCore 2 | VERSION ${QWINDOWKIT_VERSION} 3 | LANGUAGES CXX 4 | ) 5 | 6 | set(_src 7 | qwkglobal.h 8 | qwkglobal_p.h 9 | qwkglobal.cpp 10 | windowagentbase.h 11 | windowagentbase_p.h 12 | windowagentbase.cpp 13 | windowitemdelegate_p.h 14 | windowitemdelegate.cpp 15 | kernel/nativeeventfilter_p.h 16 | kernel/nativeeventfilter.cpp 17 | kernel/sharedeventfilter_p.h 18 | kernel/sharedeventfilter.cpp 19 | kernel/winidchangeeventfilter_p.h 20 | kernel/winidchangeeventfilter.cpp 21 | shared/systemwindow_p.h 22 | contexts/abstractwindowcontext_p.h 23 | contexts/abstractwindowcontext.cpp 24 | ) 25 | 26 | set(_links_private) 27 | 28 | if(WIN32) 29 | list(APPEND _src 30 | qwindowkit_windows.h 31 | qwindowkit_windows.cpp 32 | shared/qwkwindowsextra_p.h 33 | shared/windows10borderhandler_p.h 34 | ) 35 | elseif(APPLE) 36 | else() 37 | list(APPEND _src 38 | qwindowkit_linux.h 39 | ) 40 | endif() 41 | 42 | if(QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT) 43 | list(APPEND _src 44 | contexts/qtwindowcontext_p.h 45 | contexts/qtwindowcontext.cpp 46 | ) 47 | else() 48 | if(WIN32) 49 | list(APPEND _src 50 | contexts/win32windowcontext_p.h 51 | contexts/win32windowcontext.cpp 52 | ) 53 | list(APPEND _links_private uxtheme) 54 | elseif(APPLE) 55 | list(APPEND _src 56 | contexts/cocoawindowcontext_p.h 57 | contexts/cocoawindowcontext.mm 58 | ) 59 | list(APPEND _links_private 60 | "-framework Foundation" 61 | "-framework Cocoa" 62 | "-framework AppKit" 63 | ) 64 | else() 65 | list(APPEND _src 66 | contexts/qtwindowcontext_p.h 67 | contexts/qtwindowcontext.cpp 68 | ) 69 | endif() 70 | endif() 71 | 72 | set(_sync_include_options) 73 | 74 | if(QWINDOWKIT_ENABLE_STYLE_AGENT) 75 | list(APPEND _src 76 | style/styleagent.h 77 | style/styleagent_p.h 78 | style/styleagent.cpp 79 | ) 80 | 81 | if(WIN32) 82 | list(APPEND _src style/styleagent_win.cpp) 83 | elseif(APPLE) 84 | list(APPEND _src style/styleagent_mac.mm) 85 | else() 86 | list(APPEND _src style/styleagent_linux.cpp) 87 | endif() 88 | else() 89 | list(APPEND _sync_include_options EXCLUDE "src/core/style/\\.+") 90 | endif() 91 | 92 | qwk_add_library(${PROJECT_NAME} AUTOGEN 93 | SOURCES ${_src} 94 | FEATURES cxx_std_17 95 | LINKS_PRIVATE ${_links_private} 96 | QT_LINKS Core Gui 97 | QT_INCLUDE_PRIVATE Core Gui 98 | INCLUDE_PRIVATE * 99 | PREFIX QWK_CORE 100 | SYNC_INCLUDE_OPTIONS ${_sync_include_options} 101 | ) 102 | 103 | set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE) 104 | set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} core PARENT_SCOPE) -------------------------------------------------------------------------------- /src/core/contexts/abstractwindowcontext_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef ABSTRACTWINDOWCONTEXT_P_H 6 | #define ABSTRACTWINDOWCONTEXT_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace QWK { 34 | 35 | class QWK_CORE_EXPORT AbstractWindowContext : public QObject, 36 | public NativeEventDispatcher, 37 | public SharedEventDispatcher { 38 | Q_OBJECT 39 | public: 40 | AbstractWindowContext(); 41 | ~AbstractWindowContext() override; 42 | 43 | public: 44 | void setup(QObject *host, WindowItemDelegate *delegate); 45 | 46 | inline QObject *host() const; 47 | inline QWindow *window() const; 48 | inline WId windowId() const; 49 | inline WindowItemDelegate *delegate() const; 50 | 51 | inline bool isHitTestVisible(const QObject *obj) const; 52 | bool setHitTestVisible(QObject *obj, bool visible); 53 | 54 | inline QObject *systemButton(WindowAgentBase::SystemButton button) const; 55 | bool setSystemButton(WindowAgentBase::SystemButton button, QObject *obj); 56 | 57 | inline QObject *titleBar() const; 58 | bool setTitleBar(QObject *obj); 59 | 60 | #ifdef Q_OS_MAC 61 | inline ScreenRectCallback systemButtonAreaCallback() const; 62 | void setSystemButtonAreaCallback(const ScreenRectCallback &callback); 63 | #endif 64 | 65 | bool isInSystemButtons(const QPoint &pos, WindowAgentBase::SystemButton *button) const; 66 | bool isInTitleBarDraggableArea(const QPoint &pos) const; 67 | 68 | inline bool isHostWidthFixed() const { 69 | return m_windowHandle 70 | ? ((m_windowHandle->flags() & Qt::MSWindowsFixedSizeDialogHint) || 71 | m_windowHandle->minimumWidth() == m_windowHandle->maximumWidth()) 72 | : false; 73 | } 74 | inline bool isHostHeightFixed() const { 75 | return m_windowHandle 76 | ? ((m_windowHandle->flags() & Qt::MSWindowsFixedSizeDialogHint) || 77 | m_windowHandle->minimumHeight() == m_windowHandle->maximumHeight()) 78 | : false; 79 | } 80 | inline bool isHostSizeFixed() const { 81 | return m_windowHandle ? ((m_windowHandle->flags() & Qt::MSWindowsFixedSizeDialogHint) || 82 | m_windowHandle->minimumSize() == m_windowHandle->maximumSize()) 83 | : false; 84 | } 85 | 86 | virtual QString key() const; 87 | 88 | enum WindowContextHook { 89 | CentralizeHook = 1, 90 | RaiseWindowHook, 91 | ShowSystemMenuHook, 92 | DefaultColorsHook, 93 | DrawWindows10BorderHook_Emulated, // Only works on Windows 10, emulated workaround 94 | DrawWindows10BorderHook_Native, // Only works on Windows 10, native workaround 95 | SystemButtonAreaChangedHook, // Only works on Mac 96 | }; 97 | virtual void virtual_hook(int id, void *data); 98 | 99 | void showSystemMenu(const QPoint &pos); 100 | void notifyWinIdChange(); 101 | 102 | virtual QVariant windowAttribute(const QString &key) const; 103 | virtual bool setWindowAttribute(const QString &key, const QVariant &attribute); 104 | 105 | protected: 106 | bool eventFilter(QObject *obj, QEvent *event) override; 107 | 108 | protected: 109 | virtual void winIdChanged(WId winId, WId oldWinId) = 0; 110 | virtual bool windowAttributeChanged(const QString &key, const QVariant &attribute, 111 | const QVariant &oldAttribute); 112 | 113 | protected: 114 | QObject *m_host{}; 115 | std::unique_ptr m_delegate; 116 | QPointer m_windowHandle; 117 | WId m_windowId{}; 118 | 119 | QVector> m_hitTestVisibleItems; 120 | #ifdef Q_OS_MAC 121 | ScreenRectCallback m_systemButtonAreaCallback; 122 | #endif 123 | 124 | QPointer m_titleBar{}; 125 | std::array, WindowAgentBase::Close + 1> m_systemButtons{}; 126 | 127 | std::list> m_windowAttributesOrder; 128 | QHash m_windowAttributes; 129 | 130 | std::unique_ptr m_winIdChangeEventFilter; 131 | 132 | void removeSystemButtonsAndHitTestItems(); 133 | }; 134 | 135 | inline QObject *AbstractWindowContext::host() const { 136 | return m_host; 137 | } 138 | 139 | inline QWindow *AbstractWindowContext::window() const { 140 | return m_windowHandle; 141 | } 142 | 143 | inline WId AbstractWindowContext::windowId() const { 144 | return m_windowId; 145 | } 146 | 147 | inline WindowItemDelegate *AbstractWindowContext::delegate() const { 148 | return m_delegate.get(); 149 | } 150 | 151 | inline bool AbstractWindowContext::isHitTestVisible(const QObject *obj) const { 152 | return m_hitTestVisibleItems.contains(const_cast(obj)); 153 | } 154 | 155 | inline QObject * 156 | AbstractWindowContext::systemButton(WindowAgentBase::SystemButton button) const { 157 | return m_systemButtons[button]; 158 | } 159 | 160 | inline QObject *AbstractWindowContext::titleBar() const { 161 | return m_titleBar; 162 | } 163 | 164 | #ifdef Q_OS_MAC 165 | inline ScreenRectCallback AbstractWindowContext::systemButtonAreaCallback() const { 166 | return m_systemButtonAreaCallback; 167 | } 168 | #endif 169 | 170 | } 171 | 172 | #endif // ABSTRACTWINDOWCONTEXT_P_H 173 | -------------------------------------------------------------------------------- /src/core/contexts/cocoawindowcontext_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef COCOAWINDOWCONTEXT_P_H 6 | #define COCOAWINDOWCONTEXT_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | 19 | namespace QWK { 20 | 21 | class CocoaWindowContext : public AbstractWindowContext { 22 | Q_OBJECT 23 | public: 24 | CocoaWindowContext(); 25 | ~CocoaWindowContext() override; 26 | 27 | QString key() const override; 28 | void virtual_hook(int id, void *data) override; 29 | 30 | QVariant windowAttribute(const QString &key) const override; 31 | 32 | protected: 33 | void winIdChanged(WId winId, WId oldWinId) override; 34 | bool windowAttributeChanged(const QString &key, const QVariant &attribute, 35 | const QVariant &oldAttribute) override; 36 | 37 | protected: 38 | std::unique_ptr cocoaWindowEventFilter; 39 | }; 40 | 41 | } 42 | 43 | #endif // COCOAWINDOWCONTEXT_P_H 44 | -------------------------------------------------------------------------------- /src/core/contexts/qtwindowcontext_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QTWINDOWCONTEXT_P_H 6 | #define QTWINDOWCONTEXT_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | 19 | namespace QWK { 20 | 21 | class QtWindowContext : public AbstractWindowContext { 22 | Q_OBJECT 23 | public: 24 | QtWindowContext(); 25 | ~QtWindowContext() override; 26 | 27 | QString key() const override; 28 | void virtual_hook(int id, void *data) override; 29 | 30 | protected: 31 | void winIdChanged(WId winId, WId oldWinId) override; 32 | 33 | protected: 34 | std::unique_ptr qtWindowEventFilter; 35 | }; 36 | 37 | } 38 | 39 | #endif // QTWINDOWCONTEXT_P_H 40 | -------------------------------------------------------------------------------- /src/core/contexts/win32windowcontext_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WIN32WINDOWCONTEXT_P_H 6 | #define WIN32WINDOWCONTEXT_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | 20 | namespace QWK { 21 | 22 | class Win32WindowContext : public AbstractWindowContext { 23 | Q_OBJECT 24 | public: 25 | Win32WindowContext(); 26 | ~Win32WindowContext() override; 27 | 28 | enum WindowPart { 29 | Outside, 30 | ClientArea, 31 | ChromeButton, 32 | ResizeBorder, 33 | FixedBorder, 34 | TitleBar, 35 | }; 36 | Q_ENUM(WindowPart) 37 | 38 | QString key() const override; 39 | void virtual_hook(int id, void *data) override; 40 | 41 | QVariant windowAttribute(const QString &key) const override; 42 | 43 | protected: 44 | void winIdChanged(WId winId, WId oldWinId) override; 45 | bool windowAttributeChanged(const QString &key, const QVariant &attribute, 46 | const QVariant &oldAttribute) override; 47 | 48 | public: 49 | bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); 50 | 51 | bool systemMenuHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, 52 | LRESULT *result); 53 | 54 | // In order to perfectly apply Windows 11 Snap Layout into the Qt window, we need to 55 | // intercept and emulate most of the mouse events, so that the processing logic 56 | // is quite complex. Simultaneously, in order to make the handling code of other 57 | // Windows messages clearer, we have separated them into this function. 58 | bool snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, 59 | LRESULT *result); 60 | 61 | bool customWindowHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, 62 | LRESULT *result); 63 | 64 | bool nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, 65 | LRESULT *result); 66 | 67 | protected: 68 | // The last hit test result, helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. 69 | WindowPart lastHitTestResult = WindowPart::Outside; 70 | int lastHitTestResultRaw = HTNOWHERE; 71 | 72 | // Whether the last mouse leave message is blocked, mainly for handling the unexpected 73 | // WM_MOUSELEAVE. 74 | bool mouseLeaveBlocked = false; 75 | 76 | // For emulating traditional icon button behavior 77 | uint64_t iconButtonClickTime = 0; 78 | int iconButtonClickLevel = 0; 79 | }; 80 | 81 | } 82 | 83 | #endif // WIN32WINDOWCONTEXT_P_H 84 | -------------------------------------------------------------------------------- /src/core/kernel/nativeeventfilter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "nativeeventfilter_p.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace QWK { 11 | 12 | NativeEventFilter::NativeEventFilter() : m_nativeDispatcher(nullptr) { 13 | } 14 | 15 | NativeEventFilter::~NativeEventFilter() { 16 | if (m_nativeDispatcher) 17 | m_nativeDispatcher->removeNativeEventFilter(this); 18 | } 19 | 20 | NativeEventDispatcher::NativeEventDispatcher() = default; 21 | 22 | NativeEventDispatcher::~NativeEventDispatcher() { 23 | for (const auto &observer : std::as_const(m_nativeEventFilters)) { 24 | observer->m_nativeDispatcher = nullptr; 25 | } 26 | } 27 | 28 | bool NativeEventDispatcher::nativeDispatch(const QByteArray &eventType, void *message, 29 | QT_NATIVE_EVENT_RESULT_TYPE *result) { 30 | for (const auto &ef : std::as_const(m_nativeEventFilters)) { 31 | if (ef->nativeEventFilter(eventType, message, result)) 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | void NativeEventDispatcher::installNativeEventFilter(NativeEventFilter *filter) { 38 | if (!filter || filter->m_nativeDispatcher) 39 | return; 40 | 41 | m_nativeEventFilters.append(filter); 42 | filter->m_nativeDispatcher = this; 43 | } 44 | 45 | void NativeEventDispatcher::removeNativeEventFilter(NativeEventFilter *filter) { 46 | if (!m_nativeEventFilters.removeOne(filter)) { 47 | return; 48 | } 49 | filter->m_nativeDispatcher = nullptr; 50 | } 51 | 52 | 53 | // Avoid adding multiple global native event filters to QGuiApplication 54 | // in this library. 55 | class AppMasterNativeEventFilter : public QAbstractNativeEventFilter, 56 | public NativeEventDispatcher { 57 | public: 58 | AppMasterNativeEventFilter() { 59 | qApp->installNativeEventFilter(this); 60 | } 61 | 62 | // The base class removes automatically 63 | ~AppMasterNativeEventFilter() override = default; 64 | 65 | bool nativeEventFilter(const QByteArray &eventType, void *message, 66 | QT_NATIVE_EVENT_RESULT_TYPE *result) override { 67 | return nativeDispatch(eventType, message, result); 68 | } 69 | 70 | static inline AppMasterNativeEventFilter *instance = nullptr; 71 | 72 | friend class AppNativeEventFilter; 73 | }; 74 | 75 | AppNativeEventFilter::AppNativeEventFilter() { 76 | if (!AppMasterNativeEventFilter::instance) { 77 | AppMasterNativeEventFilter::instance = new AppMasterNativeEventFilter(); 78 | } 79 | AppMasterNativeEventFilter::instance->installNativeEventFilter(this); 80 | } 81 | 82 | AppNativeEventFilter::~AppNativeEventFilter() { 83 | AppMasterNativeEventFilter::instance->removeNativeEventFilter(this); 84 | if (AppMasterNativeEventFilter::instance->m_nativeEventFilters.isEmpty()) { 85 | delete AppMasterNativeEventFilter::instance; 86 | AppMasterNativeEventFilter::instance = nullptr; 87 | } 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/core/kernel/nativeeventfilter_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef NATIVEEVENTFILTER_P_H 6 | #define NATIVEEVENTFILTER_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | 19 | namespace QWK { 20 | 21 | class NativeEventFilter; 22 | 23 | class QWK_CORE_EXPORT NativeEventDispatcher { 24 | public: 25 | NativeEventDispatcher(); 26 | virtual ~NativeEventDispatcher(); 27 | 28 | public: 29 | virtual bool nativeDispatch(const QByteArray &eventType, void *message, 30 | QT_NATIVE_EVENT_RESULT_TYPE *result); 31 | 32 | public: 33 | void installNativeEventFilter(NativeEventFilter *filter); 34 | void removeNativeEventFilter(NativeEventFilter *filter); 35 | 36 | protected: 37 | QList m_nativeEventFilters; 38 | 39 | friend class NativeEventFilter; 40 | 41 | Q_DISABLE_COPY(NativeEventDispatcher) 42 | }; 43 | 44 | class QWK_CORE_EXPORT NativeEventFilter { 45 | public: 46 | NativeEventFilter(); 47 | virtual ~NativeEventFilter(); 48 | 49 | public: 50 | virtual bool nativeEventFilter(const QByteArray &eventType, void *message, 51 | QT_NATIVE_EVENT_RESULT_TYPE *result) = 0; 52 | 53 | protected: 54 | NativeEventDispatcher *m_nativeDispatcher; 55 | 56 | friend class NativeEventDispatcher; 57 | 58 | Q_DISABLE_COPY(NativeEventFilter) 59 | }; 60 | 61 | // Automatically install to QCoreApplication at construction 62 | class QWK_CORE_EXPORT AppNativeEventFilter : public NativeEventFilter { 63 | public: 64 | AppNativeEventFilter(); 65 | ~AppNativeEventFilter() override; 66 | }; 67 | 68 | } 69 | 70 | #endif // NATIVEEVENTFILTER_P_H 71 | -------------------------------------------------------------------------------- /src/core/kernel/sharedeventfilter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "sharedeventfilter_p.h" 6 | 7 | namespace QWK { 8 | 9 | SharedEventFilter::SharedEventFilter() : m_sharedDispatcher(nullptr) { 10 | } 11 | 12 | SharedEventFilter::~SharedEventFilter() { 13 | if (m_sharedDispatcher) 14 | m_sharedDispatcher->removeSharedEventFilter(this); 15 | } 16 | 17 | SharedEventDispatcher::SharedEventDispatcher() = default; 18 | 19 | SharedEventDispatcher::~SharedEventDispatcher() { 20 | for (const auto &observer : std::as_const(m_sharedEventFilters)) { 21 | observer->m_sharedDispatcher = nullptr; 22 | } 23 | } 24 | 25 | bool SharedEventDispatcher::sharedDispatch(QObject *obj, QEvent *event) { 26 | for (const auto &ef : std::as_const(m_sharedEventFilters)) { 27 | if (ef->sharedEventFilter(obj, event)) 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | void SharedEventDispatcher::installSharedEventFilter(SharedEventFilter *filter) { 34 | if (!filter || filter->m_sharedDispatcher) 35 | return; 36 | 37 | m_sharedEventFilters.append(filter); 38 | filter->m_sharedDispatcher = this; 39 | } 40 | 41 | void SharedEventDispatcher::removeSharedEventFilter(SharedEventFilter *filter) { 42 | if (!m_sharedEventFilters.removeOne(filter)) { 43 | return; 44 | } 45 | filter->m_sharedDispatcher = nullptr; 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/core/kernel/sharedeventfilter_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef SHAREDEVENTFILTER_P_H 6 | #define SHAREDEVENTFILTER_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | 19 | namespace QWK { 20 | 21 | class SharedEventFilter; 22 | 23 | class QWK_CORE_EXPORT SharedEventDispatcher { 24 | public: 25 | SharedEventDispatcher(); 26 | virtual ~SharedEventDispatcher(); 27 | 28 | public: 29 | virtual bool sharedDispatch(QObject *obj, QEvent *event); 30 | 31 | public: 32 | void installSharedEventFilter(SharedEventFilter *filter); 33 | void removeSharedEventFilter(SharedEventFilter *filter); 34 | 35 | protected: 36 | QList m_sharedEventFilters; 37 | 38 | friend class SharedEventFilter; 39 | 40 | Q_DISABLE_COPY(SharedEventDispatcher) 41 | }; 42 | 43 | class QWK_CORE_EXPORT SharedEventFilter { 44 | public: 45 | SharedEventFilter(); 46 | virtual ~SharedEventFilter(); 47 | 48 | public: 49 | virtual bool sharedEventFilter(QObject *obj, QEvent *event) = 0; 50 | 51 | protected: 52 | SharedEventDispatcher *m_sharedDispatcher; 53 | 54 | friend class SharedEventDispatcher; 55 | 56 | Q_DISABLE_COPY(SharedEventFilter) 57 | }; 58 | 59 | } 60 | 61 | #endif // SHAREDEVENTFILTER_P_H 62 | -------------------------------------------------------------------------------- /src/core/kernel/winidchangeeventfilter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "winidchangeeventfilter_p.h" 6 | 7 | #include 8 | 9 | #include "abstractwindowcontext_p.h" 10 | 11 | namespace QWK { 12 | 13 | WindowWinIdChangeEventFilter::WindowWinIdChangeEventFilter(QWindow *host, 14 | AbstractWindowContext *context) 15 | : WinIdChangeEventFilter(host, context), win(host), isAboutToBeDestroyed(false) { 16 | host->installEventFilter(this); 17 | } 18 | 19 | WId WindowWinIdChangeEventFilter::winId() const { 20 | auto win = static_cast(host); 21 | if (isAboutToBeDestroyed) 22 | return 0; 23 | if (auto platformWindow = win->handle()) 24 | return platformWindow->winId(); 25 | return 0; 26 | } 27 | 28 | bool WindowWinIdChangeEventFilter::eventFilter(QObject *obj, QEvent *event) { 29 | Q_UNUSED(obj) 30 | if (event->type() == QEvent::PlatformSurface) { 31 | auto e = static_cast(event); 32 | if (e->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { 33 | isAboutToBeDestroyed = true; 34 | context->notifyWinIdChange(); 35 | isAboutToBeDestroyed = false; 36 | } else { 37 | context->notifyWinIdChange(); 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/core/kernel/winidchangeeventfilter_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINIDCHANGEEVENTFILTER_P_H 6 | #define WINIDCHANGEEVENTFILTER_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace QWK { 22 | 23 | class AbstractWindowContext; 24 | 25 | class WinIdChangeEventFilter : public QObject { 26 | public: 27 | WinIdChangeEventFilter(QObject *host, AbstractWindowContext *context) 28 | : host(host), context(context) { 29 | } 30 | 31 | virtual WId winId() const = 0; 32 | 33 | protected: 34 | QObject *host; 35 | AbstractWindowContext *context; 36 | }; 37 | 38 | class QWK_CORE_EXPORT WindowWinIdChangeEventFilter : public WinIdChangeEventFilter { 39 | public: 40 | WindowWinIdChangeEventFilter(QWindow *host, AbstractWindowContext *context); 41 | 42 | WId winId() const override; 43 | 44 | protected: 45 | QWindow *win; 46 | bool isAboutToBeDestroyed; 47 | 48 | bool eventFilter(QObject *obj, QEvent *event) override; 49 | }; 50 | 51 | } 52 | 53 | #endif // WINIDCHANGEEVENTFILTER_P_H 54 | -------------------------------------------------------------------------------- /src/core/qwindowkit_linux.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QWINDOWKIT_LINUX_H 6 | #define QWINDOWKIT_LINUX_H 7 | 8 | 9 | #endif // QWINDOWKIT_LINUX_H 10 | -------------------------------------------------------------------------------- /src/core/qwindowkit_windows.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "qwindowkit_windows.h" 6 | 7 | namespace QWK { 8 | 9 | static RTL_OSVERSIONINFOW GetRealOSVersionImpl() { 10 | HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); 11 | Q_ASSERT(hMod); 12 | using RtlGetVersionPtr = NTSTATUS(WINAPI *)(PRTL_OSVERSIONINFOW); 13 | auto pRtlGetVersion = 14 | reinterpret_cast(::GetProcAddress(hMod, "RtlGetVersion")); 15 | Q_ASSERT(pRtlGetVersion); 16 | RTL_OSVERSIONINFOW rovi{}; 17 | rovi.dwOSVersionInfoSize = sizeof(rovi); 18 | pRtlGetVersion(&rovi); 19 | return rovi; 20 | } 21 | 22 | namespace Private { 23 | 24 | RTL_OSVERSIONINFOW GetRealOSVersion() { 25 | static const auto result = GetRealOSVersionImpl(); 26 | return result; 27 | } 28 | 29 | } 30 | 31 | #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 32 | WindowsRegistryKey::WindowsRegistryKey(HKEY parentHandle, QStringView subKey, 33 | REGSAM permissions, REGSAM access) { 34 | if (::RegOpenKeyExW(parentHandle, reinterpret_cast(subKey.utf16()), 0, 35 | permissions | access, &m_key) != ERROR_SUCCESS) { 36 | m_key = nullptr; 37 | } 38 | } 39 | 40 | WindowsRegistryKey::~WindowsRegistryKey() { 41 | close(); 42 | } 43 | 44 | void WindowsRegistryKey::close() { 45 | if (isValid()) { 46 | ::RegCloseKey(m_key); 47 | m_key = nullptr; 48 | } 49 | } 50 | 51 | QString WindowsRegistryKey::stringValue(QStringView subKey) const { 52 | QString result; 53 | if (!isValid()) 54 | return result; 55 | DWORD type; 56 | DWORD size; 57 | auto subKeyC = reinterpret_cast(subKey.utf16()); 58 | if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, nullptr, &size) != ERROR_SUCCESS || 59 | (type != REG_SZ && type != REG_EXPAND_SZ) || size <= 2) { 60 | return result; 61 | } 62 | // Reserve more for rare cases where trailing '\0' are missing in registry. 63 | // Rely on 0-termination since strings of size 256 padded with 0 have been 64 | // observed (QTBUG-84455). 65 | size += 2; 66 | QVarLengthArray buffer(static_cast(size)); 67 | std::fill(buffer.data(), buffer.data() + size, 0u); 68 | if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, buffer.data(), &size) == 69 | ERROR_SUCCESS) 70 | result = QString::fromWCharArray(reinterpret_cast(buffer.constData())); 71 | return result; 72 | } 73 | 74 | std::pair WindowsRegistryKey::dwordValue(QStringView subKey) const { 75 | if (!isValid()) 76 | return std::make_pair(0, false); 77 | DWORD type; 78 | auto subKeyC = reinterpret_cast(subKey.utf16()); 79 | if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS || 80 | type != REG_DWORD) { 81 | return std::make_pair(0, false); 82 | } 83 | DWORD value = 0; 84 | DWORD size = sizeof(value); 85 | const bool ok = 86 | ::RegQueryValueExW(m_key, subKeyC, nullptr, nullptr, 87 | reinterpret_cast(&value), &size) == ERROR_SUCCESS; 88 | return std::make_pair(value, ok); 89 | } 90 | #endif 91 | } -------------------------------------------------------------------------------- /src/core/qwindowkit_windows.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QWINDOWKIT_WINDOWS_H 6 | #define QWINDOWKIT_WINDOWS_H 7 | 8 | #ifndef _USER32_ 9 | # define _USER32_ 10 | #endif 11 | 12 | #ifndef _DWMAPI_ 13 | # define _DWMAPI_ 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 20 | # include 21 | #endif 22 | 23 | #include 24 | 25 | #ifndef GET_X_LPARAM 26 | # define GET_X_LPARAM(lp) (static_cast(static_cast(LOWORD(lp)))) 27 | #endif 28 | 29 | #ifndef GET_Y_LPARAM 30 | # define GET_Y_LPARAM(lp) (static_cast(static_cast(HIWORD(lp)))) 31 | #endif 32 | 33 | #ifndef RECT_WIDTH 34 | # define RECT_WIDTH(rect) ((rect).right - (rect).left) 35 | #endif 36 | 37 | #ifndef RECT_HEIGHT 38 | # define RECT_HEIGHT(rect) ((rect).bottom - (rect).top) 39 | #endif 40 | 41 | #ifndef USER_DEFAULT_SCREEN_DPI 42 | # define USER_DEFAULT_SCREEN_DPI (96) 43 | #endif 44 | 45 | // Maybe undocumented Windows messages 46 | // https://github.com/tinysec/public/blob/master/win32k/MessageTable.md 47 | // https://ulib.sourceforge.io/doxy/a00239.html 48 | #ifndef WM_UAHDESTROYWINDOW 49 | # define WM_UAHDESTROYWINDOW (0x0090) 50 | #endif 51 | 52 | #ifndef WM_UNREGISTER_WINDOW_SERVICES 53 | # define WM_UNREGISTER_WINDOW_SERVICES (0x0272) 54 | #endif 55 | 56 | #ifndef WM_NCUAHDRAWCAPTION 57 | # define WM_NCUAHDRAWCAPTION (0x00AE) 58 | #endif 59 | 60 | #ifndef WM_NCUAHDRAWFRAME 61 | # define WM_NCUAHDRAWFRAME (0x00AF) 62 | #endif 63 | 64 | namespace QWK { 65 | 66 | namespace Private { 67 | 68 | QWK_CORE_EXPORT RTL_OSVERSIONINFOW GetRealOSVersion(); 69 | 70 | inline bool IsWindows1122H2OrGreater_Real() { 71 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 72 | return (rovi.dwMajorVersion > 10) || 73 | (rovi.dwMajorVersion == 10 && 74 | (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 22621)); 75 | } 76 | 77 | inline bool IsWindows11OrGreater_Real() { 78 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 79 | return (rovi.dwMajorVersion > 10) || 80 | (rovi.dwMajorVersion == 10 && 81 | (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 22000)); 82 | } 83 | 84 | inline bool IsWindows1020H1OrGreater_Real() { 85 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 86 | return (rovi.dwMajorVersion > 10) || 87 | (rovi.dwMajorVersion == 10 && 88 | (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 19041)); 89 | } 90 | 91 | inline bool IsWindows102004OrGreater_Real() { 92 | return IsWindows1020H1OrGreater_Real(); 93 | } 94 | 95 | inline bool IsWindows101903OrGreater_Real() { 96 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 97 | return (rovi.dwMajorVersion > 10) || 98 | (rovi.dwMajorVersion == 10 && 99 | (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 18362)); 100 | } 101 | 102 | inline bool IsWindows1019H1OrGreater_Real() { 103 | return IsWindows101903OrGreater_Real(); 104 | } 105 | 106 | inline bool IsWindows101809OrGreater_Real() { 107 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 108 | return (rovi.dwMajorVersion > 10) || 109 | (rovi.dwMajorVersion == 10 && 110 | (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 17763)); 111 | } 112 | 113 | inline bool IsWindows10RS5OrGreater_Real() { 114 | return IsWindows101809OrGreater_Real(); 115 | } 116 | 117 | inline bool IsWindows10OrGreater_Real() { 118 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 119 | return rovi.dwMajorVersion >= 10; 120 | } 121 | 122 | inline bool IsWindows8Point1OrGreater_Real() { 123 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 124 | return (rovi.dwMajorVersion > 6) || 125 | (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 3); 126 | } 127 | 128 | inline bool IsWindows8OrGreater_Real() { 129 | RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); 130 | return (rovi.dwMajorVersion > 6) || 131 | (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 2); 132 | } 133 | 134 | inline bool IsWindows10Only_Real() { 135 | return IsWindows10OrGreater_Real() && !IsWindows11OrGreater_Real(); 136 | } 137 | 138 | } 139 | 140 | // 141 | // Registry Helpers 142 | // 143 | 144 | #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 145 | class QWK_CORE_EXPORT WindowsRegistryKey { 146 | public: 147 | WindowsRegistryKey(HKEY parentHandle, QStringView subKey, REGSAM permissions = KEY_READ, 148 | REGSAM access = 0); 149 | 150 | ~WindowsRegistryKey(); 151 | 152 | bool isValid() const; 153 | 154 | void close(); 155 | QString stringValue(QStringView subKey) const; 156 | std::pair dwordValue(QStringView subKey) const; 157 | 158 | private: 159 | HKEY m_key; 160 | 161 | Q_DISABLE_COPY(WindowsRegistryKey) 162 | }; 163 | 164 | inline bool WindowsRegistryKey::isValid() const { 165 | return m_key != nullptr; 166 | } 167 | #elif QT_VERSION < QT_VERSION_CHECK(6, 8, 1) 168 | using WindowsRegistryKey = QWinRegistryKey; 169 | #else 170 | class WindowsRegistryKey : public QWinRegistryKey { 171 | public: 172 | WindowsRegistryKey(HKEY parentHandle, QStringView subKey, REGSAM permissions = KEY_READ, 173 | REGSAM access = 0) 174 | : QWinRegistryKey(parentHandle, subKey, permissions, access) { 175 | } 176 | 177 | inline std::pair dwordValue(QStringView subKey) const; 178 | }; 179 | 180 | inline std::pair WindowsRegistryKey::dwordValue(QStringView subKey) const { 181 | const auto val = value(subKey); 182 | if (!val) { 183 | return {0, false}; 184 | } 185 | return {val.value(), true}; 186 | } 187 | #endif 188 | 189 | // 190 | // Version Helpers 191 | // 192 | 193 | inline bool isWin8OrGreater() { 194 | static const bool result = Private::IsWindows8OrGreater_Real(); 195 | return result; 196 | } 197 | 198 | inline bool isWin8Point1OrGreater() { 199 | static const bool result = Private::IsWindows8Point1OrGreater_Real(); 200 | return result; 201 | } 202 | 203 | inline bool isWin10OrGreater() { 204 | static const bool result = Private::IsWindows10OrGreater_Real(); 205 | return result; 206 | } 207 | 208 | inline bool isWin101809OrGreater() { 209 | static const bool result = Private::IsWindows101809OrGreater_Real(); 210 | return result; 211 | } 212 | 213 | inline bool isWin10RS5OrGreater() { 214 | return isWin101809OrGreater(); 215 | } 216 | 217 | inline bool isWin101903OrGreater() { 218 | static const bool result = Private::IsWindows101903OrGreater_Real(); 219 | return result; 220 | } 221 | 222 | inline bool isWin1019H1OrGreater() { 223 | return isWin101903OrGreater(); 224 | } 225 | 226 | inline bool isWin1020H1OrGreater() { 227 | static const bool result = Private::IsWindows1020H1OrGreater_Real(); 228 | return result; 229 | } 230 | 231 | inline bool isWin102004OrGreater() { 232 | return isWin1020H1OrGreater(); 233 | } 234 | 235 | inline bool isWin11OrGreater() { 236 | static const bool result = Private::IsWindows11OrGreater_Real(); 237 | return result; 238 | } 239 | 240 | inline bool isWin1122H2OrGreater() { 241 | static const bool result = Private::IsWindows1122H2OrGreater_Real(); 242 | return result; 243 | } 244 | 245 | inline bool isWin10Only() { 246 | static const bool result = Private::IsWindows10Only_Real(); 247 | return result; 248 | }; 249 | 250 | // 251 | // Native Event Helpers 252 | // 253 | 254 | inline bool isImmersiveColorSetChange(WPARAM wParam, LPARAM lParam) { 255 | return !wParam && lParam && 256 | std::wcscmp(reinterpret_cast(lParam), L"ImmersiveColorSet") == 0; 257 | } 258 | 259 | } 260 | 261 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 262 | Q_DECLARE_METATYPE(QMargins) 263 | #endif 264 | 265 | #endif // QWINDOWKIT_WINDOWS_H 266 | -------------------------------------------------------------------------------- /src/core/qwkglobal.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "qwkglobal_p.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace QWK { 12 | 13 | bool forwardObjectEventFilters(QObject *currentFilter, QObject *receiver, QEvent *event) { 14 | // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/corelib/kernel/qcoreapplication.cpp#L1244 15 | // Send the event through the rest event filters 16 | auto d = QObjectPrivate::get(receiver); 17 | bool findCurrent = false; 18 | if (receiver != QCoreApplication::instance() && d->extraData) { 19 | for (qsizetype i = 0; i < d->extraData->eventFilters.size(); ++i) { 20 | QObject *obj = d->extraData->eventFilters.at(i); 21 | if (!findCurrent) { 22 | if (obj == currentFilter) { 23 | findCurrent = true; // Will start to filter from the next one 24 | } 25 | continue; 26 | } 27 | 28 | if (!obj) 29 | continue; 30 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) 31 | if (QObjectPrivate::get(obj)->threadData.loadRelaxed() != 32 | d->threadData.loadRelaxed()) { 33 | #else 34 | if (QObjectPrivate::get(obj)->threadData != d->threadData) { 35 | #endif 36 | qWarning("QCoreApplication: Object event filter cannot be in a different " 37 | "thread."); 38 | continue; 39 | } 40 | if (obj->eventFilter(receiver, event)) 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/core/qwkglobal.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QWKGLOBAL_H 6 | #define QWKGLOBAL_H 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #ifndef QWK_CORE_EXPORT 14 | # ifdef QWK_CORE_STATIC 15 | # define QWK_CORE_EXPORT 16 | # else 17 | # ifdef QWK_CORE_LIBRARY 18 | # define QWK_CORE_EXPORT Q_DECL_EXPORT 19 | # else 20 | # define QWK_CORE_EXPORT Q_DECL_IMPORT 21 | # endif 22 | # endif 23 | #endif 24 | 25 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 26 | using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; 27 | using QT_ENTER_EVENT_TYPE = QEnterEvent; 28 | #else 29 | using QT_NATIVE_EVENT_RESULT_TYPE = long; 30 | using QT_ENTER_EVENT_TYPE = QEvent; 31 | #endif 32 | 33 | #ifndef QWINDOWKIT_CONFIG 34 | # define QWINDOWKIT_CONFIG(feature) ((1 / QWINDOWKIT_##feature) == 1) 35 | #endif 36 | 37 | #if defined(__GNUC__) || defined(__clang__) 38 | # define QWINDOWKIT_PRINTF_FORMAT(fmtpos, attrpos) \ 39 | __attribute__((__format__(__printf__, fmtpos, attrpos))) 40 | #else 41 | # define QWINDOWKIT_PRINTF_FORMAT(fmtpos, attrpos) 42 | #endif 43 | 44 | namespace QWK { 45 | 46 | using ScreenRectCallback = std::function; 47 | 48 | } 49 | 50 | #endif // QWKGLOBAL_H 51 | -------------------------------------------------------------------------------- /src/core/qwkglobal_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QWKGLOBAL_P_H 6 | #define QWKGLOBAL_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | QWK_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(qWindowKitLog) 24 | 25 | #define QWK_INFO qCInfo(qWindowKitLog) 26 | #define QWK_DEBUG qCDebug(qWindowKitLog) 27 | #define QWK_WARNING qCWarning(qWindowKitLog) 28 | #define QWK_CRITICAL qCCritical(qWindowKitLog) 29 | #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) 30 | # define QWK_FATAL qCFatal(qWindowKitLog) 31 | #endif 32 | 33 | #define MAKE_RGB_COLOR(r, g, b) ((quint32) (((r) &0xFF) << 16) | (((g) &0xFF) << 8) | ((b) &0xFF)) 34 | 35 | #define MAKE_RGBA_COLOR(r, g, b, a) \ 36 | ((quint32) (((a) &0xFF) << 24) | (((r) &0xFF) << 16) | (((g) &0xFF) << 8) | ((b) &0xFF)) 37 | 38 | #if defined(Q_CC_MSVC) 39 | # define QWK_NOINLINE __declspec(noinline) 40 | # define QWK_INLINE __forceinline 41 | # define QWK_USED 42 | #else 43 | # define QWK_NOINLINE __attribute__((noinline)) 44 | # define QWK_INLINE __attribute__((always_inline)) 45 | # define QWK_USED __attribute__((used)) 46 | #endif 47 | 48 | namespace QWK { 49 | 50 | inline QPoint getMouseEventScenePos(const QMouseEvent *event) { 51 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 52 | return event->scenePosition().toPoint(); 53 | #else 54 | return event->windowPos().toPoint(); 55 | #endif 56 | } 57 | 58 | inline QPoint getMouseEventGlobalPos(const QMouseEvent *event) { 59 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 60 | return event->globalPosition().toPoint(); 61 | #else 62 | return event->screenPos().toPoint(); 63 | #endif 64 | } 65 | 66 | // Be careful when apply this function to a widget 67 | QWK_CORE_EXPORT bool forwardObjectEventFilters(QObject *currentFilter, QObject *receiver, 68 | QEvent *event); 69 | } 70 | 71 | #endif // QWKGLOBAL_P_H 72 | -------------------------------------------------------------------------------- /src/core/shared/systemwindow_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef SYSTEMWINDOW_P_H 6 | #define SYSTEMWINDOW_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | namespace QWK { 23 | 24 | class WindowMoveManipulator : public QObject { 25 | public: 26 | explicit WindowMoveManipulator(QWindow *targetWindow) 27 | : QObject(targetWindow), target(targetWindow), initialMousePosition(QCursor::pos()), 28 | initialWindowPosition(targetWindow->position()) { 29 | target->installEventFilter(this); 30 | } 31 | 32 | protected: 33 | bool eventFilter(QObject *obj, QEvent *event) override { 34 | switch (event->type()) { 35 | case QEvent::MouseMove: { 36 | auto mouseEvent = static_cast(event); 37 | QPoint delta = getMouseEventGlobalPos(mouseEvent) - initialMousePosition; 38 | target->setPosition(initialWindowPosition + delta); 39 | return true; 40 | } 41 | 42 | case QEvent::MouseButtonRelease: { 43 | if (target->y() < 0) { 44 | target->setPosition(target->x(), 0); 45 | } 46 | target->removeEventFilter(this); 47 | deleteLater(); 48 | } 49 | 50 | default: 51 | break; 52 | } 53 | return false; 54 | } 55 | 56 | private: 57 | QWindow *target; 58 | QPoint initialMousePosition; 59 | QPoint initialWindowPosition; 60 | }; 61 | 62 | class WindowResizeManipulator : public QObject { 63 | public: 64 | WindowResizeManipulator(QWindow *targetWindow, Qt::Edges edges) 65 | : QObject(targetWindow), target(targetWindow), resizeEdges(edges), 66 | initialMousePosition(QCursor::pos()), initialWindowRect(target->geometry()) { 67 | target->installEventFilter(this); 68 | } 69 | 70 | protected: 71 | bool eventFilter(QObject *obj, QEvent *event) override { 72 | switch (event->type()) { 73 | case QEvent::MouseMove: { 74 | auto mouseEvent = static_cast(event); 75 | QPoint globalMousePos = getMouseEventGlobalPos(mouseEvent); 76 | QRect windowRect = initialWindowRect; 77 | 78 | if (resizeEdges & Qt::LeftEdge) { 79 | int delta = globalMousePos.x() - initialMousePosition.x(); 80 | windowRect.setLeft(initialWindowRect.left() + delta); 81 | } 82 | if (resizeEdges & Qt::RightEdge) { 83 | int delta = globalMousePos.x() - initialMousePosition.x(); 84 | windowRect.setRight(initialWindowRect.right() + delta); 85 | } 86 | if (resizeEdges & Qt::TopEdge) { 87 | int delta = globalMousePos.y() - initialMousePosition.y(); 88 | windowRect.setTop(initialWindowRect.top() + delta); 89 | } 90 | if (resizeEdges & Qt::BottomEdge) { 91 | int delta = globalMousePos.y() - initialMousePosition.y(); 92 | windowRect.setBottom(initialWindowRect.bottom() + delta); 93 | } 94 | 95 | target->setGeometry(windowRect); 96 | return true; 97 | } 98 | 99 | case QEvent::MouseButtonRelease: { 100 | target->removeEventFilter(this); 101 | deleteLater(); 102 | } 103 | 104 | default: 105 | break; 106 | } 107 | return false; 108 | } 109 | 110 | private: 111 | QWindow *target; 112 | QPoint initialMousePosition; 113 | QRect initialWindowRect; 114 | Qt::Edges resizeEdges; 115 | }; 116 | 117 | // QWindow::startSystemMove() and QWindow::startSystemResize() is first supported at Qt 5.15 118 | // QWindow::startSystemResize() returns false on macOS 119 | // QWindow::startSystemMove() and QWindow::startSystemResize() returns false on Linux Unity DE 120 | 121 | // When the new API fails, we emulate the window actions using the classical API. 122 | 123 | inline void startSystemMove(QWindow *window) { 124 | #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 125 | std::ignore = new WindowMoveManipulator(window); 126 | #elif defined(Q_OS_LINUX) 127 | if (window->startSystemMove()) { 128 | return; 129 | } 130 | std::ignore = new WindowMoveManipulator(window); 131 | #else 132 | window->startSystemMove(); 133 | #endif 134 | } 135 | 136 | inline void startSystemResize(QWindow *window, Qt::Edges edges) { 137 | #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 138 | std::ignore = new WindowResizeManipulator(window, edges); 139 | #elif defined(Q_OS_MAC) || defined(Q_OS_LINUX) 140 | if (window->startSystemResize(edges)) { 141 | return; 142 | } 143 | std::ignore = new WindowResizeManipulator(window, edges); 144 | #else 145 | window->startSystemResize(edges); 146 | #endif 147 | } 148 | 149 | } 150 | 151 | #endif // SYSTEMWINDOW_P_H 152 | -------------------------------------------------------------------------------- /src/core/shared/windows10borderhandler_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWS10BORDERHANDLER_P_H 6 | #define WINDOWS10BORDERHANDLER_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace QWK { 25 | 26 | class Windows10BorderHandler : public NativeEventFilter, public SharedEventFilter { 27 | public: 28 | inline Windows10BorderHandler(AbstractWindowContext *ctx) : ctx(ctx) { 29 | ctx->installNativeEventFilter(this); 30 | ctx->installSharedEventFilter(this); 31 | } 32 | 33 | inline void setupNecessaryAttributes() { 34 | if (!isWin11OrGreater()) { 35 | // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940 36 | // Must extend top frame to client area 37 | static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); 38 | ctx->setWindowAttribute(QStringLiteral("extra-margins"), defaultMargins); 39 | } 40 | 41 | // Enable dark mode by default, otherwise the system borders are white 42 | ctx->setWindowAttribute(QStringLiteral("dark-mode"), true); 43 | } 44 | 45 | inline bool isNormalWindow() const { 46 | return !(ctx->window()->windowStates() & 47 | (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)); 48 | } 49 | 50 | inline void drawBorderEmulated(QPainter *painter, const QRect &rect) { 51 | QRegion region(rect); 52 | void *args[] = { 53 | painter, 54 | const_cast(&rect), 55 | ®ion, 56 | }; 57 | ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Emulated, args); 58 | } 59 | 60 | inline void drawBorderNative() { 61 | ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Native, nullptr); 62 | } 63 | 64 | inline int borderThickness() const { 65 | return ctx->windowAttribute(QStringLiteral("border-thickness")).toInt(); 66 | } 67 | 68 | inline void updateExtraMargins(bool windowActive) { 69 | if (isWin11OrGreater()) { 70 | return; 71 | } 72 | 73 | // ### FIXME: transparent seam 74 | if (windowActive) { 75 | // Restore margins when the window is active 76 | static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); 77 | ctx->setWindowAttribute(QStringLiteral("extra-margins"), defaultMargins); 78 | return; 79 | } 80 | 81 | // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L904 82 | // When the window is inactive, there is a transparency bug in the top 83 | // border, and we need to extend the non-client area to the whole title 84 | // bar. 85 | QRect frame = ctx->windowAttribute(QStringLiteral("window-rect")).toRect(); 86 | QMargins margins{0, -frame.top(), 0, 0}; 87 | ctx->setWindowAttribute(QStringLiteral("extra-margins"), QVariant::fromValue(margins)); 88 | } 89 | 90 | virtual void updateGeometry() = 0; 91 | 92 | virtual bool isWindowActive() const { 93 | return ctx->window()->isActive(); 94 | } 95 | 96 | protected: 97 | bool nativeEventFilter(const QByteArray &eventType, void *message, 98 | QT_NATIVE_EVENT_RESULT_TYPE *result) override { 99 | Q_UNUSED(eventType) 100 | 101 | const auto msg = static_cast(message); 102 | switch (msg->message) { 103 | case WM_DPICHANGED: { 104 | updateGeometry(); 105 | updateExtraMargins(isWindowActive()); 106 | break; 107 | } 108 | 109 | case WM_ACTIVATE: { 110 | updateExtraMargins(LOWORD(msg->wParam) != WA_INACTIVE); 111 | break; 112 | } 113 | 114 | case WM_THEMECHANGED: 115 | case WM_SYSCOLORCHANGE: 116 | case WM_DWMCOLORIZATIONCOLORCHANGED: { 117 | // If we do not refresh this property, the native border will turn white 118 | // permanently (like the dark mode is turned off) after the user changes 119 | // the accent color in system personalization settings. 120 | // So we need this ugly hack to re-apply dark mode to get rid of this 121 | // strange Windows bug. 122 | if (ctx->windowAttribute(QStringLiteral("dark-mode")).toBool()) { 123 | ctx->setWindowAttribute(QStringLiteral("dark-mode"), true); 124 | } 125 | break; 126 | } 127 | 128 | default: 129 | break; 130 | } 131 | return false; 132 | } 133 | 134 | bool sharedEventFilter(QObject *obj, QEvent *event) override { 135 | Q_UNUSED(obj) 136 | 137 | if (event->type() == QEvent::WinIdChange) { 138 | if (ctx->windowId()) { 139 | setupNecessaryAttributes(); 140 | updateGeometry(); 141 | } 142 | } 143 | return false; 144 | } 145 | 146 | protected: 147 | AbstractWindowContext *ctx; 148 | }; 149 | 150 | } 151 | 152 | #endif // WINDOWS10BORDERHANDLER_P_H 153 | -------------------------------------------------------------------------------- /src/core/style/styleagent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "styleagent.h" 6 | #include "styleagent_p.h" 7 | 8 | #include 9 | 10 | namespace QWK { 11 | 12 | /*! 13 | \class StyleAgent 14 | \brief StyleAgent provides some features related to system theme. 15 | 16 | Qt6.6 started to support system theme detection, this class is intended as an auxiliary 17 | support for lower versions of Qt. If your Qt already supports it, it is recommended that 18 | you don't include this class in your build system. 19 | */ 20 | 21 | StyleAgentPrivate::StyleAgentPrivate() { 22 | } 23 | 24 | StyleAgentPrivate::~StyleAgentPrivate() { 25 | removeSystemThemeHook(); 26 | } 27 | 28 | void StyleAgentPrivate::init() { 29 | setupSystemThemeHook(); 30 | } 31 | 32 | void StyleAgentPrivate::notifyThemeChanged(StyleAgent::SystemTheme theme) { 33 | if (theme == systemTheme) 34 | return; 35 | systemTheme = theme; 36 | 37 | Q_Q(StyleAgent); 38 | Q_EMIT q->systemThemeChanged(); 39 | } 40 | 41 | /*! 42 | Constructor. Since it is not related to a concrete window instance, it is better to be used 43 | as a singleton. 44 | */ 45 | StyleAgent::StyleAgent(QObject *parent) : StyleAgent(*new StyleAgentPrivate(), parent) { 46 | } 47 | 48 | /*! 49 | Destructor. 50 | */ 51 | StyleAgent::~StyleAgent() { 52 | } 53 | 54 | /*! 55 | Returns the system theme. 56 | */ 57 | StyleAgent::SystemTheme StyleAgent::systemTheme() const { 58 | Q_D(const StyleAgent); 59 | return d->systemTheme; 60 | } 61 | 62 | /*! 63 | \internal 64 | */ 65 | StyleAgent::StyleAgent(StyleAgentPrivate &d, QObject *parent) : QObject(parent), d_ptr(&d) { 66 | d.q_ptr = this; 67 | 68 | d.init(); 69 | } 70 | 71 | /*! 72 | \fn void StyleAgent::systemThemeChanged() 73 | 74 | This signal is emitted when the system theme changes. 75 | */ 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/core/style/styleagent.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef STYLEAGENT_H 6 | #define STYLEAGENT_H 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace QWK { 16 | 17 | class StyleAgentPrivate; 18 | 19 | class QWK_CORE_EXPORT StyleAgent : public QObject { 20 | Q_OBJECT 21 | Q_DECLARE_PRIVATE(StyleAgent) 22 | public: 23 | explicit StyleAgent(QObject *parent = nullptr); 24 | ~StyleAgent() override; 25 | 26 | enum SystemTheme { 27 | Unknown, 28 | Light, 29 | Dark, 30 | HighContrast, 31 | }; 32 | Q_ENUM(SystemTheme) 33 | 34 | public: 35 | SystemTheme systemTheme() const; 36 | 37 | Q_SIGNALS: 38 | void systemThemeChanged(); 39 | 40 | protected: 41 | StyleAgent(StyleAgentPrivate &d, QObject *parent = nullptr); 42 | 43 | const std::unique_ptr d_ptr; 44 | }; 45 | 46 | } 47 | 48 | #endif // STYLEAGENT_H -------------------------------------------------------------------------------- /src/core/style/styleagent_linux.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "styleagent_p.h" 6 | 7 | #include 8 | 9 | namespace QWK { 10 | 11 | void StyleAgentPrivate::setupSystemThemeHook() { 12 | } 13 | 14 | void StyleAgentPrivate::removeSystemThemeHook() { 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /src/core/style/styleagent_mac.mm: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "styleagent_p.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace QWK { 12 | 13 | static StyleAgent::SystemTheme getSystemTheme() { 14 | NSString *osxMode = 15 | [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; 16 | bool isDark = [osxMode isEqualToString:@"Dark"]; 17 | return isDark ? StyleAgent::Dark : StyleAgent::Light; 18 | } 19 | 20 | static void notifyAllStyleAgents(); 21 | 22 | } 23 | 24 | // 25 | // Objective C++ Begin 26 | // 27 | 28 | @interface QWK_SystemThemeObserver : NSObject { 29 | } 30 | @end 31 | 32 | @implementation QWK_SystemThemeObserver 33 | 34 | - (id)init { 35 | self = [super init]; 36 | if (self) { 37 | [[NSDistributedNotificationCenter defaultCenter] 38 | addObserver:self 39 | selector:@selector(interfaceModeChanged:) 40 | name:@"AppleInterfaceThemeChangedNotification" 41 | object:nil]; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)dealloc { 47 | [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; 48 | [super dealloc]; 49 | } 50 | 51 | - (void)interfaceModeChanged:(NSNotification *)notification { 52 | QWK::notifyAllStyleAgents(); 53 | } 54 | 55 | @end 56 | 57 | // 58 | // Objective C++ End 59 | // 60 | 61 | 62 | namespace QWK { 63 | 64 | using StyleAgentSet = QSet; 65 | Q_GLOBAL_STATIC(StyleAgentSet, g_styleAgentSet) 66 | 67 | static QWK_SystemThemeObserver *g_systemThemeObserver = nil; 68 | 69 | void notifyAllStyleAgents() { 70 | auto theme = getSystemTheme(); 71 | for (auto &&ap : std::as_const(*g_styleAgentSet())) { 72 | ap->notifyThemeChanged(theme); 73 | } 74 | } 75 | 76 | void StyleAgentPrivate::setupSystemThemeHook() { 77 | systemTheme = getSystemTheme(); 78 | 79 | // Alloc 80 | if (g_styleAgentSet->isEmpty()) { 81 | g_systemThemeObserver = [[QWK_SystemThemeObserver alloc] init]; 82 | } 83 | 84 | g_styleAgentSet->insert(this); 85 | } 86 | 87 | void StyleAgentPrivate::removeSystemThemeHook() { 88 | if (!g_styleAgentSet->remove(this)) 89 | return; 90 | 91 | if (g_styleAgentSet->isEmpty()) { 92 | // Delete 93 | [g_systemThemeObserver release]; 94 | g_systemThemeObserver = nil; 95 | } 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/core/style/styleagent_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef STYLEAGENTPRIVATE_H 6 | #define STYLEAGENTPRIVATE_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace QWK { 22 | 23 | class StyleAgentPrivate : public QObject { 24 | Q_DECLARE_PUBLIC(StyleAgent) 25 | public: 26 | StyleAgentPrivate(); 27 | ~StyleAgentPrivate() override; 28 | 29 | void init(); 30 | 31 | StyleAgent *q_ptr; 32 | 33 | StyleAgent::SystemTheme systemTheme = StyleAgent::Unknown; 34 | 35 | void setupSystemThemeHook(); 36 | void removeSystemThemeHook(); 37 | 38 | void notifyThemeChanged(StyleAgent::SystemTheme theme); 39 | }; 40 | 41 | } 42 | 43 | #endif // STYLEAGENTPRIVATE_H -------------------------------------------------------------------------------- /src/core/style/styleagent_win.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "styleagent_p.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace QWK { 15 | 16 | using StyleAgentSet = QSet; 17 | Q_GLOBAL_STATIC(StyleAgentSet, g_styleAgentSet) 18 | 19 | static StyleAgent::SystemTheme getSystemTheme() { 20 | if (isHighContrastModeEnabled()) { 21 | return StyleAgent::HighContrast; 22 | } else if (isDarkThemeActive()) { 23 | return StyleAgent::Dark; 24 | } else { 25 | return StyleAgent::Light; 26 | } 27 | } 28 | 29 | static void notifyAllStyleAgents() { 30 | auto theme = getSystemTheme(); 31 | for (auto &&ap : std::as_const(*g_styleAgentSet())) { 32 | ap->notifyThemeChanged(theme); 33 | } 34 | } 35 | 36 | class SystemSettingEventFilter : public AppNativeEventFilter { 37 | public: 38 | bool nativeEventFilter(const QByteArray &eventType, void *message, 39 | QT_NATIVE_EVENT_RESULT_TYPE *result) override { 40 | Q_UNUSED(eventType) 41 | if (!result) { 42 | return false; 43 | } 44 | 45 | const auto msg = static_cast(message); 46 | switch (msg->message) { 47 | case WM_THEMECHANGED: 48 | case WM_SYSCOLORCHANGE: 49 | case WM_DWMCOLORIZATIONCOLORCHANGED: { 50 | notifyAllStyleAgents(); 51 | break; 52 | } 53 | 54 | case WM_SETTINGCHANGE: { 55 | if (isImmersiveColorSetChange(msg->wParam, msg->lParam)) { 56 | notifyAllStyleAgents(); 57 | } 58 | break; 59 | } 60 | 61 | default: 62 | break; 63 | } 64 | return false; 65 | } 66 | 67 | static inline SystemSettingEventFilter *instance = nullptr; 68 | 69 | static inline void install() { 70 | if (instance) { 71 | return; 72 | } 73 | instance = new SystemSettingEventFilter(); 74 | } 75 | 76 | static inline void uninstall() { 77 | if (!instance) { 78 | return; 79 | } 80 | delete instance; 81 | instance = nullptr; 82 | } 83 | }; 84 | 85 | void StyleAgentPrivate::setupSystemThemeHook() { 86 | systemTheme = getSystemTheme(); 87 | 88 | g_styleAgentSet->insert(this); 89 | SystemSettingEventFilter::install(); 90 | } 91 | 92 | void StyleAgentPrivate::removeSystemThemeHook() { 93 | if (!g_styleAgentSet->remove(this)) 94 | return; 95 | 96 | if (g_styleAgentSet->isEmpty()) { 97 | SystemSettingEventFilter::uninstall(); 98 | } 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /src/core/windowagentbase.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "windowagentbase.h" 6 | #include "windowagentbase_p.h" 7 | 8 | #include 9 | 10 | #include "qwkglobal_p.h" 11 | 12 | #if defined(Q_OS_WINDOWS) && !QWINDOWKIT_CONFIG(ENABLE_QT_WINDOW_CONTEXT) 13 | # include "win32windowcontext_p.h" 14 | #elif defined(Q_OS_MAC) && !QWINDOWKIT_CONFIG(ENABLE_QT_WINDOW_CONTEXT) 15 | # include "cocoawindowcontext_p.h" 16 | #else 17 | # include "qtwindowcontext_p.h" 18 | #endif 19 | 20 | Q_LOGGING_CATEGORY(qWindowKitLog, "qwindowkit") 21 | 22 | namespace QWK { 23 | 24 | /*! 25 | \namespace QWK 26 | \brief QWindowKit namespace 27 | */ 28 | 29 | /*! 30 | \class WindowAgentBase 31 | \brief WindowAgentBase is the base class of the specifiy window agent for QtWidgets and 32 | QtQuick. 33 | 34 | It processes some system events to remove the window's default title bar, and provides some 35 | shared methods for derived classes to call. 36 | */ 37 | 38 | WindowAgentBasePrivate::WindowContextFactoryMethod 39 | WindowAgentBasePrivate::windowContextFactoryMethod = nullptr; 40 | 41 | WindowAgentBasePrivate::WindowAgentBasePrivate() : q_ptr(nullptr), context(nullptr) { 42 | } 43 | 44 | WindowAgentBasePrivate::~WindowAgentBasePrivate() = default; 45 | 46 | void WindowAgentBasePrivate::init() { 47 | } 48 | 49 | AbstractWindowContext *WindowAgentBasePrivate::createContext() const { 50 | if (windowContextFactoryMethod) { 51 | return windowContextFactoryMethod(); 52 | } 53 | 54 | #if defined(Q_OS_WINDOWS) && !QWINDOWKIT_CONFIG(ENABLE_QT_WINDOW_CONTEXT) 55 | return new Win32WindowContext(); 56 | #elif defined(Q_OS_MAC) && !QWINDOWKIT_CONFIG(ENABLE_QT_WINDOW_CONTEXT) 57 | return new CocoaWindowContext(); 58 | #else 59 | return new QtWindowContext(); 60 | #endif 61 | } 62 | 63 | void WindowAgentBasePrivate::setup(QObject *host, WindowItemDelegate *delegate) { 64 | auto ctx = createContext(); 65 | ctx->setup(host, delegate); 66 | context.reset(ctx); 67 | } 68 | 69 | /*! 70 | Destructor. 71 | */ 72 | WindowAgentBase::~WindowAgentBase() = default; 73 | 74 | /*! 75 | Returns the window attribute value. 76 | 77 | \sa setWindowAttribute() 78 | */ 79 | QVariant WindowAgentBase::windowAttribute(const QString &key) const { 80 | Q_D(const WindowAgentBase); 81 | return d->context->windowAttribute(key); 82 | } 83 | 84 | /*! 85 | Sets the platform-related attribute for the window. Available attributes: 86 | 87 | On Windows, 88 | \li \c dwm-blur: Specify a boolean value to enable or disable dwm blur effect, this 89 | attribute is available on Windows 10 or later. 90 | \li \c dark-mode: Specify a boolean value to enable or disable the dark mode, it is 91 | enabled by default on Windows 10 if the system borders config is enabled. This 92 | attribute is available on Windows 10 or later. 93 | \li \c acrylic-material: Specify a boolean value to enable or disable acrylic material, 94 | this attribute is only available on Windows 11. 95 | \li \c mica: Specify a boolean value to enable or disable mica material, 96 | this attribute is only available on Windows 11. 97 | \li \c mica-alt: Specify a boolean value to enable or disable mica-alt material, 98 | this attribute is only available on Windows 11. 99 | \li \c extra-margins: Specify a margin value to change the \c dwm extended area 100 | geometry, you shouldn't change this attribute because it may break the 101 | internal state. 102 | \li \c border-thickness: Returns the system border thickness. (Readonly) 103 | \li \c title-bar-height: Returns the system title bar height, some system features may 104 | be related to this property so that it is recommended to set the custom title bar 105 | height to this value. (Readonly) 106 | 107 | On macOS, 108 | \li \c no-system-buttons: Specify a boolean value to set the system buttons' 109 | visibility. 110 | \li \c blur-effect: You can specify a string value, "dark" to enable dark mode, "light" 111 | to set enable mode, "none" to disable. You can also specify a boolean value, 112 | \c true to enable current theme mode, \c false to disable. 113 | \li \c title-bar-height: Returns the system title bar height, the system button display 114 | area will be limited to this height. (Readonly) 115 | */ 116 | bool WindowAgentBase::setWindowAttribute(const QString &key, const QVariant &attribute) { 117 | Q_D(WindowAgentBase); 118 | return d->context->setWindowAttribute(key, attribute); 119 | } 120 | 121 | /*! 122 | Shows the system menu, it's only implemented on Windows. 123 | */ 124 | void WindowAgentBase::showSystemMenu(const QPoint &pos) { 125 | Q_D(WindowAgentBase); 126 | d->context->showSystemMenu(pos); 127 | } 128 | 129 | /*! 130 | Makes the window show in center of the current screen. 131 | */ 132 | void WindowAgentBase::centralize() { 133 | Q_D(WindowAgentBase); 134 | d->context->virtual_hook(AbstractWindowContext::CentralizeHook, nullptr); 135 | } 136 | 137 | /*! 138 | Brings the window to top. 139 | */ 140 | void WindowAgentBase::raise() { 141 | Q_D(WindowAgentBase); 142 | d->context->virtual_hook(AbstractWindowContext::RaiseWindowHook, nullptr); 143 | } 144 | 145 | /*! 146 | \internal 147 | */ 148 | WindowAgentBase::WindowAgentBase(WindowAgentBasePrivate &d, QObject *parent) 149 | : QObject(parent), d_ptr(&d) { 150 | d.q_ptr = this; 151 | 152 | d.init(); 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/core/windowagentbase.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWAGENTBASE_H 6 | #define WINDOWAGENTBASE_H 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace QWK { 15 | 16 | class WindowAgentBasePrivate; 17 | 18 | class QWK_CORE_EXPORT WindowAgentBase : public QObject { 19 | Q_OBJECT 20 | Q_DECLARE_PRIVATE(WindowAgentBase) 21 | public: 22 | ~WindowAgentBase() override; 23 | 24 | enum SystemButton { 25 | Unknown, 26 | WindowIcon, 27 | Help, 28 | Minimize, 29 | Maximize, 30 | Close, 31 | }; 32 | Q_ENUM(SystemButton) 33 | 34 | QVariant windowAttribute(const QString &key) const; 35 | Q_INVOKABLE bool setWindowAttribute(const QString &key, const QVariant &attribute); 36 | 37 | public Q_SLOTS: 38 | void showSystemMenu(const QPoint &pos); // Only available on Windows now 39 | void centralize(); 40 | void raise(); 41 | 42 | protected: 43 | explicit WindowAgentBase(WindowAgentBasePrivate &d, QObject *parent = nullptr); 44 | 45 | const std::unique_ptr d_ptr; 46 | }; 47 | 48 | } 49 | 50 | #endif // WINDOWAGENTBASE_H -------------------------------------------------------------------------------- /src/core/windowagentbase_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWAGENTBASEPRIVATE_H 6 | #define WINDOWAGENTBASEPRIVATE_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | 20 | namespace QWK { 21 | 22 | class QWK_CORE_EXPORT WindowAgentBasePrivate { 23 | Q_DECLARE_PUBLIC(WindowAgentBase) 24 | public: 25 | WindowAgentBasePrivate(); 26 | virtual ~WindowAgentBasePrivate(); 27 | 28 | void init(); 29 | 30 | WindowAgentBase *q_ptr; // no need to initialize 31 | 32 | virtual AbstractWindowContext *createContext() const; 33 | 34 | void setup(QObject *host, WindowItemDelegate *delegate); 35 | 36 | std::unique_ptr context; 37 | 38 | public: 39 | using WindowContextFactoryMethod = AbstractWindowContext *(*) (); 40 | 41 | static WindowContextFactoryMethod windowContextFactoryMethod; 42 | 43 | private: 44 | Q_DISABLE_COPY(WindowAgentBasePrivate) 45 | }; 46 | 47 | } 48 | 49 | #endif // WINDOWAGENTBASEPRIVATE_H -------------------------------------------------------------------------------- /src/core/windowitemdelegate.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "windowitemdelegate_p.h" 6 | 7 | namespace QWK { 8 | 9 | WindowItemDelegate::WindowItemDelegate() = default; 10 | 11 | WindowItemDelegate::~WindowItemDelegate() = default; 12 | 13 | void WindowItemDelegate::resetQtGrabbedControl(QObject *host) const { 14 | Q_UNUSED(host); 15 | } 16 | 17 | WinIdChangeEventFilter * 18 | WindowItemDelegate::createWinIdEventFilter(QObject *host, 19 | AbstractWindowContext *context) const { 20 | return new WindowWinIdChangeEventFilter(static_cast(host), context); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/core/windowitemdelegate_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WINDOWITEMDELEGATE_P_H 6 | #define WINDOWITEMDELEGATE_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | namespace QWK { 25 | 26 | class QWK_CORE_EXPORT WindowItemDelegate { 27 | public: 28 | WindowItemDelegate(); 29 | virtual ~WindowItemDelegate(); 30 | 31 | public: 32 | // Item property query 33 | virtual QWindow *window(const QObject *obj) const = 0; 34 | virtual bool isEnabled(const QObject *obj) const = 0; 35 | virtual bool isVisible(const QObject *obj) const = 0; 36 | virtual QRect mapGeometryToScene(const QObject *obj) const = 0; 37 | 38 | // Host property query 39 | virtual QWindow *hostWindow(const QObject *host) const = 0; 40 | virtual bool isWindowActive(const QObject *host) const = 0; 41 | virtual Qt::WindowStates getWindowState(const QObject *host) const = 0; 42 | virtual Qt::WindowFlags getWindowFlags(const QObject *host) const = 0; 43 | virtual QRect getGeometry(const QObject *host) const = 0; 44 | 45 | // Callbacks 46 | virtual void resetQtGrabbedControl(QObject *host) const; 47 | virtual void setWindowState(QObject *host, Qt::WindowStates state) const = 0; 48 | virtual void setCursorShape(QObject *host, Qt::CursorShape shape) const = 0; 49 | virtual void restoreCursorShape(QObject *host) const = 0; 50 | virtual void setWindowFlags(QObject *host, Qt::WindowFlags flags) const = 0; 51 | virtual void setWindowVisible(QObject *host, bool visible) const = 0; 52 | virtual void setGeometry(QObject *host, const QRect &rect) = 0; 53 | virtual void bringWindowToTop(QObject *host) const = 0; 54 | 55 | // Factories 56 | virtual WinIdChangeEventFilter * 57 | createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const; 58 | 59 | private: 60 | Q_DISABLE_COPY(WindowItemDelegate) 61 | }; 62 | 63 | } 64 | 65 | #endif // WINDOWITEMDELEGATE_P_H 66 | -------------------------------------------------------------------------------- /src/quick/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(QWKQuick 2 | VERSION ${QWINDOWKIT_VERSION} 3 | LANGUAGES CXX 4 | ) 5 | 6 | set(_src 7 | qwkquickglobal.h 8 | qwkquickglobal.cpp 9 | quickitemdelegate_p.h 10 | quickitemdelegate.cpp 11 | quickwindowagent.h 12 | quickwindowagent_p.h 13 | quickwindowagent.cpp 14 | ) 15 | 16 | if(WIN32) 17 | list(APPEND _src quickwindowagent_win.cpp) 18 | elseif(APPLE) 19 | list(APPEND _src quickwindowagent_mac.cpp) 20 | endif() 21 | 22 | qwk_add_library(${PROJECT_NAME} AUTOGEN 23 | SOURCES ${_src} 24 | FEATURES cxx_std_17 25 | LINKS QWKCore 26 | QT_LINKS Core Gui Quick 27 | QT_INCLUDE_PRIVATE Core Gui Quick 28 | INCLUDE_PRIVATE * 29 | PREFIX QWK_QUICK 30 | ) 31 | 32 | set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE) 33 | set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} quick PARENT_SCOPE) 34 | -------------------------------------------------------------------------------- /src/quick/quickitemdelegate.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "quickitemdelegate_p.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace QWK { 11 | 12 | QuickItemDelegate::QuickItemDelegate() : WindowItemDelegate() { 13 | } 14 | 15 | QuickItemDelegate::~QuickItemDelegate() = default; 16 | 17 | QWindow *QuickItemDelegate::window(const QObject *obj) const { 18 | return static_cast(obj)->window(); 19 | } 20 | 21 | bool QuickItemDelegate::isEnabled(const QObject *obj) const { 22 | return static_cast(obj)->isEnabled(); 23 | } 24 | 25 | bool QuickItemDelegate::isVisible(const QObject *obj) const { 26 | return static_cast(obj)->isVisible(); 27 | } 28 | 29 | QRect QuickItemDelegate::mapGeometryToScene(const QObject *obj) const { 30 | auto item = static_cast(obj); 31 | const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0)); 32 | const QSizeF size = item->size(); 33 | return QRectF(originPoint, size).toRect(); 34 | } 35 | 36 | QWindow *QuickItemDelegate::hostWindow(const QObject *host) const { 37 | return static_cast(const_cast(host)); 38 | } 39 | 40 | bool QuickItemDelegate::isWindowActive(const QObject *host) const { 41 | return static_cast(host)->isActive(); 42 | } 43 | 44 | Qt::WindowStates QuickItemDelegate::getWindowState(const QObject *host) const { 45 | return static_cast(host)->windowStates(); 46 | } 47 | 48 | void QuickItemDelegate::setWindowState(QObject *host, Qt::WindowStates state) const { 49 | static_cast(host)->setWindowStates(state); 50 | } 51 | 52 | void QuickItemDelegate::setCursorShape(QObject *host, const Qt::CursorShape shape) const { 53 | static_cast(host)->setCursor(QCursor(shape)); 54 | } 55 | 56 | void QuickItemDelegate::restoreCursorShape(QObject *host) const { 57 | static_cast(host)->unsetCursor(); 58 | } 59 | 60 | Qt::WindowFlags QuickItemDelegate::getWindowFlags(const QObject *host) const { 61 | return static_cast(host)->flags(); 62 | } 63 | 64 | QRect QuickItemDelegate::getGeometry(const QObject *host) const { 65 | return static_cast(host)->geometry(); 66 | } 67 | 68 | void QuickItemDelegate::setWindowFlags(QObject *host, Qt::WindowFlags flags) const { 69 | static_cast(host)->setFlags(flags); 70 | } 71 | 72 | void QuickItemDelegate::setWindowVisible(QObject *host, bool visible) const { 73 | static_cast(host)->setVisible(visible); 74 | } 75 | 76 | void QuickItemDelegate::setGeometry(QObject *host, const QRect &rect) { 77 | static_cast(host)->setGeometry(rect); 78 | } 79 | 80 | void QuickItemDelegate::bringWindowToTop(QObject *host) const { 81 | static_cast(host)->raise(); 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /src/quick/quickitemdelegate_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QUICKITEMDELEGATE_P_H 6 | #define QUICKITEMDELEGATE_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | namespace QWK { 24 | 25 | class QWK_QUICK_EXPORT QuickItemDelegate : public WindowItemDelegate { 26 | public: 27 | QuickItemDelegate(); 28 | ~QuickItemDelegate() override; 29 | 30 | public: 31 | QWindow *window(const QObject *obj) const override; 32 | bool isEnabled(const QObject *obj) const override; 33 | bool isVisible(const QObject *obj) const override; 34 | QRect mapGeometryToScene(const QObject *obj) const override; 35 | 36 | QWindow *hostWindow(const QObject *host) const override; 37 | bool isWindowActive(const QObject *host) const override; 38 | Qt::WindowStates getWindowState(const QObject *host) const override; 39 | Qt::WindowFlags getWindowFlags(const QObject *host) const override; 40 | QRect getGeometry(const QObject *host) const override; 41 | 42 | void setWindowState(QObject *host, Qt::WindowStates state) const override; 43 | void setCursorShape(QObject *host, Qt::CursorShape shape) const override; 44 | void restoreCursorShape(QObject *host) const override; 45 | void setWindowFlags(QObject *host, Qt::WindowFlags flags) const override; 46 | void setWindowVisible(QObject *host, bool visible) const override; 47 | void setGeometry(QObject *host, const QRect &rect) override; 48 | void bringWindowToTop(QObject *host) const override; 49 | }; 50 | 51 | } 52 | 53 | #endif // QUICKITEMDELEGATE_P_H 54 | -------------------------------------------------------------------------------- /src/quick/quickwindowagent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "quickwindowagent.h" 6 | #include "quickwindowagent_p.h" 7 | 8 | #include 9 | #include 10 | 11 | #include "quickitemdelegate_p.h" 12 | 13 | namespace QWK { 14 | 15 | /*! 16 | \class QuickWindowAgent 17 | \brief QuickWindowAgent is the window agent for QtQuick. 18 | 19 | It provides interfaces for QtQuick and processes some Qt events related to the QQuickItem 20 | instance. The usage of all APIs is consistent with the \a Widgets module. 21 | */ 22 | 23 | QuickWindowAgentPrivate::QuickWindowAgentPrivate() = default; 24 | 25 | QuickWindowAgentPrivate::~QuickWindowAgentPrivate() = default; 26 | 27 | void QuickWindowAgentPrivate::init() { 28 | } 29 | 30 | QuickWindowAgent::QuickWindowAgent(QObject *parent) 31 | : QuickWindowAgent(*new QuickWindowAgentPrivate(), parent) { 32 | } 33 | 34 | QuickWindowAgent::~QuickWindowAgent() = default; 35 | 36 | bool QuickWindowAgent::setup(QQuickWindow *window) { 37 | Q_ASSERT(window); 38 | if (!window) { 39 | return false; 40 | } 41 | 42 | Q_D(QuickWindowAgent); 43 | if (d->hostWindow) { 44 | return false; 45 | } 46 | 47 | d->setup(window, new QuickItemDelegate()); 48 | d->hostWindow = window; 49 | 50 | #if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) 51 | d->setupWindows10BorderWorkaround(); 52 | #endif 53 | return true; 54 | } 55 | 56 | QQuickItem *QuickWindowAgent::titleBar() const { 57 | Q_D(const QuickWindowAgent); 58 | return static_cast(d->context->titleBar()); 59 | } 60 | 61 | void QuickWindowAgent::setTitleBar(QQuickItem *item) { 62 | Q_D(QuickWindowAgent); 63 | if (!d->context->setTitleBar(item)) { 64 | return; 65 | } 66 | #ifdef Q_OS_MAC 67 | setSystemButtonArea(nullptr); 68 | #endif 69 | Q_EMIT titleBarWidgetChanged(item); 70 | } 71 | 72 | QQuickItem *QuickWindowAgent::systemButton(SystemButton button) const { 73 | Q_D(const QuickWindowAgent); 74 | return static_cast(d->context->systemButton(button)); 75 | } 76 | 77 | void QuickWindowAgent::setSystemButton(SystemButton button, QQuickItem *item) { 78 | Q_D(QuickWindowAgent); 79 | if (!d->context->setSystemButton(button, item)) { 80 | return; 81 | } 82 | Q_EMIT systemButtonChanged(button, item); 83 | } 84 | 85 | #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) 86 | bool QuickWindowAgent::isHitTestVisible(QQuickItem *item) const { 87 | #else 88 | bool QuickWindowAgent::isHitTestVisible(const QQuickItem *item) const { 89 | #endif 90 | Q_D(const QuickWindowAgent); 91 | return d->context->isHitTestVisible(item); 92 | } 93 | 94 | void QuickWindowAgent::setHitTestVisible(QQuickItem *item, bool visible) { 95 | Q_D(QuickWindowAgent); 96 | d->context->setHitTestVisible(item, visible); 97 | } 98 | 99 | /*! 100 | \internal 101 | */ 102 | QuickWindowAgent::QuickWindowAgent(QuickWindowAgentPrivate &d, QObject *parent) 103 | : WindowAgentBase(d, parent) { 104 | d.init(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/quick/quickwindowagent.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QUICKWINDOWAGENT_H 6 | #define QUICKWINDOWAGENT_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace QWK { 15 | 16 | class QuickWindowAgentPrivate; 17 | 18 | class QWK_QUICK_EXPORT QuickWindowAgent : public WindowAgentBase { 19 | Q_OBJECT 20 | Q_DECLARE_PRIVATE(QuickWindowAgent) 21 | public: 22 | explicit QuickWindowAgent(QObject *parent = nullptr); 23 | ~QuickWindowAgent() override; 24 | 25 | public: 26 | Q_INVOKABLE bool setup(QQuickWindow *window); 27 | 28 | Q_INVOKABLE QQuickItem *titleBar() const; 29 | Q_INVOKABLE void setTitleBar(QQuickItem *item); 30 | 31 | Q_INVOKABLE QQuickItem *systemButton(SystemButton button) const; 32 | Q_INVOKABLE void setSystemButton(SystemButton button, QQuickItem *item); 33 | 34 | #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) 35 | Q_INVOKABLE bool isHitTestVisible(QQuickItem *item) const; 36 | #else 37 | Q_INVOKABLE bool isHitTestVisible(const QQuickItem *item) const; 38 | #endif 39 | Q_INVOKABLE void setHitTestVisible(QQuickItem *item, bool visible = true); 40 | 41 | #ifdef Q_OS_MAC 42 | // The system button area APIs are experimental, very likely to change in the future. 43 | Q_INVOKABLE QQuickItem *systemButtonArea() const; 44 | Q_INVOKABLE void setSystemButtonArea(QQuickItem *item); 45 | 46 | Q_INVOKABLE ScreenRectCallback systemButtonAreaCallback() const; 47 | Q_INVOKABLE void setSystemButtonAreaCallback(const ScreenRectCallback &callback); 48 | #endif 49 | 50 | Q_SIGNALS: 51 | void titleBarWidgetChanged(QQuickItem *item); 52 | void systemButtonChanged(SystemButton button, QQuickItem *item); 53 | 54 | protected: 55 | QuickWindowAgent(QuickWindowAgentPrivate &d, QObject *parent = nullptr); 56 | }; 57 | 58 | } 59 | 60 | #endif // QUICKWINDOWAGENT_H 61 | -------------------------------------------------------------------------------- /src/quick/quickwindowagent_mac.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "quickwindowagent_p.h" 6 | 7 | namespace QWK { 8 | 9 | class SystemButtonAreaItemHandler : public QObject { 10 | public: 11 | SystemButtonAreaItemHandler(QQuickItem *item, AbstractWindowContext *ctx, 12 | QObject *parent = nullptr); 13 | ~SystemButtonAreaItemHandler() override = default; 14 | 15 | void updateSystemButtonArea(); 16 | 17 | protected: 18 | QQuickItem *item; 19 | AbstractWindowContext *ctx; 20 | }; 21 | 22 | SystemButtonAreaItemHandler::SystemButtonAreaItemHandler(QQuickItem *item, 23 | AbstractWindowContext *ctx, 24 | QObject *parent) 25 | : QObject(parent), item(item), ctx(ctx) { 26 | connect(item, &QQuickItem::xChanged, this, 27 | &SystemButtonAreaItemHandler::updateSystemButtonArea); 28 | connect(item, &QQuickItem::yChanged, this, 29 | &SystemButtonAreaItemHandler::updateSystemButtonArea); 30 | connect(item, &QQuickItem::widthChanged, this, 31 | &SystemButtonAreaItemHandler::updateSystemButtonArea); 32 | connect(item, &QQuickItem::heightChanged, this, 33 | &SystemButtonAreaItemHandler::updateSystemButtonArea); 34 | 35 | ctx->setSystemButtonAreaCallback([item](const QSize &) { 36 | return QRectF(item->mapToScene(QPointF(0, 0)), item->size()).toRect(); // 37 | }); 38 | } 39 | 40 | void SystemButtonAreaItemHandler::updateSystemButtonArea() { 41 | ctx->virtual_hook(AbstractWindowContext::SystemButtonAreaChangedHook, nullptr); 42 | } 43 | 44 | QQuickItem *QuickWindowAgent::systemButtonArea() const { 45 | Q_D(const QuickWindowAgent); 46 | return d->systemButtonAreaItem; 47 | } 48 | 49 | void QuickWindowAgent::setSystemButtonArea(QQuickItem *item) { 50 | Q_D(QuickWindowAgent); 51 | if (d->systemButtonAreaItem == item) 52 | return; 53 | 54 | auto ctx = d->context.get(); 55 | d->systemButtonAreaItem = item; 56 | if (!item) { 57 | d->systemButtonAreaItemHandler.reset(); 58 | ctx->setSystemButtonAreaCallback({}); 59 | return; 60 | } 61 | d->systemButtonAreaItemHandler = std::make_unique(item, ctx); 62 | } 63 | 64 | ScreenRectCallback QuickWindowAgent::systemButtonAreaCallback() const { 65 | Q_D(const QuickWindowAgent); 66 | return d->systemButtonAreaItem ? nullptr : d->context->systemButtonAreaCallback(); 67 | } 68 | 69 | void QuickWindowAgent::setSystemButtonAreaCallback(const ScreenRectCallback &callback) { 70 | Q_D(QuickWindowAgent); 71 | setSystemButtonArea(nullptr); 72 | d->context->setSystemButtonAreaCallback(callback); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/quick/quickwindowagent_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QUICKWINDOWAGENTPRIVATE_H 6 | #define QUICKWINDOWAGENTPRIVATE_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace QWK { 22 | 23 | class QuickWindowAgentPrivate : public WindowAgentBasePrivate { 24 | Q_DECLARE_PUBLIC(QuickWindowAgent) 25 | public: 26 | QuickWindowAgentPrivate(); 27 | ~QuickWindowAgentPrivate() override; 28 | 29 | void init(); 30 | 31 | // Host 32 | QQuickWindow *hostWindow{}; 33 | 34 | #ifdef Q_OS_MAC 35 | QQuickItem *systemButtonAreaItem{}; 36 | std::unique_ptr systemButtonAreaItemHandler; 37 | #endif 38 | 39 | #if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) 40 | void setupWindows10BorderWorkaround(); 41 | #endif 42 | }; 43 | 44 | } 45 | 46 | #endif // QUICKWINDOWAGENTPRIVATE_H 47 | -------------------------------------------------------------------------------- /src/quick/quickwindowagent_win.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "quickwindowagent_p.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace QWK { 14 | 15 | static inline bool isWindows1022H2OrGreater() { 16 | RTL_OSVERSIONINFOW rovi = Private::GetRealOSVersion(); 17 | return (rovi.dwMajorVersion > 10) || 18 | (rovi.dwMajorVersion == 10 && 19 | (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 19045)); 20 | } 21 | 22 | #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) 23 | 24 | class BorderItem : public QQuickPaintedItem, public Windows10BorderHandler { 25 | public: 26 | explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context); 27 | ~BorderItem() override; 28 | 29 | bool shouldEnableEmulatedPainter() const; 30 | void updateGeometry() override; 31 | 32 | public: 33 | void paint(QPainter *painter) override; 34 | void itemChange(ItemChange change, const ItemChangeData &data) override; 35 | 36 | protected: 37 | bool sharedEventFilter(QObject *obj, QEvent *event) override; 38 | bool nativeEventFilter(const QByteArray &eventType, void *message, 39 | QT_NATIVE_EVENT_RESULT_TYPE *result) override; 40 | 41 | private: 42 | volatile bool needPaint = false; 43 | 44 | void _q_afterSynchronizing(); 45 | void _q_windowActivityChanged(); 46 | }; 47 | 48 | bool BorderItem::shouldEnableEmulatedPainter() const { 49 | # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 50 | const QQuickWindow* win = window(); 51 | if (!win) { 52 | return true; 53 | } 54 | auto api = win->rendererInterface()->graphicsApi(); 55 | switch (api) { 56 | case QSGRendererInterface::OpenGL: 57 | // FIXME: experimental, try to find the exact fixed version. 58 | return !isWindows1022H2OrGreater(); 59 | case QSGRendererInterface::Direct3D11: 60 | # if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) 61 | case QSGRendererInterface::Direct3D12: 62 | # endif 63 | return false; 64 | default: 65 | break; 66 | } 67 | # endif 68 | return true; 69 | } 70 | 71 | BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context) 72 | : QQuickPaintedItem(parent), Windows10BorderHandler(context) { 73 | setAntialiasing(true); // We need anti-aliasing to give us better result. 74 | setFillColor({}); // Will improve the performance a little bit. 75 | setOpaquePainting(true); // Will also improve the performance, we don't draw 76 | // semi-transparent borders of course. 77 | 78 | auto parentPri = QQuickItemPrivate::get(parent); 79 | auto anchors = QQuickItemPrivate::get(this)->anchors(); 80 | anchors->setTop(parentPri->top()); 81 | anchors->setLeft(parentPri->left()); 82 | anchors->setRight(parentPri->right()); 83 | 84 | setZ(std::numeric_limits::max()); // Make sure our fake border always above 85 | // everything in the window. 86 | 87 | # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 88 | connect(window(), &QQuickWindow::afterSynchronizing, this, 89 | &BorderItem::_q_afterSynchronizing, Qt::DirectConnection); 90 | # endif 91 | connect(window(), &QQuickWindow::activeChanged, this, 92 | &BorderItem::_q_windowActivityChanged); 93 | 94 | // First update 95 | if (context->windowId()) { 96 | setupNecessaryAttributes(); 97 | } 98 | BorderItem::updateGeometry(); 99 | } 100 | 101 | BorderItem::~BorderItem() = default; 102 | 103 | void BorderItem::updateGeometry() { 104 | const QQuickWindow* win = window(); 105 | if (!win) { 106 | return; 107 | } 108 | setHeight(borderThickness() / win->effectiveDevicePixelRatio()); 109 | setVisible(isNormalWindow()); 110 | } 111 | 112 | void BorderItem::paint(QPainter *painter) { 113 | Q_UNUSED(painter) 114 | if (shouldEnableEmulatedPainter()) { 115 | drawBorderEmulated(painter, QRect({0, 0}, size().toSize())); 116 | } else { 117 | needPaint = true; 118 | } 119 | } 120 | 121 | void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) { 122 | QQuickPaintedItem::itemChange(change, data); 123 | switch (change) { 124 | case ItemVisibleHasChanged: 125 | case ItemDevicePixelRatioHasChanged: { 126 | updateGeometry(); 127 | break; 128 | } 129 | default: 130 | break; 131 | } 132 | } 133 | 134 | bool BorderItem::sharedEventFilter(QObject *obj, QEvent *event) { 135 | Q_UNUSED(obj) 136 | 137 | switch (event->type()) { 138 | case QEvent::WindowStateChange: { 139 | updateGeometry(); 140 | } 141 | default: 142 | break; 143 | } 144 | return Windows10BorderHandler::sharedEventFilter(obj, event); 145 | } 146 | 147 | bool BorderItem::nativeEventFilter(const QByteArray &eventType, void *message, 148 | QT_NATIVE_EVENT_RESULT_TYPE *result) { 149 | const auto msg = static_cast(message); 150 | switch (msg->message) { 151 | case WM_THEMECHANGED: 152 | case WM_SYSCOLORCHANGE: 153 | case WM_DWMCOLORIZATIONCOLORCHANGED: { 154 | update(); 155 | break; 156 | } 157 | 158 | case WM_SETTINGCHANGE: { 159 | if (isImmersiveColorSetChange(msg->wParam, msg->lParam)) { 160 | update(); 161 | } 162 | break; 163 | } 164 | 165 | default: 166 | break; 167 | } 168 | return Windows10BorderHandler::nativeEventFilter(eventType, message, result); 169 | } 170 | 171 | void BorderItem::_q_afterSynchronizing() { 172 | if (needPaint) { 173 | needPaint = false; 174 | drawBorderNative(); 175 | } 176 | } 177 | 178 | void BorderItem::_q_windowActivityChanged() { 179 | update(); 180 | } 181 | 182 | void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() { 183 | // Install painting hook 184 | auto ctx = context.get(); 185 | if (ctx->windowAttribute(QStringLiteral("win10-border-needed")).toBool()) { 186 | std::ignore = new BorderItem(hostWindow->contentItem(), ctx); 187 | } 188 | } 189 | #endif 190 | 191 | } 192 | -------------------------------------------------------------------------------- /src/quick/qwkquickglobal.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "qwkquickglobal.h" 6 | 7 | #include 8 | 9 | #include "quickwindowagent.h" 10 | 11 | namespace QWK { 12 | 13 | static constexpr const char kModuleUri[] = "QWindowKit"; 14 | 15 | void registerTypes(QQmlEngine *engine) { 16 | Q_UNUSED(engine); 17 | 18 | static bool once = false; 19 | if (once) { 20 | return; 21 | } 22 | once = true; 23 | 24 | // @uri QWindowKit 25 | qmlRegisterType(kModuleUri, 1, 0, "WindowAgent"); 26 | qmlRegisterModule(kModuleUri, 1, 0); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/quick/qwkquickglobal.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QWKQUICKGLOBAL_H 6 | #define QWKQUICKGLOBAL_H 7 | 8 | #include 9 | 10 | #ifndef QWK_QUICK_EXPORT 11 | # ifdef QWK_QUICK_STATIC 12 | # define QWK_QUICK_EXPORT 13 | # else 14 | # ifdef QWK_QUICK_LIBRARY 15 | # define QWK_QUICK_EXPORT Q_DECL_EXPORT 16 | # else 17 | # define QWK_QUICK_EXPORT Q_DECL_IMPORT 18 | # endif 19 | # endif 20 | #endif 21 | 22 | QT_BEGIN_NAMESPACE 23 | class QQmlEngine; 24 | QT_END_NAMESPACE 25 | 26 | namespace QWK { 27 | 28 | QWK_QUICK_EXPORT void registerTypes(QQmlEngine *engine); 29 | 30 | } 31 | 32 | #endif // QWKQUICKGLOBAL_H 33 | -------------------------------------------------------------------------------- /src/widgets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(QWKWidgets 2 | VERSION ${QWINDOWKIT_VERSION} 3 | LANGUAGES CXX 4 | ) 5 | 6 | set(_src 7 | qwkwidgetsglobal.h 8 | widgetitemdelegate_p.h 9 | widgetitemdelegate.cpp 10 | widgetwindowagent.h 11 | widgetwindowagent_p.h 12 | widgetwindowagent.cpp 13 | ) 14 | 15 | if(WIN32) 16 | list(APPEND _src widgetwindowagent_win.cpp) 17 | elseif(APPLE) 18 | list(APPEND _src widgetwindowagent_mac.cpp) 19 | endif() 20 | 21 | qwk_add_library(${PROJECT_NAME} AUTOGEN 22 | SOURCES ${_src} 23 | FEATURES cxx_std_17 24 | LINKS QWKCore 25 | QT_LINKS Core Gui Widgets 26 | QT_INCLUDE_PRIVATE Core Gui Widgets 27 | INCLUDE_PRIVATE * 28 | PREFIX QWK_WIDGETS 29 | ) 30 | 31 | set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE) 32 | set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} widgets PARENT_SCOPE) 33 | -------------------------------------------------------------------------------- /src/widgets/qwkwidgetsglobal.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef QWKWIDGETSGLOBAL_H 6 | #define QWKWIDGETSGLOBAL_H 7 | 8 | #include 9 | 10 | #ifndef QWK_WIDGETS_EXPORT 11 | # ifdef QWK_WIDGETS_STATIC 12 | # define QWK_WIDGETS_EXPORT 13 | # else 14 | # ifdef QWK_WIDGETS_LIBRARY 15 | # define QWK_WIDGETS_EXPORT Q_DECL_EXPORT 16 | # else 17 | # define QWK_WIDGETS_EXPORT Q_DECL_IMPORT 18 | # endif 19 | # endif 20 | #endif 21 | 22 | #endif // QWKWIDGETSGLOBAL_H 23 | -------------------------------------------------------------------------------- /src/widgets/widgetitemdelegate.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "widgetitemdelegate_p.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | extern Q_DECL_IMPORT QWidget *qt_button_down; 14 | 15 | namespace QWK { 16 | 17 | class WidgetWinIdChangeEventFilter : public WinIdChangeEventFilter { 18 | public: 19 | explicit WidgetWinIdChangeEventFilter(QObject *host, AbstractWindowContext *ctx) 20 | : WinIdChangeEventFilter(host, ctx), widget(static_cast(host)) { 21 | widget->installEventFilter(this); 22 | } 23 | 24 | WId winId() const override { 25 | return widget->effectiveWinId(); 26 | } 27 | 28 | protected: 29 | bool eventFilter(QObject *obj, QEvent *event) override { 30 | Q_UNUSED(obj) 31 | if (event->type() == QEvent::WinIdChange) { 32 | context->notifyWinIdChange(); 33 | } 34 | return false; 35 | } 36 | 37 | QWidget *widget; 38 | }; 39 | 40 | WidgetItemDelegate::WidgetItemDelegate() = default; 41 | 42 | WidgetItemDelegate::~WidgetItemDelegate() = default; 43 | 44 | QWindow *WidgetItemDelegate::window(const QObject *obj) const { 45 | return static_cast(obj)->windowHandle(); 46 | } 47 | 48 | bool WidgetItemDelegate::isEnabled(const QObject *obj) const { 49 | return static_cast(obj)->isEnabled(); 50 | } 51 | 52 | bool WidgetItemDelegate::isVisible(const QObject *obj) const { 53 | return static_cast(obj)->isVisible(); 54 | } 55 | 56 | QRect WidgetItemDelegate::mapGeometryToScene(const QObject *obj) const { 57 | auto widget = static_cast(obj); 58 | const QPoint originPoint = widget->mapTo(widget->window(), QPoint(0, 0)); 59 | const QSize size = widget->size(); 60 | return {originPoint, size}; 61 | } 62 | 63 | QWindow *WidgetItemDelegate::hostWindow(const QObject *host) const { 64 | return static_cast(host)->windowHandle(); 65 | } 66 | 67 | bool WidgetItemDelegate::isWindowActive(const QObject *host) const { 68 | return static_cast(host)->isActiveWindow(); 69 | } 70 | 71 | void WidgetItemDelegate::resetQtGrabbedControl(QObject *host) const { 72 | Q_UNUSED(host); 73 | if (!qt_button_down) { 74 | return; 75 | } 76 | static constexpr const auto invalidPos = 77 | QPoint{std::numeric_limits::lowest(), std::numeric_limits::lowest()}; 78 | const auto event = new QMouseEvent( 79 | QEvent::MouseButtonRelease, invalidPos, invalidPos, invalidPos, Qt::LeftButton, 80 | QGuiApplication::mouseButtons() ^ Qt::LeftButton, QGuiApplication::keyboardModifiers()); 81 | QCoreApplication::postEvent(qt_button_down, event); 82 | qt_button_down = nullptr; 83 | } 84 | 85 | Qt::WindowStates WidgetItemDelegate::getWindowState(const QObject *host) const { 86 | return static_cast(host)->windowState(); 87 | } 88 | 89 | void WidgetItemDelegate::setWindowState(QObject *host, Qt::WindowStates state) const { 90 | static_cast(host)->setWindowState(state); 91 | } 92 | 93 | void WidgetItemDelegate::setCursorShape(QObject *host, Qt::CursorShape shape) const { 94 | static_cast(host)->setCursor(QCursor(shape)); 95 | } 96 | 97 | void WidgetItemDelegate::restoreCursorShape(QObject *host) const { 98 | static_cast(host)->unsetCursor(); 99 | } 100 | 101 | Qt::WindowFlags WidgetItemDelegate::getWindowFlags(const QObject *host) const { 102 | return static_cast(host)->windowFlags(); 103 | } 104 | 105 | QRect WidgetItemDelegate::getGeometry(const QObject *host) const { 106 | return static_cast(host)->geometry(); 107 | } 108 | 109 | void WidgetItemDelegate::setWindowFlags(QObject *host, Qt::WindowFlags flags) const { 110 | static_cast(host)->setWindowFlags(flags); 111 | } 112 | 113 | void WidgetItemDelegate::setWindowVisible(QObject *host, bool visible) const { 114 | static_cast(host)->setVisible(visible); 115 | } 116 | 117 | void WidgetItemDelegate::setGeometry(QObject *host, const QRect &rect) { 118 | static_cast(host)->setGeometry(rect); 119 | } 120 | 121 | void WidgetItemDelegate::bringWindowToTop(QObject *host) const { 122 | static_cast(host)->raise(); 123 | } 124 | 125 | WinIdChangeEventFilter * 126 | WidgetItemDelegate::createWinIdEventFilter(QObject *host, 127 | AbstractWindowContext *context) const { 128 | return new WidgetWinIdChangeEventFilter(host, context); 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /src/widgets/widgetitemdelegate_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WIDGETITEMDELEGATE_P_H 6 | #define WIDGETITEMDELEGATE_P_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | namespace QWK { 24 | 25 | class QWK_WIDGETS_EXPORT WidgetItemDelegate : public WindowItemDelegate { 26 | public: 27 | WidgetItemDelegate(); 28 | ~WidgetItemDelegate() override; 29 | 30 | public: 31 | QWindow *window(const QObject *obj) const override; 32 | bool isEnabled(const QObject *obj) const override; 33 | bool isVisible(const QObject *obj) const override; 34 | QRect mapGeometryToScene(const QObject *obj) const override; 35 | 36 | QWindow *hostWindow(const QObject *host) const override; 37 | bool isWindowActive(const QObject *host) const override; 38 | Qt::WindowStates getWindowState(const QObject *host) const override; 39 | Qt::WindowFlags getWindowFlags(const QObject *host) const override; 40 | QRect getGeometry(const QObject *host) const override; 41 | 42 | void resetQtGrabbedControl(QObject *host) const override; 43 | void setWindowState(QObject *host, Qt::WindowStates state) const override; 44 | void setCursorShape(QObject *host, Qt::CursorShape shape) const override; 45 | void restoreCursorShape(QObject *host) const override; 46 | void setWindowFlags(QObject *host, Qt::WindowFlags flags) const override; 47 | void setWindowVisible(QObject *host, bool visible) const override; 48 | void setGeometry(QObject *host, const QRect &rect) override; 49 | void bringWindowToTop(QObject *host) const override; 50 | 51 | WinIdChangeEventFilter * 52 | createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const override; 53 | }; 54 | 55 | } 56 | 57 | #endif // WIDGETITEMDELEGATE_P_H 58 | -------------------------------------------------------------------------------- /src/widgets/widgetwindowagent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "widgetwindowagent.h" 6 | #include "widgetwindowagent_p.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "widgetitemdelegate_p.h" 13 | 14 | namespace QWK { 15 | 16 | /*! 17 | \class WidgetWindowAgent 18 | \brief WindowAgentBase is the window agent for QtWidgets. 19 | 20 | It provides interfaces for QtWidgets and processes some Qt events related to the QWidget 21 | instance. 22 | */ 23 | 24 | WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() = default; 25 | 26 | WidgetWindowAgentPrivate::~WidgetWindowAgentPrivate() = default; 27 | 28 | void WidgetWindowAgentPrivate::init() { 29 | } 30 | 31 | /*! 32 | Constructs a widget agent, it's better to set the widget to setup as \a parent. 33 | */ 34 | WidgetWindowAgent::WidgetWindowAgent(QObject *parent) 35 | : WidgetWindowAgent(*new WidgetWindowAgentPrivate(), parent) { 36 | } 37 | 38 | /*! 39 | Destructor. 40 | */ 41 | WidgetWindowAgent::~WidgetWindowAgent() = default; 42 | 43 | /*! 44 | Installs the window agent on the widget. The window agent will take over some of the window 45 | events, making the window look frameless. 46 | */ 47 | bool WidgetWindowAgent::setup(QWidget *w) { 48 | Q_ASSERT(w); 49 | if (!w) { 50 | return false; 51 | } 52 | 53 | Q_D(WidgetWindowAgent); 54 | if (d->hostWidget) { 55 | return false; 56 | } 57 | 58 | // Qt will create invisible native window container for native QWidget 59 | // without this attribute, and this behavior will break QWK functionality. 60 | // So far enabling this attribute is a must for QWK users. 61 | w->setAttribute(Qt::WA_DontCreateNativeAncestors); 62 | // Make sure the native window handle is actually created before we apply 63 | // various hooks. 64 | //w->setAttribute(Qt::WA_NativeWindow); // ### FIXME: Check 65 | 66 | d->setup(w, new WidgetItemDelegate()); 67 | d->hostWidget = w; 68 | 69 | #if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) 70 | d->setupWindows10BorderWorkaround(); 71 | #endif 72 | return true; 73 | } 74 | 75 | /*! 76 | Returns the title bar widget. 77 | */ 78 | QWidget *WidgetWindowAgent::titleBar() const { 79 | Q_D(const WidgetWindowAgent); 80 | return static_cast(d->context->titleBar()); 81 | } 82 | 83 | /*! 84 | Sets the title bar widget, all system button and hit-test visible widget references that 85 | have been set will be removed. 86 | */ 87 | void WidgetWindowAgent::setTitleBar(QWidget *w) { 88 | Q_D(WidgetWindowAgent); 89 | if (!d->context->setTitleBar(w)) { 90 | return; 91 | } 92 | #ifdef Q_OS_MAC 93 | setSystemButtonArea(nullptr); 94 | #endif 95 | Q_EMIT titleBarChanged(w); 96 | } 97 | 98 | /*! 99 | Returns the system button of the given type. 100 | */ 101 | QWidget *WidgetWindowAgent::systemButton(SystemButton button) const { 102 | Q_D(const WidgetWindowAgent); 103 | return static_cast(d->context->systemButton(button)); 104 | } 105 | 106 | /*! 107 | Sets the system button of the given type, the system buttons always receive mouse events so 108 | you don't need to call \c setHitTestVisible for them. 109 | */ 110 | void WidgetWindowAgent::setSystemButton(SystemButton button, QWidget *w) { 111 | Q_D(WidgetWindowAgent); 112 | if (!d->context->setSystemButton(button, w)) { 113 | return; 114 | } 115 | Q_EMIT systemButtonChanged(button, w); 116 | } 117 | 118 | /*! 119 | Returns \a true if the widget can receive mouse events on title bar. 120 | */ 121 | bool WidgetWindowAgent::isHitTestVisible(const QWidget *w) const { 122 | Q_D(const WidgetWindowAgent); 123 | return d->context->isHitTestVisible(w); 124 | } 125 | 126 | /*! 127 | Makes the widget able to receive mouse events on title bar if \a visible is \c true. 128 | You're supposed to make sure that the specified widget \a w is a child or descendant 129 | of the title bar widget. 130 | */ 131 | void WidgetWindowAgent::setHitTestVisible(QWidget *w, bool visible) { 132 | Q_D(WidgetWindowAgent); 133 | d->context->setHitTestVisible(w, visible); 134 | } 135 | 136 | /*! 137 | \internal 138 | */ 139 | WidgetWindowAgent::WidgetWindowAgent(WidgetWindowAgentPrivate &d, QObject *parent) 140 | : WindowAgentBase(d, parent) { 141 | d.init(); 142 | } 143 | 144 | /*! 145 | \fn void WidgetWindowAgent::titleBarChanged(const QWidget *w) 146 | 147 | This signal is emitted when the title bar widget is replaced. 148 | */ 149 | 150 | /*! 151 | \fn void WidgetWindowAgent::systemButtonChanged(SystemButton button, const QWidget *w) 152 | 153 | This signal is emitted when a system button is replaced. 154 | */ 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/widgets/widgetwindowagent.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WIDGETWINDOWAGENT_H 6 | #define WIDGETWINDOWAGENT_H 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace QWK { 14 | 15 | class WidgetWindowAgentPrivate; 16 | 17 | class QWK_WIDGETS_EXPORT WidgetWindowAgent : public WindowAgentBase { 18 | Q_OBJECT 19 | Q_DECLARE_PRIVATE(WidgetWindowAgent) 20 | public: 21 | explicit WidgetWindowAgent(QObject *parent = nullptr); 22 | ~WidgetWindowAgent() override; 23 | 24 | public: 25 | bool setup(QWidget *w); 26 | 27 | QWidget *titleBar() const; 28 | void setTitleBar(QWidget *w); 29 | 30 | QWidget *systemButton(SystemButton button) const; 31 | void setSystemButton(SystemButton button, QWidget *w); 32 | 33 | #ifdef Q_OS_MAC 34 | // The system button area APIs are experimental, very likely to change in the future. 35 | QWidget *systemButtonArea() const; 36 | void setSystemButtonArea(QWidget *widget); 37 | 38 | ScreenRectCallback systemButtonAreaCallback() const; 39 | void setSystemButtonAreaCallback(const ScreenRectCallback &callback); 40 | #endif 41 | 42 | bool isHitTestVisible(const QWidget *w) const; 43 | void setHitTestVisible(QWidget *w, bool visible = true); 44 | 45 | Q_SIGNALS: 46 | void titleBarChanged(QWidget *w); 47 | void systemButtonChanged(SystemButton button, QWidget *w); 48 | 49 | protected: 50 | WidgetWindowAgent(WidgetWindowAgentPrivate &d, QObject *parent = nullptr); 51 | }; 52 | 53 | } 54 | 55 | #endif // WIDGETWINDOWAGENT_H -------------------------------------------------------------------------------- /src/widgets/widgetwindowagent_mac.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "widgetwindowagent_p.h" 6 | 7 | #include 8 | 9 | namespace QWK { 10 | 11 | static inline QRect getWidgetSceneRect(QWidget *widget) { 12 | return {widget->mapTo(widget->window(), QPoint()), widget->size()}; 13 | } 14 | 15 | class SystemButtonAreaWidgetEventFilter : public QObject { 16 | public: 17 | SystemButtonAreaWidgetEventFilter(QWidget *widget, AbstractWindowContext *ctx, 18 | QObject *parent = nullptr) 19 | : QObject(parent), widget(widget), ctx(ctx) { 20 | widget->installEventFilter(this); 21 | ctx->setSystemButtonAreaCallback([widget](const QSize &) { 22 | return getWidgetSceneRect(widget); // 23 | }); 24 | } 25 | ~SystemButtonAreaWidgetEventFilter() override = default; 26 | 27 | protected: 28 | bool eventFilter(QObject *obj, QEvent *event) override { 29 | Q_UNUSED(obj) 30 | switch (event->type()) { 31 | case QEvent::Move: 32 | case QEvent::Resize: { 33 | ctx->virtual_hook(AbstractWindowContext::SystemButtonAreaChangedHook, nullptr); 34 | break; 35 | } 36 | 37 | default: 38 | break; 39 | } 40 | return false; 41 | } 42 | 43 | protected: 44 | QWidget *widget; 45 | AbstractWindowContext *ctx; 46 | }; 47 | 48 | /*! 49 | Returns the widget that acts as the system button area. 50 | */ 51 | QWidget *WidgetWindowAgent::systemButtonArea() const { 52 | Q_D(const WidgetWindowAgent); 53 | return d->systemButtonAreaWidget; 54 | } 55 | 56 | /*! 57 | Sets the widget that acts as the system button area. The system button will be centered in 58 | its area, it is recommended to place the widget in a layout and set a fixed size policy. 59 | 60 | The system button will be visible in the system title bar area. 61 | */ 62 | void WidgetWindowAgent::setSystemButtonArea(QWidget *widget) { 63 | Q_D(WidgetWindowAgent); 64 | if (d->systemButtonAreaWidget == widget) 65 | return; 66 | 67 | auto ctx = d->context.get(); 68 | d->systemButtonAreaWidget = widget; 69 | if (!widget) { 70 | d->context->setSystemButtonAreaCallback({}); 71 | d->systemButtonAreaWidgetEventFilter.reset(); 72 | return; 73 | } 74 | d->systemButtonAreaWidgetEventFilter = 75 | std::make_unique(widget, ctx); 76 | } 77 | 78 | /*! 79 | Returns the the system button area callback. 80 | */ 81 | ScreenRectCallback WidgetWindowAgent::systemButtonAreaCallback() const { 82 | Q_D(const WidgetWindowAgent); 83 | return d->systemButtonAreaWidget ? nullptr : d->context->systemButtonAreaCallback(); 84 | } 85 | 86 | /*! 87 | Sets the the system button area callback, the \c size of the callback is the native title 88 | bar size. 89 | 90 | The system button position will be updated when the window resizes. 91 | */ 92 | void WidgetWindowAgent::setSystemButtonAreaCallback(const ScreenRectCallback &callback) { 93 | Q_D(WidgetWindowAgent); 94 | setSystemButtonArea(nullptr); 95 | d->context->setSystemButtonAreaCallback(callback); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/widgets/widgetwindowagent_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #ifndef WIDGETWINDOWAGENTPRIVATE_H 6 | #define WIDGETWINDOWAGENTPRIVATE_H 7 | 8 | // 9 | // W A R N I N G !!! 10 | // ----------------- 11 | // 12 | // This file is not part of the QWindowKit API. It is used purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or may even be removed. 15 | // 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace QWK { 22 | 23 | class WidgetWindowAgentPrivate : public WindowAgentBasePrivate { 24 | Q_DECLARE_PUBLIC(WidgetWindowAgent) 25 | public: 26 | WidgetWindowAgentPrivate(); 27 | ~WidgetWindowAgentPrivate(); 28 | 29 | void init(); 30 | 31 | // Host 32 | QWidget *hostWidget{}; 33 | 34 | #ifdef Q_OS_MAC 35 | QWidget *systemButtonAreaWidget{}; 36 | std::unique_ptr systemButtonAreaWidgetEventFilter; 37 | #endif 38 | 39 | #if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) 40 | void setupWindows10BorderWorkaround(); 41 | std::unique_ptr borderHandler; 42 | #endif 43 | }; 44 | 45 | } 46 | 47 | #endif // WIDGETWINDOWAGENTPRIVATE_H 48 | -------------------------------------------------------------------------------- /src/widgets/widgetwindowagent_win.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) 2 | // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | #include "widgetwindowagent_p.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace QWK { 18 | 19 | #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) 20 | // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowsbackingstore.cpp#L42 21 | // In QtWidgets applications, when repainting happens, QPA at the last calls 22 | // QWindowsBackingStore::flush() to draw the contents of the buffer to the screen, we need to 23 | // call GDI drawing the top border after that. 24 | 25 | // After debugging, we know that there are two situations that will lead to repaint. 26 | // 27 | // 1. Windows sends a WM_PAINT message, after which Qt immediately generates a QExposeEvent or 28 | // QResizeEvent and send it to the corresponding QWidgetWindow instance, calling "flush" at the 29 | // end of its handler. 30 | // 31 | // 2. When a timer or user input triggers Qt to repaint spontaneously, the corresponding 32 | // QWidget receives a QEvent::UpdateRequest event and also calls "flush" at the end of its 33 | // handler. 34 | // 35 | // The above two cases are mutually exclusive, so we just need to intercept the two events 36 | // separately and draw the border area after the "flush" is called. 37 | 38 | // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowswindow.cpp#L2440 39 | // Note that we can not draw the border right after WM_PAINT comes or right before the WndProc 40 | // returns, because Qt calls BeginPaint() and EndPaint() itself. We should make sure that we 41 | // draw the top border between these two calls, otherwise some display exceptions may arise. 42 | 43 | class WidgetBorderHandler : public QObject, public Windows10BorderHandler { 44 | public: 45 | explicit WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx, 46 | QObject *parent = nullptr) 47 | : QObject(parent), Windows10BorderHandler(ctx), widget(widget) { 48 | widget->installEventFilter(this); 49 | 50 | // First update 51 | if (ctx->windowId()) { 52 | setupNecessaryAttributes(); 53 | } 54 | WidgetBorderHandler::updateGeometry(); 55 | } 56 | 57 | void updateGeometry() override { 58 | // The window top border is manually painted by QWK so we want to give 59 | // some margins to avoid it covering real window contents, however, we 60 | // found that there are some rounding issues for the thin border and 61 | // thus this small trick doesn't work very well when the DPR is not 62 | // integer. So far we haven't found a perfect solution, so just don't 63 | // set any margins. In theory the window content will only be covered 64 | // by 1px or so, it should not be a serious issue in the real world. 65 | // 66 | // widget->setContentsMargins(isNormalWindow() ? QMargins(0, borderThickness(), 0, 0) 67 | // : QMargins()); 68 | } 69 | 70 | bool isWindowActive() const override { 71 | return widget->isActiveWindow(); 72 | } 73 | 74 | inline void forwardEventToWidgetAndDraw(QWidget *w, QEvent *event) { 75 | // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qapplication.cpp#L3286 76 | // Deliver the event 77 | if (!forwardObjectEventFilters(this, w, event)) { 78 | // Let the widget paint first 79 | std::ignore = static_cast(w)->event(event); 80 | QCoreApplicationPrivate::setEventSpontaneous(event, false); 81 | } 82 | 83 | // Due to the timer or user action, Qt will repaint some regions spontaneously, 84 | // even if there is no WM_PAINT message, we must wait for it to finish painting 85 | // and then update the top border area. 86 | drawBorderNative(); 87 | } 88 | 89 | inline void forwardEventToWindowAndDraw(QWindow *window, QEvent *event) { 90 | // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qapplication.cpp#L3286 91 | // Deliver the event 92 | if (!forwardObjectEventFilters(ctx, window, event)) { 93 | // Let Qt paint first 94 | std::ignore = static_cast(window)->event(event); 95 | QCoreApplicationPrivate::setEventSpontaneous(event, false); 96 | } 97 | 98 | // Upon receiving the WM_PAINT message, Qt will repaint the entire view, and we 99 | // must wait for it to finish painting before drawing this top border area. 100 | drawBorderNative(); 101 | } 102 | 103 | protected: 104 | bool sharedEventFilter(QObject *obj, QEvent *event) override { 105 | Q_UNUSED(obj) 106 | 107 | switch (event->type()) { 108 | case QEvent::Expose: { 109 | // Qt will absolutely send a QExposeEvent or QResizeEvent to the QWindow when it 110 | // receives a WM_PAINT message. When the control flow enters the expose handler, 111 | // Qt must have already called BeginPaint() and it's the best time for us to 112 | // draw the top border. 113 | 114 | // Since a QExposeEvent will be sent immediately after the QResizeEvent, we can 115 | // simply ignore it. 116 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 117 | struct ExposeEvent : public QExposeEvent { 118 | inline const QRegion &getRegion() const { return m_region; } 119 | }; 120 | auto ee = static_cast(event); 121 | bool exposeRegionValid = !ee->getRegion().isNull(); 122 | #else 123 | auto ee = static_cast(event); 124 | bool exposeRegionValid = !ee->region().isNull(); 125 | #endif 126 | auto window = widget->windowHandle(); 127 | if (window->isExposed() && isNormalWindow() && exposeRegionValid) { 128 | forwardEventToWindowAndDraw(window, event); 129 | return true; 130 | } 131 | break; 132 | } 133 | default: 134 | break; 135 | } 136 | return Windows10BorderHandler::sharedEventFilter(obj, event); 137 | } 138 | 139 | bool eventFilter(QObject *obj, QEvent *event) override { 140 | Q_UNUSED(obj) 141 | 142 | switch (event->type()) { 143 | case QEvent::UpdateRequest: { 144 | if (!isNormalWindow()) 145 | break; 146 | forwardEventToWidgetAndDraw(widget, event); 147 | return true; 148 | } 149 | 150 | case QEvent::WindowStateChange: { 151 | updateGeometry(); 152 | break; 153 | } 154 | 155 | case QEvent::WindowActivate: 156 | case QEvent::WindowDeactivate: { 157 | widget->update(); 158 | break; 159 | } 160 | 161 | default: 162 | break; 163 | } 164 | return false; 165 | } 166 | 167 | QWidget *widget; 168 | }; 169 | 170 | void WidgetWindowAgentPrivate::setupWindows10BorderWorkaround() { 171 | // Install painting hook 172 | auto ctx = context.get(); 173 | if (ctx->windowAttribute(QStringLiteral("win10-border-needed")).toBool()) { 174 | borderHandler = std::make_unique(hostWidget, ctx); 175 | } 176 | } 177 | #endif 178 | 179 | } 180 | --------------------------------------------------------------------------------