Add cross-platform Acrylic Effect
Currently still lacking Linux and macOS support, but will be added sooner. Signed-off-by: Yuhang Zhao <2546789017@qq.com>
|
@ -20,26 +20,45 @@ endif()
|
|||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Gui REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui REQUIRED)
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets)
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
|
||||
|
||||
set(SOURCES
|
||||
qtacrylichelper.qrc
|
||||
framelesshelper_global.h
|
||||
framelesswindowsmanager.h
|
||||
framelesswindowsmanager.cpp
|
||||
utilities.h
|
||||
utilities.cpp
|
||||
qtacryliceffecthelper.h
|
||||
qtacryliceffecthelper.cpp
|
||||
)
|
||||
|
||||
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
list(APPEND SOURCES
|
||||
qtacrylicwidget.h
|
||||
qtacrylicwidget.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
||||
list(APPEND SOURCES
|
||||
framelessquickhelper.h
|
||||
framelessquickhelper.cpp
|
||||
qtacrylicitem.h
|
||||
qtacrylicitem.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
winnativeeventfilter.h
|
||||
winnativeeventfilter.cpp
|
||||
utilities_win32.cpp
|
||||
framelesshelper_win32.h
|
||||
framelesshelper_win32.cpp
|
||||
qtacryliceffecthelper_win32.h
|
||||
qtacryliceffecthelper_win32.cpp
|
||||
)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
|
@ -60,6 +79,7 @@ if(NOT BUILD_SHARED_LIBS)
|
|||
FRAMELESSHELPER_STATIC
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE /utf-8)
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
|
||||
|
@ -67,29 +87,44 @@ if(MSVC)
|
|||
target_link_options(${PROJECT_NAME} PRIVATE /GUARD:CF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
QT_NO_CAST_FROM_ASCII
|
||||
QT_NO_CAST_TO_ASCII
|
||||
QT_NO_KEYWORDS
|
||||
QT_DEPRECATED_WARNINGS
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0x060000
|
||||
FRAMELESSHELPER_BUILD_LIBRARY
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
WNEF_LINK_SYSLIB
|
||||
UNICODE
|
||||
_UNICODE
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
user32 shell32 gdi32 dwmapi shcore uxtheme d2d1
|
||||
user32 shell32 gdi32 dwmapi
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::GuiPrivate
|
||||
)
|
||||
|
||||
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Quick
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
|
||||
)
|
||||
|
|
|
@ -42,7 +42,7 @@ If you are using part of or all the code from this repository in your own projec
|
|||
- Won't cover the task bar when maximized (Win32 only).
|
||||
- Won't block the auto-hide task bar when maximized (Win32 only).
|
||||
- No flickers when resizing.
|
||||
- Load all native APIs at run-time, no need to link to any system libraries directly (Win32 only).
|
||||
- Support the Acrylic Effect.
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -121,7 +121,7 @@ Please refer to <https://github.com/wangwenx190/framelesshelper/issues> for more
|
|||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -47,7 +47,7 @@ helper.setIgnoreAreas(&widget, {{0, 0, 30, 40}, {40, 0, 30, 40}});
|
|||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
- Won't cover the task bar when maximized.
|
||||
- Won't block the auto-hide task bar when maximized.
|
||||
- No flickers when resizing.
|
||||
- Load all Win32 APIs at run-time, no need to link to any system libraries directly.
|
||||
- Support the Acrylic Effect.
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -165,7 +165,7 @@ Thanks [**Julien Maille**](https://github.com/JulienMaille) for adding the `QMai
|
|||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>497</width>
|
||||
<height>348</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<normaloff>../windows.ico</normaloff>../windows.ico</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>497</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuItem_1">
|
||||
<property name="title">
|
||||
<string>Menu 1</string>
|
||||
</property>
|
||||
<addaction name="actionItem_1"/>
|
||||
<addaction name="actionItem_2"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuItem_2">
|
||||
<property name="title">
|
||||
<string>Menu 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMenu_3">
|
||||
<property name="title">
|
||||
<string>Menu 3</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuItem_1"/>
|
||||
<addaction name="menuItem_2"/>
|
||||
<addaction name="menuMenu_3"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QDockWidget" name="dockWidget">
|
||||
<property name="windowTitle">
|
||||
<string>Dock 1</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>1</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents"/>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dockWidget_2">
|
||||
<property name="windowTitle">
|
||||
<string>Dock 2</string>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>1</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_2"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionAction_1"/>
|
||||
<addaction name="actionAction_2"/>
|
||||
</widget>
|
||||
<action name="actionAction_1">
|
||||
<property name="text">
|
||||
<string>Action 1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAction_2">
|
||||
<property name="text">
|
||||
<string>Action 2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionItem_1">
|
||||
<property name="text">
|
||||
<string>Item 1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionItem_2">
|
||||
<property name="text">
|
||||
<string>Item 2</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,6 +0,0 @@
|
|||
TARGET = QMainWindow
|
||||
TEMPLATE = app
|
||||
QT += widgets
|
||||
SOURCES += main.cpp
|
||||
FORMS += TitleBar.ui MainWindow.ui
|
||||
include($$PWD/../common.pri)
|
|
@ -1,267 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TitleBar</class>
|
||||
<widget class="QWidget" name="TitleBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>423</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#iconButton, #minimizeButton, #maximizeButton, #closeButton {
|
||||
border-style: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#minimizeButton:hover, #maximizeButton:hover {
|
||||
background-color: #80c7c7c7;
|
||||
}
|
||||
|
||||
#minimizeButton:pressed, #maximizeButton:pressed {
|
||||
background-color: #80808080;
|
||||
}
|
||||
|
||||
#closeButton:hover {
|
||||
background-color: #e81123;
|
||||
}
|
||||
|
||||
#closeButton:pressed {
|
||||
background-color: #8c0a15;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="iconButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>26</width>
|
||||
<height>26</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>26</width>
|
||||
<height>26</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="titleLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>262</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="minimizeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Minimize</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../images.qrc">
|
||||
<normaloff>:/images/button_minimize_black.svg</normaloff>:/images/button_minimize_black.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="maximizeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Maximize</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../images.qrc">
|
||||
<normaloff>:/images/button_maximize_black.svg</normaloff>
|
||||
<normalon>:/images/button_restore_black.svg</normalon>:/images/button_maximize_black.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../images.qrc">
|
||||
<normaloff>:/images/button_close_black.svg</normaloff>:/images/button_close_black.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>46</width>
|
||||
<height>31</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
<include location="../images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../../framelesswindowsmanager.h"
|
||||
#include "ui_MainWindow.h"
|
||||
#include "ui_TitleBar.h"
|
||||
#include <QApplication>
|
||||
#include <QStyleOption>
|
||||
#include <QWidget>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
// High DPI scaling is enabled by default from Qt 6
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Windows: we are using the manifest file to get maximum compatibility
|
||||
// because some APIs are not supprted on old systems such as Windows 7
|
||||
// and Windows 8. And once we have set the DPI awareness level in the
|
||||
// manifest file, any attemptation to try to change it through API will
|
||||
// fail. In other words, Qt won't be able to enable or disable high DPI
|
||||
// scaling or change the DPI awareness level once we have set it in the
|
||||
// manifest file. So the following two lines are uesless actually (However,
|
||||
// they are still useful on other platforms).
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
// Don't round the scale factor.
|
||||
// This will break QWidget applications because they can't render correctly.
|
||||
// Qt Quick applications won't have this issue.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QApplication application(argc, argv);
|
||||
|
||||
QMainWindow *mainWindow = new QMainWindow;
|
||||
Ui::MainWindow appMainWindow;
|
||||
appMainWindow.setupUi(mainWindow);
|
||||
|
||||
QWidget *widget = new QWidget;
|
||||
Ui::TitleBar titleBarWidget;
|
||||
titleBarWidget.setupUi(widget);
|
||||
|
||||
QMenuBar *menuBar = mainWindow->menuBar();
|
||||
titleBarWidget.horizontalLayout->insertWidget(1, menuBar);
|
||||
menuBar->setMaximumHeight(20);
|
||||
|
||||
mainWindow->setMenuWidget(widget);
|
||||
|
||||
QObject::connect(mainWindow,
|
||||
&QMainWindow::windowIconChanged,
|
||||
titleBarWidget.iconButton,
|
||||
&QPushButton::setIcon);
|
||||
QObject::connect(mainWindow,
|
||||
&QMainWindow::windowTitleChanged,
|
||||
titleBarWidget.titleLabel,
|
||||
&QLabel::setText);
|
||||
QObject::connect(titleBarWidget.closeButton,
|
||||
&QPushButton::clicked,
|
||||
mainWindow,
|
||||
&QMainWindow::close);
|
||||
QObject::connect(titleBarWidget.minimizeButton,
|
||||
&QPushButton::clicked,
|
||||
mainWindow,
|
||||
&QMainWindow::showMinimized);
|
||||
QObject::connect(titleBarWidget.maximizeButton,
|
||||
&QPushButton::clicked,
|
||||
[mainWindow, titleBarWidget]() {
|
||||
if (mainWindow->isMaximized()) {
|
||||
mainWindow->showNormal();
|
||||
titleBarWidget.maximizeButton->setToolTip(QObject::tr("Maximize"));
|
||||
} else {
|
||||
mainWindow->showMaximized();
|
||||
titleBarWidget.maximizeButton->setToolTip(QObject::tr("Restore"));
|
||||
}
|
||||
});
|
||||
|
||||
QStyleOption option;
|
||||
option.initFrom(mainWindow);
|
||||
const QIcon icon = mainWindow->style()->standardIcon(QStyle::SP_ComputerIcon, &option);
|
||||
mainWindow->setWindowIcon(icon);
|
||||
mainWindow->setWindowTitle(QObject::tr("Hello, World!"));
|
||||
|
||||
mainWindow->createWinId(); // Qt's internal function, make sure it's a top level window.
|
||||
const QWindow *win = mainWindow->windowHandle();
|
||||
FramelessWindowsManager::addWindow(win);
|
||||
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.minimizeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.maximizeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.closeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, appMainWindow.menubar);
|
||||
|
||||
mainWindow->resize(800, 600);
|
||||
|
||||
mainWindow->show();
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(QQPlayer LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
find_package(Qt5 COMPONENTS Quick REQUIRED)
|
||||
|
||||
set(source_files qml.qrc images.qrc main.cpp ../../framelessquickhelper.h ../../framelessquickhelper.cpp)
|
||||
|
||||
if(WIN32)
|
||||
enable_language(RC)
|
||||
list(APPEND source_files QQPlayer.exe.manifest QQPlayer.rc ../../winnativeeventfilter.h ../../winnativeeventfilter.cpp)
|
||||
else()
|
||||
list(APPEND source_files ../../framelesshelper.h ../../framelesshelper.cpp)
|
||||
endif()
|
||||
|
||||
add_executable(QQPlayer WIN32 ${source_files})
|
||||
|
||||
if(MSVC)
|
||||
# Silence a compiler warning caused by the Chinese comments in the source file.
|
||||
target_compile_options(QQPlayer PRIVATE -utf-8)
|
||||
endif()
|
||||
target_compile_definitions(QQPlayer PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
|
||||
target_link_libraries(QQPlayer PRIVATE Qt::Quick Qt::GuiPrivate)
|
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity type="win32" name="com.wangwenx190.demo.qqplayer" version="1.0.0.0"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 7 and Windows Server 2008 R2 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 and Windows Server 2012 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 and Windows Server 2012 R2 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
Before Width: | Height: | Size: 133 KiB |
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
IDI_ICON1 ICON "QQPlayer.ico"
|
||||
|
||||
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "QQPlayer.exe.manifest"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "Built by the Qt Toolkit."
|
||||
VALUE "CompanyName", "wangwenx190"
|
||||
VALUE "FileDescription", "QQPlayer DEMO"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "InternalName", "qqplayer-demo"
|
||||
VALUE "LegalCopyright", "MIT License"
|
||||
#ifdef _DEBUG
|
||||
VALUE "OriginalFilename", "QQPlayerd.exe"
|
||||
#else
|
||||
VALUE "OriginalFilename", "QQPlayer.exe"
|
||||
#endif
|
||||
VALUE "ProductName", "QQPlayer"
|
||||
VALUE "ProductVersion", "1.0.0.0"
|
||||
VALUE "LegalTrademarks", "Tencent"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
|
@ -1,12 +0,0 @@
|
|||
Qt Quick 模仿的 QQ播放器
|
||||
|
||||
只做了主界面,而且只有界面,没有功能,仅供展示
|
||||
|
||||
可以看出,只要有贴图,Qt Quick 做这种界面很容易的
|
||||
|
||||
按钮的图片有些糊,而且边缘呈锯齿状,是因为我是直接从截图里扣下来的
|
||||
,没有仔细处理。由于只是示例项目,因此不会再进行任何额外处理了。
|
||||
如果是公司的项目,肯定会有正规的高清贴图的。
|
||||
|
||||
所有来自QQ播放器的图片以及图标,版权均归腾讯所有。
|
||||
此仓库里提供的所有相关的素材文件,仅供学习交流,请勿传播甚至商用。
|
|
@ -1,13 +0,0 @@
|
|||
@echo off
|
||||
call "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
call "%SystemDrive%\Qt\5.15.0\msvc2019_64\bin\qtenv2.bat"
|
||||
cd /d "%~dp0"
|
||||
if exist build rd /s /q build
|
||||
md build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=bin -GNinja ..
|
||||
cmake --build .
|
||||
windeployqt --force --no-translations --no-system-d3d-compiler --no-virtualkeyboard --no-compiler-runtime --no-angle --no-opengl-sw --dir "%~dp0build\bin\qml" --libdir "%~dp0build\bin" --plugindir "%~dp0build\bin\plugins" --qmldir "%~dp0resources\qml" "%~dp0build\bin\QQPlayer.exe"
|
||||
cd /d "%~dp0"
|
||||
start explorer /select,"%~dp0build\bin\QQPlayer.exe"
|
||||
exit /b
|
|
@ -1,28 +0,0 @@
|
|||
<RCC>
|
||||
<qresource prefix="/images">
|
||||
<file alias="background.png">resources/images/background.png</file>
|
||||
<file alias="button_close_black.svg">../../resources/images/button_close_black.svg</file>
|
||||
<file alias="button_close_white.svg">../../resources/images/button_close_white.svg</file>
|
||||
<file alias="button_maximize_black.svg">../../resources/images/button_maximize_black.svg</file>
|
||||
<file alias="button_maximize_white.svg">../../resources/images/button_maximize_white.svg</file>
|
||||
<file alias="button_minimize_black.svg">../../resources/images/button_minimize_black.svg</file>
|
||||
<file alias="button_minimize_white.svg">../../resources/images/button_minimize_white.svg</file>
|
||||
<file alias="button_restore_black.svg">../../resources/images/button_restore_black.svg</file>
|
||||
<file alias="button_restore_white.svg">../../resources/images/button_restore_white.svg</file>
|
||||
<file alias="logo.png">resources/images/logo.png</file>
|
||||
<file alias="open_file_blue.png">resources/images/open_file_blue.png</file>
|
||||
<file alias="open_file_white.png">resources/images/open_file_white.png</file>
|
||||
<file alias="main_menu_blue.png">resources/images/main_menu_blue.png</file>
|
||||
<file alias="main_menu_white.png">resources/images/main_menu_white.png</file>
|
||||
<file alias="play_blue.png">resources/images/play_blue.png</file>
|
||||
<file alias="play_blue_light.png">resources/images/play_blue_light.png</file>
|
||||
<file alias="stop_blue.png">resources/images/stop_blue.png</file>
|
||||
<file alias="stop_blue_light.png">resources/images/stop_blue_light.png</file>
|
||||
<file alias="previous_blue.png">resources/images/previous_blue.png</file>
|
||||
<file alias="previous_blue_light.png">resources/images/previous_blue_light.png</file>
|
||||
<file alias="next_blue.png">resources/images/next_blue.png</file>
|
||||
<file alias="next_blue_light.png">resources/images/next_blue_light.png</file>
|
||||
<file alias="volume_blue.png">resources/images/volume_blue.png</file>
|
||||
<file alias="volume_blue_light.png">resources/images/volume_blue_light.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../../framelessquickhelper.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Enable Qt RHI, the default backend on Windows is D3D 11.1
|
||||
qputenv("QSG_RHI", "1");
|
||||
qputenv("QSG_INFO", "1");
|
||||
#if (QT_VERSION <= QT_VERSION_CHECK(6, 0, 0))
|
||||
QGuiApplication::setAttribute(
|
||||
Qt::ApplicationAttribute::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(
|
||||
Qt::ApplicationAttribute::AA_UseHighDpiPixmaps);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
QGuiApplication::setApplicationName(QString::fromUtf8("QQPlayer"));
|
||||
QGuiApplication::setApplicationDisplayName(QString::fromUtf8("QQPlayer"));
|
||||
#ifndef Q_OS_WINDOWS
|
||||
QGuiApplication::setApplicationVersion(QString::fromUtf8("1.0.0"));
|
||||
#endif
|
||||
QGuiApplication::setOrganizationName(QString::fromUtf8("wangwenx190"));
|
||||
QGuiApplication::setOrganizationDomain(
|
||||
QString::fromUtf8("wangwenx190.github.io"));
|
||||
QGuiApplication application(argc, argv);
|
||||
QQmlApplicationEngine engine;
|
||||
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0,
|
||||
"FramelessHelper");
|
||||
const QUrl mainQmlUrl(QString::fromUtf8("qrc:/qml/main.qml"));
|
||||
const QMetaObject::Connection connection = QObject::connect(
|
||||
&engine, &QQmlApplicationEngine::objectCreated, &application,
|
||||
[&mainQmlUrl, &connection](QObject *object, const QUrl &url) {
|
||||
if (url != mainQmlUrl) {
|
||||
return;
|
||||
}
|
||||
if (!object) {
|
||||
QGuiApplication::exit(-1);
|
||||
} else {
|
||||
QObject::disconnect(connection);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
engine.load(mainQmlUrl);
|
||||
return QGuiApplication::exec();
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<RCC>
|
||||
<qresource prefix="/qml">
|
||||
<file alias="main.qml">resources/qml/main.qml</file>
|
||||
<file alias="CloseButton.qml">resources/qml/CloseButton.qml</file>
|
||||
<file alias="MaximizeButton.qml">resources/qml/MaximizeButton.qml</file>
|
||||
<file alias="MinimizeButton.qml">resources/qml/MinimizeButton.qml</file>
|
||||
<file alias="OpenFileButton.qml">resources/qml/OpenFileButton.qml</file>
|
||||
<file alias="MainMenuButton.qml">resources/qml/MainMenuButton.qml</file>
|
||||
<file alias="PlayButton.qml">resources/qml/PlayButton.qml</file>
|
||||
<file alias="StopButton.qml">resources/qml/StopButton.qml</file>
|
||||
<file alias="PreviousButton.qml">resources/qml/PreviousButton.qml</file>
|
||||
<file alias="NextButton.qml">resources/qml/NextButton.qml</file>
|
||||
<file alias="VolumeButton.qml">resources/qml/VolumeButton.qml</file>
|
||||
<file alias="ProgressSlider.qml">resources/qml/ProgressSlider.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
Before Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 75
|
||||
implicitHeight: 15
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/main_menu_blue.png" : "qrc:/images/main_menu_white.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 45
|
||||
implicitHeight: 30
|
||||
|
||||
ToolTip.visible: hovered && !down
|
||||
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
ToolTip.text: qsTr("Minimize")
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: "qrc:/images/button_minimize_white.svg"
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
visible: control.down || control.hovered
|
||||
color: control.down ? "#808080" : (control.hovered ? "#c7c7c7" : "transparent")
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 16
|
||||
implicitHeight: 14
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/next_blue_light.png" : "qrc:/images/next_blue.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 187
|
||||
implicitHeight: 50
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/open_file_blue.png" : "qrc:/images/open_file_white.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 45
|
||||
implicitHeight: 45
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/play_blue_light.png" : "qrc:/images/play_blue.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 16
|
||||
implicitHeight: 14
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/previous_blue_light.png" : "qrc:/images/previous_blue.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Slider {
|
||||
id: control
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
property int progressDuration: 200
|
||||
property int heightDuration: 100
|
||||
property int hoveredHeight: 10
|
||||
property int normalHeight: 4
|
||||
property color backgroundColor: "#bdbebf"
|
||||
property color foregroundColor: "#21be2b"
|
||||
property color handleColor: "#f0f0f0"
|
||||
property int handleWidth: 14
|
||||
property int handleBorderWidth: 0
|
||||
property color handleBorderColor: "transparent"
|
||||
property bool handleVisibility: true
|
||||
|
||||
background: Rectangle {
|
||||
x: 0
|
||||
y: control.availableHeight / 2 - height / 2
|
||||
implicitWidth: 200
|
||||
implicitHeight: control.hovered ? control.hoveredHeight : control.normalHeight
|
||||
width: control.availableWidth
|
||||
height: implicitHeight
|
||||
color: control.backgroundColor
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: control.heightDuration
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: control.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: control.foregroundColor
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: control.progressDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
visible: control.handleVisibility
|
||||
x: control.visualPosition * (control.availableWidth - width)
|
||||
y: control.availableHeight / 2 - height / 2
|
||||
implicitWidth: control.handleWidth
|
||||
implicitHeight: implicitWidth
|
||||
radius: implicitHeight / 2
|
||||
color: control.handleColor
|
||||
border.width: control.handleBorderWidth
|
||||
border.color: control.handleBorderColor
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: control.progressDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 14
|
||||
implicitHeight: 14
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/stop_blue_light.png" : "qrc:/images/stop_blue.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: 18
|
||||
implicitHeight: 14
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: control.down
|
||||
|| control.hovered ? "qrc:/images/volume_blue_light.png" : "qrc:/images/volume_blue.png"
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import wangwenx190.Utils 1.0
|
||||
|
||||
Window {
|
||||
id: window
|
||||
visible: true
|
||||
width: 800
|
||||
height: 540
|
||||
title: qsTr("QQ Player")
|
||||
|
||||
property color themeColor: "#111111"
|
||||
|
||||
FramelessHelper {
|
||||
id: framelessHelper
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: titleBar
|
||||
height: framelessHelper.titleBarHeight
|
||||
color: window.themeColor
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
MainMenuButton {
|
||||
id: mainMenuButton
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
mainMenuButton)
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
MinimizeButton {
|
||||
id: minimizeButton
|
||||
onClicked: window.showMinimized()
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
minimizeButton)
|
||||
}
|
||||
|
||||
MaximizeButton {
|
||||
id: maximizeButton
|
||||
maximized: window.visibility === Window.Maximized
|
||||
onClicked: {
|
||||
if (maximized) {
|
||||
window.showNormal()
|
||||
} else {
|
||||
window.showMaximized()
|
||||
}
|
||||
}
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
maximizeButton)
|
||||
}
|
||||
|
||||
CloseButton {
|
||||
id: closeButton
|
||||
onClicked: window.close()
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
closeButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backgroundImage
|
||||
anchors.top: titleBar.bottom
|
||||
anchors.bottom: controlPanel.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
source: "qrc:/images/background.png"
|
||||
|
||||
Image {
|
||||
id: logoImage
|
||||
width: 123
|
||||
height: 99
|
||||
anchors.centerIn: parent
|
||||
source: "qrc:/images/logo.png"
|
||||
}
|
||||
|
||||
OpenFileButton {
|
||||
id: openFileButton
|
||||
anchors.top: logoImage.bottom
|
||||
anchors.topMargin: 25
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: controlPanel
|
||||
height: 63
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: window.themeColor
|
||||
|
||||
ProgressSlider {
|
||||
id: progressSlider
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: -2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
foregroundColor: "#1ab7e4"
|
||||
handleColor: "#923cf2"
|
||||
}
|
||||
|
||||
StopButton {
|
||||
id: stopButton
|
||||
anchors.right: previousButton.left
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
PreviousButton {
|
||||
id: previousButton
|
||||
anchors.right: playButton.left
|
||||
anchors.rightMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
PlayButton {
|
||||
id: playButton
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
NextButton {
|
||||
id: nextButton
|
||||
anchors.left: playButton.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
VolumeButton {
|
||||
id: volumeButton
|
||||
anchors.left: nextButton.right
|
||||
anchors.leftMargin: 30
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: framelessHelper.removeWindowFrame()
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
TARGET = QWidget
|
||||
TEMPLATE = app
|
||||
QT += widgets
|
||||
SOURCES += main.cpp
|
||||
include($$PWD/../common.pri)
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../../framelesswindowsmanager.h"
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
// High DPI scaling is enabled by default from Qt 6
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Windows: we are using the manifest file to get maximum compatibility
|
||||
// because some APIs are not supprted on old systems such as Windows 7
|
||||
// and Windows 8. And once we have set the DPI awareness level in the
|
||||
// manifest file, any attemptation to try to change it through API will
|
||||
// fail. In other words, Qt won't be able to enable or disable high DPI
|
||||
// scaling or change the DPI awareness level once we have set it in the
|
||||
// manifest file. So the following two lines are uesless actually (However,
|
||||
// they are still useful on other platforms).
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
// Don't round the scale factor.
|
||||
// This will break QWidget applications because they can't render correctly.
|
||||
// Qt Quick applications won't have this issue.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QApplication application(argc, argv);
|
||||
|
||||
QWidget widget;
|
||||
widget.setContentsMargins(2, 2, 2, 2);
|
||||
QLabel *label = new QLabel;
|
||||
QObject::connect(&widget, &QWidget::windowTitleChanged, label, &QLabel::setText);
|
||||
QPushButton *minimizeButton = new QPushButton;
|
||||
minimizeButton->setText(QObject::tr("Minimize"));
|
||||
QObject::connect(minimizeButton, &QPushButton::clicked, &widget, &QWidget::showMinimized);
|
||||
QPushButton *maximizeButton = new QPushButton;
|
||||
maximizeButton->setText(QObject::tr("Maximize"));
|
||||
QObject::connect(maximizeButton, &QPushButton::clicked, [&widget, &maximizeButton]() {
|
||||
if (widget.isMaximized()) {
|
||||
widget.showNormal();
|
||||
maximizeButton->setText(QObject::tr("Maximize"));
|
||||
} else {
|
||||
widget.showMaximized();
|
||||
maximizeButton->setText(QObject::tr("Restore"));
|
||||
}
|
||||
});
|
||||
QPushButton *closeButton = new QPushButton;
|
||||
closeButton->setText(QObject::tr("Close"));
|
||||
QObject::connect(closeButton, &QPushButton::clicked, &widget, &QWidget::close);
|
||||
QHBoxLayout *tbLayout = new QHBoxLayout;
|
||||
tbLayout->setContentsMargins(0, 0, 0, 0);
|
||||
tbLayout->setSpacing(0);
|
||||
tbLayout->addSpacing(15);
|
||||
tbLayout->addWidget(label);
|
||||
tbLayout->addStretch();
|
||||
tbLayout->addWidget(minimizeButton);
|
||||
tbLayout->addWidget(maximizeButton);
|
||||
tbLayout->addWidget(closeButton);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->addLayout(tbLayout);
|
||||
mainLayout->addStretch();
|
||||
widget.setLayout(mainLayout);
|
||||
widget.setWindowTitle(QObject::tr("Hello, World!"));
|
||||
|
||||
widget.createWinId(); // Qt's internal function, make sure it's a top level window.
|
||||
const QWindow *win = widget.windowHandle();
|
||||
FramelessWindowsManager::addWindow(win);
|
||||
FramelessWindowsManager::addIgnoreObject(win, minimizeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, maximizeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, closeButton);
|
||||
|
||||
widget.resize(800, 600);
|
||||
|
||||
widget.show();
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
|
@ -1,258 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "widget.h"
|
||||
#include "../../framelesshelper.h"
|
||||
#include <QEvent>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
Q_GLOBAL_STATIC(FramelessHelper, framelessHelper)
|
||||
|
||||
ContentsWidget::ContentsWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setAttribute(Qt::WA_StyledBackground);
|
||||
}
|
||||
|
||||
void ContentsWidget::setShouldDrawWindowBorder(const bool val)
|
||||
{
|
||||
m_bShouldDrawWindowBorder = val;
|
||||
update();
|
||||
}
|
||||
|
||||
bool ContentsWidget::getShouldDrawWindowBorder() const
|
||||
{
|
||||
return m_bShouldDrawWindowBorder;
|
||||
}
|
||||
|
||||
void ContentsWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
if (m_bShouldDrawWindowBorder) {
|
||||
QPainter painter(this);
|
||||
painter.save();
|
||||
painter.setPen({window()->isActiveWindow() ? Qt::black : Qt::darkGray, 1.5});
|
||||
painter.drawLine(0, 0, width(), 0);
|
||||
painter.drawLine(0, height(), width(), height());
|
||||
painter.drawLine(0, 0, 0, height());
|
||||
painter.drawLine(width(), 0, width(), height());
|
||||
painter.restore();
|
||||
}
|
||||
}
|
||||
|
||||
Widget::Widget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
titleBarHeight = framelessHelper()->getTitleBarHeight();
|
||||
createWinId();
|
||||
setupUi();
|
||||
initBackgroundWindow();
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
bool Widget::isNormal() const
|
||||
{
|
||||
return (!isMinimized() && !isMaximized() && !isFullScreen());
|
||||
}
|
||||
|
||||
void Widget::setupUi()
|
||||
{
|
||||
contentsWidget = new ContentsWidget(this);
|
||||
contentsWidget->setObjectName(QString::fromUtf8("contentsWidget"));
|
||||
contentsWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
const QSize systemButtonSize = {qRound(titleBarHeight * 1.5), titleBarHeight};
|
||||
const QSize systemIconSize = {17, 17};
|
||||
titleBarWidget = new QWidget(contentsWidget);
|
||||
titleBarWidget->setObjectName(QString::fromUtf8("titleBarWidget"));
|
||||
titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
titleBarWidget->setFixedHeight(titleBarHeight);
|
||||
windowIconButton = new QPushButton(this);
|
||||
windowIconButton->setObjectName(QString::fromUtf8("windowIconButton"));
|
||||
windowIconButton->setFixedSize(systemIconSize);
|
||||
windowIconButton->setIconSize(systemIconSize);
|
||||
connect(this, &Widget::windowIconChanged, windowIconButton, &QPushButton::setIcon);
|
||||
windowTitleLabel = new QLabel(this);
|
||||
QFont f = font();
|
||||
f.setPointSizeF(8.63);
|
||||
windowTitleLabel->setFont(f);
|
||||
connect(this, &Widget::windowTitleChanged, windowTitleLabel, &QLabel::setText);
|
||||
minimizeButton = new QPushButton(this);
|
||||
minimizeButton->setObjectName(QString::fromUtf8("minimizeButton"));
|
||||
minimizeButton->setFixedSize(systemButtonSize);
|
||||
minimizeButton->setIconSize(systemButtonSize);
|
||||
minimizeButton->setIcon(QIcon(QString::fromUtf8(":/images/button_minimize_black.svg")));
|
||||
connect(minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
|
||||
maximizeButton = new QPushButton(this);
|
||||
maximizeButton->setObjectName(QString::fromUtf8("maximizeButton"));
|
||||
maximizeButton->setFixedSize(systemButtonSize);
|
||||
maximizeButton->setIconSize(systemButtonSize);
|
||||
maximizeButton->setIcon(QIcon(QString::fromUtf8(":/images/button_maximize_black.svg")));
|
||||
connect(maximizeButton, &QPushButton::clicked, this, [this]() {
|
||||
if (isMaximized()) {
|
||||
showNormal();
|
||||
} else {
|
||||
showMaximized();
|
||||
}
|
||||
});
|
||||
closeButton = new QPushButton(this);
|
||||
closeButton->setObjectName(QString::fromUtf8("closeButton"));
|
||||
closeButton->setFixedSize(systemButtonSize);
|
||||
closeButton->setIconSize(systemButtonSize);
|
||||
closeButton->setIcon(QIcon(QString::fromUtf8(":/images/button_close_black.svg")));
|
||||
connect(closeButton, &QPushButton::clicked, this, &Widget::close);
|
||||
const auto titleBarWidgetLayout = new QHBoxLayout(titleBarWidget);
|
||||
titleBarWidgetLayout->setSpacing(0);
|
||||
titleBarWidgetLayout->setContentsMargins(0, 0, 0, 0);
|
||||
titleBarWidgetLayout->addSpacerItem(
|
||||
new QSpacerItem(7, 20, QSizePolicy::Fixed, QSizePolicy::Fixed));
|
||||
titleBarWidgetLayout->addWidget(windowIconButton);
|
||||
titleBarWidgetLayout->addSpacerItem(
|
||||
new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Fixed));
|
||||
titleBarWidgetLayout->addWidget(windowTitleLabel);
|
||||
titleBarWidgetLayout->addStretch();
|
||||
titleBarWidgetLayout->addWidget(minimizeButton);
|
||||
titleBarWidgetLayout->addWidget(maximizeButton);
|
||||
titleBarWidgetLayout->addWidget(closeButton);
|
||||
titleBarWidget->setLayout(titleBarWidgetLayout);
|
||||
const auto contentsWidgetLayout = new QVBoxLayout(contentsWidget);
|
||||
contentsWidgetLayout->setSpacing(0);
|
||||
contentsWidgetLayout->setContentsMargins(1, 1, 1, 0);
|
||||
contentsWidgetLayout->addWidget(titleBarWidget);
|
||||
contentsWidgetLayout->addStretch();
|
||||
contentsWidget->setLayout(contentsWidgetLayout);
|
||||
const auto backgroundWindowLayout = new QVBoxLayout(this);
|
||||
backgroundWindowLayout->setSpacing(0);
|
||||
backgroundWindowLayout->addWidget(contentsWidget);
|
||||
setLayout(backgroundWindowLayout);
|
||||
setStyleSheet(QString::fromUtf8(R"(
|
||||
#contentsWidget {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#titleBarWidget {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#windowIconButton, #minimizeButton, #maximizeButton, #closeButton {
|
||||
border-style: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#minimizeButton:hover, #maximizeButton:hover {
|
||||
background-color: #80c7c7c7;
|
||||
}
|
||||
|
||||
#minimizeButton:pressed, #maximizeButton:pressed {
|
||||
background-color: #80808080;
|
||||
}
|
||||
|
||||
#closeButton:hover {
|
||||
background-color: #e81123;
|
||||
}
|
||||
|
||||
#closeButton:pressed {
|
||||
background-color: #8c0a15;
|
||||
}
|
||||
)"));
|
||||
}
|
||||
|
||||
void Widget::initBackgroundWindow()
|
||||
{
|
||||
QWindow *win = windowHandle();
|
||||
framelessHelper()->removeWindowFrame(win);
|
||||
framelessHelper()->addIgnoreObject(win, windowIconButton);
|
||||
framelessHelper()->addIgnoreObject(win, minimizeButton);
|
||||
framelessHelper()->addIgnoreObject(win, maximizeButton);
|
||||
framelessHelper()->addIgnoreObject(win, closeButton);
|
||||
framelessHelper()->setTitleBarHeight(titleBarHeight + framelessHelper()->getBorderHeight());
|
||||
//setAttribute(Qt::WA_Hover);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
shadowEffect = new QGraphicsDropShadowEffect(this);
|
||||
shadowEffect->setOffset(0, 0);
|
||||
contentsWidget->setGraphicsEffect(shadowEffect);
|
||||
setFrameShadowEnabled();
|
||||
setFrameShadowActive();
|
||||
}
|
||||
|
||||
void Widget::setFrameShadowEnabled(const bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
const int bw = framelessHelper()->getBorderWidth();
|
||||
const int bh = framelessHelper()->getBorderHeight();
|
||||
layout()->setContentsMargins(bw, bh, bw, bh);
|
||||
shadowEffect->setEnabled(true);
|
||||
} else {
|
||||
shadowEffect->setEnabled(false);
|
||||
layout()->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setFrameShadowActive(const bool active)
|
||||
{
|
||||
if (active) {
|
||||
shadowEffect->setColor({0, 0, 0, 80});
|
||||
shadowEffect->setBlurRadius(25);
|
||||
} else {
|
||||
shadowEffect->setColor({0, 0, 0, 60});
|
||||
shadowEffect->setBlurRadius(20);
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
Q_ASSERT(object);
|
||||
Q_ASSERT(event);
|
||||
if (object == this) {
|
||||
switch (event->type()) {
|
||||
case QEvent::WindowStateChange: {
|
||||
const bool normal = isNormal();
|
||||
contentsWidget->setShouldDrawWindowBorder(normal);
|
||||
setFrameShadowEnabled(normal);
|
||||
framelessHelper()->setTitleBarHeight(
|
||||
titleBarHeight + (normal ? framelessHelper()->getBorderHeight() : 0));
|
||||
const QString maxIconPath = QString::fromUtf8(":/images/button_maximize_black.svg");
|
||||
const QString restoreIconPath = QString::fromUtf8(":/images/button_restore_black.svg");
|
||||
maximizeButton->setIcon(QIcon(normal ? maxIconPath : restoreIconPath));
|
||||
} break;
|
||||
case QEvent::WindowActivate: {
|
||||
if (isNormal()) {
|
||||
setFrameShadowActive();
|
||||
}
|
||||
} break;
|
||||
case QEvent::WindowDeactivate: {
|
||||
if (isNormal()) {
|
||||
setFrameShadowActive(false);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QWidget::eventFilter(object, event);
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
|
||||
#define Q_DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Q_DISABLE_MOVE(Class)
|
||||
#endif
|
||||
|
||||
class QGraphicsDropShadowEffect;
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
|
||||
class ContentsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(ContentsWidget)
|
||||
|
||||
public:
|
||||
explicit ContentsWidget(QWidget *parent = nullptr);
|
||||
~ContentsWidget() override = default;
|
||||
|
||||
void setShouldDrawWindowBorder(const bool val);
|
||||
bool getShouldDrawWindowBorder() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
bool m_bShouldDrawWindowBorder = true;
|
||||
};
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Widget)
|
||||
|
||||
public:
|
||||
explicit Widget(QWidget *parent = nullptr);
|
||||
~Widget() override = default;
|
||||
|
||||
bool isNormal() const;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void initBackgroundWindow();
|
||||
void setFrameShadowEnabled(const bool enable = true);
|
||||
void setFrameShadowActive(const bool active = true);
|
||||
|
||||
private:
|
||||
int titleBarHeight = 30;
|
||||
ContentsWidget *contentsWidget = nullptr;
|
||||
QGraphicsDropShadowEffect *shadowEffect = nullptr;
|
||||
QWidget *titleBarWidget = nullptr;
|
||||
QPushButton *windowIconButton = nullptr;
|
||||
QLabel *windowTitleLabel = nullptr;
|
||||
QPushButton *minimizeButton = nullptr;
|
||||
QPushButton *maximizeButton = nullptr;
|
||||
QPushButton *closeButton = nullptr;
|
||||
};
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import wangwenx190.Utils 1.0
|
||||
|
||||
Window {
|
||||
id: window
|
||||
visible: true
|
||||
width: 800
|
||||
height: 600
|
||||
title: qsTr("Hello, World!")
|
||||
color: blurEffectCheckBox.checked ? "transparent" : "#f0f0f0"
|
||||
|
||||
FramelessHelper {
|
||||
id: framelessHelper
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: titleBar
|
||||
height: framelessHelper.titleBarHeight
|
||||
color: extendToTitleBarCheckBox.checked ? "transparent" : ((window.active && framelessHelper.colorizationEnabled) ? framelessHelper.colorizationColor : "white")
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleBarText
|
||||
text: window.title
|
||||
font.pointSize: 13
|
||||
color: window.active ? (framelessHelper.colorizationEnabled ? "white" : "black") : "gray"
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
MinimizeButton {
|
||||
id: minimizeButton
|
||||
onClicked: window.showMinimized()
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
minimizeButton)
|
||||
}
|
||||
|
||||
MaximizeButton {
|
||||
id: maximizeButton
|
||||
maximized: window.visibility === Window.Maximized
|
||||
onClicked: {
|
||||
if (maximized) {
|
||||
window.showNormal()
|
||||
} else {
|
||||
window.showMaximized()
|
||||
}
|
||||
}
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
maximizeButton)
|
||||
}
|
||||
|
||||
CloseButton {
|
||||
id: closeButton
|
||||
onClicked: window.close()
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
closeButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: content
|
||||
color: "transparent"
|
||||
anchors {
|
||||
top: titleBar.bottom
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 25
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pointSize: 15
|
||||
font.bold: true
|
||||
text: qsTr("Current system theme: %1").arg(framelessHelper.darkThemeEnabled ? "dark theme" : "light theme")
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pointSize: 15
|
||||
font.bold: true
|
||||
text: qsTr("Transparency effect: %1").arg(framelessHelper.transparencyEffectEnabled ? "enabled" : "disabled")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: blurEffectCheckBox
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pointSize: 15
|
||||
font.bold: true
|
||||
text: qsTr("Enable blur effect")
|
||||
onCheckedChanged: framelessHelper.setBlurEffectEnabled(checked, forceAcrylicCheckBox.checked)
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: forceAcrylicCheckBox
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pointSize: 15
|
||||
font.bold: true
|
||||
text: qsTr("Force enabling Acrylic effect")
|
||||
enabled: framelessHelper.canHaveWindowFrame
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: extendToTitleBarCheckBox
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pointSize: 15
|
||||
font.bold: true
|
||||
text: qsTr("Extend to title bar")
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: qsTr("Move to desktop center")
|
||||
font.pointSize: 15
|
||||
font.bold: true
|
||||
onClicked: framelessHelper.moveWindowToDesktopCenter(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: topFrame
|
||||
visible: framelessHelper.canHaveWindowFrame && (window.visibility === Window.Windowed)
|
||||
color: window.active ? (framelessHelper.colorizationEnabled ? framelessHelper.colorizationColor : "#707070") : "#aaaaaa"
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: 1
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
framelessHelper.setWindowFrameVisible(framelessHelper.canHaveWindowFrame)
|
||||
framelessHelper.removeWindowFrame()
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
TARGET = Win32Demo
|
||||
TEMPLATE = app
|
||||
QT += gui-private widgets
|
||||
HEADERS += widget.h
|
||||
SOURCES += widget.cpp main.cpp
|
||||
include($$PWD/../common.pri)
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "widget.h"
|
||||
#include <QApplication>
|
||||
#include <QStyleOption>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
// High DPI scaling is enabled by default from Qt 6
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
// Windows: we are using the manifest file to get maximum compatibility
|
||||
// because some APIs are not supprted on old systems such as Windows 7
|
||||
// and Windows 8. And once we have set the DPI awareness level in the
|
||||
// manifest file, any attemptation to try to change it through API will
|
||||
// fail. In other words, Qt won't be able to enable or disable high DPI
|
||||
// scaling or change the DPI awareness level once we have set it in the
|
||||
// manifest file. So the following two lines are uesless actually (However,
|
||||
// they are still useful on other platforms).
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
// Don't round the scale factor.
|
||||
// This will break QWidget applications because they can't render correctly.
|
||||
// Qt Quick applications won't have this issue.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QApplication application(argc, argv);
|
||||
|
||||
QApplication::setFont({QString::fromUtf8("Microsoft YaHei")});
|
||||
|
||||
Widget widget;
|
||||
|
||||
QStyleOption option;
|
||||
option.initFrom(&widget);
|
||||
widget.setWindowIcon(widget.style()->standardIcon(QStyle::SP_ComputerIcon, &option));
|
||||
widget.setWindowTitle(QObject::tr("Hello, World!"));
|
||||
|
||||
widget.show();
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
|
@ -1,632 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "widget.h"
|
||||
#include "../../winnativeeventfilter.h"
|
||||
#include <QCheckBox>
|
||||
#include <QColorDialog>
|
||||
#include <QEvent>
|
||||
#include <QGuiApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWindow>
|
||||
#include <qt_windows.h>
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#else
|
||||
#include <qpa/qplatformwindow.h>
|
||||
#include <qpa/qplatformwindow_p.h>
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(QMargins)
|
||||
|
||||
// Some old SDK doesn't have this value.
|
||||
#ifndef WM_DPICHANGED
|
||||
#define WM_DPICHANGED 0x02E0
|
||||
#endif
|
||||
|
||||
// Copied from windowsx.h
|
||||
#define GET_X_LPARAM(lp) ((int) (short) LOWORD(lp))
|
||||
#define GET_Y_LPARAM(lp) ((int) (short) HIWORD(lp))
|
||||
|
||||
namespace {
|
||||
|
||||
const Widget::Win10Version g_vAcrylicEffectVersion = Widget::Win10Version::Win10_1803;
|
||||
|
||||
const QColor g_cDefaultActiveBorderColor = {"#707070"};
|
||||
const QColor g_cDefaultInactiveBorderColor = {"#aaaaaa"};
|
||||
QColor g_cColorizationColor = Qt::white;
|
||||
|
||||
const char g_sUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
|
||||
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
|
||||
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
|
||||
|
||||
const QString g_sSystemButtonsStyleSheet = QString::fromUtf8(R"(
|
||||
#iconButton, #minimizeButton, #maximizeButton, #closeButton {
|
||||
border-style: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#minimizeButton:hover, #maximizeButton:hover {
|
||||
background-color: #80c7c7c7;
|
||||
}
|
||||
|
||||
#minimizeButton:pressed, #maximizeButton:pressed {
|
||||
background-color: #80808080;
|
||||
}
|
||||
|
||||
#closeButton:hover {
|
||||
background-color: #e81123;
|
||||
}
|
||||
|
||||
#closeButton:pressed {
|
||||
background-color: #8c0a15;
|
||||
}
|
||||
)");
|
||||
|
||||
const QString g_sTitleLabelStyleSheet = QString::fromUtf8(R"(
|
||||
#titleLabel {
|
||||
color: rgb(%1, %2, %3);
|
||||
}
|
||||
)");
|
||||
|
||||
const QString g_sTitleBarStyleSheet = QString::fromUtf8(R"(
|
||||
#titleBarWidget {
|
||||
background-color: rgba(%1, %2, %3, %4);
|
||||
border-top: 1px solid rgba(%5, %6, %7, %8);
|
||||
}
|
||||
)");
|
||||
|
||||
const QString g_sMinimizeButtonImageDark = QString::fromUtf8(":/images/button_minimize_black.svg");
|
||||
const QString g_sMaximizeButtonImageDark = QString::fromUtf8(":/images/button_maximize_black.svg");
|
||||
const QString g_sRestoreButtonImageDark = QString::fromUtf8(":/images/button_restore_black.svg");
|
||||
const QString g_sCloseButtonImageDark = QString::fromUtf8(":/images/button_close_black.svg");
|
||||
const QString g_sMinimizeButtonImageLight = QString::fromUtf8(":/images/button_minimize_white.svg");
|
||||
const QString g_sMaximizeButtonImageLight = QString::fromUtf8(":/images/button_maximize_white.svg");
|
||||
const QString g_sRestoreButtonImageLight = QString::fromUtf8(":/images/button_restore_white.svg");
|
||||
const QString g_sCloseButtonImageLight = QString::fromUtf8(":/images/button_close_white.svg");
|
||||
|
||||
void updateQtFrame(const QWindow *window, const int tbh)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
QMargins margins = {0, -tbh, 0, 0};
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QPlatformWindow *platformWindow = window->handle();
|
||||
if (platformWindow) {
|
||||
QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow,
|
||||
QString::fromUtf8(
|
||||
"WindowsCustomMargins"),
|
||||
QVariant::fromValue(margins));
|
||||
}
|
||||
#else
|
||||
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
|
||||
window->handle());
|
||||
if (platformWindow) {
|
||||
platformWindow->setCustomMargins(margins);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Widget::Widget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
createWinId(); // Qt's internal function, make sure it's a top level window.
|
||||
initializeWindow();
|
||||
}
|
||||
|
||||
void Widget::triggerFrameChange()
|
||||
{
|
||||
SetWindowPos(reinterpret_cast<HWND>(windowHandle()->winId()),
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER
|
||||
| SWP_NOOWNERZORDER);
|
||||
}
|
||||
|
||||
void Widget::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Widget"));
|
||||
iconButton->setText({});
|
||||
titleLabel->setText({});
|
||||
minimizeButton->setText({});
|
||||
maximizeButton->setText({});
|
||||
closeButton->setText({});
|
||||
customizeTitleBarCB->setText(tr("Enable customized title bar"));
|
||||
preserveWindowFrameCB->setText(tr("Preserve window frame"));
|
||||
blurEffectCB->setText(tr("Enable blur effect"));
|
||||
extendToTitleBarCB->setText(tr("Extend to title bar"));
|
||||
forceAcrylicCB->setText(tr("Force enabling Acrylic effect"));
|
||||
resizableCB->setText(tr("Resizable"));
|
||||
}
|
||||
|
||||
void Widget::setupUi()
|
||||
{
|
||||
resize(1056, 600);
|
||||
verticalLayout_3 = new QVBoxLayout(this);
|
||||
titleBarWidget = new QWidget(this);
|
||||
titleBarWidget->setObjectName(QString::fromUtf8("titleBarWidget"));
|
||||
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(titleBarWidget->sizePolicy().hasHeightForWidth());
|
||||
titleBarWidget->setSizePolicy(sizePolicy);
|
||||
const int titleBarHeight
|
||||
= WinNativeEventFilter::getSystemMetric(windowHandle(),
|
||||
WinNativeEventFilter::SystemMetric::TitleBarHeight,
|
||||
false);
|
||||
titleBarWidget->setMinimumSize(QSize(0, titleBarHeight));
|
||||
titleBarWidget->setMaximumSize(QSize(16777215, titleBarHeight));
|
||||
horizontalLayout = new QHBoxLayout(titleBarWidget);
|
||||
horizontalLayout->setSpacing(0);
|
||||
horizontalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
horizontalSpacer_7 = new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
horizontalLayout->addSpacerItem(horizontalSpacer_7);
|
||||
iconButton = new QPushButton(titleBarWidget);
|
||||
iconButton->setObjectName(QString::fromUtf8("iconButton"));
|
||||
horizontalLayout->addWidget(iconButton);
|
||||
horizontalSpacer = new QSpacerItem(3, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
horizontalLayout->addSpacerItem(horizontalSpacer);
|
||||
titleLabel = new QLabel(titleBarWidget);
|
||||
titleLabel->setObjectName(QString::fromUtf8("titleLabel"));
|
||||
QFont font;
|
||||
font.setPointSize(10);
|
||||
titleLabel->setFont(font);
|
||||
horizontalLayout->addWidget(titleLabel);
|
||||
horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
horizontalLayout->addSpacerItem(horizontalSpacer_2);
|
||||
minimizeButton = new QPushButton(titleBarWidget);
|
||||
minimizeButton->setObjectName(QString::fromUtf8("minimizeButton"));
|
||||
QSizePolicy sizePolicy1(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
sizePolicy1.setHorizontalStretch(0);
|
||||
sizePolicy1.setVerticalStretch(0);
|
||||
sizePolicy1.setHeightForWidth(minimizeButton->sizePolicy().hasHeightForWidth());
|
||||
minimizeButton->setSizePolicy(sizePolicy1);
|
||||
const QSize systemButtonSize = {qRound(titleBarHeight * 1.5), titleBarHeight};
|
||||
minimizeButton->setMinimumSize(systemButtonSize);
|
||||
minimizeButton->setMaximumSize(systemButtonSize);
|
||||
QIcon icon;
|
||||
icon.addFile(g_sMinimizeButtonImageDark, {}, QIcon::Normal, QIcon::Off);
|
||||
minimizeButton->setIcon(icon);
|
||||
minimizeButton->setIconSize(systemButtonSize);
|
||||
horizontalLayout->addWidget(minimizeButton);
|
||||
maximizeButton = new QPushButton(titleBarWidget);
|
||||
maximizeButton->setObjectName(QString::fromUtf8("maximizeButton"));
|
||||
sizePolicy1.setHeightForWidth(maximizeButton->sizePolicy().hasHeightForWidth());
|
||||
maximizeButton->setSizePolicy(sizePolicy1);
|
||||
maximizeButton->setMinimumSize(systemButtonSize);
|
||||
maximizeButton->setMaximumSize(systemButtonSize);
|
||||
QIcon icon1;
|
||||
icon1.addFile(g_sMaximizeButtonImageDark, {}, QIcon::Normal, QIcon::Off);
|
||||
maximizeButton->setIcon(icon1);
|
||||
maximizeButton->setIconSize(systemButtonSize);
|
||||
horizontalLayout->addWidget(maximizeButton);
|
||||
closeButton = new QPushButton(titleBarWidget);
|
||||
closeButton->setObjectName(QString::fromUtf8("closeButton"));
|
||||
sizePolicy1.setHeightForWidth(closeButton->sizePolicy().hasHeightForWidth());
|
||||
closeButton->setSizePolicy(sizePolicy1);
|
||||
closeButton->setMinimumSize(systemButtonSize);
|
||||
closeButton->setMaximumSize(systemButtonSize);
|
||||
QIcon icon2;
|
||||
icon2.addFile(g_sCloseButtonImageDark, {}, QIcon::Normal, QIcon::Off);
|
||||
closeButton->setIcon(icon2);
|
||||
closeButton->setIconSize(systemButtonSize);
|
||||
horizontalLayout->addWidget(closeButton);
|
||||
verticalLayout_3->addWidget(titleBarWidget);
|
||||
contentsWidget = new QWidget(this);
|
||||
QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
sizePolicy2.setHorizontalStretch(0);
|
||||
sizePolicy2.setVerticalStretch(0);
|
||||
sizePolicy2.setHeightForWidth(contentsWidget->sizePolicy().hasHeightForWidth());
|
||||
contentsWidget->setSizePolicy(sizePolicy2);
|
||||
verticalLayout_2 = new QVBoxLayout(contentsWidget);
|
||||
verticalSpacer_2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
verticalLayout_2->addSpacerItem(verticalSpacer_2);
|
||||
horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
horizontalLayout_2->addSpacerItem(horizontalSpacer_3);
|
||||
controlPanelWidget = new QWidget(contentsWidget);
|
||||
QSizePolicy sizePolicy3(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
sizePolicy3.setHorizontalStretch(0);
|
||||
sizePolicy3.setVerticalStretch(0);
|
||||
sizePolicy3.setHeightForWidth(controlPanelWidget->sizePolicy().hasHeightForWidth());
|
||||
controlPanelWidget->setSizePolicy(sizePolicy3);
|
||||
verticalLayout = new QVBoxLayout(controlPanelWidget);
|
||||
customizeTitleBarCB = new QCheckBox(controlPanelWidget);
|
||||
QFont font1;
|
||||
font1.setPointSize(15);
|
||||
font1.setBold(true);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
font1.setWeight(QFont::Bold);
|
||||
#else
|
||||
font1.setWeight(75);
|
||||
#endif
|
||||
customizeTitleBarCB->setFont(font1);
|
||||
verticalLayout->addWidget(customizeTitleBarCB);
|
||||
preserveWindowFrameCB = new QCheckBox(controlPanelWidget);
|
||||
preserveWindowFrameCB->setFont(font1);
|
||||
verticalLayout->addWidget(preserveWindowFrameCB);
|
||||
blurEffectCB = new QCheckBox(controlPanelWidget);
|
||||
blurEffectCB->setFont(font1);
|
||||
verticalLayout->addWidget(blurEffectCB);
|
||||
extendToTitleBarCB = new QCheckBox(controlPanelWidget);
|
||||
extendToTitleBarCB->setFont(font1);
|
||||
verticalLayout->addWidget(extendToTitleBarCB);
|
||||
forceAcrylicCB = new QCheckBox(controlPanelWidget);
|
||||
forceAcrylicCB->setFont(font1);
|
||||
forceAcrylicCB->setEnabled(m_bCanAcrylicBeEnabled);
|
||||
verticalLayout->addWidget(forceAcrylicCB);
|
||||
resizableCB = new QCheckBox(controlPanelWidget);
|
||||
resizableCB->setFont(font1);
|
||||
verticalLayout->addWidget(resizableCB);
|
||||
horizontalLayout_2->addWidget(controlPanelWidget);
|
||||
horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
horizontalLayout_2->addSpacerItem(horizontalSpacer_4);
|
||||
verticalLayout_2->addLayout(horizontalLayout_2);
|
||||
verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
verticalLayout_2->addSpacerItem(verticalSpacer);
|
||||
horizontalLayout_3 = new QHBoxLayout();
|
||||
horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
horizontalLayout_3->addSpacerItem(horizontalSpacer_5);
|
||||
horizontalSpacer_6 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
horizontalLayout_3->addSpacerItem(horizontalSpacer_6);
|
||||
verticalLayout_2->addLayout(horizontalLayout_3);
|
||||
verticalLayout_3->addWidget(contentsWidget);
|
||||
if (shouldDrawBorder()) {
|
||||
layout()->setContentsMargins(1, 1, 1, 1);
|
||||
} else {
|
||||
layout()->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
bool Widget::isNormaled() const
|
||||
{
|
||||
return !isMinimized() && !isMaximized() && !isFullScreen();
|
||||
}
|
||||
|
||||
bool Widget::shouldDrawBorder(const bool ignoreWindowState) const
|
||||
{
|
||||
return m_bIsWin10OrGreater && (ignoreWindowState ? true : isNormaled())
|
||||
&& !preserveWindowFrameCB->isChecked() && customizeTitleBarCB->isChecked();
|
||||
}
|
||||
|
||||
bool Widget::shouldDrawThemedBorder(const bool ignoreWindowState) const
|
||||
{
|
||||
return (shouldDrawBorder(ignoreWindowState) && WinNativeEventFilter::isColorizationEnabled());
|
||||
}
|
||||
|
||||
bool Widget::shouldDrawThemedTitleBar() const
|
||||
{
|
||||
return m_bIsWin10OrGreater && WinNativeEventFilter::isColorizationEnabled();
|
||||
}
|
||||
|
||||
QColor Widget::activeBorderColor()
|
||||
{
|
||||
return WinNativeEventFilter::isColorizationEnabled() ? g_cColorizationColor
|
||||
: g_cDefaultActiveBorderColor;
|
||||
}
|
||||
|
||||
QColor Widget::inactiveBorderColor()
|
||||
{
|
||||
return g_cDefaultInactiveBorderColor;
|
||||
}
|
||||
|
||||
QColor Widget::borderColor() const
|
||||
{
|
||||
return isActiveWindow() ? activeBorderColor() : inactiveBorderColor();
|
||||
}
|
||||
|
||||
bool Widget::isWin10OrGreater(const Win10Version subVer)
|
||||
{
|
||||
return (QOperatingSystemVersion::current()
|
||||
>= ((subVer == Win10Version::Windows10)
|
||||
? QOperatingSystemVersion::Windows10
|
||||
: QOperatingSystemVersion(QOperatingSystemVersion::Windows,
|
||||
10,
|
||||
0,
|
||||
static_cast<int>(subVer))));
|
||||
}
|
||||
|
||||
bool Widget::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
Q_ASSERT(object);
|
||||
Q_ASSERT(event);
|
||||
if (object == this) {
|
||||
switch (event->type()) {
|
||||
case QEvent::WindowStateChange: {
|
||||
if (shouldDrawBorder(true)) {
|
||||
if (isMaximized()) {
|
||||
layout()->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
if (isNormaled()) {
|
||||
layout()->setContentsMargins(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
updateTitleBar();
|
||||
break;
|
||||
}
|
||||
case QEvent::WinIdChange:
|
||||
WinNativeEventFilter::addFramelessWindow(windowHandle());
|
||||
break;
|
||||
case QEvent::WindowActivate:
|
||||
case QEvent::WindowDeactivate:
|
||||
updateTitleBar();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QWidget::eventFilter(object, event);
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool Widget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
|
||||
#else
|
||||
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
|
||||
#endif
|
||||
{
|
||||
Q_ASSERT(eventType == "windows_generic_MSG");
|
||||
Q_ASSERT(message);
|
||||
Q_ASSERT(result);
|
||||
if (customizeTitleBarCB && customizeTitleBarCB->isChecked()) {
|
||||
const auto msg = static_cast<LPMSG>(message);
|
||||
switch (msg->message) {
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED: {
|
||||
g_cColorizationColor = QColor::fromRgba(msg->wParam);
|
||||
if (shouldDrawThemedBorder()) {
|
||||
update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_DPICHANGED:
|
||||
case WM_NCPAINT:
|
||||
update();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QWidget::nativeEvent(eventType, message, result);
|
||||
}
|
||||
|
||||
void Widget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
if (shouldDrawBorder()) {
|
||||
QPainter painter(this);
|
||||
painter.save();
|
||||
painter.setPen({borderColor(), 1});
|
||||
painter.drawLine(0, 0, width(), 0);
|
||||
painter.drawLine(width() - 1, 0, width() - 1, height());
|
||||
painter.drawLine(width(), height() - 1, 0, height() - 1);
|
||||
painter.drawLine(0, height(), 0, 0);
|
||||
painter.restore();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::updateTitleBar()
|
||||
{
|
||||
const bool themedTitleBar = shouldDrawThemedTitleBar() && isActiveWindow();
|
||||
if (themedTitleBar && !m_bExtendToTitleBar) {
|
||||
minimizeButton->setIcon(QIcon(g_sMinimizeButtonImageLight));
|
||||
closeButton->setIcon(QIcon(g_sCloseButtonImageLight));
|
||||
if (isMaximized()) {
|
||||
maximizeButton->setIcon(QIcon(g_sRestoreButtonImageLight));
|
||||
}
|
||||
if (isNormaled()) {
|
||||
maximizeButton->setIcon(QIcon(g_sMaximizeButtonImageLight));
|
||||
}
|
||||
} else {
|
||||
minimizeButton->setIcon(QIcon(g_sMinimizeButtonImageDark));
|
||||
closeButton->setIcon(QIcon(g_sCloseButtonImageDark));
|
||||
if (isMaximized()) {
|
||||
maximizeButton->setIcon(QIcon(g_sRestoreButtonImageDark));
|
||||
}
|
||||
if (isNormaled()) {
|
||||
maximizeButton->setIcon(QIcon(g_sMaximizeButtonImageDark));
|
||||
}
|
||||
}
|
||||
const QColor titleBarBackgroundColor = m_bExtendToTitleBar
|
||||
? Qt::transparent
|
||||
: (themedTitleBar ? g_cColorizationColor : Qt::white);
|
||||
const QColor titleBarTextColor = isActiveWindow()
|
||||
? ((!themedTitleBar || m_bExtendToTitleBar) ? Qt::black
|
||||
: Qt::white)
|
||||
: QColor("#999999");
|
||||
const QColor titleBarBorderColor = (!m_bIsWin10OrGreater || shouldDrawBorder() || isMaximized()
|
||||
|| isFullScreen())
|
||||
? Qt::transparent
|
||||
: borderColor();
|
||||
titleBarWidget->setStyleSheet(
|
||||
g_sSystemButtonsStyleSheet
|
||||
+ g_sTitleLabelStyleSheet.arg(QString::number(titleBarTextColor.red()),
|
||||
QString::number(titleBarTextColor.green()),
|
||||
QString::number(titleBarTextColor.blue()))
|
||||
+ g_sTitleBarStyleSheet.arg(QString::number(titleBarBackgroundColor.red()),
|
||||
QString::number(titleBarBackgroundColor.green()),
|
||||
QString::number(titleBarBackgroundColor.blue()),
|
||||
QString::number(titleBarBackgroundColor.alpha()),
|
||||
QString::number(titleBarBorderColor.red()),
|
||||
QString::number(titleBarBorderColor.green()),
|
||||
QString::number(titleBarBorderColor.blue()),
|
||||
QString::number(titleBarBorderColor.alpha())));
|
||||
}
|
||||
|
||||
void Widget::initializeOptions()
|
||||
{
|
||||
if (m_bIsWin10OrGreater) {
|
||||
//preserveWindowFrameCB->click();
|
||||
if (m_bCanAcrylicBeEnabled) {
|
||||
forceAcrylicCB->click();
|
||||
}
|
||||
}
|
||||
customizeTitleBarCB->click();
|
||||
extendToTitleBarCB->click();
|
||||
blurEffectCB->click();
|
||||
resizableCB->click();
|
||||
m_bCanShowColorDialog = true;
|
||||
}
|
||||
|
||||
void Widget::setupConnections()
|
||||
{
|
||||
connect(minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
|
||||
connect(maximizeButton, &QPushButton::clicked, this, [this]() {
|
||||
if (isMaximized()) {
|
||||
showNormal();
|
||||
} else {
|
||||
showMaximized();
|
||||
}
|
||||
});
|
||||
connect(closeButton, &QPushButton::clicked, this, &Widget::close);
|
||||
connect(this, &Widget::windowTitleChanged, titleLabel, &QLabel::setText);
|
||||
connect(this, &Widget::windowIconChanged, iconButton, &QPushButton::setIcon);
|
||||
connect(customizeTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
const bool enable = state == Qt::Checked;
|
||||
preserveWindowFrameCB->setEnabled(enable);
|
||||
updateQtFrame(windowHandle(),
|
||||
enable ? WinNativeEventFilter::getSystemMetric(
|
||||
windowHandle(),
|
||||
WinNativeEventFilter::SystemMetric::TitleBarHeight,
|
||||
true,
|
||||
true)
|
||||
: 0);
|
||||
titleBarWidget->setVisible(enable);
|
||||
if (enable) {
|
||||
qunsetenv(g_sUseNativeTitleBar);
|
||||
} else {
|
||||
qputenv(g_sUseNativeTitleBar, "1");
|
||||
}
|
||||
triggerFrameChange();
|
||||
update();
|
||||
});
|
||||
connect(preserveWindowFrameCB, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
const bool enable = state == Qt::Checked;
|
||||
if (enable) {
|
||||
qputenv(g_sPreserveWindowFrame, "1");
|
||||
} else {
|
||||
qunsetenv(g_sPreserveWindowFrame);
|
||||
}
|
||||
if (!enable && shouldDrawBorder()) {
|
||||
layout()->setContentsMargins(1, 1, 1, 1);
|
||||
} else {
|
||||
layout()->setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
triggerFrameChange();
|
||||
updateTitleBar();
|
||||
update();
|
||||
});
|
||||
connect(blurEffectCB, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
const bool enable = state == Qt::Checked;
|
||||
QColor color = {0, 0, 0, 127};
|
||||
const bool useAcrylicEffect = m_bCanAcrylicBeEnabled && forceAcrylicCB->isChecked();
|
||||
if (useAcrylicEffect) {
|
||||
if (enable && m_bCanShowColorDialog) {
|
||||
color = QColorDialog::getColor(color,
|
||||
this,
|
||||
tr("Please select a gradient color"),
|
||||
QColorDialog::ShowAlphaChannel);
|
||||
}
|
||||
}
|
||||
// Qt will paint a solid white background to the window,
|
||||
// it will cover the blurred effect, so we need to
|
||||
// make the background become totally transparent. Achieve
|
||||
// this by setting a palette to the window.
|
||||
QPalette palette = {};
|
||||
if (enable) {
|
||||
palette.setColor(QPalette::Window, Qt::transparent);
|
||||
}
|
||||
setPalette(palette);
|
||||
WinNativeEventFilter::setBlurEffectEnabled(windowHandle(), enable, color);
|
||||
update();
|
||||
if (useAcrylicEffect && enable && WinNativeEventFilter::isTransparencyEffectEnabled()) {
|
||||
QMessageBox::warning(this,
|
||||
tr("BUG Warning!"),
|
||||
tr("You have enabled the transparency effect in the personalize "
|
||||
"settings.\nDragging will be very laggy when the Acrylic "
|
||||
"effect is enabled.\nDisabling the transparency effect can "
|
||||
"solve this issue temporarily."));
|
||||
}
|
||||
});
|
||||
connect(extendToTitleBarCB, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
m_bExtendToTitleBar = state == Qt::Checked;
|
||||
updateTitleBar();
|
||||
});
|
||||
connect(forceAcrylicCB, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
if (!m_bCanAcrylicBeEnabled) {
|
||||
return;
|
||||
}
|
||||
if (state == Qt::Checked) {
|
||||
qputenv(g_sForceUseAcrylicEffect, "1");
|
||||
} else {
|
||||
qunsetenv(g_sForceUseAcrylicEffect);
|
||||
}
|
||||
if (blurEffectCB->isChecked()) {
|
||||
blurEffectCB->click();
|
||||
blurEffectCB->click();
|
||||
}
|
||||
});
|
||||
connect(resizableCB, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
const bool enable = state == Qt::Checked;
|
||||
maximizeButton->setEnabled(enable);
|
||||
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, !enable);
|
||||
show();
|
||||
});
|
||||
}
|
||||
|
||||
void Widget::initializeFramelessFunctions()
|
||||
{
|
||||
WinNativeEventFilter::addFramelessWindow(windowHandle());
|
||||
WinNativeEventFilter::setIgnoredObjects(windowHandle(),
|
||||
{minimizeButton, maximizeButton, closeButton});
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
void Widget::initializeVariables()
|
||||
{
|
||||
m_bIsWin10OrGreater = isWin10OrGreater();
|
||||
if (m_bIsWin10OrGreater) {
|
||||
m_bCanAcrylicBeEnabled = isWin10OrGreater(g_vAcrylicEffectVersion);
|
||||
g_cColorizationColor = WinNativeEventFilter::getColorizationColor();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::initializeWindow()
|
||||
{
|
||||
initializeVariables();
|
||||
setupUi();
|
||||
updateTitleBar();
|
||||
initializeFramelessFunctions();
|
||||
setupConnections();
|
||||
initializeOptions();
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
|
||||
#define Q_DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Q_DISABLE_MOVE(Class)
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
|
||||
QT_FORWARD_DECLARE_CLASS(QSpacerItem)
|
||||
QT_FORWARD_DECLARE_CLASS(QPushButton)
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
QT_FORWARD_DECLARE_CLASS(QCheckBox)
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Widget)
|
||||
|
||||
public:
|
||||
enum class Win10Version : int {
|
||||
Win10_1507 = 10240,
|
||||
Win10_1511 = 10586,
|
||||
Win10_1607 = 14393,
|
||||
Win10_1703 = 15063,
|
||||
Win10_1709 = 16299,
|
||||
Win10_1803 = 17134,
|
||||
Win10_1809 = 17763,
|
||||
Win10_1903 = 18362,
|
||||
Win10_1909 = 18363,
|
||||
Win10_2004 = 19041,
|
||||
Win10_20H2 = 19042,
|
||||
Windows10 = Win10_1507
|
||||
};
|
||||
Q_ENUM(Win10Version)
|
||||
|
||||
explicit Widget(QWidget *parent = nullptr);
|
||||
~Widget() override = default;
|
||||
|
||||
bool isNormaled() const;
|
||||
|
||||
bool shouldDrawBorder(const bool ignoreWindowState = false) const;
|
||||
bool shouldDrawThemedBorder(const bool ignoreWindowState = false) const;
|
||||
bool shouldDrawThemedTitleBar() const;
|
||||
|
||||
static QColor activeBorderColor();
|
||||
static QColor inactiveBorderColor();
|
||||
QColor borderColor() const;
|
||||
|
||||
static bool isWin10OrGreater(const Win10Version subVer = Win10Version::Windows10);
|
||||
|
||||
public Q_SLOTS:
|
||||
void retranslateUi();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
#else
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||
#endif
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void updateTitleBar();
|
||||
void initializeOptions();
|
||||
void setupConnections();
|
||||
void initializeFramelessFunctions();
|
||||
void initializeVariables();
|
||||
void initializeWindow();
|
||||
void triggerFrameChange();
|
||||
|
||||
private:
|
||||
bool m_bIsWin10OrGreater = false, m_bCanAcrylicBeEnabled = false, m_bExtendToTitleBar = false,
|
||||
m_bCanShowColorDialog = false;
|
||||
QVBoxLayout *verticalLayout_3 = nullptr, *verticalLayout_2 = nullptr, *verticalLayout = nullptr;
|
||||
QWidget *titleBarWidget = nullptr, *contentsWidget = nullptr, *controlPanelWidget = nullptr;
|
||||
QHBoxLayout *horizontalLayout = nullptr, *horizontalLayout_2 = nullptr,
|
||||
*horizontalLayout_3 = nullptr;
|
||||
QSpacerItem *horizontalSpacer_7 = nullptr, *horizontalSpacer = nullptr,
|
||||
*horizontalSpacer_2 = nullptr, *verticalSpacer_2 = nullptr,
|
||||
*horizontalSpacer_3 = nullptr, *horizontalSpacer_4 = nullptr,
|
||||
*verticalSpacer = nullptr, *horizontalSpacer_5 = nullptr,
|
||||
*horizontalSpacer_6 = nullptr;
|
||||
QPushButton *iconButton = nullptr, *minimizeButton = nullptr, *maximizeButton = nullptr,
|
||||
*closeButton = nullptr;
|
||||
QLabel *titleLabel = nullptr;
|
||||
QCheckBox *customizeTitleBarCB = nullptr, *preserveWindowFrameCB = nullptr,
|
||||
*blurEffectCB = nullptr, *extendToTitleBarCB = nullptr, *forceAcrylicCB = nullptr,
|
||||
*resizableCB = nullptr;
|
||||
};
|
|
@ -10,10 +10,12 @@ RESOURCES += $$PWD/images.qrc
|
|||
win32 {
|
||||
DEFINES += \
|
||||
WIN32_LEAN_AND_MEAN \
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_CRT_SECURE_NO_WARNINGS \
|
||||
UNICODE \
|
||||
_UNICODE
|
||||
CONFIG += windeployqt
|
||||
CONFIG -= embed_manifest_exe
|
||||
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi -lshcore -ld2d1 -luxtheme
|
||||
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi
|
||||
RC_FILE = $$PWD/windows.rc
|
||||
OTHER_FILES += $$PWD/windows.manifest
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
TEMPLATE = subdirs
|
||||
CONFIG -= ordered
|
||||
qtHaveModule(widgets) {
|
||||
SUBDIRS += QWidget QMainWindow
|
||||
win32: SUBDIRS += Win32Demo
|
||||
versionAtLeast(QT_VERSION, 5.15.0): SUBDIRS += QWidget2
|
||||
}
|
||||
qtHaveModule(quick): SUBDIRS += Quick
|
||||
qtHaveModule(widgets): SUBDIRS += widget
|
||||
qtHaveModule(quick): SUBDIRS += quick
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,9 +23,11 @@
|
|||
*/
|
||||
|
||||
#include "../../framelessquickhelper.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickStyle>
|
||||
#include "../../qtacrylicitem.h"
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtQml/qqmlapplicationengine.h>
|
||||
#include <QtQuickControls2/qquickstyle.h>
|
||||
#include <QtQuick/qquickwindow.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -46,22 +48,26 @@ int main(int argc, char *argv[])
|
|||
// Don't round the scale factor.
|
||||
// This will break QWidget applications because they can't render correctly.
|
||||
// Qt Quick applications won't have this issue.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QGuiApplication application(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
QQuickStyle::setStyle(QString::fromUtf8("Basic"));
|
||||
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
|
||||
QQuickStyle::setStyle(QStringLiteral("Basic"));
|
||||
#else
|
||||
QQuickStyle::setStyle(QString::fromUtf8("Default"));
|
||||
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
|
||||
QQuickStyle::setStyle(QStringLiteral("Default"));
|
||||
#endif
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0, "FramelessHelper");
|
||||
const QUrl mainQmlUrl(QString::fromUtf8("qrc:///qml/main.qml"));
|
||||
qmlRegisterType<QtAcrylicItem>("wangwenx190.Utils", 1, 0, "AcrylicItem");
|
||||
|
||||
const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/main.qml"));
|
||||
const QMetaObject::Connection connection = QObject::connect(
|
||||
&engine,
|
||||
&QQmlApplicationEngine::objectCreated,
|
|
@ -1,7 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="qml/+windows/main.qml">qml/main_windows.qml</file>
|
||||
<file alias="qml/+unix/main.qml">qml/main_unix.qml</file>
|
||||
<file>qml/main.qml</file>
|
||||
<file>qml/MinimizeButton.qml</file>
|
||||
<file>qml/MaximizeButton.qml</file>
|
||||
<file>qml/CloseButton.qml</file>
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -24,6 +24,7 @@
|
|||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import wangwenx190.Utils 1.0
|
||||
|
||||
Window {
|
||||
|
@ -32,19 +33,37 @@ Window {
|
|||
width: 800
|
||||
height: 600
|
||||
title: qsTr("Hello, World!")
|
||||
color: "transparent"
|
||||
|
||||
FramelessHelper {
|
||||
id: framelessHelper
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 500
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: label.text = Qt.formatTime(new Date(), "hh:mm:ss")
|
||||
}
|
||||
|
||||
AcrylicItem {
|
||||
id: acrylicItem
|
||||
anchors.fill: parent
|
||||
frameVisible: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: titleBar
|
||||
height: framelessHelper.titleBarHeight
|
||||
color: "white"
|
||||
color: "transparent"
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 1
|
||||
left: parent.left
|
||||
leftMargin: 1
|
||||
right: parent.right
|
||||
rightMargin: 1
|
||||
}
|
||||
|
||||
Text {
|
||||
|
@ -64,8 +83,7 @@ Window {
|
|||
MinimizeButton {
|
||||
id: minimizeButton
|
||||
onClicked: window.showMinimized()
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
minimizeButton)
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(minimizeButton)
|
||||
}
|
||||
|
||||
MaximizeButton {
|
||||
|
@ -78,29 +96,32 @@ Window {
|
|||
window.showMaximized()
|
||||
}
|
||||
}
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
maximizeButton)
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(maximizeButton)
|
||||
}
|
||||
|
||||
CloseButton {
|
||||
id: closeButton
|
||||
onClicked: window.close()
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(
|
||||
closeButton)
|
||||
Component.onCompleted: framelessHelper.addIgnoreObject(closeButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: content
|
||||
color: "#f0f0f0"
|
||||
anchors {
|
||||
top: titleBar.bottom
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
Label {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
font {
|
||||
pointSize: 70
|
||||
bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: framelessHelper.removeWindowFrame(true)
|
||||
Component.onCompleted: {
|
||||
framelessHelper.removeWindowFrame()
|
||||
framelessHelper.setAcrylicEffectEnabled(true)
|
||||
}
|
||||
|
||||
// Re-draw the window frame.
|
||||
onActiveChanged: acrylicItem.update()
|
||||
onVisibilityChanged: acrylicItem.update()
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
TARGET = Quick
|
||||
TEMPLATE = app
|
||||
QT += quick quickcontrols2
|
||||
HEADERS += ../../framelessquickhelper.h
|
||||
SOURCES += ../../framelessquickhelper.cpp main.cpp
|
||||
CONFIG(release, debug|release): CONFIG += qtquickcompiler
|
||||
SOURCES += main.cpp
|
||||
RESOURCES += qml.qrc
|
||||
include($$PWD/../common.pri)
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -22,9 +22,8 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtWidgets/qapplication.h>
|
||||
#include "widget.h"
|
||||
#include <QApplication>
|
||||
#include <QStyleOption>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
@ -45,19 +44,16 @@ int main(int argc, char *argv[])
|
|||
// Don't round the scale factor.
|
||||
// This will break QWidget applications because they can't render correctly.
|
||||
// Qt Quick applications won't have this issue.
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QApplication application(argc, argv);
|
||||
|
||||
Widget widget;
|
||||
QStyleOption option;
|
||||
option.initFrom(&widget);
|
||||
const QIcon icon = widget.style()->standardIcon(QStyle::SP_ComputerIcon, &option);
|
||||
widget.setWindowIcon(icon);
|
||||
widget.setWindowTitle(QObject::tr("Hello, World!"));
|
||||
widget.resize(800, 600);
|
||||
widget.moveToDesktopCenter();
|
||||
widget.show();
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "widget.h"
|
||||
#include <QtGui/qscreen.h>
|
||||
#include <QtWidgets/qlayout.h>
|
||||
#include <QtWidgets/qlabel.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtWidgets/qpushbutton.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include "../../utilities.h"
|
||||
#include "../../framelesswindowsmanager.h"
|
||||
|
||||
Widget::Widget(QWidget *parent) : QtAcrylicWidget(parent)
|
||||
{
|
||||
createWinId();
|
||||
setupUi();
|
||||
startTimer(500);
|
||||
}
|
||||
|
||||
Widget::~Widget() = default;
|
||||
|
||||
void Widget::moveToDesktopCenter()
|
||||
{
|
||||
const QSize ss = screen()->size();
|
||||
const int newX = (ss.width() - width()) / 2;
|
||||
const int newY = (ss.height() - height()) / 2;
|
||||
move(newX, newY);
|
||||
}
|
||||
|
||||
void Widget::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
QtAcrylicWidget::timerEvent(event);
|
||||
m_label->setText(QTime::currentTime().toString(QStringLiteral("hh:mm:ss")));
|
||||
}
|
||||
|
||||
void Widget::setupUi()
|
||||
{
|
||||
const QWindow *win = windowHandle();
|
||||
const int titleBarHeight = Utilities::getSystemMetric(win, Utilities::SystemMetric::TitleBarHeight, false);
|
||||
const QSize systemButtonSize = {qRound(titleBarHeight * 1.5), titleBarHeight};
|
||||
//const QSizePolicy systemButtonSizePolicy = {QSizePolicy::Fixed, QSizePolicy::Fixed};
|
||||
m_minimizeButton = new QPushButton(this);
|
||||
m_minimizeButton->setObjectName(QStringLiteral("MinimizeButton"));
|
||||
m_minimizeButton->setFixedSize(systemButtonSize);
|
||||
m_minimizeButton->setIcon(QIcon{QStringLiteral(":/images/button_minimize_black.svg")});
|
||||
m_minimizeButton->setIconSize(systemButtonSize);
|
||||
connect(m_minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
|
||||
m_maximizeButton = new QPushButton(this);
|
||||
m_maximizeButton->setObjectName(QStringLiteral("MaximizeButton"));
|
||||
m_maximizeButton->setFixedSize(systemButtonSize);
|
||||
m_maximizeButton->setIcon(QIcon{QStringLiteral(":/images/button_maximize_black.svg")});
|
||||
m_maximizeButton->setIconSize(systemButtonSize);
|
||||
connect(m_maximizeButton, &QPushButton::clicked, this, [this](){
|
||||
if (isMaximized()) {
|
||||
showNormal();
|
||||
m_maximizeButton->setIcon(QIcon{QStringLiteral(":/images/button_maximize_black.svg")});
|
||||
} else {
|
||||
showMaximized();
|
||||
m_maximizeButton->setIcon(QIcon{QStringLiteral(":/images/button_restore_black.svg")});
|
||||
}
|
||||
});
|
||||
m_closeButton = new QPushButton(this);
|
||||
m_closeButton->setObjectName(QStringLiteral("CloseButton"));
|
||||
m_closeButton->setFixedSize(systemButtonSize);
|
||||
m_closeButton->setIcon(QIcon{QStringLiteral(":/images/button_close_black.svg")});
|
||||
m_closeButton->setIconSize(systemButtonSize);
|
||||
connect(m_closeButton, &QPushButton::clicked, this, &Widget::close);
|
||||
FramelessWindowsManager::addIgnoreObject(win, m_minimizeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, m_maximizeButton);
|
||||
FramelessWindowsManager::addIgnoreObject(win, m_closeButton);
|
||||
const auto systemButtonLayout = new QHBoxLayout;
|
||||
systemButtonLayout->setContentsMargins(0, 0, 0, 0);
|
||||
systemButtonLayout->setSpacing(0);
|
||||
systemButtonLayout->addStretch();
|
||||
systemButtonLayout->addWidget(m_minimizeButton);
|
||||
systemButtonLayout->addWidget(m_maximizeButton);
|
||||
systemButtonLayout->addWidget(m_closeButton);
|
||||
m_label = new QLabel(this);
|
||||
QFont font = QGuiApplication::font();
|
||||
font.setBold(true);
|
||||
font.setPointSize(70);
|
||||
m_label->setFont(font);
|
||||
m_label->setFrameShape(QFrame::NoFrame);
|
||||
const auto contentLayout = new QHBoxLayout;
|
||||
contentLayout->addStretch();
|
||||
contentLayout->addWidget(m_label);
|
||||
contentLayout->addStretch();
|
||||
const auto mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(1, 1, 1, 1);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->addLayout(systemButtonLayout);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->addLayout(contentLayout);
|
||||
mainLayout->addStretch();
|
||||
setLayout(mainLayout);
|
||||
setStyleSheet(QStringLiteral(R"(
|
||||
#MinimizeButton, #MaximizeButton, #CloseButton {
|
||||
border-style: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#MinimizeButton:hover, #MaximizeButton:hover {
|
||||
background-color: #80c7c7c7;
|
||||
}
|
||||
|
||||
#MinimizeButton:pressed, #MaximizeButton:pressed {
|
||||
background-color: #80808080;
|
||||
}
|
||||
|
||||
#CloseButton:hover {
|
||||
background-color: #e81123;
|
||||
}
|
||||
|
||||
#CloseButton:pressed {
|
||||
background-color: #8c0a15;
|
||||
}
|
||||
)"));
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -22,26 +22,35 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
#pragma once
|
||||
|
||||
Button {
|
||||
id: control
|
||||
#include "../../qtacrylicwidget.h"
|
||||
|
||||
implicitWidth: 45
|
||||
implicitHeight: 30
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
QT_FORWARD_DECLARE_CLASS(QPushButton)
|
||||
QT_END_NAMESPACE
|
||||
|
||||
ToolTip.visible: hovered && !down
|
||||
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
ToolTip.text: qsTr("Close")
|
||||
class Widget : public QtAcrylicWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(Widget)
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: "qrc:/images/button_close_white.svg"
|
||||
}
|
||||
public:
|
||||
explicit Widget(QWidget *parent = nullptr);
|
||||
~Widget() override;
|
||||
|
||||
background: Rectangle {
|
||||
visible: control.down || control.hovered
|
||||
color: control.down ? "#8c0a15" : (control.hovered ? "#e81123" : "transparent")
|
||||
}
|
||||
}
|
||||
Q_INVOKABLE void moveToDesktopCenter();
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
|
||||
private:
|
||||
QLabel *m_label = nullptr;
|
||||
QPushButton *m_minimizeButton = nullptr;
|
||||
QPushButton *m_maximizeButton = nullptr;
|
||||
QPushButton *m_closeButton = nullptr;
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
TARGET = QWidget2
|
||||
TARGET = Widget
|
||||
TEMPLATE = app
|
||||
QT += widgets
|
||||
HEADERS += widget.h
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -37,3 +37,43 @@
|
|||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINDOWS)
|
||||
#define Q_OS_WINDOWS
|
||||
#endif
|
||||
|
||||
#ifndef Q_DISABLE_MOVE
|
||||
#define Q_DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
#endif
|
||||
|
||||
#ifndef Q_DISABLE_COPY_MOVE
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Q_DISABLE_MOVE(Class)
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
|
||||
#define qAsConst(i) std::as_const(i)
|
||||
#endif
|
||||
|
||||
#ifndef _flh_framelessMode_flag
|
||||
#define _flh_framelessMode_flag "_FRAMELESSHELPER_FRAMELESS_MODE_ENABLED"
|
||||
#endif
|
||||
|
||||
#ifndef _flh_borderWidth_flag
|
||||
#define _flh_borderWidth_flag "_FRAMELESSHELPER_WINDOW_BORDER_WIDTH"
|
||||
#endif
|
||||
|
||||
#ifndef _flh_borderHeight_flag
|
||||
#define _flh_borderHeight_flag "_FRAMELESSHELPER_WINDOW_BORDER_HEIGHT"
|
||||
#endif
|
||||
|
||||
#ifndef _flh_titleBarHeight_flag
|
||||
#define _flh_titleBarHeight_flag "_FRAMELESSHELPER_WINDOW_TITLE_BAR_HEIGHT"
|
||||
#endif
|
||||
|
||||
#ifndef _flh_ignoredObjects_flag
|
||||
#define _flh_ignoredObjects_flag "_FRAMELESSHELPER_WINDOW_TITLE_BAR_IGNORED_OBJECTS"
|
||||
#endif
|
||||
|
|
|
@ -30,45 +30,14 @@
|
|||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "winnativeeventfilter.h"
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QLibrary>
|
||||
#include <QSettings>
|
||||
#include <QWindow>
|
||||
#include <QtMath>
|
||||
#include <qpa/qplatformwindow.h>
|
||||
#include <qt_windows.h>
|
||||
#include <dwmapi.h>
|
||||
#include "framelesshelper_win32.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <shellapi.h>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
#include <QOperatingSystemVersion>
|
||||
#else
|
||||
#include <QSysInfo>
|
||||
#endif
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#else
|
||||
#include <qpa/qplatformwindow_p.h>
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(QMargins)
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
|
||||
#define qAsConst(i) std::as_const(i)
|
||||
#endif
|
||||
|
||||
#ifndef USER_DEFAULT_SCREEN_DPI
|
||||
// Only available since Windows Vista
|
||||
#define USER_DEFAULT_SCREEN_DPI 96
|
||||
#endif
|
||||
|
||||
#ifndef SM_CXPADDEDBORDER
|
||||
// Only available since Windows Vista
|
||||
#define SM_CXPADDEDBORDER 92
|
||||
#endif
|
||||
#include "utilities.h"
|
||||
|
||||
#ifndef WM_NCUAHDRAWCAPTION
|
||||
// Not documented, only available since Windows Vista
|
||||
|
@ -80,121 +49,11 @@ Q_DECLARE_METATYPE(QMargins)
|
|||
#define WM_NCUAHDRAWFRAME 0x00AF
|
||||
#endif
|
||||
|
||||
#ifndef WM_DWMCOMPOSITIONCHANGED
|
||||
// Only available since Windows Vista
|
||||
#define WM_DWMCOMPOSITIONCHANGED 0x031E
|
||||
#endif
|
||||
|
||||
#ifndef WM_DPICHANGED
|
||||
// Only available since Windows 8.1
|
||||
#define WM_DPICHANGED 0x02E0
|
||||
#endif
|
||||
|
||||
#ifndef ABM_GETAUTOHIDEBAREX
|
||||
// Only available since Windows 8.1
|
||||
#define ABM_GETAUTOHIDEBAREX 0x0000000b
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
enum : WORD { DwmwaUseImmersiveDarkMode = 20, DwmwaUseImmersiveDarkModeBefore20h1 = 19 };
|
||||
|
||||
using WINDOWCOMPOSITIONATTRIB = enum _WINDOWCOMPOSITIONATTRIB
|
||||
{
|
||||
WCA_ACCENT_POLICY = 19
|
||||
};
|
||||
|
||||
using WINDOWCOMPOSITIONATTRIBDATA = struct _WINDOWCOMPOSITIONATTRIBDATA
|
||||
{
|
||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
||||
PVOID pvData;
|
||||
SIZE_T cbData;
|
||||
};
|
||||
|
||||
using ACCENT_STATE = enum _ACCENT_STATE {
|
||||
ACCENT_DISABLED = 0,
|
||||
ACCENT_ENABLE_GRADIENT = 1,
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
||||
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
|
||||
ACCENT_INVALID_STATE = 5
|
||||
};
|
||||
|
||||
using ACCENT_POLICY = struct _ACCENT_POLICY
|
||||
{
|
||||
ACCENT_STATE AccentState;
|
||||
DWORD AccentFlags;
|
||||
COLORREF GradientColor;
|
||||
DWORD AnimationId;
|
||||
};
|
||||
|
||||
using IMMERSIVE_HC_CACHE_MODE = enum _IMMERSIVE_HC_CACHE_MODE {
|
||||
IHCM_USE_CACHED_VALUE,
|
||||
IHCM_REFRESH
|
||||
};
|
||||
|
||||
using PREFERRED_APP_MODE = enum _PREFERRED_APP_MODE {
|
||||
Default,
|
||||
AllowDark,
|
||||
ForceDark,
|
||||
ForceLight,
|
||||
Max
|
||||
};
|
||||
|
||||
bool isWin8OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isWin8Point1OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8_1;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isWin10OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isWin10OrGreater(const int ver)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current()
|
||||
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, ver);
|
||||
#else
|
||||
Q_UNUSED(ver)
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isAcrylicEffectAvailable()
|
||||
{
|
||||
if (!isWin10OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
const QOperatingSystemVersion currentVersion = QOperatingSystemVersion::current();
|
||||
if (currentVersion > QOperatingSystemVersion::Windows10) {
|
||||
return true;
|
||||
}
|
||||
return ((currentVersion.microVersion() >= 16190) && (currentVersion.microVersion() < 18362));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef IsMinimized
|
||||
// Only available since Windows 2000
|
||||
#define IsMinimized(h) IsIconic(h)
|
||||
|
@ -215,108 +74,16 @@ bool isAcrylicEffectAvailable()
|
|||
#define GET_Y_LPARAM(lp) ((int) (short) HIWORD(lp))
|
||||
#endif
|
||||
|
||||
using MONITOR_DPI_TYPE = enum _MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI = 0 };
|
||||
static const char envVarUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
|
||||
static const char envVarPreserveWindowFrame[] = "WNEF_PRESERVE_WINDOW_FRAME";
|
||||
static const char envVarForceWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
|
||||
|
||||
using PROCESS_DPI_AWARENESS = enum _PROCESS_DPI_AWARENESS
|
||||
{
|
||||
PROCESS_DPI_UNAWARE = 0,
|
||||
PROCESS_SYSTEM_DPI_AWARE = 1,
|
||||
PROCESS_PER_MONITOR_DPI_AWARE = 2
|
||||
};
|
||||
|
||||
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, WINDOWCOMPOSITIONATTRIBDATA *);
|
||||
using ShouldAppsUseDarkModePtr = BOOL(WINAPI *)();
|
||||
using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL);
|
||||
using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL);
|
||||
using IsDarkModeAllowedForWindowPtr = BOOL(WINAPI *)(HWND);
|
||||
using GetIsImmersiveColorUsingHighContrastPtr = BOOL(WINAPI *)(IMMERSIVE_HC_CACHE_MODE);
|
||||
using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)();
|
||||
using ShouldSystemUseDarkModePtr = BOOL(WINAPI *)();
|
||||
using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE);
|
||||
using IsDarkModeAllowedForAppPtr = BOOL(WINAPI *)();
|
||||
|
||||
using GetDpiForMonitorPtr = HRESULT(WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
|
||||
using GetProcessDpiAwarenessPtr = HRESULT(WINAPI *)(HANDLE, PROCESS_DPI_AWARENESS *);
|
||||
using GetSystemDpiForProcessPtr = UINT(WINAPI *)(HANDLE);
|
||||
using GetDpiForWindowPtr = UINT(WINAPI *)(HWND);
|
||||
using GetDpiForSystemPtr = UINT(WINAPI *)();
|
||||
using GetSystemMetricsForDpiPtr = int(WINAPI *)(int, UINT);
|
||||
using AdjustWindowRectExForDpiPtr = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT);
|
||||
|
||||
// Internal data structure.
|
||||
using WNEF_CORE_DATA = struct _WNEF_CORE_DATA
|
||||
{
|
||||
_WNEF_CORE_DATA() { load(); }
|
||||
|
||||
SetWindowCompositionAttributePtr SetWindowCompositionAttributePFN = nullptr;
|
||||
ShouldAppsUseDarkModePtr ShouldAppsUseDarkModePFN = nullptr;
|
||||
AllowDarkModeForWindowPtr AllowDarkModeForWindowPFN = nullptr;
|
||||
AllowDarkModeForAppPtr AllowDarkModeForAppPFN = nullptr;
|
||||
IsDarkModeAllowedForWindowPtr IsDarkModeAllowedForWindowPFN = nullptr;
|
||||
GetIsImmersiveColorUsingHighContrastPtr GetIsImmersiveColorUsingHighContrastPFN = nullptr;
|
||||
RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyStatePFN = nullptr;
|
||||
ShouldSystemUseDarkModePtr ShouldSystemUseDarkModePFN = nullptr;
|
||||
SetPreferredAppModePtr SetPreferredAppModePFN = nullptr;
|
||||
IsDarkModeAllowedForAppPtr IsDarkModeAllowedForAppPFN = nullptr;
|
||||
|
||||
GetDpiForMonitorPtr GetDpiForMonitorPFN = nullptr;
|
||||
GetProcessDpiAwarenessPtr GetProcessDpiAwarenessPFN = nullptr;
|
||||
GetSystemDpiForProcessPtr GetSystemDpiForProcessPFN = nullptr;
|
||||
GetDpiForWindowPtr GetDpiForWindowPFN = nullptr;
|
||||
GetDpiForSystemPtr GetDpiForSystemPFN = nullptr;
|
||||
GetSystemMetricsForDpiPtr GetSystemMetricsForDpiPFN = nullptr;
|
||||
AdjustWindowRectExForDpiPtr AdjustWindowRectExForDpiPFN = nullptr;
|
||||
|
||||
void load()
|
||||
{
|
||||
QLibrary User32Dll(QString::fromUtf8("User32"));
|
||||
SetWindowCompositionAttributePFN = reinterpret_cast<SetWindowCompositionAttributePtr>(User32Dll.resolve("SetWindowCompositionAttribute"));
|
||||
GetDpiForWindowPFN = reinterpret_cast<GetDpiForWindowPtr>(User32Dll.resolve("GetDpiForWindow"));
|
||||
GetDpiForSystemPFN = reinterpret_cast<GetDpiForSystemPtr>(User32Dll.resolve("GetDpiForSystem"));
|
||||
GetSystemMetricsForDpiPFN = reinterpret_cast<GetSystemMetricsForDpiPtr>(User32Dll.resolve("GetSystemMetricsForDpi"));
|
||||
AdjustWindowRectExForDpiPFN = reinterpret_cast<AdjustWindowRectExForDpiPtr>(User32Dll.resolve("AdjustWindowRectExForDpi"));
|
||||
GetSystemDpiForProcessPFN = reinterpret_cast<GetSystemDpiForProcessPtr>(User32Dll.resolve("GetSystemDpiForProcess"));
|
||||
|
||||
QLibrary UxThemeDll(QString::fromUtf8("UxTheme"));
|
||||
ShouldAppsUseDarkModePFN = reinterpret_cast<ShouldAppsUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(132)));
|
||||
AllowDarkModeForWindowPFN = reinterpret_cast<AllowDarkModeForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(133)));
|
||||
AllowDarkModeForAppPFN = reinterpret_cast<AllowDarkModeForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
|
||||
RefreshImmersiveColorPolicyStatePFN = reinterpret_cast<RefreshImmersiveColorPolicyStatePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(104)));
|
||||
IsDarkModeAllowedForWindowPFN = reinterpret_cast<IsDarkModeAllowedForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(137)));
|
||||
GetIsImmersiveColorUsingHighContrastPFN = reinterpret_cast<GetIsImmersiveColorUsingHighContrastPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(106)));
|
||||
ShouldSystemUseDarkModePFN = reinterpret_cast<ShouldSystemUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(138)));
|
||||
SetPreferredAppModePFN = reinterpret_cast<SetPreferredAppModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
|
||||
IsDarkModeAllowedForAppPFN = reinterpret_cast<IsDarkModeAllowedForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(139)));
|
||||
|
||||
QLibrary SHCoreDll(QString::fromUtf8("SHCore"));
|
||||
GetDpiForMonitorPFN = reinterpret_cast<GetDpiForMonitorPtr>(SHCoreDll.resolve("GetDpiForMonitor"));
|
||||
GetProcessDpiAwarenessPFN = reinterpret_cast<GetProcessDpiAwarenessPtr>(SHCoreDll.resolve("GetProcessDpiAwareness"));
|
||||
}
|
||||
|
||||
QScopedPointer<WinNativeEventFilter> m_instance;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Q_GLOBAL_STATIC(WNEF_CORE_DATA, coreData)
|
||||
|
||||
namespace {
|
||||
|
||||
const quint32 m_defaultDotsPerInch = USER_DEFAULT_SCREEN_DPI;
|
||||
|
||||
const qreal m_defaultDevicePixelRatio = 1.0;
|
||||
|
||||
const char envVarUseNativeTitleBar[] = "WNEF_USE_NATIVE_TITLE_BAR";
|
||||
const char envVarPreserveWindowFrame[] = "WNEF_PRESERVE_WINDOW_FRAME";
|
||||
const char envVarForceWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
|
||||
const char envVarForceAcrylic[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
|
||||
|
||||
bool shouldUseNativeTitleBar()
|
||||
static inline bool shouldUseNativeTitleBar()
|
||||
{
|
||||
return qEnvironmentVariableIsSet(envVarUseNativeTitleBar);
|
||||
}
|
||||
|
||||
bool shouldHaveWindowFrame()
|
||||
static inline bool shouldHaveWindowFrame()
|
||||
{
|
||||
if (shouldUseNativeTitleBar()) {
|
||||
// We have to use the original window frame unconditionally if we
|
||||
|
@ -332,161 +99,94 @@ bool shouldHaveWindowFrame()
|
|||
if (should) {
|
||||
// If you preserve the window frame on Win7~8.1,
|
||||
// the window will have a terrible appearance.
|
||||
return isWin10OrGreater();
|
||||
return Utilities::isWin10OrGreater();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool forceEnableAcrylicOnWin10()
|
||||
{
|
||||
return qEnvironmentVariableIsSet(envVarForceAcrylic);
|
||||
}
|
||||
|
||||
bool isDwmCompositionEnabled()
|
||||
{
|
||||
// Since Win8, DWM composition is always enabled and can't be disabled.
|
||||
// In other words, DwmIsCompositionEnabled will always return TRUE on
|
||||
// systems newer than Win7.
|
||||
BOOL enabled = FALSE;
|
||||
return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled;
|
||||
}
|
||||
|
||||
QWindow *findWindow(const HWND handle)
|
||||
{
|
||||
Q_ASSERT(handle);
|
||||
const auto wid = reinterpret_cast<WId>(handle);
|
||||
const QWindowList windows = QGuiApplication::topLevelWindows();
|
||||
for (auto &&window : qAsConst(windows)) {
|
||||
if (window && window->handle()) {
|
||||
if (window->winId() == wid) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void triggerFrameChange(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
SetWindowPos(reinterpret_cast<HWND>(window->winId()), nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
||||
}
|
||||
|
||||
void updateFrameMargins(const QWindow *window, bool reset)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
|
||||
DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(window->winId()), &margins);
|
||||
}
|
||||
|
||||
// The standard values of border width, border height and title bar height
|
||||
// when DPI is 96.
|
||||
const int m_defaultBorderWidth = 8, m_defaultBorderHeight = 8, m_defaultTitleBarHeight = 31;
|
||||
|
||||
// The thickness of an auto-hide taskbar in pixels.
|
||||
const int kAutoHideTaskbarThicknessPx = 2;
|
||||
const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
|
||||
static const int kAutoHideTaskbarThicknessPx = 2;
|
||||
static const int kAutoHideTaskbarThicknessPy = kAutoHideTaskbarThicknessPx;
|
||||
|
||||
const QString g_sDwmRegistryKey = QString::fromUtf8(
|
||||
R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)");
|
||||
const QString g_sPersonalizeRegistryKey = QString::fromUtf8(
|
||||
R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
|
||||
static QScopedPointer<FramelessHelperWin> g_instance;
|
||||
|
||||
const char m_framelessMode[] = "_WNEF_FRAMELESS_MODE_ENABLED";
|
||||
const char m_borderWidth[] = "_WNEF_WINDOW_BORDER_WIDTH";
|
||||
const char m_borderHeight[] = "_WNEF_WINDOW_BORDER_HEIGHT";
|
||||
const char m_titleBarHeight[] = "_WNEF_TITLE_BAR_HEIGHT";
|
||||
const char m_ignoredObjects[] = "_WNEF_TITLE_BAR_IGNORED_OBJECTS";
|
||||
|
||||
void setup()
|
||||
static inline void setup()
|
||||
{
|
||||
if (coreData()->m_instance.isNull()) {
|
||||
coreData()->m_instance.reset(new WinNativeEventFilter);
|
||||
qApp->installNativeEventFilter(coreData()->m_instance.get());
|
||||
if (g_instance.isNull()) {
|
||||
g_instance.reset(new FramelessHelperWin);
|
||||
qApp->installNativeEventFilter(g_instance.get());
|
||||
}
|
||||
}
|
||||
|
||||
void installHelper(QWindow *window, const bool enable)
|
||||
static inline void installHelper(QWindow *window, const bool enable)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
window->setProperty(m_framelessMode, enable);
|
||||
const int tbh = enable ? WinNativeEventFilter::getSystemMetric(
|
||||
window, WinNativeEventFilter::SystemMetric::TitleBarHeight, true, true)
|
||||
: 0;
|
||||
const QMargins margins = {0, -tbh, 0, 0};
|
||||
const QVariant marginsVar = QVariant::fromValue(margins);
|
||||
window->setProperty("_q_windowsCustomMargins", marginsVar);
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QPlatformWindow *platformWindow = window->handle();
|
||||
if (platformWindow) {
|
||||
QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow,
|
||||
QString::fromUtf8(
|
||||
"WindowsCustomMargins"),
|
||||
marginsVar);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
#else
|
||||
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
|
||||
window->handle());
|
||||
if (platformWindow) {
|
||||
platformWindow->setCustomMargins(margins);
|
||||
}
|
||||
#endif
|
||||
updateFrameMargins(window, !enable);
|
||||
triggerFrameChange(window);
|
||||
window->setProperty(_flh_framelessMode_flag, enable);
|
||||
Utilities::updateQtFrameMargins(window, enable);
|
||||
Utilities::updateFrameMargins(window, !enable);
|
||||
Utilities::triggerFrameChange(window);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
FramelessHelperWin::FramelessHelperWin() = default;
|
||||
|
||||
WinNativeEventFilter::WinNativeEventFilter() = default;
|
||||
|
||||
WinNativeEventFilter::~WinNativeEventFilter()
|
||||
FramelessHelperWin::~FramelessHelperWin()
|
||||
{
|
||||
if (!coreData()->m_instance.isNull()) {
|
||||
qApp->removeNativeEventFilter(coreData()->m_instance.get());
|
||||
if (!g_instance.isNull()) {
|
||||
qApp->removeNativeEventFilter(g_instance.get());
|
||||
}
|
||||
}
|
||||
|
||||
void WinNativeEventFilter::addFramelessWindow(QWindow *window)
|
||||
void FramelessHelperWin::addFramelessWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
setup();
|
||||
installHelper(window, true);
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isWindowFrameless(const QWindow *window)
|
||||
bool FramelessHelperWin::isWindowFrameless(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
return window->property(m_framelessMode).toBool();
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
return window->property(_flh_framelessMode_flag).toBool();
|
||||
}
|
||||
|
||||
void WinNativeEventFilter::removeFramelessWindow(QWindow *window)
|
||||
void FramelessHelperWin::removeFramelessWindow(QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
installHelper(window, false);
|
||||
}
|
||||
|
||||
void WinNativeEventFilter::setIgnoredObjects(QWindow *window, const QObjectList &objects)
|
||||
void FramelessHelperWin::setIgnoredObjects(QWindow *window, const QObjectList &objects)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
window->setProperty(m_ignoredObjects, QVariant::fromValue(objects));
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
window->setProperty(_flh_ignoredObjects_flag, QVariant::fromValue(objects));
|
||||
}
|
||||
|
||||
QObjectList WinNativeEventFilter::getIgnoredObjects(const QWindow *window)
|
||||
QObjectList FramelessHelperWin::getIgnoredObjects(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
return qvariant_cast<QObjectList>(window->property(m_ignoredObjects));
|
||||
if (!window) {
|
||||
return {};
|
||||
}
|
||||
return qvariant_cast<QObjectList>(window->property(_flh_ignoredObjects_flag));
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||
void *message,
|
||||
qintptr *result)
|
||||
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
|
||||
#else
|
||||
bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||
void *message,
|
||||
long *result)
|
||||
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
|
||||
#endif
|
||||
{
|
||||
// "result" can't be null in theory and I don't see any projects check
|
||||
|
@ -514,8 +214,8 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
// Anyway, we should skip it in this case.
|
||||
return false;
|
||||
}
|
||||
const QWindow *window = findWindow(msg->hwnd);
|
||||
if (!window || (window && !window->property(m_framelessMode).toBool())) {
|
||||
const QWindow *window = Utilities::findWindow(reinterpret_cast<WId>(msg->hwnd));
|
||||
if (!window || (window && !window->property(_flh_framelessMode_flag).toBool())) {
|
||||
return false;
|
||||
}
|
||||
switch (msg->message) {
|
||||
|
@ -625,11 +325,11 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
// The value of border width and border height should be
|
||||
// identical in most cases, when the scale factor is 1.0, it
|
||||
// should be eight pixels.
|
||||
const int bh = getSystemMetric(window, SystemMetric::BorderHeight, true);
|
||||
const int bh = getSystemMetric(window, Utilities::SystemMetric::BorderHeight, true);
|
||||
clientRect->top += bh;
|
||||
if (!shouldHaveWindowFrame()) {
|
||||
clientRect->bottom -= bh;
|
||||
const int bw = getSystemMetric(window, SystemMetric::BorderWidth, true);
|
||||
const int bw = getSystemMetric(window, Utilities::SystemMetric::BorderWidth, true);
|
||||
clientRect->left += bw;
|
||||
clientRect->right -= bw;
|
||||
}
|
||||
|
@ -652,7 +352,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
// Due to ABM_GETAUTOHIDEBAREX only exists from Win8.1,
|
||||
// we have to use another way to judge this if we are
|
||||
// running on Windows 7 or Windows 8.
|
||||
if (isWin8Point1OrGreater()) {
|
||||
if (Utilities::isWin8Point1OrGreater()) {
|
||||
MONITORINFO monitorInfo;
|
||||
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
|
||||
monitorInfo.cbSize = sizeof(monitorInfo);
|
||||
|
@ -749,7 +449,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
case WM_NCPAINT: {
|
||||
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
|
||||
|
||||
if (!isDwmCompositionEnabled() && !shouldHaveWindowFrame()) {
|
||||
if (!Utilities::isBlurEffectSupported() && !shouldHaveWindowFrame()) {
|
||||
// Only block WM_NCPAINT when DWM composition is disabled. If
|
||||
// it's blocked when DWM composition is enabled, the frame
|
||||
// shadow won't be drawn.
|
||||
|
@ -763,7 +463,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
if (shouldHaveWindowFrame()) {
|
||||
break;
|
||||
} else {
|
||||
if (isDwmCompositionEnabled()) {
|
||||
if (Utilities::isBlurEffectSupported()) {
|
||||
// DefWindowProc won't repaint the window border if lParam
|
||||
// (normally a HRGN) is -1. See the following link's "lParam"
|
||||
// section:
|
||||
|
@ -870,6 +570,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
}
|
||||
const auto mapOriginPointToWindow = [](const QObject *obj) -> QPointF {
|
||||
Q_ASSERT(obj);
|
||||
if (!obj) {
|
||||
return {};
|
||||
}
|
||||
QPointF point = {obj->property("x").toReal(), obj->property("y").toReal()};
|
||||
for (QObject *parent = obj->parent(); parent; parent = parent->parent()) {
|
||||
point += {parent->property("x").toReal(), parent->property("y").toReal()};
|
||||
|
@ -896,11 +599,10 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
const QPointF localMouse = {static_cast<qreal>(winLocalMouse.x),
|
||||
static_cast<qreal>(winLocalMouse.y)};
|
||||
const bool isInIgnoreObjects = isInSpecificObjects(globalMouse,
|
||||
qvariant_cast<QObjectList>(
|
||||
window->property(m_ignoredObjects)),
|
||||
qvariant_cast<QObjectList>(window->property(_flh_ignoredObjects_flag)),
|
||||
dpr);
|
||||
const int bh = getSystemMetric(window, SystemMetric::BorderHeight, true);
|
||||
const int tbh = getSystemMetric(window, SystemMetric::TitleBarHeight, true);
|
||||
const int bh = getSystemMetric(window, Utilities::SystemMetric::BorderHeight, true);
|
||||
const int tbh = getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, true);
|
||||
const bool isTitleBar = (localMouse.y() <= tbh) && !isInIgnoreObjects;
|
||||
const bool isTop = localMouse.y() <= bh;
|
||||
if (shouldHaveWindowFrame()) {
|
||||
|
@ -933,7 +635,7 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
GetClientRect(msg->hwnd, &clientRect);
|
||||
const LONG ww = clientRect.right;
|
||||
const LONG wh = clientRect.bottom;
|
||||
const int bw = getSystemMetric(window, SystemMetric::BorderWidth, true);
|
||||
const int bw = getSystemMetric(window, Utilities::SystemMetric::BorderWidth, true);
|
||||
if (IsMaximized(msg->hwnd)) {
|
||||
if (isTitleBar) {
|
||||
return HTCAPTION;
|
||||
|
@ -1006,10 +708,10 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
// Prevent Windows from drawing the default title bar by temporarily
|
||||
// toggling the WS_VISIBLE style.
|
||||
SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle & ~WS_VISIBLE);
|
||||
triggerFrameChange(window);
|
||||
Utilities::triggerFrameChange(window);
|
||||
const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
|
||||
SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle);
|
||||
triggerFrameChange(window);
|
||||
Utilities::triggerFrameChange(window);
|
||||
*result = ret;
|
||||
return true;
|
||||
}
|
||||
|
@ -1019,199 +721,29 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
return false;
|
||||
}
|
||||
|
||||
void WinNativeEventFilter::setBorderWidth(QWindow *window, const int bw)
|
||||
void FramelessHelperWin::setBorderWidth(QWindow *window, const int bw)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
window->setProperty(m_borderWidth, bw);
|
||||
}
|
||||
|
||||
void WinNativeEventFilter::setBorderHeight(QWindow *window, const int bh)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
window->setProperty(m_borderHeight, bh);
|
||||
}
|
||||
|
||||
void WinNativeEventFilter::setTitleBarHeight(QWindow *window, const int tbh)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
window->setProperty(m_titleBarHeight, tbh);
|
||||
}
|
||||
|
||||
int WinNativeEventFilter::getSystemMetric(const QWindow *window,
|
||||
const SystemMetric metric,
|
||||
const bool dpiAware,
|
||||
const bool forceSystemValue)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
const qreal dpr = dpiAware ? window->devicePixelRatio() : m_defaultDevicePixelRatio;
|
||||
const auto getSystemMetricsForWindow = [dpr](const int index, const bool dpiAware) -> int {
|
||||
if (coreData()->GetSystemMetricsForDpiPFN) {
|
||||
const quint32 dpi = dpiAware ? qRound(m_defaultDotsPerInch * dpr)
|
||||
: m_defaultDotsPerInch;
|
||||
return coreData()->GetSystemMetricsForDpiPFN(index, dpi);
|
||||
} else {
|
||||
const int value = GetSystemMetrics(index);
|
||||
return dpiAware ? value : qRound(value / dpr);
|
||||
}
|
||||
};
|
||||
int ret = 0;
|
||||
switch (metric) {
|
||||
case SystemMetric::BorderWidth: {
|
||||
const int bw = window->property(m_borderWidth).toInt();
|
||||
if ((bw > 0) && !forceSystemValue) {
|
||||
ret = qRound(bw * dpr);
|
||||
} else {
|
||||
const int result_nondpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, false)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
|
||||
const int result_dpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, true)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
|
||||
const int result = dpiAware ? result_dpi : result_nondpi;
|
||||
ret = result > 0 ? result : qRound(m_defaultBorderWidth * dpr);
|
||||
}
|
||||
} break;
|
||||
case SystemMetric::BorderHeight: {
|
||||
const int bh = window->property(m_borderHeight).toInt();
|
||||
if ((bh > 0) && !forceSystemValue) {
|
||||
ret = qRound(bh * dpr);
|
||||
} else {
|
||||
const int result_nondpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, false)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
|
||||
const int result_dpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, true)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
|
||||
const int result = dpiAware ? result_dpi : result_nondpi;
|
||||
ret = result > 0 ? result : qRound(m_defaultBorderHeight * dpr);
|
||||
}
|
||||
} break;
|
||||
case SystemMetric::TitleBarHeight: {
|
||||
const int tbh = window->property(m_titleBarHeight).toInt();
|
||||
if ((tbh > 0) && !forceSystemValue) {
|
||||
// Special case: this is the user defined value,
|
||||
// don't change it and just return it untouched.
|
||||
return qRound(tbh * dpr);
|
||||
} else {
|
||||
const int result_nondpi = getSystemMetricsForWindow(SM_CYCAPTION, false);
|
||||
const int result_dpi = getSystemMetricsForWindow(SM_CYCAPTION, true);
|
||||
const int result = dpiAware ? result_dpi : result_nondpi;
|
||||
ret = result > 0 ? result : qRound(m_defaultTitleBarHeight * dpr);
|
||||
}
|
||||
} break;
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
// When dpr = 1.0 (DPI = 96):
|
||||
// SM_CXSIZEFRAME = SM_CYSIZEFRAME = 4px
|
||||
// SM_CXPADDEDBORDER = 4px
|
||||
// SM_CYCAPTION = 23px
|
||||
// Border Width = Border Height = SM_C(X|Y)SIZEFRAME + SM_CXPADDEDBORDER = 8px
|
||||
// Title Bar Height = Border Height + SM_CYCAPTION = 31px
|
||||
// dpr = 1.25 --> Title Bar Height = 38px
|
||||
// dpr = 1.5 --> Title Bar Height = 45px
|
||||
// dpr = 1.75 --> Title Bar Height = 51px
|
||||
ret += (metric == SystemMetric::TitleBarHeight)
|
||||
? getSystemMetric(window, SystemMetric::BorderHeight, dpiAware)
|
||||
: 0;
|
||||
return ret;
|
||||
window->setProperty(_flh_borderWidth_flag, bw);
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::setBlurEffectEnabled(const QWindow *window,
|
||||
const bool enabled,
|
||||
const QColor &gradientColor)
|
||||
void FramelessHelperWin::setBorderHeight(QWindow *window, const int bh)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
const auto hwnd = reinterpret_cast<HWND>(window->winId());
|
||||
// We prefer DwmEnableBlurBehindWindow on Windows 7.
|
||||
if (isWin8OrGreater() && coreData()->SetWindowCompositionAttributePFN) {
|
||||
ACCENT_POLICY accentPolicy;
|
||||
SecureZeroMemory(&accentPolicy, sizeof(accentPolicy));
|
||||
WINDOWCOMPOSITIONATTRIBDATA wcaData;
|
||||
SecureZeroMemory(&wcaData, sizeof(wcaData));
|
||||
wcaData.Attrib = WCA_ACCENT_POLICY;
|
||||
wcaData.pvData = &accentPolicy;
|
||||
wcaData.cbData = sizeof(accentPolicy);
|
||||
if (enabled) {
|
||||
if (isAcrylicEffectAvailable() || (isWin10OrGreater() && forceEnableAcrylicOnWin10())) {
|
||||
accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
|
||||
// The gradient color must be set otherwise it'll look
|
||||
// like a classic blur. Use semi-transparent gradient
|
||||
// color to get better appearance.
|
||||
const QColor color = gradientColor.isValid() ? gradientColor : Qt::white;
|
||||
accentPolicy.GradientColor = qRgba(color.blue(), color.green(), color.red(), color.alpha());
|
||||
} else if (isWin10OrGreater()) {
|
||||
// Windows 10
|
||||
accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND;
|
||||
} else {
|
||||
// Windows 8 and 8.1.
|
||||
accentPolicy.AccentState = ACCENT_ENABLE_TRANSPARENTGRADIENT;
|
||||
}
|
||||
} else {
|
||||
accentPolicy.AccentState = ACCENT_DISABLED;
|
||||
}
|
||||
return coreData()->SetWindowCompositionAttributePFN(hwnd, &wcaData);
|
||||
} else {
|
||||
DWM_BLURBEHIND dwmBB;
|
||||
SecureZeroMemory(&dwmBB, sizeof(dwmBB));
|
||||
dwmBB.dwFlags = DWM_BB_ENABLE;
|
||||
dwmBB.fEnable = enabled ? TRUE : FALSE;
|
||||
return SUCCEEDED(DwmEnableBlurBehindWindow(hwnd, &dwmBB));
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
window->setProperty(_flh_borderHeight_flag, bh);
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isColorizationEnabled()
|
||||
{
|
||||
if (!isWin10OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
bool ok = false;
|
||||
const QSettings registry(g_sDwmRegistryKey, QSettings::NativeFormat);
|
||||
const bool colorPrevalence
|
||||
= registry.value(QString::fromUtf8("ColorPrevalence"), 0).toULongLong(&ok) != 0;
|
||||
return (ok && colorPrevalence);
|
||||
}
|
||||
|
||||
QColor WinNativeEventFilter::getColorizationColor()
|
||||
{
|
||||
DWORD color = 0;
|
||||
BOOL opaqueBlend = FALSE;
|
||||
return SUCCEEDED(DwmGetColorizationColor(&color, &opaqueBlend)) ? QColor::fromRgba(color) : Qt::darkGray;
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isLightThemeEnabled()
|
||||
{
|
||||
return !isDarkThemeEnabled();
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isDarkThemeEnabled()
|
||||
{
|
||||
return coreData()->ShouldSystemUseDarkModePFN ? coreData()->ShouldSystemUseDarkModePFN() : false;
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isHighContrastModeEnabled()
|
||||
{
|
||||
HIGHCONTRASTW hc;
|
||||
SecureZeroMemory(&hc, sizeof(hc));
|
||||
hc.cbSize = sizeof(hc);
|
||||
return SystemParametersInfoW(SPI_GETHIGHCONTRAST, 0, &hc, 0) ? (hc.dwFlags & HCF_HIGHCONTRASTON) : false;
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isDarkFrameEnabled(const QWindow *window)
|
||||
void FramelessHelperWin::setTitleBarHeight(QWindow *window, const int tbh)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!isWin10OrGreater(17763)) {
|
||||
return false;
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(window->winId());
|
||||
BOOL result = FALSE;
|
||||
const bool ok = SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|
||||
|| SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
|
||||
return (ok && result);
|
||||
}
|
||||
|
||||
bool WinNativeEventFilter::isTransparencyEffectEnabled()
|
||||
{
|
||||
if (!isWin10OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
bool ok = false;
|
||||
const QSettings registry(g_sPersonalizeRegistryKey, QSettings::NativeFormat);
|
||||
const bool enableTransparency
|
||||
= registry.value(QString::fromUtf8("EnableTransparency"), 0).toULongLong(&ok) != 0;
|
||||
return (ok && enableTransparency);
|
||||
window->setProperty(_flh_titleBarHeight_flag, tbh);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -25,33 +25,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QColor>
|
||||
#include <QObject>
|
||||
#include <QtCore/qabstractnativeeventfilter.h>
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_FORWARD_DECLARE_CLASS(QWindow)
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
|
||||
#define Q_DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Q_DISABLE_MOVE(Class)
|
||||
#endif
|
||||
|
||||
class FRAMELESSHELPER_EXPORT WinNativeEventFilter : public QAbstractNativeEventFilter
|
||||
class FRAMELESSHELPER_EXPORT FramelessHelperWin : public QAbstractNativeEventFilter
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(WinNativeEventFilter)
|
||||
Q_DISABLE_COPY_MOVE(FramelessHelperWin)
|
||||
|
||||
public:
|
||||
enum class SystemMetric { BorderWidth, BorderHeight, TitleBarHeight };
|
||||
|
||||
explicit WinNativeEventFilter();
|
||||
~WinNativeEventFilter() override;
|
||||
explicit FramelessHelperWin();
|
||||
~FramelessHelperWin() override;
|
||||
|
||||
static void addFramelessWindow(QWindow *window);
|
||||
static bool isWindowFrameless(const QWindow *window);
|
||||
|
@ -64,38 +51,6 @@ public:
|
|||
static void setBorderHeight(QWindow *window, const int bh);
|
||||
static void setTitleBarHeight(QWindow *window, const int tbh);
|
||||
|
||||
static int getSystemMetric(const QWindow *window,
|
||||
const SystemMetric metric,
|
||||
const bool dpiAware,
|
||||
const bool forceSystemValue = false);
|
||||
|
||||
// Enable or disable the blur effect for a specific window.
|
||||
// On Win10 it's the Acrylic effect.
|
||||
static bool setBlurEffectEnabled(const QWindow *window,
|
||||
const bool enabled,
|
||||
const QColor &gradientColor = Qt::white);
|
||||
|
||||
// Query whether colorization is enabled or not.
|
||||
static bool isColorizationEnabled();
|
||||
|
||||
// Acquire the theme/colorization color set by the user.
|
||||
static QColor getColorizationColor();
|
||||
|
||||
// Query whether the user is using the light theme or not.
|
||||
static bool isLightThemeEnabled();
|
||||
|
||||
// Query whether the user is using the dark theme or not.
|
||||
static bool isDarkThemeEnabled();
|
||||
|
||||
// Query whether the high contrast mode is enabled or not.
|
||||
static bool isHighContrastModeEnabled();
|
||||
|
||||
// Query whether the given window is using dark frame or not.
|
||||
static bool isDarkFrameEnabled(const QWindow *window);
|
||||
|
||||
// Query whether the transparency effect is enabled or not.
|
||||
static bool isTransparencyEffectEnabled();
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
#else
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,28 +23,17 @@
|
|||
*/
|
||||
|
||||
#include "framelessquickhelper.h"
|
||||
|
||||
#include "framelesswindowsmanager.h"
|
||||
#include <QQuickWindow>
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include "winnativeeventfilter.h"
|
||||
#include <QOperatingSystemVersion>
|
||||
#endif
|
||||
#include <QtQuick/qquickwindow.h>
|
||||
#include "utilities.h"
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
namespace {
|
||||
|
||||
const char g_sPreserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
|
||||
const char g_sForceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
// Windows only variables:
|
||||
static const char g_preserveWindowFrame[] = "WNEF_FORCE_PRESERVE_WINDOW_FRAME";
|
||||
static const char g_forceUseAcrylicEffect[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
|
||||
|
||||
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
startTimer(500);
|
||||
#endif
|
||||
}
|
||||
|
||||
int FramelessQuickHelper::borderWidth() const
|
||||
|
@ -91,47 +80,40 @@ void FramelessQuickHelper::setResizable(const bool val)
|
|||
Q_EMIT resizableChanged(val);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
bool FramelessQuickHelper::canHaveWindowFrame() const
|
||||
{
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
|
||||
}
|
||||
|
||||
bool FramelessQuickHelper::colorizationEnabled() const
|
||||
{
|
||||
return WinNativeEventFilter::isColorizationEnabled();
|
||||
return Utilities::isColorizationEnabled();
|
||||
}
|
||||
|
||||
QColor FramelessQuickHelper::colorizationColor() const
|
||||
{
|
||||
return WinNativeEventFilter::getColorizationColor();
|
||||
return Utilities::getColorizationColor();
|
||||
}
|
||||
|
||||
bool FramelessQuickHelper::lightThemeEnabled() const
|
||||
{
|
||||
return WinNativeEventFilter::isLightThemeEnabled();
|
||||
return Utilities::isLightThemeEnabled();
|
||||
}
|
||||
|
||||
bool FramelessQuickHelper::darkThemeEnabled() const
|
||||
{
|
||||
return WinNativeEventFilter::isDarkThemeEnabled();
|
||||
return Utilities::isDarkThemeEnabled();
|
||||
}
|
||||
|
||||
bool FramelessQuickHelper::highContrastModeEnabled() const
|
||||
{
|
||||
return WinNativeEventFilter::isHighContrastModeEnabled();
|
||||
return Utilities::isHighContrastModeEnabled();
|
||||
}
|
||||
|
||||
bool FramelessQuickHelper::darkFrameEnabled() const
|
||||
{
|
||||
return WinNativeEventFilter::isDarkFrameEnabled(window());
|
||||
return Utilities::isDarkFrameEnabled(window());
|
||||
}
|
||||
|
||||
bool FramelessQuickHelper::transparencyEffectEnabled() const
|
||||
{
|
||||
return WinNativeEventFilter::isTransparencyEffectEnabled();
|
||||
return Utilities::isTransparencyEffectEnabled();
|
||||
}
|
||||
#endif
|
||||
|
||||
void FramelessQuickHelper::removeWindowFrame()
|
||||
{
|
||||
|
@ -141,10 +123,12 @@ void FramelessQuickHelper::removeWindowFrame()
|
|||
void FramelessQuickHelper::addIgnoreObject(QQuickItem *val)
|
||||
{
|
||||
Q_ASSERT(val);
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
FramelessWindowsManager::addIgnoreObject(window(), val);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
void FramelessQuickHelper::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
QQuickItem::timerEvent(event);
|
||||
|
@ -157,24 +141,12 @@ void FramelessQuickHelper::timerEvent(QTimerEvent *event)
|
|||
Q_EMIT transparencyEffectEnabledChanged(transparencyEffectEnabled());
|
||||
}
|
||||
|
||||
void FramelessQuickHelper::setWindowFrameVisible(const bool value)
|
||||
{
|
||||
if (value) {
|
||||
qputenv(g_sPreserveWindowFrame, "1");
|
||||
} else {
|
||||
qunsetenv(g_sPreserveWindowFrame);
|
||||
}
|
||||
}
|
||||
|
||||
void FramelessQuickHelper::setBlurEffectEnabled(const bool enabled,
|
||||
const bool forceAcrylic,
|
||||
const QColor &gradientColor)
|
||||
void FramelessQuickHelper::setAcrylicEffectEnabled(const bool enabled, const bool forceAcrylic, const QColor &gradientColor)
|
||||
{
|
||||
if (forceAcrylic) {
|
||||
qputenv(g_sForceUseAcrylicEffect, "1");
|
||||
qputenv(g_forceUseAcrylicEffect, "1");
|
||||
} else {
|
||||
qunsetenv(g_sForceUseAcrylicEffect);
|
||||
qunsetenv(g_forceUseAcrylicEffect);
|
||||
}
|
||||
WinNativeEventFilter::setBlurEffectEnabled(window(), enabled, gradientColor);
|
||||
Utilities::setAcrylicEffectEnabled(window(), enabled, gradientColor);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -24,24 +24,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QQuickItem>
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtQuick/qquickitem.h>
|
||||
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)) \
|
||||
&& !defined(Q_OS_WINDOWS)
|
||||
#define Q_OS_WINDOWS
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
|
||||
#define Q_DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Q_DISABLE_MOVE(Class)
|
||||
#endif
|
||||
|
||||
class FramelessQuickHelper : public QQuickItem
|
||||
class FRAMELESSHELPER_EXPORT FramelessQuickHelper : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
|
||||
|
@ -50,21 +36,15 @@ class FramelessQuickHelper : public QQuickItem
|
|||
#endif
|
||||
Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged)
|
||||
Q_PROPERTY(int borderHeight READ borderHeight WRITE setBorderHeight NOTIFY borderHeightChanged)
|
||||
Q_PROPERTY(
|
||||
int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
|
||||
Q_PROPERTY(int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
|
||||
Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged)
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_PROPERTY(bool canHaveWindowFrame READ canHaveWindowFrame CONSTANT)
|
||||
Q_PROPERTY(bool colorizationEnabled READ colorizationEnabled NOTIFY colorizationEnabledChanged)
|
||||
Q_PROPERTY(QColor colorizationColor READ colorizationColor NOTIFY colorizationColorChanged)
|
||||
Q_PROPERTY(bool lightThemeEnabled READ lightThemeEnabled NOTIFY lightThemeEnabledChanged)
|
||||
Q_PROPERTY(bool darkThemeEnabled READ darkThemeEnabled NOTIFY darkThemeEnabledChanged)
|
||||
Q_PROPERTY(bool highContrastModeEnabled READ highContrastModeEnabled NOTIFY
|
||||
highContrastModeEnabledChanged)
|
||||
Q_PROPERTY(bool highContrastModeEnabled READ highContrastModeEnabled NOTIFY highContrastModeEnabledChanged)
|
||||
Q_PROPERTY(bool darkFrameEnabled READ darkFrameEnabled NOTIFY darkFrameEnabledChanged)
|
||||
Q_PROPERTY(bool transparencyEffectEnabled READ transparencyEffectEnabled NOTIFY
|
||||
transparencyEffectEnabledChanged)
|
||||
#endif
|
||||
Q_PROPERTY(bool transparencyEffectEnabled READ transparencyEffectEnabled NOTIFY transparencyEffectEnabledChanged)
|
||||
|
||||
public:
|
||||
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
|
||||
|
@ -82,8 +62,6 @@ public:
|
|||
bool resizable() const;
|
||||
void setResizable(const bool val);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
bool canHaveWindowFrame() const;
|
||||
bool colorizationEnabled() const;
|
||||
QColor colorizationColor() const;
|
||||
bool lightThemeEnabled() const;
|
||||
|
@ -91,31 +69,20 @@ public:
|
|||
bool highContrastModeEnabled() const;
|
||||
bool darkFrameEnabled() const;
|
||||
bool transparencyEffectEnabled() const;
|
||||
#endif
|
||||
|
||||
public Q_SLOTS:
|
||||
void removeWindowFrame();
|
||||
|
||||
void addIgnoreObject(QQuickItem *val);
|
||||
void setAcrylicEffectEnabled(const bool enabled = true, const bool forceAcrylic = false, const QColor &gradientColor = {});
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
void setWindowFrameVisible(const bool value = true);
|
||||
void setBlurEffectEnabled(const bool enabled = true,
|
||||
const bool forceAcrylic = false,
|
||||
const QColor &gradientColor = Qt::white);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void borderWidthChanged(int);
|
||||
void borderHeightChanged(int);
|
||||
void titleBarHeightChanged(int);
|
||||
void resizableChanged(bool);
|
||||
#ifdef Q_OS_WINDOWS
|
||||
void colorizationEnabledChanged(bool);
|
||||
void colorizationColorChanged(const QColor &);
|
||||
void lightThemeEnabledChanged(bool);
|
||||
|
@ -123,5 +90,4 @@ Q_SIGNALS:
|
|||
void highContrastModeEnabledChanged(bool);
|
||||
void darkFrameEnabledChanged(bool);
|
||||
void transparencyEffectEnabledChanged(bool);
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -23,11 +23,10 @@
|
|||
*/
|
||||
|
||||
#include "framelesswindowsmanager.h"
|
||||
|
||||
#include <QWindow>
|
||||
|
||||
#include <QtGui/qwindow.h>
|
||||
#include "utilities.h"
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include "winnativeeventfilter.h"
|
||||
#include "framelesshelper_win32.h"
|
||||
#else
|
||||
#include "framelesshelper.h"
|
||||
#endif
|
||||
|
@ -41,8 +40,11 @@ FramelessWindowsManager::FramelessWindowsManager() = default;
|
|||
void FramelessWindowsManager::addWindow(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
WinNativeEventFilter::addFramelessWindow(const_cast<QWindow *>(window));
|
||||
FramelessHelperWin::addFramelessWindow(const_cast<QWindow *>(window));
|
||||
#else
|
||||
framelessHelper()->removeWindowFrame(const_cast<QWindow *>(window));
|
||||
#endif
|
||||
|
@ -51,10 +53,13 @@ void FramelessWindowsManager::addWindow(const QWindow *window)
|
|||
void FramelessWindowsManager::addIgnoreObject(const QWindow *window, QObject *object)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QObjectList objects = WinNativeEventFilter::getIgnoredObjects(window);
|
||||
QObjectList objects = FramelessHelperWin::getIgnoredObjects(window);
|
||||
objects.append(object);
|
||||
WinNativeEventFilter::setIgnoredObjects(const_cast<QWindow *>(window), objects);
|
||||
FramelessHelperWin::setIgnoredObjects(const_cast<QWindow *>(window), objects);
|
||||
#else
|
||||
framelessHelper()->addIgnoreObject(window, object);
|
||||
#endif
|
||||
|
@ -64,11 +69,12 @@ int FramelessWindowsManager::getBorderWidth(const QWindow *window)
|
|||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(window);
|
||||
return WinNativeEventFilter::getSystemMetric(window,
|
||||
WinNativeEventFilter::SystemMetric::BorderWidth,
|
||||
false);
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderWidth, false);
|
||||
#else
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(window);
|
||||
return framelessHelper()->getBorderWidth();
|
||||
#endif
|
||||
}
|
||||
|
@ -77,9 +83,12 @@ void FramelessWindowsManager::setBorderWidth(const QWindow *window, const int va
|
|||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(window);
|
||||
WinNativeEventFilter::setBorderWidth(const_cast<QWindow *>(window), value);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
FramelessHelperWin::setBorderWidth(const_cast<QWindow *>(window), value);
|
||||
#else
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(window);
|
||||
framelessHelper()->setBorderWidth(value);
|
||||
#endif
|
||||
}
|
||||
|
@ -88,11 +97,12 @@ int FramelessWindowsManager::getBorderHeight(const QWindow *window)
|
|||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(window);
|
||||
return WinNativeEventFilter::getSystemMetric(window,
|
||||
WinNativeEventFilter::SystemMetric::BorderHeight,
|
||||
false);
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderHeight, false);
|
||||
#else
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(window);
|
||||
return framelessHelper()->getBorderHeight();
|
||||
#endif
|
||||
}
|
||||
|
@ -101,9 +111,12 @@ void FramelessWindowsManager::setBorderHeight(const QWindow *window, const int v
|
|||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(window);
|
||||
WinNativeEventFilter::setBorderHeight(const_cast<QWindow *>(window), value);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
FramelessHelperWin::setBorderHeight(const_cast<QWindow *>(window), value);
|
||||
#else
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(window);
|
||||
framelessHelper()->setBorderHeight(value);
|
||||
#endif
|
||||
}
|
||||
|
@ -112,11 +125,12 @@ int FramelessWindowsManager::getTitleBarHeight(const QWindow *window)
|
|||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(window);
|
||||
return WinNativeEventFilter::getSystemMetric(window,
|
||||
WinNativeEventFilter::SystemMetric::TitleBarHeight,
|
||||
false);
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
return Utilities::getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, false);
|
||||
#else
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(window);
|
||||
return framelessHelper()->getTitleBarHeight();
|
||||
#endif
|
||||
}
|
||||
|
@ -125,9 +139,12 @@ void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int
|
|||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(window);
|
||||
WinNativeEventFilter::setTitleBarHeight(const_cast<QWindow *>(window), value);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
FramelessHelperWin::setTitleBarHeight(const_cast<QWindow *>(window), value);
|
||||
#else
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(window);
|
||||
framelessHelper()->setTitleBarHeight(value);
|
||||
#endif
|
||||
}
|
||||
|
@ -135,6 +152,9 @@ void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int
|
|||
bool FramelessWindowsManager::getResizable(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
if (window->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)) {
|
||||
return false;
|
||||
|
@ -153,6 +173,9 @@ bool FramelessWindowsManager::getResizable(const QWindow *window)
|
|||
void FramelessWindowsManager::setResizable(const QWindow *window, const bool value)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
const_cast<QWindow *>(window)->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value);
|
||||
#else
|
||||
|
|
|
@ -27,26 +27,11 @@
|
|||
#include "framelesshelper_global.h"
|
||||
#include <QRect>
|
||||
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)) \
|
||||
&& !defined(Q_OS_WINDOWS)
|
||||
#define Q_OS_WINDOWS
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
QT_FORWARD_DECLARE_CLASS(QObject)
|
||||
QT_FORWARD_DECLARE_CLASS(QWindow)
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
|
||||
#define Q_DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define Q_DISABLE_COPY_MOVE(Class) \
|
||||
Q_DISABLE_COPY(Class) \
|
||||
Q_DISABLE_MOVE(Class)
|
||||
#endif
|
||||
|
||||
class FRAMELESSHELPER_EXPORT FramelessWindowsManager
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(FramelessWindowsManager)
|
||||
|
|
40
lib.pro
|
@ -14,14 +14,42 @@ DEFINES += \
|
|||
HEADERS += \
|
||||
framelesshelper_global.h \
|
||||
framelesshelper.h \
|
||||
framelesswindowsmanager.h
|
||||
framelesswindowsmanager.h \
|
||||
utilities.h \
|
||||
qtacryliceffecthelper.h
|
||||
SOURCES += \
|
||||
framelesshelper.cpp \
|
||||
framelesswindowsmanager.cpp
|
||||
framelesswindowsmanager.cpp \
|
||||
utilities.cpp \
|
||||
qtacryliceffecthelper.cpp
|
||||
qtHaveModule(widgets) {
|
||||
QT += widgets
|
||||
HEADERS += qtacrylicwidget.h
|
||||
SOURCES += qtacrylicwidget.cpp
|
||||
}
|
||||
qtHaveModule(quick) {
|
||||
QT += quick
|
||||
HEADERS += \
|
||||
framelessquickhelper.h \
|
||||
qtacrylicitem.h
|
||||
SOURCES += \
|
||||
framelessquickhelper.cpp \
|
||||
qtacrylicitem.cpp
|
||||
}
|
||||
RESOURCES += qtacrylichelper.qrc
|
||||
win32 {
|
||||
DEFINES += WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS
|
||||
HEADERS += winnativeeventfilter.h
|
||||
SOURCES += winnativeeventfilter.cpp
|
||||
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi -lshcore -ld2d1 -luxtheme
|
||||
DEFINES += \
|
||||
WIN32_LEAN_AND_MEAN \
|
||||
_CRT_SECURE_NO_WARNINGS \
|
||||
UNICODE \
|
||||
_UNICODE
|
||||
HEADERS += \
|
||||
framelesshelper_win32.h \
|
||||
qtacryliceffecthelper_win32.h
|
||||
SOURCES += \
|
||||
utilities_win32.cpp \
|
||||
framelesshelper_win32.cpp \
|
||||
qtacryliceffecthelper_win32.cpp
|
||||
LIBS += -luser32 -lshell32 -lgdi32 -ldwmapi
|
||||
RC_FILE = framelesshelper.rc
|
||||
}
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qtacryliceffecthelper.h"
|
||||
#include "utilities.h"
|
||||
#include <QtGui/qpainter.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
|
||||
QtAcrylicEffectHelper::QtAcrylicEffectHelper()
|
||||
{
|
||||
Q_INIT_RESOURCE(qtacrylichelper);
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||
#ifdef Q_OS_MACOS
|
||||
if (Utilities::isAcrylicEffectSupported()) {
|
||||
m_tintOpacity = 0.6;
|
||||
}
|
||||
#endif
|
||||
m_frameColor = Utilities::getNativeWindowFrameColor(true);
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::install(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
if (m_window != window) {
|
||||
m_window = const_cast<QWindow *>(window);
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::uninstall()
|
||||
{
|
||||
if (m_window) {
|
||||
m_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::clearWallpaper()
|
||||
{
|
||||
if (!m_bluredWallpaper.isNull()) {
|
||||
m_bluredWallpaper = {};
|
||||
}
|
||||
}
|
||||
|
||||
QBrush QtAcrylicEffectHelper::getAcrylicBrush() const
|
||||
{
|
||||
return m_acrylicBrush;
|
||||
}
|
||||
|
||||
QColor QtAcrylicEffectHelper::getTintColor() const
|
||||
{
|
||||
return m_tintColor;
|
||||
}
|
||||
|
||||
qreal QtAcrylicEffectHelper::getTintOpacity() const
|
||||
{
|
||||
return m_tintOpacity;
|
||||
}
|
||||
|
||||
qreal QtAcrylicEffectHelper::getNoiseOpacity() const
|
||||
{
|
||||
return m_noiseOpacity;
|
||||
}
|
||||
|
||||
QPixmap QtAcrylicEffectHelper::getBluredWallpaper() const
|
||||
{
|
||||
return m_bluredWallpaper;
|
||||
}
|
||||
|
||||
QColor QtAcrylicEffectHelper::getFrameColor() const
|
||||
{
|
||||
return m_frameColor;
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::setTintColor(const QColor &value)
|
||||
{
|
||||
m_tintColor = value;
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::setTintOpacity(qreal value)
|
||||
{
|
||||
m_tintOpacity = value;
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::setNoiseOpacity(qreal value)
|
||||
{
|
||||
m_noiseOpacity = value;
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::setFrameColor(const QColor &value)
|
||||
{
|
||||
m_frameColor = value;
|
||||
}
|
||||
|
||||
QtAcrylicEffectHelper::~QtAcrylicEffectHelper() = default;
|
||||
|
||||
void QtAcrylicEffectHelper::paintWindowBackground(QPainter *painter, const QRegion &clip)
|
||||
{
|
||||
Q_ASSERT(painter);
|
||||
Q_ASSERT(!clip.isEmpty());
|
||||
if (!painter || clip.isEmpty() || !m_window) {
|
||||
return;
|
||||
}
|
||||
painter->save();
|
||||
painter->setClipRegion(clip);
|
||||
paintBackground(painter, clip.boundingRect());
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::paintWindowBackground(QPainter *painter, const QRect &rect)
|
||||
{
|
||||
Q_ASSERT(painter);
|
||||
Q_ASSERT(rect.isValid());
|
||||
if (!painter || !rect.isValid() || !m_window) {
|
||||
return;
|
||||
}
|
||||
painter->save();
|
||||
painter->setClipRegion({rect});
|
||||
paintBackground(painter, rect);
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::paintBackground(QPainter *painter, const QRect &rect)
|
||||
{
|
||||
Q_ASSERT(painter);
|
||||
Q_ASSERT(rect.isValid());
|
||||
if (!painter || !rect.isValid() || !m_window) {
|
||||
return;
|
||||
}
|
||||
if (Utilities::isAcrylicEffectSupported()) {
|
||||
const QPainter::CompositionMode mode = painter->compositionMode();
|
||||
painter->setCompositionMode(QPainter::CompositionMode_Clear);
|
||||
painter->fillRect(rect, Qt::white);
|
||||
painter->setCompositionMode(mode);
|
||||
} else {
|
||||
// Emulate blur behind window by blurring the desktop wallpaper.
|
||||
updateBehindWindowBackground();
|
||||
painter->drawPixmap(QPoint{0, 0}, m_bluredWallpaper, rect);
|
||||
}
|
||||
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
painter->setOpacity(1);
|
||||
painter->fillRect(rect, m_acrylicBrush);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::paintWindowFrame(QPainter *painter, const QRect &rect)
|
||||
{
|
||||
Q_ASSERT(painter);
|
||||
if (!painter || !m_window) {
|
||||
return;
|
||||
}
|
||||
if (m_window->windowState() != Qt::WindowNoState) {
|
||||
// We shouldn't draw the window frame when it's minimized/maximized/fullscreen.
|
||||
return;
|
||||
}
|
||||
const int width = rect.isValid() ? rect.width() : m_window->width();
|
||||
const int height = rect.isValid() ? rect.height() : m_window->height();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
const QList<QLine> lines = {
|
||||
#else
|
||||
const QVector<QLine> lines = {
|
||||
#endif
|
||||
{0, 0, width, 0},
|
||||
{width - 1, 0, width - 1, height},
|
||||
{width, height - 1, 0, height - 1},
|
||||
{0, height, 0, 0}
|
||||
};
|
||||
const bool active = m_window->isActive();
|
||||
const QColor color = (active && m_frameColor.isValid() && (m_frameColor != Qt::transparent)) ? m_frameColor : Utilities::getNativeWindowFrameColor(active);
|
||||
painter->save();
|
||||
painter->setPen({color, 1});
|
||||
painter->drawLines(lines);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::updateAcrylicBrush(const QColor &alternativeTintColor)
|
||||
{
|
||||
const auto getAppropriateTintColor = [&alternativeTintColor, this]() -> QColor {
|
||||
if (alternativeTintColor.isValid() && (alternativeTintColor != Qt::transparent)) {
|
||||
return alternativeTintColor;
|
||||
}
|
||||
if (m_tintColor.isValid() && (m_tintColor != Qt::transparent)) {
|
||||
return m_tintColor;
|
||||
}
|
||||
return Qt::white;
|
||||
};
|
||||
static const QImage noiseTexture(QStringLiteral(":/QtAcrylicHelper/Noise.png"));
|
||||
QImage acrylicTexture({64, 64}, QImage::Format_ARGB32_Premultiplied);
|
||||
QColor fillColor = Qt::transparent;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
if (!Utilities::isMSWin10AcrylicEffectAvailable()) {
|
||||
// Add a soft light layer for the background.
|
||||
fillColor = Qt::white;
|
||||
fillColor.setAlpha(150);
|
||||
}
|
||||
#endif
|
||||
acrylicTexture.fill(fillColor);
|
||||
QPainter painter(&acrylicTexture);
|
||||
painter.setOpacity(m_tintOpacity);
|
||||
painter.fillRect(QRect{0, 0, acrylicTexture.width(), acrylicTexture.height()}, getAppropriateTintColor());
|
||||
painter.setOpacity(m_noiseOpacity);
|
||||
painter.fillRect(QRect{0, 0, acrylicTexture.width(), acrylicTexture.height()}, noiseTexture);
|
||||
m_acrylicBrush = acrylicTexture;
|
||||
}
|
||||
|
||||
void QtAcrylicEffectHelper::updateBehindWindowBackground()
|
||||
{
|
||||
if (!m_bluredWallpaper.isNull()) {
|
||||
return;
|
||||
}
|
||||
const QSize size = Utilities::getScreenAvailableGeometry().size();
|
||||
m_bluredWallpaper = QPixmap(size);
|
||||
m_bluredWallpaper.fill(Qt::transparent);
|
||||
QImage image = Utilities::getDesktopWallpaperImage();
|
||||
// On some platforms we may not be able to get the desktop wallpaper, such as Linux and WebAssembly.
|
||||
if (image.isNull()) {
|
||||
return;
|
||||
}
|
||||
const Utilities::DesktopWallpaperAspectStyle aspectStyle = Utilities::getDesktopWallpaperAspectStyle();
|
||||
QImage buffer(size, QImage::Format_ARGB32_Premultiplied);
|
||||
#ifdef Q_OS_WINDOWS
|
||||
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::Central) {
|
||||
buffer.fill(Qt::black);
|
||||
}
|
||||
#endif
|
||||
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::IgnoreRatio ||
|
||||
aspectStyle == Utilities::DesktopWallpaperAspectStyle::KeepRatio ||
|
||||
aspectStyle == Utilities::DesktopWallpaperAspectStyle::KeepRatioByExpanding) {
|
||||
Qt::AspectRatioMode mode = Qt::KeepAspectRatioByExpanding;
|
||||
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::IgnoreRatio) {
|
||||
mode = Qt::IgnoreAspectRatio;
|
||||
} else if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::KeepRatio) {
|
||||
mode = Qt::KeepAspectRatio;
|
||||
}
|
||||
QSize newSize = image.size();
|
||||
newSize.scale(size, mode);
|
||||
image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
|
||||
}
|
||||
if (aspectStyle == Utilities::DesktopWallpaperAspectStyle::Tiled) {
|
||||
QPainter painterBuffer(&buffer);
|
||||
painterBuffer.fillRect(QRect{{0, 0}, size}, image);
|
||||
} else {
|
||||
QPainter painterBuffer(&buffer);
|
||||
const QRect rect = Utilities::alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), {{0, 0}, size});
|
||||
painterBuffer.drawImage(rect.topLeft(), image);
|
||||
}
|
||||
QPainter painter(&m_bluredWallpaper);
|
||||
#if 1
|
||||
Utilities::blurImage(&painter, buffer, 128, false, false);
|
||||
#else
|
||||
painter.drawImage(QPoint{0, 0}, buffer);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtGui/qbrush.h>
|
||||
|
||||
class FRAMELESSHELPER_EXPORT QtAcrylicEffectHelper
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QtAcrylicEffectHelper)
|
||||
|
||||
public:
|
||||
explicit QtAcrylicEffectHelper();
|
||||
~QtAcrylicEffectHelper();
|
||||
|
||||
void install(const QWindow *window);
|
||||
void uninstall();
|
||||
|
||||
void clearWallpaper();
|
||||
|
||||
QBrush getAcrylicBrush() const;
|
||||
QColor getTintColor() const;
|
||||
qreal getTintOpacity() const;
|
||||
qreal getNoiseOpacity() const;
|
||||
QPixmap getBluredWallpaper() const;
|
||||
QColor getFrameColor() const;
|
||||
|
||||
void setTintColor(const QColor &value);
|
||||
void setTintOpacity(qreal value);
|
||||
void setNoiseOpacity(qreal value);
|
||||
void setFrameColor(const QColor &value);
|
||||
|
||||
void paintWindowBackground(QPainter *painter, const QRegion &clip);
|
||||
void paintWindowBackground(QPainter *painter, const QRect &rect);
|
||||
void paintWindowFrame(QPainter *painter, const QRect &rect = {});
|
||||
void updateAcrylicBrush(const QColor &alternativeTintColor = {});
|
||||
|
||||
private:
|
||||
void paintBackground(QPainter *painter, const QRect &rect);
|
||||
void updateBehindWindowBackground();
|
||||
|
||||
private:
|
||||
QWindow *m_window = nullptr;
|
||||
QBrush m_acrylicBrush = {};
|
||||
QColor m_tintColor = {};
|
||||
qreal m_tintOpacity = 0.7;
|
||||
qreal m_noiseOpacity = 0.04;
|
||||
QPixmap m_bluredWallpaper = {};
|
||||
QColor m_frameColor = {};
|
||||
};
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qtacryliceffecthelper_win32.h"
|
||||
#include "utilities.h"
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
|
||||
#ifndef WM_DWMCOMPOSITIONCHANGED
|
||||
// Only available since Windows Vista
|
||||
#define WM_DWMCOMPOSITIONCHANGED 0x031E
|
||||
#endif
|
||||
|
||||
#ifndef WM_DWMCOLORIZATIONCOLORCHANGED
|
||||
// Only available since Windows Vista
|
||||
#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
|
||||
#endif
|
||||
|
||||
#ifndef WM_DPICHANGED
|
||||
// Only available since Windows 8.1
|
||||
#define WM_DPICHANGED 0x02E0
|
||||
#endif
|
||||
|
||||
const int QtAcrylicWinUpdateEvent::QtAcrylicEffectChangeEventId = QEvent::registerEventType();
|
||||
|
||||
QtAcrylicWinUpdateEvent::QtAcrylicWinUpdateEvent(const bool clearWallpaper) : QEvent(static_cast<QEvent::Type>(QtAcrylicEffectChangeEventId))
|
||||
{
|
||||
m_shouldClearPreviousWallpaper = clearWallpaper;
|
||||
}
|
||||
|
||||
QtAcrylicWinUpdateEvent::~QtAcrylicWinUpdateEvent() = default;
|
||||
|
||||
static QScopedPointer<QtAcrylicWinEventFilter> g_instance;
|
||||
|
||||
QtAcrylicWinEventFilter::QtAcrylicWinEventFilter() = default;
|
||||
|
||||
QtAcrylicWinEventFilter::~QtAcrylicWinEventFilter()
|
||||
{
|
||||
// FIXME
|
||||
//unsetup();
|
||||
}
|
||||
|
||||
void QtAcrylicWinEventFilter::setup()
|
||||
{
|
||||
if (g_instance.isNull()) {
|
||||
g_instance.reset(new QtAcrylicWinEventFilter);
|
||||
qApp->installNativeEventFilter(g_instance.get());
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicWinEventFilter::unsetup()
|
||||
{
|
||||
if (!g_instance.isNull()) {
|
||||
qApp->removeNativeEventFilter(g_instance.get());
|
||||
g_instance.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
bool QtAcrylicWinEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
|
||||
#else
|
||||
bool QtAcrylicWinEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
|
||||
#endif
|
||||
{
|
||||
Q_ASSERT(eventType == "windows_generic_MSG");
|
||||
Q_ASSERT(message);
|
||||
Q_UNUSED(result); // We don't need this parameter.
|
||||
if ((eventType != "windows_generic_MSG") || !message) {
|
||||
return false;
|
||||
}
|
||||
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
|
||||
const auto msg = *reinterpret_cast<MSG **>(message);
|
||||
#else
|
||||
const auto msg = static_cast<LPMSG>(message);
|
||||
#endif
|
||||
bool shouldUpdate = false;
|
||||
bool shouldClearWallpaper = false;
|
||||
switch (msg->message) {
|
||||
case WM_SETTINGCHANGE: {
|
||||
if (msg->wParam == SPI_SETDESKWALLPAPER) {
|
||||
shouldClearWallpaper = true;
|
||||
shouldUpdate = true;
|
||||
}
|
||||
if ((msg->wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(msg->lParam)) == QStringLiteral("ImmersiveColorSet"))) {
|
||||
shouldUpdate = true;
|
||||
}
|
||||
} break;
|
||||
case WM_THEMECHANGED:
|
||||
case WM_DWMCOMPOSITIONCHANGED:
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
||||
case WM_DPICHANGED:
|
||||
shouldUpdate = true;
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
if (shouldUpdate) {
|
||||
const QWindow *window = Utilities::findWindow(reinterpret_cast<WId>(msg->hwnd));
|
||||
if (window) {
|
||||
const QScopedPointer<QEvent> updateEvent(new QtAcrylicWinUpdateEvent(shouldClearWallpaper));
|
||||
QCoreApplication::sendEvent(const_cast<QWindow *>(window), updateEvent.get());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -22,28 +22,41 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
#pragma once
|
||||
|
||||
Button {
|
||||
id: control
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtCore/qcoreevent.h>
|
||||
#include <QtCore/qabstractnativeeventfilter.h>
|
||||
|
||||
implicitWidth: 45
|
||||
implicitHeight: 30
|
||||
class FRAMELESSHELPER_EXPORT QtAcrylicWinUpdateEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
static const int QtAcrylicEffectChangeEventId;
|
||||
|
||||
property bool maximized: false
|
||||
explicit QtAcrylicWinUpdateEvent(const bool clearWallpaper = false);
|
||||
~QtAcrylicWinUpdateEvent() override;
|
||||
|
||||
ToolTip.visible: hovered && !down
|
||||
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
|
||||
ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize")
|
||||
|
||||
contentItem: Image {
|
||||
anchors.fill: control
|
||||
source: maximized ? "qrc:/images/button_restore_white.svg" : "qrc:/images/button_maximize_white.svg"
|
||||
inline bool shouldClearPreviousWallpaper() const
|
||||
{
|
||||
return m_shouldClearPreviousWallpaper;
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
visible: control.down || control.hovered
|
||||
color: control.down ? "#808080" : (control.hovered ? "#c7c7c7" : "transparent")
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool m_shouldClearPreviousWallpaper = false;
|
||||
};
|
||||
|
||||
class FRAMELESSHELPER_EXPORT QtAcrylicWinEventFilter : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
explicit QtAcrylicWinEventFilter();
|
||||
~QtAcrylicWinEventFilter() override;
|
||||
|
||||
static void setup();
|
||||
static void unsetup();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
#else
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
|
||||
#endif
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/QtAcrylicHelper">
|
||||
<file alias="Noise.png">resources/noise.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qtacrylicitem.h"
|
||||
#include <QtQuick/qquickwindow.h>
|
||||
#include "utilities.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtGui/qpainter.h>
|
||||
|
||||
QtAcrylicItem::QtAcrylicItem(QQuickItem *parent) : QQuickPaintedItem(parent)
|
||||
{
|
||||
connect(this, &QtAcrylicItem::windowChanged, this, [this](QQuickWindow *win){
|
||||
m_acrylicHelper.uninstall();
|
||||
if (win) {
|
||||
m_acrylicHelper.install(win);
|
||||
m_acrylicHelper.updateAcrylicBrush();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QtAcrylicItem::~QtAcrylicItem() = default;
|
||||
|
||||
void QtAcrylicItem::paint(QPainter *painter)
|
||||
{
|
||||
const QRect rect = {0, 0, qRound(width()), qRound(height())};
|
||||
m_acrylicHelper.paintWindowBackground(painter, rect);
|
||||
if (m_frameVisible) {
|
||||
m_acrylicHelper.paintWindowFrame(painter, rect);
|
||||
}
|
||||
}
|
||||
|
||||
QColor QtAcrylicItem::tintColor() const
|
||||
{
|
||||
const QColor color = m_acrylicHelper.getTintColor();
|
||||
if (color.isValid() && (color != Qt::transparent)) {
|
||||
return color;
|
||||
} else {
|
||||
return /*palette().color(backgroundRole())*/Qt::white;
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicItem::setTintColor(const QColor &value)
|
||||
{
|
||||
if (!value.isValid()) {
|
||||
qWarning() << "Tint color not valid.";
|
||||
return;
|
||||
}
|
||||
if (m_acrylicHelper.getTintColor() != value) {
|
||||
m_acrylicHelper.setTintColor(value);
|
||||
#if 0
|
||||
QPalette pal = palette();
|
||||
pal.setColor(backgroundRole(), m_acrylicHelper.getTintColor());
|
||||
setPalette(pal);
|
||||
#endif
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
Q_EMIT tintColorChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qreal QtAcrylicItem::tintOpacity() const
|
||||
{
|
||||
return m_acrylicHelper.getTintOpacity();
|
||||
}
|
||||
|
||||
void QtAcrylicItem::setTintOpacity(qreal value)
|
||||
{
|
||||
if (m_acrylicHelper.getTintOpacity() != value) {
|
||||
m_acrylicHelper.setTintOpacity(value);
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
Q_EMIT tintOpacityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qreal QtAcrylicItem::noiseOpacity() const
|
||||
{
|
||||
return m_acrylicHelper.getNoiseOpacity();
|
||||
}
|
||||
|
||||
void QtAcrylicItem::setNoiseOpacity(qreal value)
|
||||
{
|
||||
if (m_acrylicHelper.getNoiseOpacity() != value) {
|
||||
m_acrylicHelper.setNoiseOpacity(value);
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
Q_EMIT noiseOpacityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool QtAcrylicItem::frameVisible() const
|
||||
{
|
||||
return m_frameVisible;
|
||||
}
|
||||
|
||||
void QtAcrylicItem::setFrameVisible(bool value)
|
||||
{
|
||||
if (m_frameVisible != value) {
|
||||
m_frameVisible = value;
|
||||
update();
|
||||
Q_EMIT frameVisibleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QColor QtAcrylicItem::frameColor() const
|
||||
{
|
||||
return m_acrylicHelper.getFrameColor();
|
||||
}
|
||||
|
||||
void QtAcrylicItem::setFrameColor(const QColor &value)
|
||||
{
|
||||
if (m_acrylicHelper.getFrameColor() != value) {
|
||||
m_acrylicHelper.setFrameColor(value);
|
||||
update();
|
||||
Q_EMIT frameColorChanged();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtQuick/qquickpainteditem.h>
|
||||
#include "qtacryliceffecthelper.h"
|
||||
|
||||
class FRAMELESSHELPER_EXPORT QtAcrylicItem : public QQuickPaintedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
QML_NAMED_ELEMENT(AcrylicItem)
|
||||
#endif
|
||||
Q_DISABLE_COPY_MOVE(QtAcrylicItem)
|
||||
Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged)
|
||||
Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged)
|
||||
Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged)
|
||||
Q_PROPERTY(bool frameVisible READ frameVisible WRITE setFrameVisible NOTIFY frameVisibleChanged)
|
||||
Q_PROPERTY(QColor frameColor READ frameColor WRITE setFrameColor NOTIFY frameColorChanged)
|
||||
|
||||
public:
|
||||
explicit QtAcrylicItem(QQuickItem *parent = nullptr);
|
||||
~QtAcrylicItem() override;
|
||||
|
||||
void paint(QPainter *painter) override;
|
||||
|
||||
QColor tintColor() const;
|
||||
void setTintColor(const QColor &value);
|
||||
|
||||
qreal tintOpacity() const;
|
||||
void setTintOpacity(qreal value);
|
||||
|
||||
qreal noiseOpacity() const;
|
||||
void setNoiseOpacity(qreal value);
|
||||
|
||||
bool frameVisible() const;
|
||||
void setFrameVisible(bool value);
|
||||
|
||||
QColor frameColor() const;
|
||||
void setFrameColor(const QColor &value);
|
||||
|
||||
Q_SIGNALS:
|
||||
void tintColorChanged();
|
||||
void tintOpacityChanged();
|
||||
void noiseOpacityChanged();
|
||||
void frameVisibleChanged();
|
||||
void frameColorChanged();
|
||||
|
||||
private:
|
||||
QtAcrylicEffectHelper m_acrylicHelper;
|
||||
bool m_frameVisible = true;
|
||||
};
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qtacrylicwidget.h"
|
||||
#include "utilities.h"
|
||||
#include "framelesswindowsmanager.h"
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtGui/qevent.h>
|
||||
#include <QtGui/qpainter.h>
|
||||
|
||||
QtAcrylicWidget::QtAcrylicWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setAutoFillBackground(false);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
}
|
||||
|
||||
QtAcrylicWidget::~QtAcrylicWidget() = default;
|
||||
|
||||
QColor QtAcrylicWidget::tintColor() const
|
||||
{
|
||||
const QColor color = m_acrylicHelper.getTintColor();
|
||||
if (color.isValid() && (color != Qt::transparent)) {
|
||||
return color;
|
||||
} else {
|
||||
return palette().color(backgroundRole());
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::setTintColor(const QColor &value)
|
||||
{
|
||||
if (!value.isValid()) {
|
||||
qWarning() << "Tint color not valid.";
|
||||
return;
|
||||
}
|
||||
if (m_acrylicHelper.getTintColor() != value) {
|
||||
m_acrylicHelper.setTintColor(value);
|
||||
QPalette pal = palette();
|
||||
pal.setColor(backgroundRole(), m_acrylicHelper.getTintColor());
|
||||
setPalette(pal);
|
||||
//m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
Q_EMIT tintColorChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qreal QtAcrylicWidget::tintOpacity() const
|
||||
{
|
||||
return m_acrylicHelper.getTintOpacity();
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::setTintOpacity(qreal value)
|
||||
{
|
||||
if (m_acrylicHelper.getTintOpacity() != value) {
|
||||
m_acrylicHelper.setTintOpacity(value);
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
Q_EMIT tintOpacityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qreal QtAcrylicWidget::noiseOpacity() const
|
||||
{
|
||||
return m_acrylicHelper.getNoiseOpacity();
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::setNoiseOpacity(qreal value)
|
||||
{
|
||||
if (m_acrylicHelper.getNoiseOpacity() != value) {
|
||||
m_acrylicHelper.setNoiseOpacity(value);
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
Q_EMIT noiseOpacityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool QtAcrylicWidget::frameVisible() const
|
||||
{
|
||||
return m_frameVisible;
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::setFrameVisible(bool value)
|
||||
{
|
||||
if (m_frameVisible != value) {
|
||||
m_frameVisible = value;
|
||||
update();
|
||||
Q_EMIT frameVisibleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QColor QtAcrylicWidget::frameColor() const
|
||||
{
|
||||
return m_acrylicHelper.getFrameColor();
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::setFrameColor(const QColor &value)
|
||||
{
|
||||
if (m_acrylicHelper.getFrameColor() != value) {
|
||||
m_acrylicHelper.setFrameColor(value);
|
||||
update();
|
||||
Q_EMIT frameColorChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::showEvent(QShowEvent *event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
static bool inited = false;
|
||||
if (!inited) {
|
||||
const QWindow *win = windowHandle();
|
||||
FramelessWindowsManager::addWindow(win);
|
||||
Utilities::setAcrylicEffectEnabled(win, true);
|
||||
m_acrylicHelper.install(win);
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
update();
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
m_acrylicHelper.paintWindowBackground(&painter, event->region());
|
||||
if (frameVisible()) {
|
||||
m_acrylicHelper.paintWindowFrame(&painter);
|
||||
}
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void QtAcrylicWidget::changeEvent(QEvent *event)
|
||||
{
|
||||
QWidget::changeEvent(event);
|
||||
switch (event->type()) {
|
||||
case QEvent::PaletteChange:
|
||||
m_acrylicHelper.updateAcrylicBrush(tintColor());
|
||||
break;
|
||||
case QEvent::ActivationChange:
|
||||
case QEvent::WindowStateChange:
|
||||
update();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtWidgets/qwidget.h>
|
||||
#include "qtacryliceffecthelper.h"
|
||||
|
||||
class FRAMELESSHELPER_EXPORT QtAcrylicWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(QtAcrylicWidget)
|
||||
Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged)
|
||||
Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged)
|
||||
Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged)
|
||||
Q_PROPERTY(bool frameVisible READ frameVisible WRITE setFrameVisible NOTIFY frameVisibleChanged)
|
||||
Q_PROPERTY(QColor frameColor READ frameColor WRITE setFrameColor NOTIFY frameColorChanged)
|
||||
|
||||
public:
|
||||
explicit QtAcrylicWidget(QWidget *parent = nullptr);
|
||||
~QtAcrylicWidget() override;
|
||||
|
||||
QColor tintColor() const;
|
||||
void setTintColor(const QColor &value);
|
||||
|
||||
qreal tintOpacity() const;
|
||||
void setTintOpacity(qreal value);
|
||||
|
||||
qreal noiseOpacity() const;
|
||||
void setNoiseOpacity(qreal value);
|
||||
|
||||
bool frameVisible() const;
|
||||
void setFrameVisible(bool value);
|
||||
|
||||
QColor frameColor() const;
|
||||
void setFrameColor(const QColor &value);
|
||||
|
||||
Q_SIGNALS:
|
||||
void tintColorChanged();
|
||||
void tintOpacityChanged();
|
||||
void noiseOpacityChanged();
|
||||
void frameVisibleChanged();
|
||||
void frameColorChanged();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
QtAcrylicEffectHelper m_acrylicHelper;
|
||||
bool m_frameVisible = true;
|
||||
};
|
After Width: | Height: | Size: 266 KiB |
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "utilities.h"
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
#include <QtGui/qscreen.h>
|
||||
#include <QtGui/qpa/qplatformwindow.h>
|
||||
#include <QtGui/qpainter.h>
|
||||
#include <QtGui/private/qmemrotate_p.h>
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
#else
|
||||
#include <QtGui/qpa/qplatformwindow_p.h>
|
||||
#endif
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
#include <QtCore/qoperatingsystemversion.h>
|
||||
#else
|
||||
#include <QtCore/qsysinfo.h>
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(QMargins)
|
||||
|
||||
/*
|
||||
* Copied from https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/effects/qpixmapfilter.cpp
|
||||
* With minor modifications, most of them are format changes.
|
||||
*/
|
||||
|
||||
#ifndef AVG
|
||||
#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
|
||||
#endif
|
||||
|
||||
#ifndef AVG16
|
||||
#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
|
||||
#endif
|
||||
|
||||
template<const int shift>
|
||||
static inline int qt_static_shift(const int value)
|
||||
{
|
||||
if (shift == 0) {
|
||||
return value;
|
||||
} else if (shift > 0) {
|
||||
return value << (uint(shift) & 0x1f);
|
||||
} else {
|
||||
return value >> (uint(-shift) & 0x1f);
|
||||
}
|
||||
}
|
||||
|
||||
template<const int aprec, const int zprec>
|
||||
static inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, const int alpha)
|
||||
{
|
||||
QRgb *pixel = reinterpret_cast<QRgb *>(bptr);
|
||||
#define Z_MASK (0xff << zprec)
|
||||
const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
|
||||
const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
|
||||
const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
|
||||
const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
|
||||
#undef Z_MASK
|
||||
const int zR_zprec = zR >> aprec;
|
||||
const int zG_zprec = zG >> aprec;
|
||||
const int zB_zprec = zB >> aprec;
|
||||
const int zA_zprec = zA >> aprec;
|
||||
zR += alpha * (R_zprec - zR_zprec);
|
||||
zG += alpha * (G_zprec - zG_zprec);
|
||||
zB += alpha * (B_zprec - zB_zprec);
|
||||
zA += alpha * (A_zprec - zA_zprec);
|
||||
#define ZA_MASK (0xff << (zprec + aprec))
|
||||
*pixel =
|
||||
qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
|
||||
| qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
|
||||
| qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
|
||||
| qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
|
||||
#undef ZA_MASK
|
||||
}
|
||||
|
||||
static const int alphaIndex = ((QSysInfo::ByteOrder == QSysInfo::BigEndian) ? 0 : 3);
|
||||
|
||||
template<const int aprec, const int zprec>
|
||||
static inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, const int alpha)
|
||||
{
|
||||
const int A_zprec = int(*(bptr)) << zprec;
|
||||
const int z_zprec = z >> aprec;
|
||||
z += alpha * (A_zprec - z_zprec);
|
||||
*(bptr) = z >> (zprec + aprec);
|
||||
}
|
||||
|
||||
template<const int aprec, const int zprec, const bool alphaOnly>
|
||||
static inline void qt_blurrow(QImage &im, const int line, const int alpha)
|
||||
{
|
||||
uchar *bptr = im.scanLine(line);
|
||||
int zR = 0, zG = 0, zB = 0, zA = 0;
|
||||
if (alphaOnly && (im.format() != QImage::Format_Indexed8)) {
|
||||
bptr += alphaIndex;
|
||||
}
|
||||
const int stride = im.depth() >> 3;
|
||||
const int im_width = im.width();
|
||||
for (int index = 0; index < im_width; ++index) {
|
||||
if (alphaOnly) {
|
||||
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
|
||||
} else {
|
||||
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
|
||||
}
|
||||
bptr += stride;
|
||||
}
|
||||
bptr -= stride;
|
||||
for (int index = im_width - 2; index >= 0; --index) {
|
||||
bptr -= stride;
|
||||
if (alphaOnly) {
|
||||
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
|
||||
} else {
|
||||
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* expblur(QImage &img, const qreal radius)
|
||||
*
|
||||
* Based on exponential blur algorithm by Jani Huhtanen
|
||||
*
|
||||
* In-place blur of image 'img' with kernel
|
||||
* of approximate radius 'radius'.
|
||||
*
|
||||
* Blurs with two sided exponential impulse
|
||||
* response.
|
||||
*
|
||||
* aprec = precision of alpha parameter
|
||||
* in fixed-point format 0.aprec
|
||||
*
|
||||
* zprec = precision of state parameters
|
||||
* zR,zG,zB and zA in fp format 8.zprec
|
||||
*/
|
||||
template<const int aprec, const int zprec, const bool alphaOnly>
|
||||
static inline void expblur(QImage &img, const qreal radius, const bool improvedQuality = false, const int transposed = 0)
|
||||
{
|
||||
qreal _radius = radius;
|
||||
// halve the radius if we're using two passes
|
||||
if (improvedQuality) {
|
||||
_radius *= 0.5;
|
||||
}
|
||||
Q_ASSERT((img.format() == QImage::Format_ARGB32_Premultiplied)
|
||||
|| (img.format() == QImage::Format_RGB32)
|
||||
|| (img.format() == QImage::Format_Indexed8)
|
||||
|| (img.format() == QImage::Format_Grayscale8));
|
||||
// choose the alpha such that pixels at radius distance from a fully
|
||||
// saturated pixel will have an alpha component of no greater than
|
||||
// the cutOffIntensity
|
||||
const qreal cutOffIntensity = 2;
|
||||
const int alpha = _radius <= qreal(1e-5)
|
||||
? ((1 << aprec)-1)
|
||||
: qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / _radius)));
|
||||
int img_height = img.height();
|
||||
for (int row = 0; row < img_height; ++row) {
|
||||
for (int i = 0; i <= int(improvedQuality); ++i) {
|
||||
qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
|
||||
}
|
||||
}
|
||||
QImage temp(img.height(), img.width(), img.format());
|
||||
temp.setDevicePixelRatio(img.devicePixelRatio());
|
||||
if (transposed >= 0) {
|
||||
if (img.depth() == 8) {
|
||||
qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
|
||||
img.width(), img.height(), img.bytesPerLine(),
|
||||
reinterpret_cast<quint8*>(temp.bits()),
|
||||
temp.bytesPerLine());
|
||||
} else {
|
||||
qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
|
||||
img.width(), img.height(), img.bytesPerLine(),
|
||||
reinterpret_cast<quint32*>(temp.bits()),
|
||||
temp.bytesPerLine());
|
||||
}
|
||||
} else {
|
||||
if (img.depth() == 8) {
|
||||
qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
|
||||
img.width(), img.height(), img.bytesPerLine(),
|
||||
reinterpret_cast<quint8*>(temp.bits()),
|
||||
temp.bytesPerLine());
|
||||
} else {
|
||||
qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
|
||||
img.width(), img.height(), img.bytesPerLine(),
|
||||
reinterpret_cast<quint32*>(temp.bits()),
|
||||
temp.bytesPerLine());
|
||||
}
|
||||
}
|
||||
img_height = temp.height();
|
||||
for (int row = 0; row < img_height; ++row) {
|
||||
for (int i = 0; i <= int(improvedQuality); ++i) {
|
||||
qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
|
||||
}
|
||||
}
|
||||
if (transposed == 0) {
|
||||
if (img.depth() == 8) {
|
||||
qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
|
||||
temp.width(), temp.height(), temp.bytesPerLine(),
|
||||
reinterpret_cast<quint8*>(img.bits()),
|
||||
img.bytesPerLine());
|
||||
} else {
|
||||
qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
|
||||
temp.width(), temp.height(), temp.bytesPerLine(),
|
||||
reinterpret_cast<quint32*>(img.bits()),
|
||||
img.bytesPerLine());
|
||||
}
|
||||
} else {
|
||||
img = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static inline QImage qt_halfScaled(const QImage &source)
|
||||
{
|
||||
if (source.width() < 2 || source.height() < 2) {
|
||||
return {};
|
||||
}
|
||||
QImage srcImage = source;
|
||||
if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
|
||||
// assumes grayscale
|
||||
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
||||
dest.setDevicePixelRatio(source.devicePixelRatio());
|
||||
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
|
||||
qsizetype sx = srcImage.bytesPerLine();
|
||||
qsizetype sx2 = sx << 1;
|
||||
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
|
||||
qsizetype dx = dest.bytesPerLine();
|
||||
int ww = dest.width();
|
||||
int hh = dest.height();
|
||||
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
||||
const uchar *p1 = src;
|
||||
const uchar *p2 = src + sx;
|
||||
uchar *q = dst;
|
||||
for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2) {
|
||||
*q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
} else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
|
||||
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
||||
dest.setDevicePixelRatio(source.devicePixelRatio());
|
||||
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
|
||||
qsizetype sx = srcImage.bytesPerLine();
|
||||
qsizetype sx2 = sx << 1;
|
||||
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
|
||||
qsizetype dx = dest.bytesPerLine();
|
||||
int ww = dest.width();
|
||||
int hh = dest.height();
|
||||
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
||||
const uchar *p1 = src;
|
||||
const uchar *p2 = src + sx;
|
||||
uchar *q = dst;
|
||||
for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
|
||||
// alpha
|
||||
q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
|
||||
// rgb
|
||||
const quint16 p16_1 = (p1[2] << 8) | p1[1];
|
||||
const quint16 p16_2 = (p1[5] << 8) | p1[4];
|
||||
const quint16 p16_3 = (p2[2] << 8) | p2[1];
|
||||
const quint16 p16_4 = (p2[5] << 8) | p2[4];
|
||||
const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
|
||||
q[1] = result & 0xff;
|
||||
q[2] = result >> 8;
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
} else if ((source.format() != QImage::Format_ARGB32_Premultiplied) && (source.format() != QImage::Format_RGB32)) {
|
||||
srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
||||
dest.setDevicePixelRatio(source.devicePixelRatio());
|
||||
const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
|
||||
qsizetype sx = srcImage.bytesPerLine() >> 2;
|
||||
qsizetype sx2 = sx << 1;
|
||||
quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
|
||||
qsizetype dx = dest.bytesPerLine() >> 2;
|
||||
int ww = dest.width();
|
||||
int hh = dest.height();
|
||||
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
||||
const quint32 *p1 = src;
|
||||
const quint32 *p2 = src + sx;
|
||||
quint32 *q = dst;
|
||||
for (int x = ww; x; --x, q++, p1 += 2, p2 += 2) {
|
||||
*q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
void Utilities::blurImage(QPainter *painter, QImage &blurImage, const qreal radius, const bool quality, const bool alphaOnly, const int transposed)
|
||||
{
|
||||
if ((blurImage.format() != QImage::Format_ARGB32_Premultiplied) && (blurImage.format() != QImage::Format_RGB32)) {
|
||||
blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
qreal _radius = radius;
|
||||
qreal scale = 1;
|
||||
if ((_radius >= 4) && (blurImage.width() >= 2) && (blurImage.height() >= 2)) {
|
||||
blurImage = qt_halfScaled(blurImage);
|
||||
scale = 2;
|
||||
_radius *= 0.5;
|
||||
}
|
||||
if (alphaOnly) {
|
||||
expblur<12, 10, true>(blurImage, _radius, quality, transposed);
|
||||
} else {
|
||||
expblur<12, 10, false>(blurImage, _radius, quality, transposed);
|
||||
}
|
||||
if (painter) {
|
||||
painter->scale(scale, scale);
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
painter->drawImage(QRect{QPoint{0, 0}, blurImage.size() / blurImage.devicePixelRatio()}, blurImage);
|
||||
}
|
||||
}
|
||||
|
||||
void Utilities::blurImage(QImage &blurImage, const qreal radius, const bool quality, const int transposed)
|
||||
{
|
||||
if ((blurImage.format() == QImage::Format_Indexed8) || (blurImage.format() == QImage::Format_Grayscale8)) {
|
||||
expblur<12, 10, true>(blurImage, radius, quality, transposed);
|
||||
} else {
|
||||
expblur<12, 10, false>(blurImage, radius, quality, transposed);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Copied from https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/styles/qstyle.cpp
|
||||
* With minor modifications, most of them are format changes.
|
||||
*/
|
||||
|
||||
static inline Qt::Alignment visualAlignment(const Qt::LayoutDirection direction, const Qt::Alignment alignment)
|
||||
{
|
||||
return QGuiApplicationPrivate::visualAlignment(direction, alignment);
|
||||
}
|
||||
|
||||
QRect Utilities::alignedRect(const Qt::LayoutDirection direction, const Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
|
||||
{
|
||||
const Qt::Alignment align = visualAlignment(direction, alignment);
|
||||
int x = rectangle.x();
|
||||
int y = rectangle.y();
|
||||
const int w = size.width();
|
||||
const int h = size.height();
|
||||
if ((align & Qt::AlignVCenter) == Qt::AlignVCenter) {
|
||||
y += rectangle.size().height() / 2 - h / 2;
|
||||
} else if ((align & Qt::AlignBottom) == Qt::AlignBottom) {
|
||||
y += rectangle.size().height() - h;
|
||||
}
|
||||
if ((align & Qt::AlignRight) == Qt::AlignRight) {
|
||||
x += rectangle.size().width() - w;
|
||||
} else if ((align & Qt::AlignHCenter) == Qt::AlignHCenter) {
|
||||
x += rectangle.size().width() / 2 - w / 2;
|
||||
}
|
||||
return {x, y, w, h};
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool Utilities::isWin7OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows7;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS7;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utilities::isWin8OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utilities::isWin8Point1OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8_1;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utilities::isWin10OrGreater()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
|
||||
#else
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utilities::isWin10OrGreater(const int subVer)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, subVer);
|
||||
#else
|
||||
Q_UNUSED(ver);
|
||||
return QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Utilities::isMSWin10AcrylicEffectAvailable()
|
||||
{
|
||||
if (!isWin10OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
const QOperatingSystemVersion currentVersion = QOperatingSystemVersion::current();
|
||||
if (currentVersion > QOperatingSystemVersion::Windows10) {
|
||||
return true;
|
||||
}
|
||||
return ((currentVersion.microVersion() >= 16190) && (currentVersion.microVersion() < 18362));
|
||||
#else
|
||||
// TODO
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
QWindow *Utilities::findWindow(const WId winId)
|
||||
{
|
||||
Q_ASSERT(winId);
|
||||
if (!winId) {
|
||||
return nullptr;
|
||||
}
|
||||
const QWindowList windows = QGuiApplication::topLevelWindows();
|
||||
for (auto &&window : qAsConst(windows)) {
|
||||
if (window && window->handle()) {
|
||||
if (window->winId() == winId) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Utilities::isAcrylicEffectSupported()
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
return isWin10OrGreater() || (QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXYosemite);
|
||||
#else
|
||||
// TODO
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Utilities::updateQtFrameMargins(QWindow *window, const bool enable)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
const int tbh = enable ? Utilities::getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, true, true) : 0;
|
||||
const QMargins margins = {0, -tbh, 0, 0};
|
||||
const QVariant marginsVar = QVariant::fromValue(margins);
|
||||
window->setProperty("_q_windowsCustomMargins", marginsVar);
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QPlatformWindow *platformWindow = window->handle();
|
||||
if (platformWindow) {
|
||||
QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow, QStringLiteral("WindowsCustomMargins"), marginsVar);
|
||||
}
|
||||
#else
|
||||
auto *platformWindow = dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(
|
||||
window->handle());
|
||||
if (platformWindow) {
|
||||
platformWindow->setCustomMargins(margins);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QRect Utilities::getScreenAvailableGeometry()
|
||||
{
|
||||
return QGuiApplication::primaryScreen()->availableGeometry();
|
||||
}
|
||||
|
||||
QColor Utilities::getNativeWindowFrameColor(const bool isActive)
|
||||
{
|
||||
if (!isActive) {
|
||||
return Qt::darkGray;
|
||||
}
|
||||
// TODO: what about Linux and macOS?
|
||||
return isWin10OrGreater() ? getColorizationColor() : Qt::black;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtGui/qcolor.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
|
||||
namespace Utilities {
|
||||
|
||||
enum class SystemMetric
|
||||
{
|
||||
BorderWidth,
|
||||
BorderHeight,
|
||||
TitleBarHeight
|
||||
};
|
||||
|
||||
enum class DesktopWallpaperAspectStyle
|
||||
{
|
||||
Central,
|
||||
Tiled,
|
||||
IgnoreRatio,
|
||||
KeepRatio,
|
||||
KeepRatioByExpanding
|
||||
};
|
||||
|
||||
FRAMELESSHELPER_EXPORT bool isWin7OrGreater();
|
||||
FRAMELESSHELPER_EXPORT bool isWin8OrGreater();
|
||||
FRAMELESSHELPER_EXPORT bool isWin8Point1OrGreater();
|
||||
FRAMELESSHELPER_EXPORT bool isWin10OrGreater();
|
||||
FRAMELESSHELPER_EXPORT bool isWin10OrGreater(const int subVer);
|
||||
FRAMELESSHELPER_EXPORT bool isMSWin10AcrylicEffectAvailable();
|
||||
FRAMELESSHELPER_EXPORT bool isBlurEffectSupported();
|
||||
FRAMELESSHELPER_EXPORT bool isAcrylicEffectSupported();
|
||||
FRAMELESSHELPER_EXPORT int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue = false);
|
||||
FRAMELESSHELPER_EXPORT bool setAcrylicEffectEnabled(const QWindow *window, const bool enabled, const QColor &gradientColor = {});
|
||||
FRAMELESSHELPER_EXPORT bool isColorizationEnabled();
|
||||
FRAMELESSHELPER_EXPORT QColor getColorizationColor();
|
||||
FRAMELESSHELPER_EXPORT bool isLightThemeEnabled();
|
||||
FRAMELESSHELPER_EXPORT bool isDarkThemeEnabled();
|
||||
FRAMELESSHELPER_EXPORT bool isHighContrastModeEnabled();
|
||||
FRAMELESSHELPER_EXPORT bool isDarkFrameEnabled(const QWindow *window);
|
||||
FRAMELESSHELPER_EXPORT bool isTransparencyEffectEnabled();
|
||||
FRAMELESSHELPER_EXPORT QWindow *findWindow(const WId winId);
|
||||
FRAMELESSHELPER_EXPORT void triggerFrameChange(const QWindow *window);
|
||||
FRAMELESSHELPER_EXPORT void updateFrameMargins(const QWindow *window, const bool reset);
|
||||
FRAMELESSHELPER_EXPORT void updateQtFrameMargins(QWindow *window, const bool enable);
|
||||
FRAMELESSHELPER_EXPORT QImage getDesktopWallpaperImage(const int screen = -1);
|
||||
FRAMELESSHELPER_EXPORT DesktopWallpaperAspectStyle getDesktopWallpaperAspectStyle(const int screen = -1);
|
||||
FRAMELESSHELPER_EXPORT quint32 getWindowDpi(const QWindow *window);
|
||||
FRAMELESSHELPER_EXPORT QMargins getWindowNativeFrameMargins(const QWindow *window);
|
||||
FRAMELESSHELPER_EXPORT QRect getScreenAvailableGeometry();
|
||||
FRAMELESSHELPER_EXPORT QRect alignedRect(const Qt::LayoutDirection direction, const Qt::Alignment alignment, const QSize &size, const QRect &rectangle);
|
||||
FRAMELESSHELPER_EXPORT void blurImage(QImage &blurImage, const qreal radius, const bool quality, const int transposed = 0);
|
||||
FRAMELESSHELPER_EXPORT void blurImage(QPainter *painter, QImage &blurImage, const qreal radius, const bool quality, const bool alphaOnly, const int transposed = 0);
|
||||
FRAMELESSHELPER_EXPORT QColor getNativeWindowFrameColor(const bool isActive = true);
|
||||
|
||||
}
|
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "utilities.h"
|
||||
#include <QtCore/qsettings.h>
|
||||
#include <QtCore/qlibrary.h>
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <dwmapi.h>
|
||||
|
||||
#ifndef USER_DEFAULT_SCREEN_DPI
|
||||
// Only available since Windows Vista
|
||||
#define USER_DEFAULT_SCREEN_DPI 96
|
||||
#endif
|
||||
|
||||
#ifndef SM_CXPADDEDBORDER
|
||||
// Only available since Windows Vista
|
||||
#define SM_CXPADDEDBORDER 92
|
||||
#endif
|
||||
|
||||
enum : WORD
|
||||
{
|
||||
DwmwaUseImmersiveDarkMode = 20,
|
||||
DwmwaUseImmersiveDarkModeBefore20h1 = 19
|
||||
};
|
||||
|
||||
using WINDOWCOMPOSITIONATTRIB = enum _WINDOWCOMPOSITIONATTRIB
|
||||
{
|
||||
WCA_ACCENT_POLICY = 19
|
||||
};
|
||||
|
||||
using WINDOWCOMPOSITIONATTRIBDATA = struct _WINDOWCOMPOSITIONATTRIBDATA
|
||||
{
|
||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
||||
PVOID pvData;
|
||||
SIZE_T cbData;
|
||||
};
|
||||
|
||||
using ACCENT_STATE = enum _ACCENT_STATE
|
||||
{
|
||||
ACCENT_DISABLED = 0,
|
||||
ACCENT_ENABLE_GRADIENT = 1,
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
||||
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
|
||||
ACCENT_INVALID_STATE = 5
|
||||
};
|
||||
|
||||
using ACCENT_POLICY = struct _ACCENT_POLICY
|
||||
{
|
||||
ACCENT_STATE AccentState;
|
||||
DWORD AccentFlags;
|
||||
COLORREF GradientColor;
|
||||
DWORD AnimationId;
|
||||
};
|
||||
|
||||
using IMMERSIVE_HC_CACHE_MODE = enum _IMMERSIVE_HC_CACHE_MODE
|
||||
{
|
||||
IHCM_USE_CACHED_VALUE,
|
||||
IHCM_REFRESH
|
||||
};
|
||||
|
||||
using PREFERRED_APP_MODE = enum _PREFERRED_APP_MODE
|
||||
{
|
||||
Default,
|
||||
AllowDark,
|
||||
ForceDark,
|
||||
ForceLight,
|
||||
Max
|
||||
};
|
||||
|
||||
static const QString g_dwmRegistryKey = QStringLiteral(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)");
|
||||
static const QString g_personalizeRegistryKey = QStringLiteral(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
|
||||
|
||||
// The standard values of border width, border height and title bar height when DPI is 96.
|
||||
static const int g_defaultBorderWidth = 8, g_defaultBorderHeight = 8, g_defaultTitleBarHeight = 31;
|
||||
|
||||
using MONITOR_DPI_TYPE = enum _MONITOR_DPI_TYPE
|
||||
{
|
||||
MDT_EFFECTIVE_DPI = 0
|
||||
};
|
||||
|
||||
using PROCESS_DPI_AWARENESS = enum _PROCESS_DPI_AWARENESS
|
||||
{
|
||||
PROCESS_DPI_UNAWARE = 0,
|
||||
PROCESS_SYSTEM_DPI_AWARE = 1,
|
||||
PROCESS_PER_MONITOR_DPI_AWARE = 2
|
||||
};
|
||||
|
||||
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, WINDOWCOMPOSITIONATTRIBDATA *);
|
||||
using ShouldAppsUseDarkModePtr = BOOL(WINAPI *)();
|
||||
using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL);
|
||||
using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL);
|
||||
using IsDarkModeAllowedForWindowPtr = BOOL(WINAPI *)(HWND);
|
||||
using GetIsImmersiveColorUsingHighContrastPtr = BOOL(WINAPI *)(IMMERSIVE_HC_CACHE_MODE);
|
||||
using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)();
|
||||
using ShouldSystemUseDarkModePtr = BOOL(WINAPI *)();
|
||||
using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE);
|
||||
using IsDarkModeAllowedForAppPtr = BOOL(WINAPI *)();
|
||||
|
||||
using GetDpiForMonitorPtr = HRESULT(WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
|
||||
using GetProcessDpiAwarenessPtr = HRESULT(WINAPI *)(HANDLE, PROCESS_DPI_AWARENESS *);
|
||||
using GetSystemDpiForProcessPtr = UINT(WINAPI *)(HANDLE);
|
||||
using GetDpiForWindowPtr = UINT(WINAPI *)(HWND);
|
||||
using GetDpiForSystemPtr = UINT(WINAPI *)();
|
||||
using GetSystemMetricsForDpiPtr = int(WINAPI *)(int, UINT);
|
||||
using AdjustWindowRectExForDpiPtr = BOOL(WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT);
|
||||
|
||||
using Win32Data = struct _FLH_UTILITIES_WIN32_DATA
|
||||
{
|
||||
_FLH_UTILITIES_WIN32_DATA() { load(); }
|
||||
|
||||
SetWindowCompositionAttributePtr SetWindowCompositionAttributePFN = nullptr;
|
||||
ShouldAppsUseDarkModePtr ShouldAppsUseDarkModePFN = nullptr;
|
||||
AllowDarkModeForWindowPtr AllowDarkModeForWindowPFN = nullptr;
|
||||
AllowDarkModeForAppPtr AllowDarkModeForAppPFN = nullptr;
|
||||
IsDarkModeAllowedForWindowPtr IsDarkModeAllowedForWindowPFN = nullptr;
|
||||
GetIsImmersiveColorUsingHighContrastPtr GetIsImmersiveColorUsingHighContrastPFN = nullptr;
|
||||
RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyStatePFN = nullptr;
|
||||
ShouldSystemUseDarkModePtr ShouldSystemUseDarkModePFN = nullptr;
|
||||
SetPreferredAppModePtr SetPreferredAppModePFN = nullptr;
|
||||
IsDarkModeAllowedForAppPtr IsDarkModeAllowedForAppPFN = nullptr;
|
||||
|
||||
GetDpiForMonitorPtr GetDpiForMonitorPFN = nullptr;
|
||||
GetProcessDpiAwarenessPtr GetProcessDpiAwarenessPFN = nullptr;
|
||||
GetSystemDpiForProcessPtr GetSystemDpiForProcessPFN = nullptr;
|
||||
GetDpiForWindowPtr GetDpiForWindowPFN = nullptr;
|
||||
GetDpiForSystemPtr GetDpiForSystemPFN = nullptr;
|
||||
GetSystemMetricsForDpiPtr GetSystemMetricsForDpiPFN = nullptr;
|
||||
AdjustWindowRectExForDpiPtr AdjustWindowRectExForDpiPFN = nullptr;
|
||||
|
||||
void load()
|
||||
{
|
||||
QLibrary User32Dll(QStringLiteral("User32"));
|
||||
SetWindowCompositionAttributePFN = reinterpret_cast<SetWindowCompositionAttributePtr>(User32Dll.resolve("SetWindowCompositionAttribute"));
|
||||
GetDpiForWindowPFN = reinterpret_cast<GetDpiForWindowPtr>(User32Dll.resolve("GetDpiForWindow"));
|
||||
GetDpiForSystemPFN = reinterpret_cast<GetDpiForSystemPtr>(User32Dll.resolve("GetDpiForSystem"));
|
||||
GetSystemMetricsForDpiPFN = reinterpret_cast<GetSystemMetricsForDpiPtr>(User32Dll.resolve("GetSystemMetricsForDpi"));
|
||||
AdjustWindowRectExForDpiPFN = reinterpret_cast<AdjustWindowRectExForDpiPtr>(User32Dll.resolve("AdjustWindowRectExForDpi"));
|
||||
GetSystemDpiForProcessPFN = reinterpret_cast<GetSystemDpiForProcessPtr>(User32Dll.resolve("GetSystemDpiForProcess"));
|
||||
|
||||
QLibrary UxThemeDll(QStringLiteral("UxTheme"));
|
||||
ShouldAppsUseDarkModePFN = reinterpret_cast<ShouldAppsUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(132)));
|
||||
AllowDarkModeForWindowPFN = reinterpret_cast<AllowDarkModeForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(133)));
|
||||
AllowDarkModeForAppPFN = reinterpret_cast<AllowDarkModeForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
|
||||
RefreshImmersiveColorPolicyStatePFN = reinterpret_cast<RefreshImmersiveColorPolicyStatePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(104)));
|
||||
IsDarkModeAllowedForWindowPFN = reinterpret_cast<IsDarkModeAllowedForWindowPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(137)));
|
||||
GetIsImmersiveColorUsingHighContrastPFN = reinterpret_cast<GetIsImmersiveColorUsingHighContrastPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(106)));
|
||||
ShouldSystemUseDarkModePFN = reinterpret_cast<ShouldSystemUseDarkModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(138)));
|
||||
SetPreferredAppModePFN = reinterpret_cast<SetPreferredAppModePtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(135)));
|
||||
IsDarkModeAllowedForAppPFN = reinterpret_cast<IsDarkModeAllowedForAppPtr>(UxThemeDll.resolve(MAKEINTRESOURCEA(139)));
|
||||
|
||||
QLibrary SHCoreDll(QStringLiteral("SHCore"));
|
||||
GetDpiForMonitorPFN = reinterpret_cast<GetDpiForMonitorPtr>(SHCoreDll.resolve("GetDpiForMonitor"));
|
||||
GetProcessDpiAwarenessPFN = reinterpret_cast<GetProcessDpiAwarenessPtr>(SHCoreDll.resolve("GetProcessDpiAwareness"));
|
||||
}
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(Win32Data, win32Data)
|
||||
|
||||
static const char envVarForceAcrylic[] = "WNEF_FORCE_ACRYLIC_ON_WIN10";
|
||||
|
||||
static inline bool forceEnableAcrylicOnWin10()
|
||||
{
|
||||
return qEnvironmentVariableIsSet(envVarForceAcrylic);
|
||||
}
|
||||
|
||||
bool Utilities::isBlurEffectSupported()
|
||||
{
|
||||
if (isWin8OrGreater()) {
|
||||
return true;
|
||||
}
|
||||
BOOL enabled = FALSE;
|
||||
if (FAILED(DwmIsCompositionEnabled(&enabled))) {
|
||||
qWarning() << "DwmIsCompositionEnabled failed.";
|
||||
return false;
|
||||
}
|
||||
return isWin7OrGreater() && (enabled == TRUE);
|
||||
}
|
||||
|
||||
int Utilities::getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
const qreal dpr = dpiAware ? window->devicePixelRatio() : 1.0;
|
||||
const auto getSystemMetricsForWindow = [dpr](const int index, const bool dpiAware) -> int {
|
||||
if (win32Data()->GetSystemMetricsForDpiPFN) {
|
||||
const quint32 dpi = dpiAware ? qRound(USER_DEFAULT_SCREEN_DPI * dpr) : USER_DEFAULT_SCREEN_DPI;
|
||||
return win32Data()->GetSystemMetricsForDpiPFN(index, dpi);
|
||||
} else {
|
||||
const int value = GetSystemMetrics(index);
|
||||
return dpiAware ? value : qRound(value / dpr);
|
||||
}
|
||||
};
|
||||
int ret = 0;
|
||||
switch (metric) {
|
||||
case SystemMetric::BorderWidth: {
|
||||
const int bw = window->property(_flh_borderWidth_flag).toInt();
|
||||
if ((bw > 0) && !forceSystemValue) {
|
||||
ret = qRound(bw * dpr);
|
||||
} else {
|
||||
const int result_nondpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, false)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
|
||||
const int result_dpi = getSystemMetricsForWindow(SM_CXSIZEFRAME, true)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
|
||||
const int result = dpiAware ? result_dpi : result_nondpi;
|
||||
ret = result > 0 ? result : qRound(g_defaultBorderWidth * dpr);
|
||||
}
|
||||
} break;
|
||||
case SystemMetric::BorderHeight: {
|
||||
const int bh = window->property(_flh_borderHeight_flag).toInt();
|
||||
if ((bh > 0) && !forceSystemValue) {
|
||||
ret = qRound(bh * dpr);
|
||||
} else {
|
||||
const int result_nondpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, false)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, false);
|
||||
const int result_dpi = getSystemMetricsForWindow(SM_CYSIZEFRAME, true)
|
||||
+ getSystemMetricsForWindow(SM_CXPADDEDBORDER, true);
|
||||
const int result = dpiAware ? result_dpi : result_nondpi;
|
||||
ret = result > 0 ? result : qRound(g_defaultBorderHeight * dpr);
|
||||
}
|
||||
} break;
|
||||
case SystemMetric::TitleBarHeight: {
|
||||
const int tbh = window->property(_flh_titleBarHeight_flag).toInt();
|
||||
if ((tbh > 0) && !forceSystemValue) {
|
||||
// Special case: this is the user defined value,
|
||||
// don't change it and just return it untouched.
|
||||
return qRound(tbh * dpr);
|
||||
} else {
|
||||
const int result_nondpi = getSystemMetricsForWindow(SM_CYCAPTION, false);
|
||||
const int result_dpi = getSystemMetricsForWindow(SM_CYCAPTION, true);
|
||||
const int result = dpiAware ? result_dpi : result_nondpi;
|
||||
ret = result > 0 ? result : qRound(g_defaultTitleBarHeight * dpr);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
// When dpr = 1.0 (DPI = 96):
|
||||
// SM_CXSIZEFRAME = SM_CYSIZEFRAME = 4px
|
||||
// SM_CXPADDEDBORDER = 4px
|
||||
// SM_CYCAPTION = 23px
|
||||
// Border Width = Border Height = SM_C(X|Y)SIZEFRAME + SM_CXPADDEDBORDER = 8px
|
||||
// Title Bar Height = Border Height + SM_CYCAPTION = 31px
|
||||
// dpr = 1.25 --> Title Bar Height = 38px
|
||||
// dpr = 1.5 --> Title Bar Height = 45px
|
||||
// dpr = 1.75 --> Title Bar Height = 51px
|
||||
ret += (metric == SystemMetric::TitleBarHeight) ? getSystemMetric(window, SystemMetric::BorderHeight, dpiAware) : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Utilities::setAcrylicEffectEnabled(const QWindow *window, const bool enabled, const QColor &gradientColor)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
bool result = false;
|
||||
const auto hwnd = reinterpret_cast<HWND>(window->winId());
|
||||
// We prefer DwmEnableBlurBehindWindow on Windows 7.
|
||||
if (isWin8OrGreater() && win32Data()->SetWindowCompositionAttributePFN) {
|
||||
ACCENT_POLICY accentPolicy;
|
||||
SecureZeroMemory(&accentPolicy, sizeof(accentPolicy));
|
||||
WINDOWCOMPOSITIONATTRIBDATA wcaData;
|
||||
SecureZeroMemory(&wcaData, sizeof(wcaData));
|
||||
wcaData.Attrib = WCA_ACCENT_POLICY;
|
||||
wcaData.pvData = &accentPolicy;
|
||||
wcaData.cbData = sizeof(accentPolicy);
|
||||
if (enabled) {
|
||||
// The gradient color must be set otherwise it'll look like a classic blur.
|
||||
// Use semi-transparent gradient color to get better appearance.
|
||||
if (gradientColor.isValid()) {
|
||||
accentPolicy.GradientColor = qRgba(gradientColor.blue(), gradientColor.green(), gradientColor.red(), gradientColor.alpha());
|
||||
} else {
|
||||
const QColor colorizationColor = getColorizationColor();
|
||||
accentPolicy.GradientColor =
|
||||
RGB(qRound(colorizationColor.red() * (colorizationColor.alpha() / 255.0) + 255 - colorizationColor.alpha()),
|
||||
qRound(colorizationColor.green() * (colorizationColor.alpha() / 255.0) + 255 - colorizationColor.alpha()),
|
||||
qRound(colorizationColor.blue() * (colorizationColor.alpha() / 255.0) + 255 - colorizationColor.alpha()));
|
||||
}
|
||||
if (isMSWin10AcrylicEffectAvailable() || (isWin10OrGreater() && forceEnableAcrylicOnWin10())) {
|
||||
accentPolicy.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
|
||||
if (!gradientColor.isValid()) {
|
||||
accentPolicy.GradientColor = 0x01FFFFFF;
|
||||
}
|
||||
} else {
|
||||
accentPolicy.AccentState = ACCENT_ENABLE_BLURBEHIND;
|
||||
}
|
||||
} else {
|
||||
accentPolicy.AccentState = ACCENT_DISABLED;
|
||||
}
|
||||
result = win32Data()->SetWindowCompositionAttributePFN(hwnd, &wcaData) == TRUE;
|
||||
if (!result) {
|
||||
qWarning() << "SetWindowCompositionAttribute failed.";
|
||||
}
|
||||
} else {
|
||||
DWM_BLURBEHIND dwmBB;
|
||||
SecureZeroMemory(&dwmBB, sizeof(dwmBB));
|
||||
dwmBB.dwFlags = DWM_BB_ENABLE;
|
||||
dwmBB.fEnable = enabled ? TRUE : FALSE;
|
||||
result = SUCCEEDED(DwmEnableBlurBehindWindow(hwnd, &dwmBB));
|
||||
if (!result) {
|
||||
qWarning() << "DwmEnableBlurBehindWindow failed.";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Utilities::isColorizationEnabled()
|
||||
{
|
||||
if (!isWin10OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
bool ok = false;
|
||||
const QSettings registry(g_dwmRegistryKey, QSettings::NativeFormat);
|
||||
const bool colorPrevalence
|
||||
= registry.value(QStringLiteral("ColorPrevalence"), 0).toULongLong(&ok) != 0;
|
||||
return (ok && colorPrevalence);
|
||||
}
|
||||
|
||||
QColor Utilities::getColorizationColor()
|
||||
{
|
||||
DWORD color = 0;
|
||||
BOOL opaqueBlend = FALSE;
|
||||
if (FAILED(DwmGetColorizationColor(&color, &opaqueBlend))) {
|
||||
qWarning() << "DwmGetColorizationColor failed.";
|
||||
return Qt::darkGray;
|
||||
}
|
||||
return QColor::fromRgba(color);
|
||||
}
|
||||
|
||||
bool Utilities::isLightThemeEnabled()
|
||||
{
|
||||
return !isDarkThemeEnabled();
|
||||
}
|
||||
|
||||
bool Utilities::isDarkThemeEnabled()
|
||||
{
|
||||
return win32Data()->ShouldSystemUseDarkModePFN ? win32Data()->ShouldSystemUseDarkModePFN() : false;
|
||||
}
|
||||
|
||||
bool Utilities::isHighContrastModeEnabled()
|
||||
{
|
||||
HIGHCONTRASTW hc;
|
||||
SecureZeroMemory(&hc, sizeof(hc));
|
||||
hc.cbSize = sizeof(hc);
|
||||
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, 0, &hc, 0) == FALSE) {
|
||||
qWarning() << "SystemParametersInfoW failed.";
|
||||
return false;
|
||||
}
|
||||
return hc.dwFlags & HCF_HIGHCONTRASTON;
|
||||
}
|
||||
|
||||
bool Utilities::isDarkFrameEnabled(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
if (!isWin10OrGreater(17763)) {
|
||||
return false;
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(window->winId());
|
||||
BOOL result = FALSE;
|
||||
const bool ok = SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|
||||
|| SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
|
||||
return (ok && result);
|
||||
}
|
||||
|
||||
bool Utilities::isTransparencyEffectEnabled()
|
||||
{
|
||||
if (!isWin10OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
bool ok = false;
|
||||
const QSettings registry(g_personalizeRegistryKey, QSettings::NativeFormat);
|
||||
const bool enableTransparency
|
||||
= registry.value(QStringLiteral("EnableTransparency"), 0).toULongLong(&ok) != 0;
|
||||
return (ok && enableTransparency);
|
||||
}
|
||||
|
||||
void Utilities::triggerFrameChange(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
if (SetWindowPos(reinterpret_cast<HWND>(window->winId()), nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER) == FALSE) {
|
||||
qWarning() << "SetWindowPos failed.";
|
||||
}
|
||||
}
|
||||
|
||||
void Utilities::updateFrameMargins(const QWindow *window, const bool reset)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
|
||||
if (FAILED(DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(window->winId()), &margins))) {
|
||||
qWarning() << "DwmExtendFrameIntoClientArea failed.";
|
||||
}
|
||||
}
|
||||
|
||||
QImage Utilities::getDesktopWallpaperImage(const int screen)
|
||||
{
|
||||
Q_UNUSED(screen);
|
||||
WCHAR path[MAX_PATH];
|
||||
if (SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, path, 0) == FALSE) {
|
||||
qWarning() << "SystemParametersInfoW failed.";
|
||||
return {};
|
||||
}
|
||||
return QImage(QString::fromWCharArray(path));
|
||||
}
|
||||
|
||||
Utilities::DesktopWallpaperAspectStyle Utilities::getDesktopWallpaperAspectStyle(const int screen)
|
||||
{
|
||||
Q_UNUSED(screen);
|
||||
const QSettings settings(QStringLiteral(R"(HKEY_CURRENT_USER\Control Panel\Desktop)"), QSettings::NativeFormat);
|
||||
const DWORD style = settings.value(QStringLiteral("WallpaperStyle")).toULongLong();
|
||||
switch (style) {
|
||||
case 0: {
|
||||
if (settings.value(QStringLiteral("TileWallpaper")).toBool()) {
|
||||
return DesktopWallpaperAspectStyle::Tiled;
|
||||
} else {
|
||||
return DesktopWallpaperAspectStyle::Central;
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
return DesktopWallpaperAspectStyle::IgnoreRatio;
|
||||
case 6:
|
||||
return DesktopWallpaperAspectStyle::KeepRatio;
|
||||
default:
|
||||
return DesktopWallpaperAspectStyle::KeepRatioByExpanding;
|
||||
}
|
||||
}
|
||||
|
||||
quint32 Utilities::getWindowDpi(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(window->winId());
|
||||
Q_ASSERT(hwnd);
|
||||
if (!hwnd) {
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi)) {
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
if (win32Data()->GetDpiForWindowPFN) {
|
||||
return win32Data()->GetDpiForWindowPFN(hwnd);
|
||||
} else if (win32Data()->GetSystemDpiForProcessPFN) {
|
||||
return win32Data()->GetSystemDpiForProcessPFN(GetCurrentProcess());
|
||||
} else if (win32Data()->GetDpiForSystemPFN) {
|
||||
return win32Data()->GetDpiForSystemPFN();
|
||||
} else if (win32Data()->GetDpiForMonitorPFN) {
|
||||
const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
if (monitor) {
|
||||
UINT dpiX = USER_DEFAULT_SCREEN_DPI, dpiY = USER_DEFAULT_SCREEN_DPI;
|
||||
if (SUCCEEDED(win32Data()->GetDpiForMonitorPFN(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
|
||||
return dpiX;
|
||||
} else {
|
||||
qWarning() << "GetDpiForMonitor failed";
|
||||
}
|
||||
} else {
|
||||
qWarning() << "MonitorFromWindow failed.";
|
||||
}
|
||||
}
|
||||
// Using Direct2D to get DPI is deprecated.
|
||||
const HDC hdc = GetDC(nullptr);
|
||||
if (hdc) {
|
||||
const int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
if (dpiX > 0) {
|
||||
return dpiX;
|
||||
} else {
|
||||
qWarning() << "GetDeviceCaps failed.";
|
||||
}
|
||||
}
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
|
||||
QMargins Utilities::getWindowNativeFrameMargins(const QWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return {};
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(window->winId());
|
||||
Q_ASSERT(hwnd);
|
||||
if (!hwnd) {
|
||||
return {};
|
||||
}
|
||||
RECT rect = {0, 0, 0, 0};
|
||||
const LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
|
||||
const LONG_PTR exStyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
|
||||
if (win32Data()->AdjustWindowRectExForDpiPFN) {
|
||||
if (win32Data()->AdjustWindowRectExForDpiPFN(&rect, style, FALSE, exStyle, getWindowDpi(window)) == FALSE) {
|
||||
qWarning() << "AdjustWindowRectExForDpiPFN failed.";
|
||||
}
|
||||
} else {
|
||||
if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE) {
|
||||
qWarning() << "AdjustWindowRectEx failed.";
|
||||
}
|
||||
}
|
||||
return {qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)};
|
||||
}
|