add support for frameless window!

This commit is contained in:
Skarn
2020-11-04 18:33:21 +03:00
parent 4f8a5b68fe
commit deffc255ff
101 changed files with 8327 additions and 0 deletions

View File

@@ -186,6 +186,7 @@ if (USE_SQL)
endif()
add_subdirectory (src/external/qt-color-widgets)
add_subdirectory (src/external/framelesshelper)
# Add the found include directories to our include list.
include_directories (SYSTEM "${CMAKE_SOURCE_DIR}/include/")
@@ -220,6 +221,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/libpng" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/libtxc_dxtn" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/pngpp" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/zlib" )
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
COLLECT_FILES(false noggit_root_sources src/noggit .cpp)
COLLECT_FILES(true noggit_ui_sources src/noggit/ui .cpp)
@@ -315,6 +317,7 @@ TARGET_LINK_LIBRARIES (noggit
Qt5::OpenGL
Qt5::OpenGLExtensions
ColorWidgets-qt5
FramelessHelper
)
set (_noggit_revision_output_dir "${CMAKE_BINARY_DIR}/revision_output")

71
src/external/framelesshelper/.gitignore vendored Normal file
View File

@@ -0,0 +1,71 @@
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.dll
*.dylib
# Qt-es
object_script.*.Release
object_script.*.Debug
*_plugin_import.cpp
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
moc_*.h
qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
# Qt unit tests
target_wrapper.*
# QtCreator
*.autosave
# QtCreator Qml
*.qmlproject.user
*.qmlproject.user.*
# QtCreator CMake
CMakeLists.txt.user*
# My
[Bb]in/
[Bb]in64/
[Bb]uild*/
*.7z
*.zip
*.rar
*.tar
*.gz
*.xz
*.exe
*.lib
*.pdb
*.ilk
*.exp
*.obj
build.user.bat
build.user.sh
user.conf
[Dd]oc/
[Dd]ocs/
Thumbs.db
*.rc
*.qm
*.bin
*.run
.qmake.conf

View File

@@ -0,0 +1,92 @@
cmake_minimum_required(VERSION 3.15)
project(FramelessHelper LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
if(WIN32)
set(CMAKE_DEBUG_POSTFIX d)
else()
set(CMAKE_DEBUG_POSTFIX _debug)
endif()
find_package(Qt5 COMPONENTS Gui REQUIRED)
find_package(Qt5 COMPONENTS Widgets Quick)
set(SOURCES
framelesshelper_global.h
framelesswindowsmanager.h
framelesswindowsmanager.cpp
)
if(Qt5Quick_FOUND)
list(APPEND SOURCES
framelessquickhelper.h
framelessquickhelper.cpp
)
endif()
if(WIN32)
list(APPEND SOURCES
winnativeeventfilter.h
winnativeeventfilter.cpp
)
else()
list(APPEND SOURCES
framelesshelper.h
framelesshelper.cpp
)
endif()
if(WIN32 AND BUILD_SHARED_LIBS)
enable_language(RC)
list(APPEND SOURCES framelesshelper.rc)
endif()
add_library(${PROJECT_NAME} ${SOURCES})
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(${PROJECT_NAME} PUBLIC
FRAMELESSHELPER_STATIC
)
endif()
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /utf-8)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
target_compile_options(${PROJECT_NAME} PRIVATE /guard:cf)
target_link_options(${PROJECT_NAME} PRIVATE /GUARD:CF)
endif()
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
FRAMELESSHELPER_BUILD_LIBRARY
)
if(WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE
WIN32_LEAN_AND_MEAN
_CRT_SECURE_NO_WARNINGS
WNEF_LINK_SYSLIB
)
target_link_libraries(${PROJECT_NAME} PRIVATE
user32 shell32 gdi32 dwmapi shcore uxtheme d2d1
)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::GuiPrivate)
if(Qt5Widgets_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Widgets)
endif()
if(Qt5Quick_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt::Quick)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
)

21
src/external/framelesshelper/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

149
src/external/framelesshelper/README.md vendored Normal file
View File

@@ -0,0 +1,149 @@
# FramelessHelper
If you are using part of or all the code from this repository in your own projects, it's my pleasure and I'm happy that it can help you. But I hope you can tell me the URL of the homepage or repository of your projects, whether your projects are close-sourced or commercial products do not matter. I'll link to your homepage or repository in this README file. It would be much better if you can provide me some screenshots of your software for demonstration.
如果您正在使用此项目的部分或全部代码,这是我的荣幸,我很高兴能帮到您,但我同时也希望,您能将您项目的首页或仓库的网址告诉我(闭源或收费都没关系),我将在这个自述文件中链接到您所提供的网址,以供展示。如果您能一并提供一些软件运行时的截图,那就更好了。
## Screenshots - Win32
![Win32Demo 1](/screenshots/win32demo1.png)
![Win32Demo 2](/screenshots/win32demo2.png)
![Win32Demo 3](/screenshots/win32demo3.png)
![Win32Demo 4](/screenshots/win32demo4.png)
![Win32Demo 5](/screenshots/win32demo5.png)
![Win32Demo 6](/screenshots/win32demo6.png)
## Screenshots - Cross platform
![zhihuiyanglao](/screenshots/zhihuiyanglao.png)
![QQ Player](/screenshots/qqplayer.png)
![Qt Widgets example](/screenshots/widgets.png)
![Qt MainWindow example](/screenshots/mainwindow.png)
![Qt Quick example](/screenshots/quick.png)
## Features
- Support Windows, X11, Wayland and macOS.
- Frameless but have frame shadow.
- Draggable and resizable.
- Automatically high DPI scaling.
- Multi-monitor support (different resolution and DPI).
- Have animations when minimizing, maximizing and restoring.
- Support tiled and stack windows by DWM (Win32 only).
- Won't cover the task bar when maximized (Win32 only).
- Won't block the auto-hide task bar when maximized (Win32 only).
- No flickers when resizing.
- Load all native APIs at run-time, no need to link to any system libraries directly (Win32 only).
## Usage
```cpp
#include "framelesswindowsmanager.h"
#include <QWidget>
int main(int argc, char *argv[]) {
QWidget widget;
// Do this before the widget is shown.
FramelessWindowsManager::addWindow(&widget);
widget.show();
}
```
Please refer to [the QWidget example](/examples/QWidget/main.cpp) for more detailed information.
### Ignore areas and etc
```cpp
// Only **TOP LEVEL** QWidgets and QWindows are supported.
QMainWindow *mainWindow = new QMainWindow;
// Disable resizing of the given window. Resizing is enabled by default.
FramelessWindowsManager::setResizable(mainWindow, false);
// All the following values should not be DPI-aware, just use the
// original numbers, assuming the scale factor is 1.0, don't scale
// them yourself, this code will do the scaling according to DPI
// internally and automatically.
// Maximum window size
FramelessWindowsManager::setMaximumSize(mainWindow, {1280, 720});
// Minimum window size
FramelessWindowsManager::setMinimumSize(mainWindow, {800, 540});
// How to set ignore areas:
// The geometry of something you already know, in window coordinates
FramelessWindowsManager::addIgnoreArea(mainWindow, {100, 0, 30, 30});
// The geometry of a widget, in window coordinates.
// It won't update automatically when the geometry of that widget has
// changed, so if you want to add a widget, which is in a layout and
// it's geometry will possibly change, to the ignore list, try the
// next method (addIgnoreObject) instead.
FramelessWindowsManager::addIgnoreArea(mainWindow, pushButton_close.geometry());
// The **POINTER** of a QWidget or QQuickItem
FramelessWindowsManager::addIgnoreObject(mainWindow, ui->pushButton_minimize);
// Move a QWidget or QWindow to the center of its current desktop.
FramelessWindowsManager::moveWindowToDesktopCenter(mainWindow);
```
## Supported Platforms
### Win32
Windows 7 ~ 10, 32 bit & 64 bit.
The code itself should be able to work on Windows Vista in theory, but Qt5 has dropped Vista support long time ago. And Qt6 will only support Win10.
### UNIX
A not too old version of Linux and macOS, 32 bit & 64 bit.
## Requirements
### Win32
| Component | Requirement | Additional Information |
| --- | --- | --- |
| Qt | >= 5.6 | Only the `gui` module is required explicitly, but to make full use of this repository, you'd better install the `widgets` and `quick` modules as well |
| Compiler | >= C++11 | MSVC, MinGW, Clang-CL, Intel-CL or cross compile from Linux/macOS are all supported |
### UNIX
| Component | Requirement | Additional Information |
| --- | --- | --- |
| Qt | >= 5.15 | This code uses two functions, [`startSystemMove`](https://doc.qt.io/qt-5/qwindow.html#startSystemMove) and [`startSystemResize`](https://doc.qt.io/qt-5/qwindow.html#startSystemResize), which are introduced in Qt 5.15 |
| Compiler | >= C++11 | MSVC, MinGW, Clang-CL, Intel-CL / GCC, Clang, ICC are all supported |
## Known Bugs
Please refer to <https://github.com/wangwenx190/framelesshelper/issues> for more information.
## License
```text
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View File

@@ -0,0 +1,69 @@
# FramelessHelper (UNIX version)
## Screenshots
TODO
## Features
- Frameless (of course!).
- Draggable.
- Resizable.
- Cross-platform: Windows, X11, Wayland, macOS (however, it doesn't look good on Windows, so you may want to try [`WinNativeEventFilter`](/winnativeeventfilter.h) instead).
As for the frame shadow and other window features, they mainly depend on your window manager.
## Usage
```cpp
QWidget widget;
FramelessHelper helper;
helper.removeWindowFrame(&widget);
widget.show();
```
### Ignore areas and etc
```cpp
helper.setIgnoreAreas(&widget, {{0, 0, 30, 40}, {40, 0, 30, 40}});
```
## Requirements
| Component | Requirement | Additional Information |
| --- | --- | --- |
| Qt | >= 5.15 | This code uses two functions, [`startSystemMove`](https://doc.qt.io/qt-5/qwindow.html#startSystemMove) and [`startSystemResize`](https://doc.qt.io/qt-5/qwindow.html#startSystemResize), which are introduced in Qt 5.15 |
| Compiler | >= C++11 | MSVC, MinGW, Clang-CL, Intel-CL / GCC, Clang, ICC are all supported |
## References for developers
- <https://doc.qt.io/qt-5/qobject.html#installEventFilter>
- <https://doc.qt.io/qt-5/qobject.html#removeEventFilter>
- <https://doc.qt.io/qt-5/qwindow.html#startSystemMove>
- <https://doc.qt.io/qt-5/qwindow.html#startSystemResize>
## License
```text
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View File

@@ -0,0 +1,203 @@
# FramelessHelper (Win32 version)
## Screenshots
![Win32Demo 1](/screenshots/win32demo1.png)
![Win32Demo 2](/screenshots/win32demo2.png)
![Win32Demo 3](/screenshots/win32demo3.png)
![Win32Demo 4](/screenshots/win32demo4.png)
![Win32Demo 5](/screenshots/win32demo5.png)
![Win32Demo 6](/screenshots/win32demo6.png)
## Features
- Frameless but have frame shadow.
- Draggable and resizable.
- Automatically high DPI scaling.
- Multi-monitor support (different resolution and DPI).
- Have animations when minimizing, maximizing and restoring.
- Support tiled and stack windows by DWM.
- Won't cover the task bar when maximized.
- Won't block the auto-hide task bar when maximized.
- No flickers when resizing.
- Load all Win32 APIs at run-time, no need to link to any system libraries directly.
## Usage
```cpp
QWidget widget;
// Do this before the widget is shown.
WinNativeEventFilter::addFramelessWindow(&widget);
widget.show();
```
Please refer to [the QWidget example](/examples/QWidget/main.cpp) for more detailed information.
### Ignore areas and etc
```cpp
WinNativeEventFilter::WINDOWDATA data = {};
// All the following values should not be DPI-aware, just use the
// original numbers, assuming the scale factor is 1.0, don't scale
// them yourself, this code will do the scaling according to DPI
// internally and automatically.
// Maximum window size
data.maximumSize = {1280, 720};
// Minimum window size
data.minimumSize = {800, 540};
// How to set ignore areas:
// The geometry of something you already know, in window coordinates
data.ignoreAreas.append({100, 0, 30, 30});
// The geometry of a widget, in window coordinates.
// It won't update automatically when the geometry of that widget has
// changed, so if you want to add a widget, which is in a layout and
// it's geometry will possibly change, to the ignore list, try the
// next method (ignoreObjects) instead.
data.ignoreAreas.append(pushButton_close.geometry());
// The **POINTER** of a QWidget or QQuickItem
data.ignoreObjects.append(ui->pushButton_minimize);
// Pass data as the second parameter
WinNativeEventFilter::addFramelessWindow(&widget, &data);
// Or
WinNativeEventFilter::setWindowData(&widget, &data);
// Or modify the window data of a specific window directly:
const auto data = WinNativeEventFilter::windowData(&widget);
if (data) {
data->borderWidth = 5;
data->borderHeight = 5;
data->titleBarHeight = 30;
}
// The frameless window is resizable by default.
WinNativeEventFilter::setWindowResizable(reinterpret_cast<void *>(mainWindow->winId()), false);
```
## Supported Platforms
Windows 7 ~ 10, 32 bit & 64 bit.
The code itself should be able to work on Windows Vista in theory, but Qt5 has dropped Vista support long time ago. And Qt6 will only support Win10.
## Requirements
| Component | Requirement | Additional Information |
| --- | --- | --- |
| Qt | >= 5.6 | Only the `gui` module is required explicitly, but to make full use of this repository, you'd better install the `widgets` and `quick` modules as well |
| Compiler | >= C++11 | MSVC, MinGW, Clang-CL, Intel-CL or cross compile from Linux are all supported |
## Known Bugs
Please refer to <https://github.com/wangwenx190/framelesshelper/issues> for more information.
## Notes for developers
- As you may have found, if you use this code, the resize areas will be inside the frameless window, however, a normal Win32 window can be resized outside of it. Here is the reason: the `WS_THICKFRAME` window style will cause a window has three transparent areas beside the window's left, right and bottom edge. Their width/height is 8px if the window is not scaled. In most cases, they are totally invisible. It's DWM's responsibility to draw and control them. They exist to let the user resize the window, visually outside of it. They are in the window area, but not the client area, so they are in the non-client area actually. But we have turned the whole window area into client area in `WM_NCCALCSIZE`, so the three transparent resize areas also become a part of the client area and thus they become visible. When we resize the window, it looks like we are resizing inside of it, however, that's because the transparent resize areas are visible now, we ARE resizing outside of the window actually. But I don't know how to make them become transparent again without breaking the frame shadow drawn by DWM. If you really want to solve it, you can try to embed your window into a larger transparent window and draw the frame shadow yourself. [See the discussions here](https://github.com/wangwenx190/framelesshelper/issues/3) for more detailed information.
- Don't change the window flags (for example, enable the Qt::FramelessWindowHint flag) because it will break the functionality of this code. I'll get rid of the window frame (including the titlebar of course) in Win32 native events.
- All traditional Win32 APIs are replaced by their DPI-aware ones, if there is one.
- Starting from Windows 10, normal windows usually have a one pixel width border line. After many times of trying, I still can't preserve it if I want to remove the window frame. I don't know how to solve this currently. If you really need it, you have to draw one manually by yourself. [See the discussions here](https://github.com/wangwenx190/framelesshelper/issues/3) for more detailed information.
- The frame shadow will get lost if the window is totally transparent. It can't be solved unless you draw the frame shadow manually.
- On Windows 7, if you disabled the Windows Aero, the frame shadow will be disabled as well because it's DWM's resposibility to draw the frame shadow.
- The border width (8 if not scaled), border height (8 if not scaled) and titlebar height (30 if not scaled) are acquired by Win32 APIs and are the same with other standard windows, and thus you should not modify them. Only modify them when you really have a good reason to do so.
- You can also copy all the code to `[virtual protected] bool QWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)` or `[virtual protected] bool QWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)`, it's the same with install a native event filter to the application.
## References for developers
### Microsoft Docs
- <https://docs.microsoft.com/en-us/archive/blogs/wpfsdk/custom-window-chrome-in-wpf>
- <https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize>
- <https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest>
- <https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate>
- <https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-erasebkgnd>
- <https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-getminmaxinfo>
- <https://docs.microsoft.com/en-us/windows/win32/dwm/customframe>
- <https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows>
### Windows Presentation Foundation (WPF)
- <https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Shell/WindowChromeWorker.cs>
### Chromium
- <https://github.com/chromium/chromium/blob/master/ui/base/win/hwnd_metrics.cc>
- <https://github.com/chromium/chromium/blob/master/ui/display/win/screen_win.cc>
- <https://github.com/chromium/chromium/blob/master/ui/views/win/hwnd_message_handler.cc>
- <https://github.com/chromium/chromium/blob/master/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc>
- <https://github.com/chromium/chromium/blob/master/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc>
- <https://github.com/chromium/chromium/blob/master/ui/views/widget/native_widget_aura.cc>
- <https://github.com/chromium/chromium/blob/master/ui/views/widget/widget.cc>
### Mozilla Firefox
- <https://github.com/mozilla/gecko-dev/blob/master/widget/windows/nsWindow.cpp>
### Windows Terminal
- <https://github.com/microsoft/terminal/blob/master/src/cascadia/WindowsTerminal/IslandWindow.cpp>
- <https://github.com/microsoft/terminal/blob/master/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp>
### GitHub
- <https://github.com/rossy/borderless-window>
- <https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window>
- <https://github.com/dfct/TrueFramelessWindow>
- <https://github.com/qtdevs/FramelessHelper>
### Qt
- <https://doc.qt.io/qt-5/qcoreapplication.html#installNativeEventFilter>
- <https://doc.qt.io/qt-5/qcoreapplication.html#removeNativeEventFilter>
- <https://doc.qt.io/qt-5/qobject.html#installEventFilter>
- <https://doc.qt.io/qt-5/qobject.html#removeEventFilter>
- <https://doc.qt.io/qt-5/qwindow.html#startSystemMove>
- <https://doc.qt.io/qt-5/qwindow.html#startSystemResize>
### 窗口样式的作用
`WS_OVERLAPPEDWINDOW` = `WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX`
`WS_OVERLAPPEDWINDOW`代表了一个正常的Win32窗口也就是说它就是正常窗口默认的窗口样式`Window style`)。
`WS_OVERLAPPED`:窗口有标题栏和边界(功能性,并不是真的要有可视化的东西);`WS_CAPTION`窗口有标题栏功能并不是真的要有个可视化的标题栏而是拥有和DWM交互的能力`WS_SYSMENU`窗口可以弹出系统菜单ALT+空格);`WS_THICKFRAME`窗口有三个不可视的拖拽区域前提是非客户区存在并且拥有DWM绘制的边框阴影没有这个样式DWM一定不会绘制边框阴影只要有就一定是自绘的`WS_MINIMIZEBOX`:窗口支持最小化(与`WS_THICKFRAME`一起使用时最小化有动画);`WS_MAXIMIZEBOX`:窗口支持最大化(与`WS_THICKFRAME`一起使用时最大化有动画)。
这些窗口样式,只有`WS_SYSMENU`是可以没有的虽然这样会导致窗口无法弹出系统菜单但如果非要做也是能用其他API模拟的比如火狐和Chrome浏览器的做法如果缺少其他窗口样式会导致窗口的正常功能产生残缺比如最大化最小化时的动画丢失缺少窗口阴影等。
参考资料:<https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles>
## Special Thanks
Thanks **Lucas** for testing this code in many various conditions.
Thanks [**Shujaat Ali Khan**](https://github.com/shujaatak) for searching so many useful articles and repositories for me.
Thanks [**Julien Maille**](https://github.com/JulienMaille) for adding the `QMainWindow` example.
## License
```text
MIT License
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,120 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "../../framelesswindowsmanager.h"
#include "ui_MainWindow.h"
#include "ui_TitleBar.h"
#include <QApplication>
#include <QStyleOption>
#include <QWidget>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
QMainWindow *mainWindow = new QMainWindow;
Ui::MainWindow appMainWindow;
appMainWindow.setupUi(mainWindow);
QWidget *widget = new QWidget;
Ui::TitleBar titleBarWidget;
titleBarWidget.setupUi(widget);
QMenuBar *menuBar = mainWindow->menuBar();
titleBarWidget.horizontalLayout->insertWidget(1, menuBar);
menuBar->setMaximumHeight(20);
mainWindow->setMenuWidget(widget);
QObject::connect(mainWindow,
&QMainWindow::windowIconChanged,
titleBarWidget.iconButton,
&QPushButton::setIcon);
QObject::connect(mainWindow,
&QMainWindow::windowTitleChanged,
titleBarWidget.titleLabel,
&QLabel::setText);
QObject::connect(titleBarWidget.closeButton,
&QPushButton::clicked,
mainWindow,
&QMainWindow::close);
QObject::connect(titleBarWidget.minimizeButton,
&QPushButton::clicked,
mainWindow,
&QMainWindow::showMinimized);
QObject::connect(titleBarWidget.maximizeButton,
&QPushButton::clicked,
[mainWindow, titleBarWidget]() {
if (mainWindow->isMaximized()) {
mainWindow->showNormal();
titleBarWidget.maximizeButton->setToolTip(QObject::tr("Maximize"));
} else {
mainWindow->showMaximized();
titleBarWidget.maximizeButton->setToolTip(QObject::tr("Restore"));
}
});
QStyleOption option;
option.initFrom(mainWindow);
const QIcon icon = mainWindow->style()->standardIcon(QStyle::SP_ComputerIcon, &option);
mainWindow->setWindowIcon(icon);
mainWindow->setWindowTitle(QObject::tr("Hello, World!"));
FramelessWindowsManager::addWindow(mainWindow);
FramelessWindowsManager::addIgnoreObject(mainWindow, titleBarWidget.minimizeButton);
FramelessWindowsManager::addIgnoreObject(mainWindow, titleBarWidget.maximizeButton);
FramelessWindowsManager::addIgnoreObject(mainWindow, titleBarWidget.closeButton);
FramelessWindowsManager::addIgnoreObject(mainWindow, appMainWindow.menubar);
mainWindow->resize(800, 600);
FramelessWindowsManager::moveWindowToDesktopCenter(mainWindow);
mainWindow->show();
return QApplication::exec();
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@@ -0,0 +1,68 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <windows.h>
IDI_ICON1 ICON "QQPlayer.ico"
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "QQPlayer.exe.manifest"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "Built by the Qt Toolkit."
VALUE "CompanyName", "wangwenx190"
VALUE "FileDescription", "QQPlayer DEMO"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "qqplayer-demo"
VALUE "LegalCopyright", "MIT License"
#ifdef _DEBUG
VALUE "OriginalFilename", "QQPlayerd.exe"
#else
VALUE "OriginalFilename", "QQPlayer.exe"
#endif
VALUE "ProductName", "QQPlayer"
VALUE "ProductVersion", "1.0.0.0"
VALUE "LegalTrademarks", "Tencent"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "../../framelessquickhelper.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[]) {
// Enable Qt RHI, the default backend on Windows is D3D 11.1
qputenv("QSG_RHI", "1");
qputenv("QSG_INFO", "1");
#if (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(
Qt::ApplicationAttribute::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(
Qt::ApplicationAttribute::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QGuiApplication::setApplicationName(QString::fromUtf8("QQPlayer"));
QGuiApplication::setApplicationDisplayName(QString::fromUtf8("QQPlayer"));
#ifndef Q_OS_WINDOWS
QGuiApplication::setApplicationVersion(QString::fromUtf8("1.0.0"));
#endif
QGuiApplication::setOrganizationName(QString::fromUtf8("wangwenx190"));
QGuiApplication::setOrganizationDomain(
QString::fromUtf8("wangwenx190.github.io"));
QGuiApplication application(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0,
"FramelessHelper");
const QUrl mainQmlUrl(QString::fromUtf8("qrc:/qml/main.qml"));
const QMetaObject::Connection connection = QObject::connect(
&engine, &QQmlApplicationEngine::objectCreated, &application,
[&mainQmlUrl, &connection](QObject *object, const QUrl &url) {
if (url != mainQmlUrl) {
return;
}
if (!object) {
QGuiApplication::exit(-1);
} else {
QObject::disconnect(connection);
}
},
Qt::QueuedConnection);
engine.load(mainQmlUrl);
return QGuiApplication::exec();
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,47 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 45
implicitHeight: 30
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Close")
contentItem: Image {
anchors.fill: control
source: "qrc:/images/button_close_white.svg"
}
background: Rectangle {
visible: control.down || control.hovered
color: control.down ? "#8c0a15" : (control.hovered ? "#e81123" : "transparent")
}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 75
implicitHeight: 15
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/main_menu_blue.png" : "qrc:/images/main_menu_white.png"
}
background: Item {}
}

View File

@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 45
implicitHeight: 30
property bool maximized: false
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize")
contentItem: Image {
anchors.fill: control
source: maximized ? "qrc:/images/button_restore_white.svg" : "qrc:/images/button_maximize_white.svg"
}
background: Rectangle {
visible: control.down || control.hovered
color: control.down ? "#808080" : (control.hovered ? "#c7c7c7" : "transparent")
}
}

View File

@@ -0,0 +1,47 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 45
implicitHeight: 30
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Minimize")
contentItem: Image {
anchors.fill: control
source: "qrc:/images/button_minimize_white.svg"
}
background: Rectangle {
visible: control.down || control.hovered
color: control.down ? "#808080" : (control.hovered ? "#c7c7c7" : "transparent")
}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 16
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/next_blue_light.png" : "qrc:/images/next_blue.png"
}
background: Item {}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 187
implicitHeight: 50
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/open_file_blue.png" : "qrc:/images/open_file_white.png"
}
background: Item {}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 45
implicitHeight: 45
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/play_blue_light.png" : "qrc:/images/play_blue.png"
}
background: Item {}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 16
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/previous_blue_light.png" : "qrc:/images/previous_blue.png"
}
background: Item {}
}

View File

@@ -0,0 +1,92 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Slider {
id: control
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
property int progressDuration: 200
property int heightDuration: 100
property int hoveredHeight: 10
property int normalHeight: 4
property color backgroundColor: "#bdbebf"
property color foregroundColor: "#21be2b"
property color handleColor: "#f0f0f0"
property int handleWidth: 14
property int handleBorderWidth: 0
property color handleBorderColor: "transparent"
property bool handleVisibility: true
background: Rectangle {
x: 0
y: control.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: control.hovered ? control.hoveredHeight : control.normalHeight
width: control.availableWidth
height: implicitHeight
color: control.backgroundColor
Behavior on implicitHeight {
NumberAnimation {
duration: control.heightDuration
}
}
Rectangle {
width: control.visualPosition * parent.width
height: parent.height
color: control.foregroundColor
Behavior on width {
NumberAnimation {
duration: control.progressDuration
}
}
}
}
handle: Rectangle {
visible: control.handleVisibility
x: control.visualPosition * (control.availableWidth - width)
y: control.availableHeight / 2 - height / 2
implicitWidth: control.handleWidth
implicitHeight: implicitWidth
radius: implicitHeight / 2
color: control.handleColor
border.width: control.handleBorderWidth
border.color: control.handleBorderColor
Behavior on x {
NumberAnimation {
duration: control.progressDuration
}
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 14
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/stop_blue_light.png" : "qrc:/images/stop_blue.png"
}
background: Item {}
}

View File

@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: control
implicitWidth: 18
implicitHeight: 14
contentItem: Image {
anchors.fill: control
source: control.down
|| control.hovered ? "qrc:/images/volume_blue_light.png" : "qrc:/images/volume_blue.png"
}
background: Item {}
}

View File

@@ -0,0 +1,170 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import wangwenx190.Utils 1.0
Window {
id: window
visible: true
width: 800
height: 540
title: qsTr("QQ Player")
property color themeColor: "#111111"
FramelessHelper {
id: framelessHelper
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: window.themeColor
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
MainMenuButton {
id: mainMenuButton
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
Component.onCompleted: framelessHelper.addIgnoreObject(
mainMenuButton)
}
Row {
anchors.top: parent.top
anchors.right: parent.right
MinimizeButton {
id: minimizeButton
onClicked: window.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(
minimizeButton)
}
MaximizeButton {
id: maximizeButton
maximized: window.visibility === Window.Maximized
onClicked: {
if (maximized) {
window.showNormal()
} else {
window.showMaximized()
}
}
Component.onCompleted: framelessHelper.addIgnoreObject(
maximizeButton)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.addIgnoreObject(
closeButton)
}
}
}
Image {
id: backgroundImage
anchors.top: titleBar.bottom
anchors.bottom: controlPanel.top
anchors.left: parent.left
anchors.right: parent.right
source: "qrc:/images/background.png"
Image {
id: logoImage
width: 123
height: 99
anchors.centerIn: parent
source: "qrc:/images/logo.png"
}
OpenFileButton {
id: openFileButton
anchors.top: logoImage.bottom
anchors.topMargin: 25
anchors.horizontalCenter: parent.horizontalCenter
}
}
Rectangle {
id: controlPanel
height: 63
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
color: window.themeColor
ProgressSlider {
id: progressSlider
anchors.bottom: parent.top
anchors.bottomMargin: -2
anchors.left: parent.left
anchors.right: parent.right
foregroundColor: "#1ab7e4"
handleColor: "#923cf2"
}
StopButton {
id: stopButton
anchors.right: previousButton.left
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
PreviousButton {
id: previousButton
anchors.right: playButton.left
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
PlayButton {
id: playButton
anchors.centerIn: parent
}
NextButton {
id: nextButton
anchors.left: playButton.right
anchors.leftMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
VolumeButton {
id: volumeButton
anchors.left: nextButton.right
anchors.leftMargin: 30
anchors.verticalCenter: parent.verticalCenter
}
}
Component.onCompleted: framelessHelper.removeWindowFrame()
}

View File

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

View File

@@ -0,0 +1,110 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "../../framelesswindowsmanager.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
QWidget widget;
widget.setContentsMargins(2, 2, 2, 2);
QLabel *label = new QLabel;
QObject::connect(&widget, &QWidget::windowTitleChanged, label, &QLabel::setText);
QPushButton *minimizeButton = new QPushButton;
minimizeButton->setText(QObject::tr("Minimize"));
QObject::connect(minimizeButton, &QPushButton::clicked, &widget, &QWidget::showMinimized);
QPushButton *maximizeButton = new QPushButton;
maximizeButton->setText(QObject::tr("Maximize"));
QObject::connect(maximizeButton, &QPushButton::clicked, [&widget, &maximizeButton]() {
if (widget.isMaximized()) {
widget.showNormal();
maximizeButton->setText(QObject::tr("Maximize"));
} else {
widget.showMaximized();
maximizeButton->setText(QObject::tr("Restore"));
}
});
QPushButton *closeButton = new QPushButton;
closeButton->setText(QObject::tr("Close"));
QObject::connect(closeButton, &QPushButton::clicked, &widget, &QWidget::close);
QHBoxLayout *tbLayout = new QHBoxLayout;
tbLayout->setContentsMargins(0, 0, 0, 0);
tbLayout->setSpacing(0);
tbLayout->addSpacing(15);
tbLayout->addWidget(label);
tbLayout->addStretch();
tbLayout->addWidget(minimizeButton);
tbLayout->addWidget(maximizeButton);
tbLayout->addWidget(closeButton);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addLayout(tbLayout);
mainLayout->addStretch();
widget.setLayout(mainLayout);
widget.setWindowTitle(QObject::tr("Hello, World!"));
FramelessWindowsManager::addWindow(&widget);
FramelessWindowsManager::addIgnoreObject(&widget, minimizeButton);
FramelessWindowsManager::addIgnoreObject(&widget, maximizeButton);
FramelessWindowsManager::addIgnoreObject(&widget, closeButton);
widget.resize(800, 600);
FramelessWindowsManager::moveWindowToDesktopCenter(&widget);
widget.show();
return QApplication::exec();
}

View File

@@ -0,0 +1,6 @@
TARGET = Quick
TEMPLATE = app
QT += quick
SOURCES += main.cpp
RESOURCES += qml.qrc
include($$PWD/../common.pri)

View File

@@ -0,0 +1,77 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "../../framelessquickhelper.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QGuiApplication application(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0, "FramelessHelper");
const QUrl mainQmlUrl(QString::fromUtf8("qrc:///qml/main.qml"));
const QMetaObject::Connection connection = QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
&application,
[&mainQmlUrl, &connection](QObject *object, const QUrl &url) {
if (url != mainQmlUrl) {
return;
}
if (!object) {
QGuiApplication::exit(-1);
} else {
QObject::disconnect(connection);
}
},
Qt::QueuedConnection);
engine.load(mainQmlUrl);
return QGuiApplication::exec();
}

View File

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

View File

@@ -0,0 +1,48 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: button
implicitWidth: 45
implicitHeight: 30
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Close")
contentItem: Image {
anchors.fill: parent
source: button.down
|| button.hovered ? "qrc:/images/button_close_white.svg" : "qrc:/images/button_close_black.svg"
}
background: Rectangle {
visible: button.down || button.hovered
color: button.down ? "#8c0a15" : (button.hovered ? "#e81123" : "transparent")
}
}

View File

@@ -0,0 +1,49 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: button
implicitWidth: 45
implicitHeight: 30
property bool maximized: false
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize")
contentItem: Image {
anchors.fill: parent
source: maximized ? "qrc:/images/button_restore_black.svg" : "qrc:/images/button_maximize_black.svg"
}
background: Rectangle {
visible: button.down || button.hovered
color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent")
}
}

View File

@@ -0,0 +1,47 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: button
implicitWidth: 45
implicitHeight: 30
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Minimize")
contentItem: Image {
anchors.fill: parent
source: "qrc:/images/button_minimize_black.svg"
}
background: Rectangle {
visible: button.down || button.hovered
color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent")
}
}

View File

@@ -0,0 +1,106 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import wangwenx190.Utils 1.0
Window {
id: window
visible: true
width: 800
height: 600
title: qsTr("Hello, World!")
FramelessHelper {
id: framelessHelper
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: "white"
anchors {
top: parent.top
left: parent.left
right: parent.right
}
Text {
id: titleBarText
text: window.title
font.pointSize: 13
color: window.active ? "black" : "gray"
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
}
Row {
anchors.top: parent.top
anchors.right: parent.right
MinimizeButton {
id: minimizeButton
onClicked: window.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(
minimizeButton)
}
MaximizeButton {
id: maximizeButton
maximized: window.visibility === Window.Maximized
onClicked: {
if (maximized) {
window.showNormal()
} else {
window.showMaximized()
}
}
Component.onCompleted: framelessHelper.addIgnoreObject(
maximizeButton)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.addIgnoreObject(
closeButton)
}
}
}
Rectangle {
id: content
color: "#f0f0f0"
anchors {
top: titleBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
Component.onCompleted: framelessHelper.removeWindowFrame(true)
}

View File

@@ -0,0 +1,177 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import wangwenx190.Utils 1.0
Window {
id: window
visible: true
width: 800
height: 600
title: qsTr("Hello, World!")
color: blurEffectCheckBox.checked ? "transparent" : "#f0f0f0"
FramelessHelper {
id: framelessHelper
}
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: extendToTitleBarCheckBox.checked ? "transparent" : ((window.active && framelessHelper.colorizationEnabled) ? framelessHelper.colorizationColor : "white")
anchors {
top: parent.top
left: parent.left
right: parent.right
}
Label {
id: titleBarText
text: window.title
font.pointSize: 13
color: window.active ? (framelessHelper.colorizationEnabled ? "white" : "black") : "gray"
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
}
Row {
anchors.top: parent.top
anchors.right: parent.right
MinimizeButton {
id: minimizeButton
onClicked: window.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(
minimizeButton)
}
MaximizeButton {
id: maximizeButton
maximized: window.visibility === Window.Maximized
onClicked: {
if (maximized) {
window.showNormal()
} else {
window.showMaximized()
}
}
Component.onCompleted: framelessHelper.addIgnoreObject(
maximizeButton)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.addIgnoreObject(
closeButton)
}
}
}
Rectangle {
id: content
color: "transparent"
anchors {
top: titleBar.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
ColumnLayout {
anchors.centerIn: parent
spacing: 25
Label {
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Current system theme: %1").arg(framelessHelper.darkThemeEnabled ? "dark theme" : "light theme")
}
Label {
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Transparency effect: %1").arg(framelessHelper.transparencyEffectEnabled ? "enabled" : "disabled")
}
CheckBox {
id: blurEffectCheckBox
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Enable blur effect")
onCheckedChanged: framelessHelper.setBlurEffectEnabled(checked, forceAcrylicCheckBox.checked)
}
CheckBox {
id: forceAcrylicCheckBox
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Force enabling Acrylic effect")
enabled: framelessHelper.canHaveWindowFrame
}
CheckBox {
id: extendToTitleBarCheckBox
Layout.alignment: Qt.AlignCenter
font.pointSize: 15
font.bold: true
text: qsTr("Extend to title bar")
}
Button {
Layout.alignment: Qt.AlignCenter
text: qsTr("Move to desktop center")
font.pointSize: 15
font.bold: true
onClicked: framelessHelper.moveWindowToDesktopCenter(true)
}
}
}
Rectangle {
id: topFrame
visible: framelessHelper.canHaveWindowFrame && (window.visibility === Window.Windowed)
color: window.active ? (framelessHelper.colorizationEnabled ? framelessHelper.colorizationColor : "#707070") : "#aaaaaa"
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: 1
}
Component.onCompleted: {
framelessHelper.setWindowFrameVisible(framelessHelper.canHaveWindowFrame)
framelessHelper.removeWindowFrame(true)
}
}

View File

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

View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "widget.h"
#include <QApplication>
#include <QStyleOption>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
// High DPI scaling is enabled by default from Qt 6
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Windows: we are using the manifest file to get maximum compatibility
// because some APIs are not supprted on old systems such as Windows 7
// and Windows 8. And once we have set the DPI awareness level in the
// manifest file, any attemptation to try to change it through API will
// fail. In other words, Qt won't be able to enable or disable high DPI
// scaling or change the DPI awareness level once we have set it in the
// manifest file. So the following two lines are uesless actually (However,
// they are still useful on other platforms).
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Don't round the scale factor.
// This will break QWidget applications because they can't render correctly.
// Qt Quick applications won't have this issue.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QApplication application(argc, argv);
QApplication::setFont({QLatin1String("Microsoft YaHei")});
Widget widget;
QStyleOption option;
option.initFrom(&widget);
widget.setWindowIcon(widget.style()->standardIcon(QStyle::SP_ComputerIcon, &option));
widget.setWindowTitle(QObject::tr("Hello, World!"));
widget.show();
return QApplication::exec();
}

View File

@@ -0,0 +1,641 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "widget.h"
#include "../../winnativeeventfilter.h"
#include <QCheckBox>
#include <QColorDialog>
#include <QEvent>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QOperatingSystemVersion>
#include <QPainter>
#include <QPushButton>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <qt_windows.h>
// Some old SDK doesn't have this value.
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0
#endif
// Copied from windowsx.h
#define GET_X_LPARAM(lp) ((int) (short) LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int) (short) HIWORD(lp))
namespace {
const Widget::Win10Version g_vAcrylicEffectVersion = Widget::Win10Version::Win10_1803;
const QColor g_cDefaultActiveBorderColor = {"#707070"};
const QColor g_cDefaultInactiveBorderColor = {"#aaaaaa"};
QColor g_cColorizationColor = Qt::white;
const char g_sUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
const char g_sDontExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME";
const QLatin1String g_sSystemButtonsStyleSheet(R"(
#iconButton, #minimizeButton, #maximizeButton, #closeButton {
background-color: transparent;
border-radius: 0px;
}
#minimizeButton:hover, #maximizeButton:hover {
border-style: none;
background-color: #80c7c7c7;
}
#minimizeButton:pressed, #maximizeButton:pressed {
border-style: none;
background-color: #80808080;
}
#closeButton:hover {
border-style: none;
background-color: #e81123;
}
#closeButton:pressed {
border-style: none;
background-color: #8c0a15;
}
)");
const
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
QString
#else
QLatin1String
#endif
g_sTitleLabelStyleSheet(
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
QString::fromLatin1(
#endif
R"(
#titleLabel {
color: rgb(%1, %2, %3);
}
)"
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
)
#endif
);
const
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
QString
#else
QLatin1String
#endif
g_sTitleBarStyleSheet(
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
QString::fromLatin1(
#endif
R"(
#titleBarWidget {
background-color: rgba(%1, %2, %3, %4);
border-top: 1px solid rgba(%5, %6, %7, %8);
}
)"
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
)
#endif
);
const QLatin1String g_sMinimizeButtonImageDark(":/images/button_minimize_black.svg");
const QLatin1String g_sMaximizeButtonImageDark(":/images/button_maximize_black.svg");
const QLatin1String g_sRestoreButtonImageDark(":/images/button_restore_black.svg");
const QLatin1String g_sCloseButtonImageDark(":/images/button_close_black.svg");
const QLatin1String g_sMinimizeButtonImageLight(":/images/button_minimize_white.svg");
const QLatin1String g_sMaximizeButtonImageLight(":/images/button_maximize_white.svg");
const QLatin1String g_sRestoreButtonImageLight(":/images/button_restore_white.svg");
const QLatin1String g_sCloseButtonImageLight(":/images/button_close_white.svg");
} // namespace
Widget::Widget(QWidget *parent) : QWidget(parent)
{
initializeWindow();
}
void Widget::retranslateUi()
{
setWindowTitle(tr("Widget"));
iconButton->setText({});
titleLabel->setText({});
minimizeButton->setText({});
maximizeButton->setText({});
closeButton->setText({});
customizeTitleBarCB->setText(tr("Enable customized title bar"));
preserveWindowFrameCB->setText(tr("Preserve window frame"));
blurEffectCB->setText(tr("Enable blur effect"));
extendToTitleBarCB->setText(tr("Extend to title bar"));
forceAcrylicCB->setText(tr("Force enabling Acrylic effect"));
resizableCB->setText(tr("Resizable"));
moveCenterButton->setText(tr("Move to desktop center"));
}
void Widget::setupUi()
{
resize(1056, 600);
verticalLayout_3 = new QVBoxLayout(this);
titleBarWidget = new QWidget(this);
titleBarWidget->setObjectName(QLatin1String("titleBarWidget"));
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(titleBarWidget->sizePolicy().hasHeightForWidth());
titleBarWidget->setSizePolicy(sizePolicy);
const int titleBarHeight
= WinNativeEventFilter::getSystemMetric(rawHandle(),
WinNativeEventFilter::SystemMetric::TitleBarHeight);
titleBarWidget->setMinimumSize(QSize(0, titleBarHeight));
titleBarWidget->setMaximumSize(QSize(16777215, titleBarHeight));
horizontalLayout = new QHBoxLayout(titleBarWidget);
horizontalLayout->setSpacing(0);
horizontalLayout->setContentsMargins(0, 0, 0, 0);
horizontalSpacer_7 = new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
horizontalLayout->addItem(horizontalSpacer_7);
iconButton = new QPushButton(titleBarWidget);
iconButton->setObjectName(QLatin1String("iconButton"));
horizontalLayout->addWidget(iconButton);
horizontalSpacer = new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
horizontalLayout->addItem(horizontalSpacer);
titleLabel = new QLabel(titleBarWidget);
titleLabel->setObjectName(QLatin1String("titleLabel"));
QFont font;
font.setPointSize(10);
titleLabel->setFont(font);
horizontalLayout->addWidget(titleLabel);
horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout->addItem(horizontalSpacer_2);
minimizeButton = new QPushButton(titleBarWidget);
minimizeButton->setObjectName(QLatin1String("minimizeButton"));
QSizePolicy sizePolicy1(QSizePolicy::Fixed, QSizePolicy::Fixed);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(minimizeButton->sizePolicy().hasHeightForWidth());
minimizeButton->setSizePolicy(sizePolicy1);
const QSize systemButtonSize = {45, 30};
minimizeButton->setMinimumSize(systemButtonSize);
minimizeButton->setMaximumSize(systemButtonSize);
QIcon icon;
icon.addFile(g_sMinimizeButtonImageDark, {}, QIcon::Normal, QIcon::Off);
minimizeButton->setIcon(icon);
minimizeButton->setIconSize(systemButtonSize);
horizontalLayout->addWidget(minimizeButton);
maximizeButton = new QPushButton(titleBarWidget);
maximizeButton->setObjectName(QLatin1String("maximizeButton"));
sizePolicy1.setHeightForWidth(maximizeButton->sizePolicy().hasHeightForWidth());
maximizeButton->setSizePolicy(sizePolicy1);
maximizeButton->setMinimumSize(systemButtonSize);
maximizeButton->setMaximumSize(systemButtonSize);
QIcon icon1;
icon1.addFile(g_sMaximizeButtonImageDark, {}, QIcon::Normal, QIcon::Off);
maximizeButton->setIcon(icon1);
maximizeButton->setIconSize(systemButtonSize);
horizontalLayout->addWidget(maximizeButton);
closeButton = new QPushButton(titleBarWidget);
closeButton->setObjectName(QLatin1String("closeButton"));
sizePolicy1.setHeightForWidth(closeButton->sizePolicy().hasHeightForWidth());
closeButton->setSizePolicy(sizePolicy1);
closeButton->setMinimumSize(systemButtonSize);
closeButton->setMaximumSize(systemButtonSize);
QIcon icon2;
icon2.addFile(g_sCloseButtonImageDark, {}, QIcon::Normal, QIcon::Off);
closeButton->setIcon(icon2);
closeButton->setIconSize(systemButtonSize);
horizontalLayout->addWidget(closeButton);
verticalLayout_3->addWidget(titleBarWidget);
contentsWidget = new QWidget(this);
QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy2.setHorizontalStretch(0);
sizePolicy2.setVerticalStretch(0);
sizePolicy2.setHeightForWidth(contentsWidget->sizePolicy().hasHeightForWidth());
contentsWidget->setSizePolicy(sizePolicy2);
verticalLayout_2 = new QVBoxLayout(contentsWidget);
verticalSpacer_2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
verticalLayout_2->addItem(verticalSpacer_2);
horizontalLayout_2 = new QHBoxLayout();
horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_2->addItem(horizontalSpacer_3);
controlPanelWidget = new QWidget(contentsWidget);
QSizePolicy sizePolicy3(QSizePolicy::Maximum, QSizePolicy::Maximum);
sizePolicy3.setHorizontalStretch(0);
sizePolicy3.setVerticalStretch(0);
sizePolicy3.setHeightForWidth(controlPanelWidget->sizePolicy().hasHeightForWidth());
controlPanelWidget->setSizePolicy(sizePolicy3);
verticalLayout = new QVBoxLayout(controlPanelWidget);
customizeTitleBarCB = new QCheckBox(controlPanelWidget);
QFont font1;
font1.setPointSize(15);
font1.setBold(true);
font1.setWeight(75);
customizeTitleBarCB->setFont(font1);
verticalLayout->addWidget(customizeTitleBarCB);
preserveWindowFrameCB = new QCheckBox(controlPanelWidget);
preserveWindowFrameCB->setFont(font1);
verticalLayout->addWidget(preserveWindowFrameCB);
blurEffectCB = new QCheckBox(controlPanelWidget);
blurEffectCB->setFont(font1);
verticalLayout->addWidget(blurEffectCB);
extendToTitleBarCB = new QCheckBox(controlPanelWidget);
extendToTitleBarCB->setFont(font1);
verticalLayout->addWidget(extendToTitleBarCB);
forceAcrylicCB = new QCheckBox(controlPanelWidget);
forceAcrylicCB->setFont(font1);
forceAcrylicCB->setEnabled(m_bCanAcrylicBeEnabled);
verticalLayout->addWidget(forceAcrylicCB);
resizableCB = new QCheckBox(controlPanelWidget);
resizableCB->setFont(font1);
verticalLayout->addWidget(resizableCB);
horizontalLayout_2->addWidget(controlPanelWidget);
horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_2->addItem(horizontalSpacer_4);
verticalLayout_2->addLayout(horizontalLayout_2);
verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
verticalLayout_2->addItem(verticalSpacer);
horizontalLayout_3 = new QHBoxLayout();
horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_3->addItem(horizontalSpacer_5);
moveCenterButton = new QPushButton(contentsWidget);
moveCenterButton->setFont(font1);
horizontalLayout_3->addWidget(moveCenterButton);
horizontalSpacer_6 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_3->addItem(horizontalSpacer_6);
verticalLayout_2->addLayout(horizontalLayout_3);
verticalLayout_3->addWidget(contentsWidget);
if (shouldDrawBorder()) {
layout()->setContentsMargins(1, 1, 1, 1);
} else {
layout()->setContentsMargins(0, 0, 0, 0);
}
retranslateUi();
}
bool Widget::isNormaled() const
{
return !isMinimized() && !isMaximized() && !isFullScreen();
}
bool Widget::shouldDrawBorder(const bool ignoreWindowState) const
{
return m_bIsWin10OrGreater && (ignoreWindowState ? true : isNormaled())
&& !preserveWindowFrameCB->isChecked() && customizeTitleBarCB->isChecked();
}
bool Widget::shouldDrawThemedBorder(const bool ignoreWindowState) const
{
return (shouldDrawBorder(ignoreWindowState) && WinNativeEventFilter::colorizationEnabled());
}
bool Widget::shouldDrawThemedTitleBar() const
{
return m_bIsWin10OrGreater && WinNativeEventFilter::colorizationEnabled();
}
QColor Widget::activeBorderColor()
{
return WinNativeEventFilter::colorizationEnabled() ? g_cColorizationColor
: g_cDefaultActiveBorderColor;
}
QColor Widget::inactiveBorderColor()
{
return g_cDefaultInactiveBorderColor;
}
QColor Widget::borderColor() const
{
return isActiveWindow() ? activeBorderColor() : inactiveBorderColor();
}
bool Widget::isWin10OrGreater(const Win10Version subVer)
{
return (QOperatingSystemVersion::current()
>= ((subVer == Win10Version::Windows10)
? QOperatingSystemVersion::Windows10
: QOperatingSystemVersion(QOperatingSystemVersion::Windows,
10,
0,
static_cast<int>(subVer))));
}
void *Widget::rawHandle() const
{
return reinterpret_cast<void *>(winId());
}
bool Widget::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (object == this) {
switch (event->type()) {
case QEvent::WindowStateChange: {
if (shouldDrawBorder(true)) {
if (isMaximized()) {
layout()->setContentsMargins(0, 0, 0, 0);
}
if (isNormaled()) {
layout()->setContentsMargins(1, 1, 1, 1);
}
}
updateTitleBar();
moveCenterButton->setEnabled(isNormaled());
break;
}
case QEvent::WinIdChange:
WinNativeEventFilter::addFramelessWindow(this);
break;
case QEvent::WindowActivate:
case QEvent::WindowDeactivate:
updateTitleBar();
break;
default:
break;
}
}
return QWidget::eventFilter(object, event);
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool Widget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
#else
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
Q_ASSERT(eventType == "windows_generic_MSG");
Q_ASSERT(message);
Q_ASSERT(result);
if (customizeTitleBarCB && customizeTitleBarCB->isChecked()) {
const auto msg = static_cast<LPMSG>(message);
switch (msg->message) {
case WM_NCRBUTTONUP: {
if (msg->wParam == HTCAPTION) {
const int x = GET_X_LPARAM(msg->lParam);
const int y = GET_Y_LPARAM(msg->lParam);
if (WinNativeEventFilter::displaySystemMenu(msg->hwnd, false, x, y)) {
*result = 0;
return true;
}
}
break;
}
case WM_DWMCOLORIZATIONCOLORCHANGED: {
g_cColorizationColor = QColor::fromRgba(msg->wParam);
if (shouldDrawThemedBorder()) {
updateWindow();
}
break;
}
case WM_DPICHANGED:
updateWindow();
break;
case WM_NCPAINT:
update();
break;
default:
break;
}
}
return QWidget::nativeEvent(eventType, message, result);
}
void Widget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (shouldDrawBorder()) {
QPainter painter(this);
painter.save();
painter.setPen({borderColor(), 2.0});
painter.drawLine(0, 0, width(), 0);
painter.drawLine(0, height(), width(), height());
painter.drawLine(0, 0, 0, height());
painter.drawLine(width(), 0, width(), height());
painter.restore();
}
}
void Widget::updateWindow()
{
WinNativeEventFilter::updateFrameMargins(rawHandle());
WinNativeEventFilter::updateWindow(rawHandle(), true, true);
update();
}
void Widget::updateTitleBar()
{
const bool themedTitleBar = shouldDrawThemedTitleBar() && isActiveWindow();
if (themedTitleBar && !m_bExtendToTitleBar) {
minimizeButton->setIcon(QIcon(g_sMinimizeButtonImageLight));
closeButton->setIcon(QIcon(g_sCloseButtonImageLight));
if (isMaximized()) {
maximizeButton->setIcon(QIcon(g_sRestoreButtonImageLight));
}
if (isNormaled()) {
maximizeButton->setIcon(QIcon(g_sMaximizeButtonImageLight));
}
} else {
minimizeButton->setIcon(QIcon(g_sMinimizeButtonImageDark));
closeButton->setIcon(QIcon(g_sCloseButtonImageDark));
if (isMaximized()) {
maximizeButton->setIcon(QIcon(g_sRestoreButtonImageDark));
}
if (isNormaled()) {
maximizeButton->setIcon(QIcon(g_sMaximizeButtonImageDark));
}
}
const QColor titleBarBackgroundColor = m_bExtendToTitleBar
? Qt::transparent
: (themedTitleBar ? g_cColorizationColor : Qt::white);
const QColor titleBarTextColor = isActiveWindow()
? ((!themedTitleBar || m_bExtendToTitleBar) ? Qt::black
: Qt::white)
: QColor("#999999");
const QColor titleBarBorderColor = (!m_bIsWin10OrGreater || shouldDrawBorder() || isMaximized()
|| isFullScreen())
? Qt::transparent
: borderColor();
titleBarWidget->setStyleSheet(
g_sSystemButtonsStyleSheet
+ g_sTitleLabelStyleSheet.arg(QString::number(titleBarTextColor.red()),
QString::number(titleBarTextColor.green()),
QString::number(titleBarTextColor.blue()))
+ g_sTitleBarStyleSheet.arg(QString::number(titleBarBackgroundColor.red()),
QString::number(titleBarBackgroundColor.green()),
QString::number(titleBarBackgroundColor.blue()),
QString::number(titleBarBackgroundColor.alpha()),
QString::number(titleBarBorderColor.red()),
QString::number(titleBarBorderColor.green()),
QString::number(titleBarBorderColor.blue()),
QString::number(titleBarBorderColor.alpha())));
}
void Widget::initializeOptions()
{
if (m_bIsWin10OrGreater) {
//preserveWindowFrameCB->click();
if (m_bCanAcrylicBeEnabled) {
forceAcrylicCB->click();
}
}
customizeTitleBarCB->click();
extendToTitleBarCB->click();
blurEffectCB->click();
resizableCB->click();
m_bCanShowColorDialog = true;
}
void Widget::setupConnections()
{
connect(iconButton, &QPushButton::clicked, this, [this]() {
POINT pos = {};
GetCursorPos(&pos);
const auto hwnd = reinterpret_cast<HWND>(rawHandle());
SendMessageW(hwnd, WM_CONTEXTMENU, reinterpret_cast<WPARAM>(hwnd), MAKELPARAM(pos.x, pos.y));
});
connect(minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
connect(maximizeButton, &QPushButton::clicked, this, [this]() {
if (isMaximized()) {
showNormal();
} else {
showMaximized();
}
});
connect(closeButton, &QPushButton::clicked, this, &Widget::close);
connect(moveCenterButton, &QPushButton::clicked, this, [this]() {
WinNativeEventFilter::moveWindowToDesktopCenter(rawHandle());
});
connect(this, &Widget::windowTitleChanged, titleLabel, &QLabel::setText);
connect(this, &Widget::windowIconChanged, iconButton, &QPushButton::setIcon);
connect(customizeTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
preserveWindowFrameCB->setEnabled(enable);
WinNativeEventFilter::updateQtFrame(windowHandle(),
enable ? WinNativeEventFilter::getSystemMetric(
rawHandle(),
WinNativeEventFilter::SystemMetric::TitleBarHeight)
: 0);
titleBarWidget->setVisible(enable);
if (enable) {
qunsetenv(g_sUseNativeTitleBar);
} else {
qputenv(g_sUseNativeTitleBar, "1");
}
updateWindow();
});
connect(preserveWindowFrameCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
if (enable) {
qputenv(g_sPreserveWindowFrame, "1");
qputenv(g_sDontExtendFrame, "1");
} else {
qunsetenv(g_sPreserveWindowFrame);
qunsetenv(g_sDontExtendFrame);
}
if (!enable && shouldDrawBorder()) {
layout()->setContentsMargins(1, 1, 1, 1);
} else {
layout()->setContentsMargins(0, 0, 0, 0);
}
updateTitleBar();
updateWindow();
});
connect(blurEffectCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
QColor color = {0, 0, 0, 127};
const bool useAcrylicEffect = m_bCanAcrylicBeEnabled && forceAcrylicCB->isChecked();
if (useAcrylicEffect) {
if (enable && m_bCanShowColorDialog) {
color = QColorDialog::getColor(color,
this,
tr("Please select a gradient color"),
QColorDialog::ShowAlphaChannel);
}
}
WinNativeEventFilter::setBlurEffectEnabled(rawHandle(), enable, color);
updateWindow();
if (useAcrylicEffect && enable && WinNativeEventFilter::transparencyEffectEnabled()) {
QMessageBox::warning(this,
tr("BUG Warning!"),
tr("You have enabled the transparency effect in the personalize "
"settings.\nDragging will be very laggy when the Acrylic "
"effect is enabled.\nDisabling the transparency effect can "
"solve this issue temporarily."));
}
});
connect(extendToTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) {
m_bExtendToTitleBar = state == Qt::Checked;
updateTitleBar();
});
connect(forceAcrylicCB, &QCheckBox::stateChanged, this, [this](int state) {
if (!m_bCanAcrylicBeEnabled) {
return;
}
if (state == Qt::Checked) {
qputenv(g_sForceUseAcrylicEffect, "1");
} else {
qunsetenv(g_sForceUseAcrylicEffect);
}
if (blurEffectCB->isChecked()) {
blurEffectCB->click();
blurEffectCB->click();
}
});
connect(resizableCB, &QCheckBox::stateChanged, this, [this](int state) {
const bool enable = state == Qt::Checked;
maximizeButton->setEnabled(enable);
WinNativeEventFilter::setWindowResizable(rawHandle(), enable);
});
}
void Widget::initializeFramelessFunctions()
{
WinNativeEventFilter::WINDOWDATA data = {};
data.ignoreObjects << iconButton << minimizeButton << maximizeButton << closeButton;
WinNativeEventFilter::addFramelessWindow(this, &data);
installEventFilter(this);
}
void Widget::initializeVariables()
{
m_bIsWin10OrGreater = isWin10OrGreater();
if (m_bIsWin10OrGreater) {
m_bCanAcrylicBeEnabled = isWin10OrGreater(g_vAcrylicEffectVersion);
g_cColorizationColor = WinNativeEventFilter::colorizationColor();
}
}
void Widget::initializeWindow()
{
initializeVariables();
setupUi();
updateTitleBar();
initializeFramelessFunctions();
setupConnections();
initializeOptions();
}

View File

@@ -0,0 +1,125 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QWidget>
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
QT_FORWARD_DECLARE_CLASS(QSpacerItem)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QCheckBox)
class Widget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Widget)
public:
enum class Win10Version : int {
Win10_1507 = 10240,
Win10_1511 = 10586,
Win10_1607 = 14393,
Win10_1703 = 15063,
Win10_1709 = 16299,
Win10_1803 = 17134,
Win10_1809 = 17763,
Win10_1903 = 18362,
Win10_1909 = 18363,
Win10_2004 = 19041,
Win10_20H2 = 19042,
Windows10 = Win10_1507
};
Q_ENUM(Win10Version)
explicit Widget(QWidget *parent = nullptr);
~Widget() override = default;
void *rawHandle() const;
bool isNormaled() const;
bool shouldDrawBorder(const bool ignoreWindowState = false) const;
bool shouldDrawThemedBorder(const bool ignoreWindowState = false) const;
bool shouldDrawThemedTitleBar() const;
static QColor activeBorderColor();
static QColor inactiveBorderColor();
QColor borderColor() const;
static bool isWin10OrGreater(const Win10Version subVer = Win10Version::Windows10);
public Q_SLOTS:
void retranslateUi();
protected:
bool eventFilter(QObject *object, QEvent *event) override;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
#else
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif
void paintEvent(QPaintEvent *event) override;
private:
void setupUi();
void updateWindow();
void updateTitleBar();
void initializeOptions();
void setupConnections();
void initializeFramelessFunctions();
void initializeVariables();
void initializeWindow();
private:
bool m_bIsWin10OrGreater = false, m_bCanAcrylicBeEnabled = false, m_bExtendToTitleBar = false,
m_bCanShowColorDialog = false;
QVBoxLayout *verticalLayout_3 = nullptr, *verticalLayout_2 = nullptr, *verticalLayout = nullptr;
QWidget *titleBarWidget = nullptr, *contentsWidget = nullptr, *controlPanelWidget = nullptr;
QHBoxLayout *horizontalLayout = nullptr, *horizontalLayout_2 = nullptr,
*horizontalLayout_3 = nullptr;
QSpacerItem *horizontalSpacer_7 = nullptr, *horizontalSpacer = nullptr,
*horizontalSpacer_2 = nullptr, *verticalSpacer_2 = nullptr,
*horizontalSpacer_3 = nullptr, *horizontalSpacer_4 = nullptr,
*verticalSpacer = nullptr, *horizontalSpacer_5 = nullptr,
*horizontalSpacer_6 = nullptr;
QPushButton *iconButton = nullptr, *minimizeButton = nullptr, *maximizeButton = nullptr,
*closeButton = nullptr, *moveCenterButton = nullptr;
QLabel *titleLabel = nullptr;
QCheckBox *customizeTitleBarCB = nullptr, *preserveWindowFrameCB = nullptr,
*blurEffectCB = nullptr, *extendToTitleBarCB = nullptr, *forceAcrylicCB = nullptr,
*resizableCB = nullptr;
};

View File

@@ -0,0 +1,23 @@
DESTDIR = $$OUT_PWD/../../bin
CONFIG += c++17 strict_c++ utf8_source warn_on
DEFINES += \
QT_NO_CAST_FROM_ASCII \
QT_NO_CAST_TO_ASCII
RESOURCES += $$PWD/images.qrc
win32 {
DEFINES += \
WIN32_LEAN_AND_MEAN \
_CRT_SECURE_NO_WARNINGS
CONFIG += windeployqt
CONFIG -= embed_manifest_exe
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi -lshcore -ld2d1 -luxtheme
RC_FILE = $$PWD/windows.rc
OTHER_FILES += $$PWD/windows.manifest
}
CONFIG(debug, debug|release) {
LIBS += -L$$OUT_PWD/../../debug
win32: LIBS += -lFramelessHelperd
else: unix: LIBS += -lFramelessHelper_debug
} else: CONFIG(release, debug|release) {
LIBS += -L$$OUT_PWD/../../release -lFramelessHelper
}

View File

@@ -0,0 +1,5 @@
TEMPLATE = subdirs
CONFIG -= ordered
qtHaveModule(widgets): SUBDIRS += QWidget QMainWindow
qtHaveModule(quick): SUBDIRS += Quick
win32: qtHaveModule(widgets): SUBDIRS += Win32Demo

View File

@@ -0,0 +1,12 @@
<RCC>
<qresource prefix="/">
<file>images/button_minimize_black.svg</file>
<file>images/button_minimize_white.svg</file>
<file>images/button_maximize_black.svg</file>
<file>images/button_maximize_white.svg</file>
<file>images/button_restore_black.svg</file>
<file>images/button_restore_white.svg</file>
<file>images/button_close_black.svg</file>
<file>images/button_close_white.svg</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,5 @@
All the SVG images in this folder are created by me, using Inkscape. No images are downloaded from the Internet, not even one pixel.
Since I copied the screenshot (when the scale factor is 1.0) pixel by pixel, they should be exactly the same with the real system buttons, in theory.
Anyone can use them freely.

View File

@@ -0,0 +1,6 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#000" stroke-width=".35278">
<path d="m6.1295 3.6601 3.2632 3.2632z"/>
<path d="m9.3927 3.6601-3.2632 3.2632z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 264 B

View File

@@ -0,0 +1,6 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#fff" stroke-width=".35278">
<path d="m6.1295 3.6601 3.2632 3.2632z"/>
<path d="m9.3927 3.6601-3.2632 3.2632z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 264 B

View File

@@ -0,0 +1,3 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<rect x="6.1736" y="3.7042" width="3.175" height="3.175" fill="none" stroke="#000" stroke-width=".35278"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,3 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<rect x="6.1736" y="3.7042" width="3.175" height="3.175" fill="none" stroke="#fff" stroke-width=".35278"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,3 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<path d="m6.35 5.4681h3.5278z" fill="none" stroke="#000" stroke-width=".35278"/>
</svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1,3 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<path d="m6.35 5.4681h3.5278z" fill="none" stroke="#fff" stroke-width=".35278"/>
</svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1,11 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#000">
<rect x="6.1736" y="4.4097" width="2.4694" height="2.4694" stroke-width=".35278"/>
<g stroke-width=".35278">
<path d="m6.8792 4.2333v-0.70556z"/>
<path d="m7.0556 3.7042h2.4694z"/>
<path d="m9.3486 3.8806v2.4694z"/>
<path d="m9.1722 6.1736h-0.35278z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 430 B

View File

@@ -0,0 +1,11 @@
<svg width="45pt" height="30pt" version="1.1" viewBox="0 0 15.875 10.583" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#fff">
<rect x="6.1736" y="4.4097" width="2.4694" height="2.4694" stroke-width=".35278"/>
<g stroke-width=".35278">
<path d="m6.8792 4.2333v-0.70556z"/>
<path d="m7.0556 3.7042h2.4694z"/>
<path d="m9.3486 3.8806v2.4694z"/>
<path d="m9.1722 6.1736h-0.35278z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

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

View File

@@ -0,0 +1,63 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <windows.h>
IDI_ICON1 ICON "windows.ico"
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "windows.manifest"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "Built by the Qt Toolkit."
VALUE "CompanyName", "wangwenx190"
VALUE "FileDescription", "A FramelessHelper based Qt application."
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", ""
VALUE "LegalCopyright", "MIT License"
VALUE "OriginalFilename", ""
VALUE "ProductName", "Test Application"
VALUE "ProductVersion", "1.0.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -0,0 +1,604 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelesshelper.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#include <QDebug>
#include <QGuiApplication>
#include <QMargins>
#include <QScreen>
#ifdef QT_WIDGETS_LIB
#include <QWidget>
#endif
#ifdef QT_QUICK_LIB
#include <QQuickItem>
#endif
#include <QEvent>
#include <QMouseEvent>
#include <QTouchEvent>
#include <QWindow>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
#include <qpa/qplatformnativeinterface.h>
#else
#include <qpa/qplatformwindow.h>
#include <qpa/qplatformwindow_p.h>
#endif
Q_DECLARE_METATYPE(QMargins)
namespace {
QWindow *getWindowHandle(QObject *val)
{
Q_ASSERT(val);
const auto validWindow = [](QWindow *window) -> QWindow * {
Q_ASSERT(window);
return window->handle() ? window : nullptr;
};
if (val->isWindowType()) {
return validWindow(qobject_cast<QWindow *>(val));
}
#ifdef QT_WIDGETS_LIB
else if (val->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(val);
if (widget && widget->isTopLevel()) {
return validWindow(widget->windowHandle());
}
}
#endif
else {
qFatal("Can't acquire the window handle: only top level QWidget and QWindow are accepted.");
}
return nullptr;
}
} // namespace
FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) {}
void FramelessHelper::updateQtFrame(QWindow *window, const int titleBarHeight)
{
Q_ASSERT(window);
if (titleBarHeight >= 0) {
// Reduce top frame to zero since we paint it ourselves. Use
// device pixel to avoid rounding errors.
const QMargins margins = {0, -titleBarHeight, 0, 0};
const QVariant marginsVar = QVariant::fromValue(margins);
// The dynamic property takes effect when creating the platform
// window.
window->setProperty("_q_windowsCustomMargins", marginsVar);
// If a platform window exists, change via native interface.
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPlatformWindow *platformWindow = window->handle();
if (platformWindow) {
QGuiApplication::platformNativeInterface()
->setWindowProperty(platformWindow,
QString::fromUtf8("WindowsCustomMargins"),
marginsVar);
}
#else
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
window->handle());
if (platformWindow) {
platformWindow->setCustomMargins(margins);
}
#endif
}
}
void FramelessHelper::moveWindowToDesktopCenter(QObject *obj)
{
Q_ASSERT(obj);
if (obj->isWindowType()) {
const auto window = qobject_cast<QWindow *>(obj);
if (window) {
const QSize ss = window->screen()->size();
const int sw = ss.width();
const int sh = ss.height();
const int ww = window->width();
const int wh = window->height();
window->setX(qRound(static_cast<qreal>(sw - ww) / 2.0));
window->setY(qRound(static_cast<qreal>(sh - wh) / 2.0));
}
}
#ifdef QT_WIDGETS_LIB
else if (obj->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(obj);
if (widget && widget->isTopLevel()) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
const QSize ss = widget->screen()->size();
#else
QSize ss = {};
QWindow *window = widget->windowHandle();
if (window) {
ss = window->screen()->size();
}
#endif
const int sw = ss.width();
const int sh = ss.height();
const int ww = widget->width();
const int wh = widget->height();
widget->move(qRound(static_cast<qreal>(sw - ww) / 2.0),
qRound(static_cast<qreal>(sh - wh) / 2.0));
}
}
#endif
else {
qFatal("The given QObject is not a top level QWidget or QWindow.");
}
}
int FramelessHelper::getBorderWidth() const
{
return m_borderWidth;
}
void FramelessHelper::setBorderWidth(const int val)
{
m_borderWidth = val;
}
int FramelessHelper::getBorderHeight() const
{
return m_borderHeight;
}
void FramelessHelper::setBorderHeight(const int val)
{
m_borderHeight = val;
}
int FramelessHelper::getTitleBarHeight() const
{
return m_titleBarHeight;
}
void FramelessHelper::setTitleBarHeight(const int val)
{
m_titleBarHeight = val;
}
QList<QRect> FramelessHelper::getIgnoreAreas(QObject *obj) const
{
Q_ASSERT(obj);
return m_ignoreAreas.value(obj);
}
void FramelessHelper::addIgnoreArea(QObject *obj, const QRect &val)
{
Q_ASSERT(obj);
QList<QRect> areas = m_ignoreAreas[obj];
areas.append(val);
m_ignoreAreas[obj] = areas;
}
QList<QRect> FramelessHelper::getDraggableAreas(QObject *obj) const
{
Q_ASSERT(obj);
return m_draggableAreas.value(obj);
}
void FramelessHelper::addDraggableArea(QObject *obj, const QRect &val)
{
Q_ASSERT(obj);
QList<QRect> areas = m_draggableAreas[obj];
areas.append(val);
m_draggableAreas[obj] = areas;
}
QList<QObject *> FramelessHelper::getIgnoreObjects(QObject *obj) const
{
Q_ASSERT(obj);
QList<QObject *> ret{};
const QList<QObject *> objs = m_ignoreObjects.value(obj);
if (!objs.isEmpty()) {
for (auto &&_obj : qAsConst(objs)) {
if (_obj) {
ret.append(_obj);
}
}
}
return ret;
}
void FramelessHelper::addIgnoreObject(QObject *obj, QObject *val)
{
Q_ASSERT(obj);
QList<QObject *> objs = m_ignoreObjects[obj];
objs.append(val);
m_ignoreObjects[obj] = objs;
}
QList<QObject *> FramelessHelper::getDraggableObjects(QObject *obj) const
{
Q_ASSERT(obj);
QList<QObject *> ret{};
const QList<QObject *> objs = m_draggableObjects.value(obj);
if (!objs.isEmpty()) {
for (auto &&_obj : qAsConst(objs)) {
if (_obj) {
ret.append(_obj);
}
}
}
return ret;
}
void FramelessHelper::addDraggableObject(QObject *obj, QObject *val)
{
Q_ASSERT(obj);
QList<QObject *> objs = m_draggableObjects[obj];
objs.append(val);
m_draggableObjects[obj] = objs;
}
bool FramelessHelper::getResizable(QObject *obj) const
{
Q_ASSERT(obj);
return !m_fixedSize.value(obj);
}
void FramelessHelper::setResizable(QObject *obj, const bool val)
{
Q_ASSERT(obj);
m_fixedSize[obj] = !val;
}
bool FramelessHelper::getTitleBarEnabled(QObject *obj) const
{
Q_ASSERT(obj);
return !m_disableTitleBar.value(obj);
}
void FramelessHelper::setTitleBarEnabled(QObject *obj, const bool val)
{
Q_ASSERT(obj);
m_disableTitleBar[obj] = !val;
}
void FramelessHelper::removeWindowFrame(QObject *obj, const bool center)
{
Q_ASSERT(obj);
// Don't miss the Qt::Window flag.
const Qt::WindowFlags flags = Qt::Window | Qt::FramelessWindowHint;
if (obj->isWindowType()) {
const auto window = qobject_cast<QWindow *>(obj);
if (window) {
window->setFlags(flags);
// MouseTracking is always enabled for QWindow.
window->installEventFilter(this);
}
}
#ifdef QT_WIDGETS_LIB
else if (obj->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(obj);
if (widget && widget->isTopLevel()) {
widget->setWindowFlags(flags);
// We can't get MouseMove events if MouseTracking is
// disabled.
widget->setMouseTracking(true);
widget->installEventFilter(this);
QWindow *window = widget->windowHandle();
if (window) {
updateQtFrame(window, m_titleBarHeight);
}
}
}
#endif
else {
qFatal("The given QObject is not a top level QWidget or QWindow.");
}
if (center) {
moveWindowToDesktopCenter(obj);
}
}
bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
const auto isWindowTopLevel = [](QObject *window) -> bool {
Q_ASSERT(window);
if (window->isWindowType()) {
const auto win = qobject_cast<QWindow *>(window);
if (win) {
return win->isTopLevel();
}
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(window);
if (widget) {
return widget->isTopLevel();
}
}
#endif
return false;
};
if (!isWindowTopLevel(object)) {
return false;
}
const auto getWindowEdges =
[this](const QPointF &point, const int ww, const int wh) -> Qt::Edges {
if (point.y() <= m_borderHeight) {
if (point.x() <= m_borderWidth) {
return Qt::Edge::TopEdge | Qt::Edge::LeftEdge;
}
if (point.x() >= (ww - m_borderWidth)) {
return Qt::Edge::TopEdge | Qt::Edge::RightEdge;
}
return Qt::Edge::TopEdge;
}
if (point.y() >= (wh - m_borderHeight)) {
if (point.x() <= m_borderWidth) {
return Qt::Edge::BottomEdge | Qt::Edge::LeftEdge;
}
if (point.x() >= (ww - m_borderWidth)) {
return Qt::Edge::BottomEdge | Qt::Edge::RightEdge;
}
return Qt::Edge::BottomEdge;
}
if (point.x() <= m_borderWidth) {
return Qt::Edge::LeftEdge;
}
if (point.x() >= (ww - m_borderWidth)) {
return Qt::Edge::RightEdge;
}
return {};
};
const auto getCursorShape = [](const Qt::Edges edges) -> Qt::CursorShape {
if ((edges.testFlag(Qt::Edge::TopEdge) && edges.testFlag(Qt::Edge::LeftEdge))
|| (edges.testFlag(Qt::Edge::BottomEdge) && edges.testFlag(Qt::Edge::RightEdge))) {
return Qt::CursorShape::SizeFDiagCursor;
}
if ((edges.testFlag(Qt::Edge::TopEdge) && edges.testFlag(Qt::Edge::RightEdge))
|| (edges.testFlag(Qt::Edge::BottomEdge) && edges.testFlag(Qt::Edge::LeftEdge))) {
return Qt::CursorShape::SizeBDiagCursor;
}
if (edges.testFlag(Qt::Edge::TopEdge) || edges.testFlag(Qt::Edge::BottomEdge)) {
return Qt::CursorShape::SizeVerCursor;
}
if (edges.testFlag(Qt::Edge::LeftEdge) || edges.testFlag(Qt::Edge::RightEdge)) {
return Qt::CursorShape::SizeHorCursor;
}
return Qt::CursorShape::ArrowCursor;
};
const auto isInSpecificAreas = [](const int x, const int y, const QList<QRect> &areas) -> bool {
if (!areas.isEmpty()) {
for (auto &&area : qAsConst(areas)) {
if (area.contains(x, y)) {
return true;
}
}
}
return false;
};
const auto isInSpecificObjects =
[](const int x, const int y, const QList<QObject *> &objects) -> bool {
if (!objects.isEmpty()) {
for (auto &&obj : qAsConst(objects)) {
if (!obj) {
continue;
}
#ifdef QT_WIDGETS_LIB
const auto widget = qobject_cast<QWidget *>(obj);
if (widget) {
const QPoint pos = widget->mapToGlobal({0, 0});
if (QRect(pos.x(), pos.y(), widget->width(), widget->height()).contains(x, y)) {
return true;
}
}
#endif
#ifdef QT_QUICK_LIB
const auto quickItem = qobject_cast<QQuickItem *>(obj);
if (quickItem) {
const QPointF pos = quickItem->mapToGlobal({0, 0});
if (QRectF(pos.x(), pos.y(), quickItem->width(), quickItem->height())
.contains(x, y)) {
return true;
}
}
#endif
}
}
return false;
};
const auto isInIgnoreAreas = [this, &isInSpecificAreas](const QPointF &point,
QObject *window) -> bool {
Q_ASSERT(window);
return isInSpecificAreas(point.x(), point.y(), getIgnoreAreas(window));
};
const auto isInIgnoreObjects = [this, &isInSpecificObjects](const QPointF &point,
QObject *window) -> bool {
Q_ASSERT(window);
#if defined(QT_WIDGETS_LIB) || defined(QT_QUICK_LIB)
return isInSpecificObjects(point.x(), point.y(), getIgnoreObjects(window));
#else
Q_UNUSED(point)
Q_UNUSED(window)
return false;
#endif
};
const auto isInDraggableAreas = [this, &isInSpecificAreas](const QPointF &point,
QObject *window) -> bool {
Q_ASSERT(window);
const auto areas = getDraggableAreas(window);
return (areas.isEmpty() ? true : isInSpecificAreas(point.x(), point.y(), areas));
};
const auto isInDraggableObjects = [this, &isInSpecificObjects](const QPointF &point,
QObject *window) -> bool {
Q_ASSERT(window);
#if defined(QT_WIDGETS_LIB) || defined(QT_QUICK_LIB)
const auto objs = getDraggableObjects(window);
return (objs.isEmpty() ? true : isInSpecificObjects(point.x(), point.y(), objs));
#else
Q_UNUSED(point)
Q_UNUSED(window)
return true;
#endif
};
const auto isResizePermitted = [&isInIgnoreAreas, &isInIgnoreObjects](const QPointF &globalPoint,
const QPointF &point,
QObject *window) -> bool {
Q_ASSERT(window);
return (!isInIgnoreAreas(point, window) && !isInIgnoreObjects(globalPoint, window));
};
const auto isInTitlebarArea = [this,
&isInDraggableAreas,
&isInDraggableObjects,
&isResizePermitted](const QPointF &globalPoint,
const QPointF &point,
QObject *window) -> bool {
Q_ASSERT(window);
const bool customDragAreas = !getDraggableAreas(window).isEmpty();
#if defined(QT_WIDGETS_LIB) || defined(QT_QUICK_LIB)
const bool customDragObjects = !getDraggableObjects(window).isEmpty();
#else
const bool customDragObjects = false;
#endif
const bool customDrag = customDragAreas || customDragObjects;
return ((customDrag ? (isInDraggableAreas(point, window)
&& isInDraggableObjects(globalPoint, window))
: (point.y() <= m_titleBarHeight))
&& isResizePermitted(globalPoint, point, window) && getTitleBarEnabled(window));
return false;
};
const auto moveOrResize =
[this, &getWindowEdges, &isResizePermitted, &isInTitlebarArea](const QPointF &globalPoint,
const QPointF &point,
QObject *object) {
Q_ASSERT(object);
QWindow *window = getWindowHandle(object);
if (window) {
const Qt::Edges edges = getWindowEdges(point, window->width(), window->height());
if (edges == Qt::Edges{}) {
if (isInTitlebarArea(globalPoint, point, object)) {
window->startSystemMove();
}
} else {
if (window->windowStates().testFlag(Qt::WindowState::WindowNoState)
&& isResizePermitted(globalPoint, point, object) && getResizable(object)) {
window->startSystemResize(edges);
}
}
} else {
qWarning().noquote() << "Can't move or resize the window: failed "
"to acquire the window handle.";
}
};
switch (event->type()) {
case QEvent::MouseButtonDblClick: {
const auto mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent) {
if (mouseEvent->button() != Qt::MouseButton::LeftButton) {
break;
}
if (isInTitlebarArea(mouseEvent->screenPos(), mouseEvent->windowPos(), object)) {
// ### FIXME: If the current object is a QWidget, we can use
// getWindowHandle(object) to get the window handle, but if we
// call showMaximized() of that window, it will not be
// maximized, it will be moved to the top-left edge of the
// screen without changing it's size instead. Why? Convert the
// object to QWidget and call showMaximized() doesn't have this
// issue.
if (object->isWindowType()) {
const auto window = qobject_cast<QWindow *>(object);
if (window) {
if (window->windowStates().testFlag(Qt::WindowState::WindowFullScreen)) {
break;
}
if (window->windowStates().testFlag(Qt::WindowState::WindowMaximized)) {
window->showNormal();
} else {
window->showMaximized();
}
window->setCursor(Qt::CursorShape::ArrowCursor);
}
}
#ifdef QT_WIDGETS_LIB
else if (object->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(object);
if (widget) {
if (widget->isFullScreen()) {
break;
}
if (widget->isMaximized()) {
widget->showNormal();
} else {
widget->showMaximized();
}
widget->setCursor(Qt::CursorShape::ArrowCursor);
}
}
#endif
}
}
} break;
case QEvent::MouseButtonPress: {
const auto mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent) {
if (mouseEvent->button() != Qt::MouseButton::LeftButton) {
break;
}
moveOrResize(mouseEvent->screenPos(), mouseEvent->windowPos(), object);
}
} break;
case QEvent::MouseMove: {
const auto mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent) {
QWindow *window = getWindowHandle(object);
if (window) {
if (window->windowStates().testFlag(Qt::WindowState::WindowNoState)
&& getResizable(object)) {
window->setCursor(getCursorShape(
getWindowEdges(mouseEvent->windowPos(), window->width(), window->height())));
}
}
#ifdef QT_WIDGETS_LIB
else {
const auto widget = qobject_cast<QWidget *>(object);
if (widget) {
if (!widget->isMinimized() && !widget->isMaximized() && !widget->isFullScreen()
&& getResizable(object)) {
widget->setCursor(getCursorShape(getWindowEdges(mouseEvent->windowPos(),
widget->width(),
widget->height())));
}
}
}
#endif
}
} break;
case QEvent::TouchBegin:
case QEvent::TouchUpdate: {
const auto point = static_cast<QTouchEvent *>(event)->touchPoints().first();
moveOrResize(point.screenPos(), point.pos(), object);
} break;
default:
break;
}
return false;
}
#endif

View File

@@ -0,0 +1,91 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#include <QHash>
#include <QObject>
#include <QRect>
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
class FRAMELESSHELPER_EXPORT FramelessHelper : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessHelper)
public:
explicit FramelessHelper(QObject *parent = nullptr);
~FramelessHelper() override = default;
void removeWindowFrame(QObject *obj, const bool center = false);
static void updateQtFrame(QWindow *window, const int titleBarHeight);
static void moveWindowToDesktopCenter(QObject *obj);
int getBorderWidth() const;
void setBorderWidth(const int val);
int getBorderHeight() const;
void setBorderHeight(const int val);
int getTitleBarHeight() const;
void setTitleBarHeight(const int val);
void addIgnoreArea(QObject *obj, const QRect &val);
QList<QRect> getIgnoreAreas(QObject *obj) const;
void addDraggableArea(QObject *obj, const QRect &val);
QList<QRect> getDraggableAreas(QObject *obj) const;
void addIgnoreObject(QObject *obj, QObject *val);
QList<QObject *> getIgnoreObjects(QObject *obj) const;
void addDraggableObject(QObject *obj, QObject *val);
QList<QObject *> getDraggableObjects(QObject *obj) const;
bool getResizable(QObject *obj) const;
void setResizable(QObject *obj, const bool val);
bool getTitleBarEnabled(QObject *obj) const;
void setTitleBarEnabled(QObject *obj, const bool val);
protected:
bool eventFilter(QObject *object, QEvent *event) override;
private:
// ### TODO: The default border width and height on Windows is 8 pixels if
// the scale factor is 1.0. Don't know how to acquire these values on UNIX
// platforms through native API.
int m_borderWidth = 8, m_borderHeight = 8, m_titleBarHeight = 30;
QHash<QObject *, QList<QRect>> m_ignoreAreas = {}, m_draggableAreas = {};
QHash<QObject *, QList<QObject *>> m_ignoreObjects = {}, m_draggableObjects = {};
QHash<QObject *, bool> m_fixedSize = {}, m_disableTitleBar = {};
};
#endif

View File

@@ -0,0 +1,5 @@
TEMPLATE = subdirs
CONFIG -= ordered
SUBDIRS += lib examples
lib.file = lib.pro
examples.depends += lib

View File

@@ -0,0 +1,62 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "wangwenx190"
VALUE "FileDescription", "Frameless Helper"
VALUE "FileVersion", "1.0.0.0"
VALUE "LegalCopyright", "MIT License"
#ifdef _DEBUG
VALUE "OriginalFilename", "FramelessHelperd.dll"
#else
VALUE "OriginalFilename", "FramelessHelper.dll"
#endif
VALUE "ProductName", "Frameless Helper"
VALUE "ProductVersion", "1.0.0.0"
VALUE "InternalName", "framelesshelper"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -0,0 +1,39 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QtCore/qglobal.h>
#ifndef FRAMELESSHELPER_EXPORT
#ifdef FRAMELESSHELPER_STATIC
#define FRAMELESSHELPER_EXPORT
#else
#ifdef FRAMELESSHELPER_BUILD_LIBRARY
#define FRAMELESSHELPER_EXPORT Q_DECL_EXPORT
#else
#define FRAMELESSHELPER_EXPORT Q_DECL_IMPORT
#endif
#endif
#endif

View File

@@ -0,0 +1,266 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelessquickhelper.h"
#include "framelesswindowsmanager.h"
#include <QtQuick/QQuickWindow>
#ifdef Q_OS_WINDOWS
#include "winnativeeventfilter.h"
#include <QOperatingSystemVersion>
#endif
#ifdef Q_OS_WINDOWS
namespace {
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
const char g_sDontExtendFrame[] = "WNEF_DO_NOT_EXTEND_FRAME";
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
} // namespace
#endif
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent)
{
#ifdef Q_OS_WINDOWS
startTimer(500);
#endif
}
int FramelessQuickHelper::borderWidth() const
{
return FramelessWindowsManager::getBorderWidth(window());
}
void FramelessQuickHelper::setBorderWidth(const int val)
{
FramelessWindowsManager::setBorderWidth(window(), val);
Q_EMIT borderWidthChanged(val);
}
int FramelessQuickHelper::borderHeight() const
{
return FramelessWindowsManager::getBorderHeight(window());
}
void FramelessQuickHelper::setBorderHeight(const int val)
{
FramelessWindowsManager::setBorderHeight(window(), val);
Q_EMIT borderHeightChanged(val);
}
int FramelessQuickHelper::titleBarHeight() const
{
return FramelessWindowsManager::getTitleBarHeight(window());
}
void FramelessQuickHelper::setTitleBarHeight(const int val)
{
FramelessWindowsManager::setTitleBarHeight(window(), val);
Q_EMIT titleBarHeightChanged(val);
}
bool FramelessQuickHelper::resizable() const
{
return FramelessWindowsManager::getResizable(window());
}
void FramelessQuickHelper::setResizable(const bool val)
{
FramelessWindowsManager::setResizable(window(), val);
Q_EMIT resizableChanged(val);
}
bool FramelessQuickHelper::titleBarEnabled() const
{
return FramelessWindowsManager::getTitleBarEnabled(window());
}
void FramelessQuickHelper::setTitleBarEnabled(const bool val)
{
FramelessWindowsManager::setTitleBarEnabled(window(), val);
Q_EMIT titleBarEnabledChanged(val);
}
#ifdef Q_OS_WINDOWS
bool FramelessQuickHelper::canHaveWindowFrame() const
{
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
}
bool FramelessQuickHelper::colorizationEnabled() const
{
return WinNativeEventFilter::colorizationEnabled();
}
QColor FramelessQuickHelper::colorizationColor() const
{
return WinNativeEventFilter::colorizationColor();
}
bool FramelessQuickHelper::lightThemeEnabled() const
{
return WinNativeEventFilter::lightThemeEnabled();
}
bool FramelessQuickHelper::darkThemeEnabled() const
{
return WinNativeEventFilter::darkThemeEnabled();
}
bool FramelessQuickHelper::highContrastModeEnabled() const
{
return WinNativeEventFilter::highContrastModeEnabled();
}
bool FramelessQuickHelper::darkFrameEnabled() const
{
return WinNativeEventFilter::darkFrameEnabled(rawHandle());
}
bool FramelessQuickHelper::transparencyEffectEnabled() const
{
return WinNativeEventFilter::transparencyEffectEnabled();
}
#endif
QSize FramelessQuickHelper::minimumSize() const
{
return FramelessWindowsManager::getMinimumSize(window());
}
void FramelessQuickHelper::setMinimumSize(const QSize &val)
{
FramelessWindowsManager::setMinimumSize(window(), val);
Q_EMIT minimumSizeChanged(val);
}
QSize FramelessQuickHelper::maximumSize() const
{
return FramelessWindowsManager::getMaximumSize(window());
}
void FramelessQuickHelper::setMaximumSize(const QSize &val)
{
FramelessWindowsManager::setMaximumSize(window(), val);
Q_EMIT maximumSizeChanged(val);
}
void FramelessQuickHelper::removeWindowFrame(const bool center)
{
FramelessWindowsManager::addWindow(window(), center);
}
QSize FramelessQuickHelper::desktopSize() const
{
return FramelessWindowsManager::getDesktopSize(window());
}
QRect FramelessQuickHelper::desktopAvailableGeometry() const
{
return FramelessWindowsManager::getDesktopAvailableGeometry(window());
}
QSize FramelessQuickHelper::desktopAvailableSize() const
{
return FramelessWindowsManager::getDesktopAvailableSize(window());
}
void FramelessQuickHelper::moveWindowToDesktopCenter(const bool realCenter)
{
FramelessWindowsManager::moveWindowToDesktopCenter(window(), realCenter);
}
void FramelessQuickHelper::addIgnoreArea(const QRect &val)
{
FramelessWindowsManager::addIgnoreArea(window(), val);
}
void FramelessQuickHelper::addDraggableArea(const QRect &val)
{
FramelessWindowsManager::addDraggableArea(window(), val);
}
void FramelessQuickHelper::addIgnoreObject(QQuickItem *val)
{
Q_ASSERT(val);
FramelessWindowsManager::addIgnoreObject(window(), val);
}
void FramelessQuickHelper::addDraggableObject(QQuickItem *val)
{
Q_ASSERT(val);
FramelessWindowsManager::addDraggableObject(window(), val);
}
#ifdef Q_OS_WINDOWS
void FramelessQuickHelper::timerEvent(QTimerEvent *event)
{
QQuickItem::timerEvent(event);
Q_EMIT colorizationEnabledChanged(colorizationEnabled());
Q_EMIT colorizationColorChanged(colorizationColor());
Q_EMIT lightThemeEnabledChanged(lightThemeEnabled());
Q_EMIT darkThemeEnabledChanged(darkThemeEnabled());
Q_EMIT highContrastModeEnabledChanged(highContrastModeEnabled());
Q_EMIT darkFrameEnabledChanged(darkFrameEnabled());
Q_EMIT transparencyEffectEnabledChanged(transparencyEffectEnabled());
}
void *FramelessQuickHelper::rawHandle() const
{
QWindow *win = window();
if (win) {
return reinterpret_cast<void *>(win->winId());
}
return nullptr;
}
void FramelessQuickHelper::setWindowFrameVisible(const bool value)
{
if (value) {
qputenv(g_sPreserveWindowFrame, "1");
qputenv(g_sDontExtendFrame, "1");
} else {
qunsetenv(g_sPreserveWindowFrame);
qunsetenv(g_sDontExtendFrame);
}
}
void FramelessQuickHelper::displaySystemMenu(const int x, const int y, const bool isRtl)
{
WinNativeEventFilter::displaySystemMenu(rawHandle(), isRtl, x, y);
}
void FramelessQuickHelper::setBlurEffectEnabled(const bool enabled,
const bool forceAcrylic,
const QColor &gradientColor)
{
if (forceAcrylic) {
qputenv(g_sForceUseAcrylicEffect, "1");
} else {
qunsetenv(g_sForceUseAcrylicEffect);
}
WinNativeEventFilter::setBlurEffectEnabled(rawHandle(), enabled, gradientColor);
}
#endif

View File

@@ -0,0 +1,158 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#include <QtQuick/QQuickItem>
#if (defined(Q_OS_WIN) || defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)) \
&& !defined(Q_OS_WINDOWS)
#define Q_OS_WINDOWS
#endif
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class FRAMELESSHELPER_EXPORT FramelessQuickHelper : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
QML_ELEMENT
#endif
Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged)
Q_PROPERTY(int borderHeight READ borderHeight WRITE setBorderHeight NOTIFY borderHeightChanged)
Q_PROPERTY(
int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged)
Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize NOTIFY minimumSizeChanged)
Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged)
Q_PROPERTY(bool titleBarEnabled READ titleBarEnabled WRITE setTitleBarEnabled NOTIFY
titleBarEnabledChanged)
#ifdef Q_OS_WINDOWS
Q_PROPERTY(bool canHaveWindowFrame READ canHaveWindowFrame CONSTANT)
Q_PROPERTY(bool colorizationEnabled READ colorizationEnabled NOTIFY colorizationEnabledChanged)
Q_PROPERTY(QColor colorizationColor READ colorizationColor NOTIFY colorizationColorChanged)
Q_PROPERTY(bool lightThemeEnabled READ lightThemeEnabled NOTIFY lightThemeEnabledChanged)
Q_PROPERTY(bool darkThemeEnabled READ darkThemeEnabled NOTIFY darkThemeEnabledChanged)
Q_PROPERTY(bool highContrastModeEnabled READ highContrastModeEnabled NOTIFY
highContrastModeEnabledChanged)
Q_PROPERTY(bool darkFrameEnabled READ darkFrameEnabled NOTIFY darkFrameEnabledChanged)
Q_PROPERTY(bool transparencyEffectEnabled READ transparencyEffectEnabled NOTIFY
transparencyEffectEnabledChanged)
#endif
public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
~FramelessQuickHelper() override = default;
int borderWidth() const;
void setBorderWidth(const int val);
int borderHeight() const;
void setBorderHeight(const int val);
int titleBarHeight() const;
void setTitleBarHeight(const int val);
bool resizable() const;
void setResizable(const bool val);
QSize minimumSize() const;
void setMinimumSize(const QSize &val);
QSize maximumSize() const;
void setMaximumSize(const QSize &val);
bool titleBarEnabled() const;
void setTitleBarEnabled(const bool val);
#ifdef Q_OS_WINDOWS
bool canHaveWindowFrame() const;
bool colorizationEnabled() const;
QColor colorizationColor() const;
bool lightThemeEnabled() const;
bool darkThemeEnabled() const;
bool highContrastModeEnabled() const;
bool darkFrameEnabled() const;
bool transparencyEffectEnabled() const;
#endif
public Q_SLOTS:
void removeWindowFrame(const bool center = false);
void moveWindowToDesktopCenter(const bool realCenter = true);
QSize desktopSize() const;
QRect desktopAvailableGeometry() const;
QSize desktopAvailableSize() const;
void addIgnoreArea(const QRect &val);
void addDraggableArea(const QRect &val);
void addIgnoreObject(QQuickItem *val);
void addDraggableObject(QQuickItem *val);
#ifdef Q_OS_WINDOWS
void setWindowFrameVisible(const bool value = true);
void displaySystemMenu(const int x, const int y, const bool isRtl = false);
void setBlurEffectEnabled(const bool enabled = true,
const bool forceAcrylic = false,
const QColor &gradientColor = Qt::white);
#endif
#ifdef Q_OS_WINDOWS
protected:
void timerEvent(QTimerEvent *event) override;
private:
void *rawHandle() const;
#endif
Q_SIGNALS:
void borderWidthChanged(int);
void borderHeightChanged(int);
void titleBarHeightChanged(int);
void resizableChanged(bool);
void minimumSizeChanged(const QSize &);
void maximumSizeChanged(const QSize &);
void titleBarEnabledChanged(bool);
#ifdef Q_OS_WINDOWS
void colorizationEnabledChanged(bool);
void colorizationColorChanged(const QColor &);
void lightThemeEnabledChanged(bool);
void darkThemeEnabledChanged(bool);
void highContrastModeEnabledChanged(bool);
void darkFrameEnabledChanged(bool);
void transparencyEffectEnabledChanged(bool);
#endif
};

View File

@@ -0,0 +1,463 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelesswindowsmanager.h"
#include <QGuiApplication>
#include <QScreen>
#include <QWindow>
#ifdef QT_WIDGETS_LIB
#include <QWidget>
#endif
#if (defined(Q_OS_WIN) || defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)) \
&& !defined(Q_OS_WINDOWS)
#define Q_OS_WINDOWS
#endif
#ifdef Q_OS_WINDOWS
#include "winnativeeventfilter.h"
#else
#include "framelesshelper.h"
#endif
namespace {
void reportError()
{
qFatal("Only top level QWidgets and QWindows are accepted.");
}
QScreen *getCurrentScreenFromWindow(QObject *window)
{
Q_ASSERT(window);
if (window->isWindowType()) {
return qobject_cast<QWindow *>(window)->screen();
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
return qobject_cast<QWidget *>(window)->screen();
#else
QWindow *win = qobject_cast<QWidget *>(window)->windowHandle();
return win ? win->screen() : nullptr;
#endif
}
#endif
else {
reportError();
}
return nullptr;
}
#ifdef Q_OS_WINDOWS
void *getRawHandleFromWindow(QObject *window)
{
Q_ASSERT(window);
if (window->isWindowType()) {
return reinterpret_cast<void *>(qobject_cast<QWindow *>(window)->winId());
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
return reinterpret_cast<void *>(qobject_cast<QWidget *>(window)->winId());
}
#endif
else {
reportError();
}
return nullptr;
}
#endif
#ifndef Q_OS_WINDOWS
using FLWM_CORE_DATA = struct _FLWM_CORE_DATA
{
FramelessHelper framelessHelper;
};
#endif
} // namespace
#ifndef Q_OS_WINDOWS
Q_GLOBAL_STATIC(FLWM_CORE_DATA, coreData)
#endif
FramelessWindowsManager::FramelessWindowsManager() {}
void FramelessWindowsManager::addWindow(QObject *window, const bool center)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
WinNativeEventFilter::addFramelessWindow(window);
#else
coreData()->framelessHelper.removeWindowFrame(window);
#endif
if (center) {
moveWindowToDesktopCenter(window);
}
}
void FramelessWindowsManager::moveWindowToDesktopCenter(QObject *window, const bool realCenter)
{
Q_ASSERT(window);
if (realCenter) {
#ifdef Q_OS_WINDOWS
WinNativeEventFilter::moveWindowToDesktopCenter(getRawHandleFromWindow(window));
#else
FramelessHelper::moveWindowToDesktopCenter(window);
#endif
} else {
QSize windowSize = {}, screenSize = {};
QRect screenGeometry = {};
if (window->isWindowType()) {
const auto win = qobject_cast<QWindow *>(window);
if (win) {
windowSize = win->size();
screenSize = getDesktopAvailableSize(win);
screenGeometry = getDesktopAvailableGeometry(win);
}
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(window);
if (widget && widget->isTopLevel()) {
windowSize = widget->size();
screenSize = getDesktopAvailableSize(widget);
screenGeometry = getDesktopAvailableGeometry(widget);
}
}
#endif
else {
reportError();
}
const int newX = qRound(static_cast<qreal>(screenSize.width() - windowSize.width()) / 2.0);
const int newY = qRound(static_cast<qreal>(screenSize.height() - windowSize.height()) / 2.0);
const int x = newX + screenGeometry.x();
const int y = newY + screenGeometry.y();
if (window->isWindowType()) {
const auto win = qobject_cast<QWindow *>(window);
if (win) {
win->setX(x);
win->setY(y);
}
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
const auto widget = qobject_cast<QWidget *>(window);
if (widget && widget->isTopLevel()) {
widget->move(x, y);
}
}
#endif
else {
reportError();
}
}
}
QSize FramelessWindowsManager::getDesktopSize(QObject *window)
{
const QScreen *screen = window ? getCurrentScreenFromWindow(window)
: QGuiApplication::primaryScreen();
return screen ? screen->size() : QSize();
}
QRect FramelessWindowsManager::getDesktopAvailableGeometry(QObject *window)
{
const QScreen *screen = window ? getCurrentScreenFromWindow(window)
: QGuiApplication::primaryScreen();
return screen ? screen->availableGeometry() : QRect();
}
QSize FramelessWindowsManager::getDesktopAvailableSize(QObject *window)
{
const QScreen *screen = window ? getCurrentScreenFromWindow(window)
: QGuiApplication::primaryScreen();
return screen ? screen->availableSize() : QSize();
}
void FramelessWindowsManager::addIgnoreArea(QObject *window, const QRect &area)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->ignoreAreas.append(area);
}
#else
coreData()->framelessHelper.addIgnoreArea(window, area);
#endif
}
void FramelessWindowsManager::addDraggableArea(QObject *window, const QRect &area)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->draggableAreas.append(area);
}
#else
coreData()->framelessHelper.addDraggableArea(window, area);
#endif
}
void FramelessWindowsManager::addIgnoreObject(QObject *window, QObject *object)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->ignoreObjects.append(object);
}
#else
coreData()->framelessHelper.addIgnoreObject(window, object);
#endif
}
void FramelessWindowsManager::addDraggableObject(QObject *window, QObject *object)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->draggableObjects.append(object);
}
#else
coreData()->framelessHelper.addDraggableObject(window, object);
#endif
}
int FramelessWindowsManager::getBorderWidth(QObject *window)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
return WinNativeEventFilter::getSystemMetric(getRawHandleFromWindow(window),
WinNativeEventFilter::SystemMetric::BorderWidth);
#else
Q_UNUSED(window)
return coreData()->framelessHelper.getBorderWidth();
#endif
}
void FramelessWindowsManager::setBorderWidth(QObject *window, const int value)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->borderWidth = value;
}
#else
Q_UNUSED(window)
coreData()->framelessHelper.setBorderWidth(value);
#endif
}
int FramelessWindowsManager::getBorderHeight(QObject *window)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
return WinNativeEventFilter::getSystemMetric(getRawHandleFromWindow(window),
WinNativeEventFilter::SystemMetric::BorderHeight);
#else
Q_UNUSED(window)
return coreData()->framelessHelper.getBorderHeight();
#endif
}
void FramelessWindowsManager::setBorderHeight(QObject *window, const int value)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->borderHeight = value;
}
#else
Q_UNUSED(window)
coreData()->framelessHelper.setBorderHeight(value);
#endif
}
int FramelessWindowsManager::getTitleBarHeight(QObject *window)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
return WinNativeEventFilter::getSystemMetric(getRawHandleFromWindow(window),
WinNativeEventFilter::SystemMetric::TitleBarHeight);
#else
Q_UNUSED(window)
return coreData()->framelessHelper.getTitleBarHeight();
#endif
}
void FramelessWindowsManager::setTitleBarHeight(QObject *window, const int value)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(window);
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->titleBarHeight = value;
}
#else
Q_UNUSED(window)
coreData()->framelessHelper.setTitleBarHeight(value);
#endif
}
bool FramelessWindowsManager::getResizable(QObject *window)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
return data ? !data->fixedSize : true;
#else
return coreData()->framelessHelper.getResizable(window);
#endif
}
void FramelessWindowsManager::setResizable(QObject *window, const bool value)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
WinNativeEventFilter::setWindowResizable(getRawHandleFromWindow(window), value);
#else
coreData()->framelessHelper.setResizable(window, value);
#endif
}
QSize FramelessWindowsManager::getMinimumSize(QObject *window)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
return data ? data->minimumSize : QSize();
#else
if (window->isWindowType()) {
return qobject_cast<QWindow *>(window)->minimumSize();
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
return qobject_cast<QWidget *>(window)->minimumSize();
}
#endif
else {
reportError();
}
return {};
#endif
}
void FramelessWindowsManager::setMinimumSize(QObject *window, const QSize &value)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->minimumSize = value;
}
#else
if (window->isWindowType()) {
qobject_cast<QWindow *>(window)->setMinimumSize(value);
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
qobject_cast<QWidget *>(window)->setMinimumSize(value);
}
#endif
else {
reportError();
}
#endif
}
QSize FramelessWindowsManager::getMaximumSize(QObject *window)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
return data ? data->maximumSize : QSize();
#else
if (window->isWindowType()) {
return qobject_cast<QWindow *>(window)->maximumSize();
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
return qobject_cast<QWidget *>(window)->maximumSize();
}
#endif
else {
reportError();
}
return {};
#endif
}
void FramelessWindowsManager::setMaximumSize(QObject *window, const QSize &value)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->maximumSize = value;
}
#else
if (window->isWindowType()) {
qobject_cast<QWindow *>(window)->setMaximumSize(value);
}
#ifdef QT_WIDGETS_LIB
else if (window->isWidgetType()) {
qobject_cast<QWidget *>(window)->setMaximumSize(value);
}
#endif
else {
reportError();
}
#endif
}
bool FramelessWindowsManager::getTitleBarEnabled(QObject *window)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
return data ? !data->disableTitleBar : true;
#else
return coreData()->framelessHelper.getTitleBarEnabled(window);
#endif
}
void FramelessWindowsManager::setTitleBarEnabled(QObject *window, const bool value)
{
Q_ASSERT(window);
#ifdef Q_OS_WINDOWS
const auto data = WinNativeEventFilter::windowData(window);
if (data) {
data->disableTitleBar = !value;
}
#else
coreData()->framelessHelper.setTitleBarEnabled(window, value);
#endif
}

View File

@@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#include <QRect>
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QObject)
QT_END_NAMESPACE
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class FRAMELESSHELPER_EXPORT FramelessWindowsManager
{
Q_DISABLE_COPY_MOVE(FramelessWindowsManager)
public:
explicit FramelessWindowsManager();
~FramelessWindowsManager() = default;
static void addWindow(QObject *window, const bool center = false);
static void moveWindowToDesktopCenter(QObject *window, const bool realCenter = true);
static QSize getDesktopSize(QObject *window = nullptr);
static QRect getDesktopAvailableGeometry(QObject *window = nullptr);
static QSize getDesktopAvailableSize(QObject *window = nullptr);
static void addIgnoreArea(QObject *window, const QRect &area);
static void addDraggableArea(QObject *window, const QRect &area);
static void addIgnoreObject(QObject *window, QObject *object);
static void addDraggableObject(QObject *window, QObject *object);
static int getBorderWidth(QObject *window);
static void setBorderWidth(QObject *window, const int value);
static int getBorderHeight(QObject *window);
static void setBorderHeight(QObject *window, const int value);
static int getTitleBarHeight(QObject *window);
static void setTitleBarHeight(QObject *window, const int value);
static bool getResizable(QObject *window);
static void setResizable(QObject *window, const bool value = true);
static QSize getMinimumSize(QObject *window);
static void setMinimumSize(QObject *window, const QSize &value);
static QSize getMaximumSize(QObject *window);
static void setMaximumSize(QObject *window, const QSize &value);
static bool getTitleBarEnabled(QObject *window);
static void setTitleBarEnabled(QObject *window, const bool value = true);
};

34
src/external/framelesshelper/lib.pro vendored Normal file
View File

@@ -0,0 +1,34 @@
TARGET = $$qtLibraryTarget(FramelessHelper)
TEMPLATE = lib
win32: DLLDESTDIR = $$OUT_PWD/bin
else: unix: DESTDIR = $$OUT_PWD/bin
QT += gui-private
qtHaveModule(widgets): QT += widgets
qtHaveModule(quick): QT += quick
CONFIG += c++17 strict_c++ utf8_source warn_on
DEFINES += \
QT_NO_CAST_FROM_ASCII \
QT_NO_CAST_TO_ASCII \
FRAMELESSHELPER_BUILD_LIBRARY
HEADERS += \
framelesshelper_global.h \
framelesswindowsmanager.h
SOURCES += framelesswindowsmanager.cpp
qtHaveModule(quick) {
HEADERS += framelessquickhelper.h
SOURCES += framelessquickhelper.cpp
}
win32 {
DEFINES += WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS
CONFIG += LINK_TO_SYSTEM_DLL
HEADERS += winnativeeventfilter.h
SOURCES += winnativeeventfilter.cpp
LINK_TO_SYSTEM_DLL {
DEFINES += WNEF_LINK_SYSLIB
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi -lshcore -ld2d1 -luxtheme
}
RC_FILE = framelesshelper.rc
} else {
HEADERS += framelesshelper.h
SOURCES += framelesshelper.cpp
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#include <QAbstractNativeEventFilter>
#include <QColor>
#include <QList>
#include <QRect>
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_FORWARD_DECLARE_CLASS(QObject)
QT_END_NAMESPACE
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
#define Q_DISABLE_MOVE(Class) \
Class(Class &&) = delete; \
Class &operator=(Class &&) = delete;
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(Class) \
Q_DISABLE_MOVE(Class)
#endif
class FRAMELESSHELPER_EXPORT WinNativeEventFilter : public QAbstractNativeEventFilter
{
Q_DISABLE_COPY_MOVE(WinNativeEventFilter)
public:
using WINDOWDATA = struct _WINDOWDATA
{
bool initialized = false /* Internal use only, don't modify it from outside */,
fixedSize = false, mouseTransparent = false, restoreDefaultWindowStyle = false,
enableLayeredWindow = false, disableTitleBar = false, enableBlurBehindWindow = false;
int borderWidth = -1, borderHeight = -1, titleBarHeight = -1;
QList<QRect> ignoreAreas = {}, draggableAreas = {};
QList<QObject *> ignoreObjects = {}, draggableObjects = {};
QSize maximumSize = {}, minimumSize = {};
QString currentScreen = {};
};
enum class SystemMetric { BorderWidth, BorderHeight, TitleBarHeight };
explicit WinNativeEventFilter();
~WinNativeEventFilter() override = default;
// Make the given window become frameless.
// The width and height will be scaled automatically according to DPI. Don't
// scale them yourself. Just pass the original value. If you don't want to
// change them, pass negative values to the parameters.
static void addFramelessWindow(void *window /* HWND */,
const WINDOWDATA *data = nullptr,
const bool center = false,
const int x = -1,
const int y = -1,
const int width = -1,
const int height = -1);
static void addFramelessWindow(QObject *window,
const WINDOWDATA *data = nullptr,
const bool center = false,
const int x = -1,
const int y = -1,
const int width = -1,
const int height = -1);
static void removeFramelessWindow(void *window /* HWND */);
static void removeFramelessWindow(QObject *window);
static void clearFramelessWindows();
// Set borderWidth, borderHeight or titleBarHeight to a negative value to
// restore default behavior.
// Note that it can only affect one specific window.
// If you want to change these values globally, use setBorderWidth instead.
static void setWindowData(void *window /* HWND */, const WINDOWDATA *data);
static void setWindowData(QObject *window, const WINDOWDATA *data);
// You can modify the given window's data directly, it's the same with using
// setWindowData.
static WINDOWDATA *windowData(void *window /* HWND */);
static WINDOWDATA *windowData(QObject *window);
// Change settings globally, not a specific window.
// These values will be scaled automatically according to DPI, don't scale
// them yourself. Just pass the original value.
static void setBorderWidth(const int bw);
static void setBorderHeight(const int bh);
static void setTitleBarHeight(const int tbh);
// System metric value of the given window (if the pointer is null,
// return the system's standard value).
static int getSystemMetric(void *handle /* HWND */,
const SystemMetric metric,
const bool dpiAware = false);
// Use this function to trigger a frame change event or redraw a
// specific window. Useful when you want to let some changes
// in effect immediately.
static void updateWindow(void *handle /* HWND */,
const bool triggerFrameChange = true,
const bool redraw = true);
// Change the geometry of a window through Win32 API.
// The width and height will be scaled automatically according to DPI. So
// just pass the original value.
static void setWindowGeometry(
void *handle /* HWND */, const int x, const int y, const int width, const int height);
// Move the window to the center of the desktop.
static void moveWindowToDesktopCenter(void *handle /* HWND */);
// Update Qt's internal data about the window frame, otherwise Qt will
// take the size of the window frame into account when anyone is trying to
// change the geometry of the window. That's not what we want.
static void updateQtFrame(QWindow *window, const int titleBarHeight);
// Display the system context menu.
static bool displaySystemMenu(void *handle /* HWND */,
const bool isRtl,
const int x,
const int y);
// Enable or disable the blur effect for a specific window.
// On Win10 it's the Acrylic effect.
static bool setBlurEffectEnabled(void *handle /* HWND */,
const bool enabled = true,
const QColor &gradientColor = Qt::white);
// Thin wrapper of DwmExtendFrameIntoClientArea().
static void updateFrameMargins(void *handle /* HWND */);
// A resizable window can be resized and maximized, however, a fixed size
// window can only be moved and minimized, it can't be resized and maximized.
static void setWindowResizable(void *handle /* HWND */, const bool resizable = true);
// Query whether colorization is enabled or not.
static bool colorizationEnabled();
// Acquire the theme/colorization color set by the user.
static QColor colorizationColor();
// Query whether the user is using the light theme or not.
static bool lightThemeEnabled();
// Query whether the user is using the dark theme or not.
static bool darkThemeEnabled();
// Query whether the high contrast mode is enabled or not.
static bool highContrastModeEnabled();
// Query whether the given window is using dark frame or not.
static bool darkFrameEnabled(void *handle /* HWND */);
// Query whether the transparency effect is enabled or not.
static bool transparencyEffectEnabled();
///////////////////////////////////////////////
/// CORE FUNCTION - THE SOUL OF THIS CODE
///////////////////////////////////////////////
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
#else
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
#endif
};

Some files were not shown because too many files have changed in this diff Show More