|
@ -5,6 +5,14 @@ project(FramelessHelper LANGUAGES CXX)
|
||||||
option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF)
|
option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF)
|
||||||
option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." ON)
|
option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." ON)
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug") AND NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.11641 7.99992L2.55835 12.558L3.44223 13.4419L8.00029 8.88381L12.5583 13.4419L13.4422 12.558L8.88417 7.99992L13.4422 3.44187L12.5583 2.55798L8.00029 7.11604L3.44223 2.55798L2.55835 3.44187L7.11641 7.99992Z" fill="#424242"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.11641 7.99992L2.55835 12.558L3.44223 13.4419L8.00029 8.88381L12.5583 13.4419L13.4422 12.558L8.88417 7.99992L13.4422 3.44187L12.5583 2.55798L8.00029 7.11604L3.44223 2.55798L2.55835 3.44187L7.11641 7.99992Z" fill="#000"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 375 B |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M3.00024 3V13H13.0002V3H3.00024ZM12.0002 12H4.00024V4H12.0002V12Z" fill="#424242"/>
|
<path d="M3.00024 3V13H13.0002V3H3.00024ZM12.0002 12H4.00024V4H12.0002V12Z" fill="#000"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 193 B |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M14.0002 7.99994V8.99994H3.00024V7.99994H14.0002Z" fill="#424242"/>
|
<path d="M14.0002 7.99994V8.99994H3.00024V7.99994H14.0002Z" fill="#000"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 180 B After Width: | Height: | Size: 177 B |
|
@ -1,4 +1,4 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M3.00024 5V14H12.0002V5H3.00024ZM11.0002 13H4.00024V6H11.0002V13Z" fill="#424242"/>
|
<path d="M3.00024 5V14H12.0002V5H3.00024ZM11.0002 13H4.00024V6H11.0002V13Z" fill="#000"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.00024 5H6.00024V4H13.0002V11H12.0002V12H14.0002V5V3H12.0002H5.00024V5Z" fill="#424242"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.00024 5H6.00024V4H13.0002V11H12.0002V12H14.0002V5V3H12.0002H5.00024V5Z" fill="#000"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 331 B |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.1161 7.99992L2.55804 12.558L3.44193 13.4419L7.99999 8.88381L12.558 13.4419L13.4419 12.558L8.88387 7.99992L13.4419 3.44187L12.558 2.55798L7.99999 7.11604L3.44193 2.55798L2.55804 3.44187L7.1161 7.99992Z" fill="#C5C5C5"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.1161 7.99992L2.55804 12.558L3.44193 13.4419L7.99999 8.88381L12.558 13.4419L13.4419 12.558L8.88387 7.99992L13.4419 3.44187L12.558 2.55798L7.99999 7.11604L3.44193 2.55798L2.55804 3.44187L7.1161 7.99992Z" fill="#fff"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 371 B |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M3 3V13H13V3H3ZM12 12H4V4H12V12Z" fill="#C5C5C5"/>
|
<path d="M3 3V13H13V3H3ZM12 12H4V4H12V12Z" fill="#fff"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 160 B |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M14 7.99994V8.99994H3V7.99994H14Z" fill="#C5C5C5"/>
|
<path d="M14 7.99994V8.99994H3V7.99994H14Z" fill="#fff"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 164 B After Width: | Height: | Size: 161 B |
|
@ -1,4 +1,4 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M3 5V14H12V5H3ZM11 13H4V6H11V13Z" fill="#C5C5C5"/>
|
<path d="M3 5V14H12V5H3ZM11 13H4V6H11V13Z" fill="#fff"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5H6V4H13V11H12V12H14V5V3H12H5V5Z" fill="#C5C5C5"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5H6V4H13V11H12V12H14V5V3H12H5V5Z" fill="#fff"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 260 B |
|
@ -92,8 +92,7 @@ void MainWindow::resetContentsMargins()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
if (Utilities::isWin10OrGreater()) {
|
if (Utilities::isWin10OrGreater()) {
|
||||||
const int frameBorderThickness = 1;
|
setContentsMargins(0, 1, 0, 0);
|
||||||
setContentsMargins(0, frameBorderThickness, 0, 0);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -107,10 +106,9 @@ void MainWindow::paintEvent(QPaintEvent *event)
|
||||||
painter.save();
|
painter.save();
|
||||||
QPen pen = {};
|
QPen pen = {};
|
||||||
pen.setColor(Utilities::getFrameBorderColor(isActiveWindow()));
|
pen.setColor(Utilities::getFrameBorderColor(isActiveWindow()));
|
||||||
const int frameBorderThickness = 1;
|
pen.setWidth(1);
|
||||||
pen.setWidth(frameBorderThickness);
|
|
||||||
painter.setPen(pen);
|
painter.setPen(pen);
|
||||||
painter.drawLine(0, frameBorderThickness, width(), frameBorderThickness);
|
painter.drawLine(0, 0, width(), 0);
|
||||||
painter.restore();
|
painter.restore();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <QtGui/qguiapplication.h>
|
#include <QtGui/qguiapplication.h>
|
||||||
#include <QtQml/qqmlapplicationengine.h>
|
#include <QtQml/qqmlapplicationengine.h>
|
||||||
|
#include <QtQuick/qquickwindow.h>
|
||||||
#include <QtQuickControls2/qquickstyle.h>
|
#include <QtQuickControls2/qquickstyle.h>
|
||||||
#include <framelessquickhelper.h>
|
#include <framelessquickhelper.h>
|
||||||
|
|
||||||
|
@ -43,6 +44,14 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
QGuiApplication application(argc, argv);
|
QGuiApplication application(argc, argv);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
QQuickWindow::setGraphicsApi(QSGRendererInterface::Direct3D11);
|
||||||
|
# elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||||
|
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Direct3D11Rhi);
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
QScopedPointer<FramelessQuickHelper> framelessHelper(new FramelessQuickHelper);
|
QScopedPointer<FramelessQuickHelper> framelessHelper(new FramelessQuickHelper);
|
||||||
QScopedPointer<FramelessQuickUtils> framelessUtils(new FramelessQuickUtils);
|
QScopedPointer<FramelessQuickUtils> framelessUtils(new FramelessQuickUtils);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ Button {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-close.svg" : "qrc:/images/dark/chrome-close.svg"
|
source: FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible
|
||||||
|
? "qrc:/images/light/chrome-close.svg" : "qrc:/images/dark/chrome-close.svg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,9 @@ Window {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: titleBar
|
id: titleBar
|
||||||
height: 30
|
height: 30
|
||||||
color: window.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor :
|
color: window.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor
|
||||||
(FramelessUtils.darkModeEnabled ? "white" : "black")) :
|
: (FramelessUtils.darkModeEnabled ? "black" : "white"))
|
||||||
(FramelessUtils.darkModeEnabled ? "#202020" : "white")
|
: (FramelessUtils.darkModeEnabled ? "#202020" : "white")
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
topMargin: windowTopBorder.height
|
topMargin: windowTopBorder.height
|
||||||
|
@ -59,10 +59,11 @@ Window {
|
||||||
Text {
|
Text {
|
||||||
id: titleBarText
|
id: titleBarText
|
||||||
text: window.title
|
text: window.title
|
||||||
font.pointSize: 13
|
font.pointSize: 11
|
||||||
color: window.active ? (FramelessUtils.darkModeEnabled ? "white" : "black") : "darkGray"
|
color: window.active ? ((FramelessUtils.darkModeEnabled
|
||||||
|
|| FramelessUtils.titleBarColorVisible) ? "white" : "black") : "darkGray"
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 15
|
anchors.leftMargin: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,10 @@ Button {
|
||||||
Image {
|
Image {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: button.maximized ?
|
source: button.maximized ?
|
||||||
(FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-restore.svg" : "qrc:/images/dark/chrome-restore.svg") :
|
(FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible
|
||||||
(FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-maximize.svg" : "qrc:/images/dark/chrome-maximize.svg")
|
? "qrc:/images/light/chrome-restore.svg" : "qrc:/images/dark/chrome-restore.svg") :
|
||||||
|
(FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible
|
||||||
|
? "qrc:/images/light/chrome-maximize.svg" : "qrc:/images/dark/chrome-maximize.svg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ Button {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-minimize.svg" : "qrc:/images/dark/chrome-minimize.svg"
|
source: FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible
|
||||||
|
? "qrc:/images/light/chrome-minimize.svg" : "qrc:/images/dark/chrome-minimize.svg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,12 @@ static const QString mainStyleSheet = QStringLiteral(R"(#MainWidget {
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
[[nodiscard]] static inline bool isTitleBarColorized()
|
||||||
|
{
|
||||||
|
const DwmColorizationArea area = Utilities::getDwmColorizationArea();
|
||||||
|
return ((area == DwmColorizationArea::TitleBar_WindowBorder) || (area == DwmColorizationArea::All));
|
||||||
|
}
|
||||||
|
|
||||||
Widget::Widget(QWidget *parent) : QWidget(parent)
|
Widget::Widget(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||||
|
@ -132,10 +138,9 @@ void Widget::paintEvent(QPaintEvent *event)
|
||||||
painter.save();
|
painter.save();
|
||||||
QPen pen = {};
|
QPen pen = {};
|
||||||
pen.setColor(Utilities::getFrameBorderColor(isActiveWindow()));
|
pen.setColor(Utilities::getFrameBorderColor(isActiveWindow()));
|
||||||
const int frameBorderThickness = 1;
|
pen.setWidth(1);
|
||||||
pen.setWidth(frameBorderThickness);
|
|
||||||
painter.setPen(pen);
|
painter.setPen(pen);
|
||||||
painter.drawLine(0, frameBorderThickness, width(), frameBorderThickness);
|
painter.drawLine(0, 0, width(), 0);
|
||||||
painter.restore();
|
painter.restore();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -285,14 +290,11 @@ void Widget::updateStyleSheet()
|
||||||
{
|
{
|
||||||
const bool active = isActiveWindow();
|
const bool active = isActiveWindow();
|
||||||
const bool dark = Utilities::shouldAppsUseDarkMode();
|
const bool dark = Utilities::shouldAppsUseDarkMode();
|
||||||
const DwmColorizationArea area = Utilities::getDwmColorizationArea();
|
const bool colorizedTitleBar = isTitleBarColorized();
|
||||||
const bool colorizedTitleBar = ((area == DwmColorizationArea::TitleBar_WindowBorder) || (area == DwmColorizationArea::All));
|
const QColor titleBarWidgetBackgroundColor = [active, colorizedTitleBar, dark]() -> QColor {
|
||||||
const QColor colorizationColor = Utilities::getDwmColorizationColor();
|
|
||||||
const QColor mainWidgetBackgroundColor = (dark ? systemDarkColor : systemLightColor);
|
|
||||||
const QColor titleBarWidgetBackgroundColor = [active, colorizedTitleBar, &colorizationColor, dark]() -> QColor {
|
|
||||||
if (active) {
|
if (active) {
|
||||||
if (colorizedTitleBar) {
|
if (colorizedTitleBar) {
|
||||||
return colorizationColor;
|
return Utilities::getDwmColorizationColor();
|
||||||
} else {
|
} else {
|
||||||
if (dark) {
|
if (dark) {
|
||||||
return QColor(Qt::black);
|
return QColor(Qt::black);
|
||||||
|
@ -308,8 +310,9 @@ void Widget::updateStyleSheet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
const QColor windowTitleLabelTextColor = (active ? (dark ? Qt::white : Qt::black) : Qt::darkGray);
|
const QColor windowTitleLabelTextColor = (active ? ((dark || colorizedTitleBar) ? Qt::white : Qt::black) : Qt::darkGray);
|
||||||
const QColor clockLabelTextColor = (dark ? Qt::white : Qt::black);
|
const QColor clockLabelTextColor = (dark ? Qt::white : Qt::black);
|
||||||
|
const QColor mainWidgetBackgroundColor = (dark ? systemDarkColor : systemLightColor);
|
||||||
setStyleSheet(mainStyleSheet.arg(mainWidgetBackgroundColor.name(), titleBarWidgetBackgroundColor.name(),
|
setStyleSheet(mainStyleSheet.arg(mainWidgetBackgroundColor.name(), titleBarWidgetBackgroundColor.name(),
|
||||||
windowTitleLabelTextColor.name(), clockLabelTextColor.name()));
|
windowTitleLabelTextColor.name(), clockLabelTextColor.name()));
|
||||||
update();
|
update();
|
||||||
|
@ -317,7 +320,7 @@ void Widget::updateStyleSheet()
|
||||||
|
|
||||||
void Widget::updateSystemButtonIcons()
|
void Widget::updateSystemButtonIcons()
|
||||||
{
|
{
|
||||||
const QString prefix = (Utilities::shouldAppsUseDarkMode() ? QStringLiteral("light") : QStringLiteral("dark"));
|
const QString prefix = ((Utilities::shouldAppsUseDarkMode() || isTitleBarColorized()) ? QStringLiteral("light") : QStringLiteral("dark"));
|
||||||
m_minimizeButton->setIcon(QIcon(QStringLiteral(":/images/%1/chrome-minimize.svg").arg(prefix)));
|
m_minimizeButton->setIcon(QIcon(QStringLiteral(":/images/%1/chrome-minimize.svg").arg(prefix)));
|
||||||
if (isMaximized() || isFullScreen()) {
|
if (isMaximized() || isFullScreen()) {
|
||||||
m_maximizeButton->setIcon(QIcon(QStringLiteral(":/images/%1/chrome-restore.svg").arg(prefix)));
|
m_maximizeButton->setIcon(QIcon(QStringLiteral(":/images/%1/chrome-restore.svg").arg(prefix)));
|
||||||
|
@ -331,8 +334,7 @@ void Widget::resetContentsMargins()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
if (Utilities::isWin10OrGreater()) {
|
if (Utilities::isWin10OrGreater()) {
|
||||||
const int frameBorderThickness = 1;
|
setContentsMargins(0, 1, 0, 0);
|
||||||
setContentsMargins(0, frameBorderThickness, 0, 0);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "framelesshelper_win32.h"
|
#include "framelesshelper_win32.h"
|
||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
|
#include <QtCore/qhash.h>
|
||||||
#include <QtCore/qmutex.h>
|
#include <QtCore/qmutex.h>
|
||||||
#include <QtCore/qvariant.h>
|
#include <QtCore/qvariant.h>
|
||||||
#include <QtCore/qcoreapplication.h>
|
#include <QtCore/qcoreapplication.h>
|
||||||
|
@ -39,6 +40,8 @@ struct FramelessHelperWinData
|
||||||
{
|
{
|
||||||
QMutex mutex = {};
|
QMutex mutex = {};
|
||||||
QScopedPointer<FramelessHelperWin> instance;
|
QScopedPointer<FramelessHelperWin> instance;
|
||||||
|
QList<WId> acceptableWinIds = {};
|
||||||
|
QHash<HWND, WNDPROC> qtWindowProcs = {};
|
||||||
|
|
||||||
explicit FramelessHelperWinData() = default;
|
explicit FramelessHelperWinData() = default;
|
||||||
~FramelessHelperWinData() = default;
|
~FramelessHelperWinData() = default;
|
||||||
|
@ -49,6 +52,127 @@ private:
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(FramelessHelperWinData, g_helper)
|
Q_GLOBAL_STATIC(FramelessHelperWinData, g_helper)
|
||||||
|
|
||||||
|
[[nodiscard]] static inline LRESULT CALLBACK HookWindowProc
|
||||||
|
(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
|
||||||
|
{
|
||||||
|
g_helper()->mutex.lock();
|
||||||
|
if (!g_helper()->qtWindowProcs.contains(hWnd)) {
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
const auto winId = reinterpret_cast<WId>(hWnd);
|
||||||
|
const auto getGlobalPosFromMouse = [lParam]() -> QPointF {
|
||||||
|
return {qreal(GET_X_LPARAM(lParam)), qreal(GET_Y_LPARAM(lParam))};
|
||||||
|
};
|
||||||
|
const auto getGlobalPosFromKeyboard = [hWnd, winId]() -> QPointF {
|
||||||
|
RECT rect = {};
|
||||||
|
if (GetWindowRect(hWnd, &rect) == FALSE) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetWindowRect"));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const bool maxOrFull = (IsMaximized(hWnd) || Utilities::isFullScreen(winId));
|
||||||
|
const int frameSizeX = Utilities::getResizeBorderThickness(winId, true, true);
|
||||||
|
const int frameSizeY = Utilities::getResizeBorderThickness(winId, false, true);
|
||||||
|
const int titleBarHeight = Utilities::getTitleBarHeight(winId, true);
|
||||||
|
const int horizontalOffset = (maxOrFull ? 0 : frameSizeX);
|
||||||
|
const int verticalOffset = (maxOrFull ? titleBarHeight : (titleBarHeight - frameSizeY));
|
||||||
|
return {qreal(rect.left + horizontalOffset), qreal(rect.top + verticalOffset)};
|
||||||
|
};
|
||||||
|
bool shouldShowSystemMenu = false;
|
||||||
|
QPointF globalPos = {};
|
||||||
|
if (uMsg == WM_NCRBUTTONUP) {
|
||||||
|
if (wParam == HTCAPTION) {
|
||||||
|
shouldShowSystemMenu = true;
|
||||||
|
globalPos = getGlobalPosFromMouse();
|
||||||
|
}
|
||||||
|
} else if (uMsg == WM_SYSCOMMAND) {
|
||||||
|
const WPARAM filteredWParam = (wParam & 0xFFF0);
|
||||||
|
if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) {
|
||||||
|
shouldShowSystemMenu = true;
|
||||||
|
globalPos = getGlobalPosFromKeyboard();
|
||||||
|
}
|
||||||
|
} else if ((uMsg == WM_KEYDOWN) || (uMsg == WM_SYSKEYDOWN)) {
|
||||||
|
const bool altPressed = ((wParam == VK_MENU) || (GetKeyState(VK_MENU) < 0));
|
||||||
|
const bool spacePressed = ((wParam == VK_SPACE) || (GetKeyState(VK_SPACE) < 0));
|
||||||
|
if (altPressed && spacePressed) {
|
||||||
|
shouldShowSystemMenu = true;
|
||||||
|
globalPos = getGlobalPosFromKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldShowSystemMenu) {
|
||||||
|
Utilities::showSystemMenu(winId, globalPos);
|
||||||
|
// QPA's internal code will handle system menu events separately, and its
|
||||||
|
// behavior is not what we would want to see because it doesn't know our
|
||||||
|
// window doesn't have any window frame now, so return early here to avoid
|
||||||
|
// entering Qt's own handling logic.
|
||||||
|
return 0; // Return 0 means we have handled this event.
|
||||||
|
}
|
||||||
|
g_helper()->mutex.lock();
|
||||||
|
const WNDPROC originalWindowProc = g_helper()->qtWindowProcs.value(hWnd);
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
Q_ASSERT(originalWindowProc);
|
||||||
|
if (originalWindowProc) {
|
||||||
|
// Hand over to Qt's original window proc function for events we are not
|
||||||
|
// interested in.
|
||||||
|
return CallWindowProcW(originalWindowProc, hWnd, uMsg, wParam, lParam);
|
||||||
|
} else {
|
||||||
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static inline bool installWindowHook(const WId winId)
|
||||||
|
{
|
||||||
|
Q_ASSERT(winId);
|
||||||
|
if (!winId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||||
|
QMutexLocker locker(&g_helper()->mutex);
|
||||||
|
if (g_helper()->qtWindowProcs.contains(hwnd)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC));
|
||||||
|
Q_ASSERT(originalWindowProc);
|
||||||
|
if (!originalWindowProc) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(HookWindowProc)) == 0) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_helper()->qtWindowProcs.insert(hwnd, originalWindowProc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static inline bool uninstallWindowHook(const WId winId)
|
||||||
|
{
|
||||||
|
Q_ASSERT(winId);
|
||||||
|
if (!winId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||||
|
QMutexLocker locker(&g_helper()->mutex);
|
||||||
|
if (!g_helper()->qtWindowProcs.contains(hwnd)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const WNDPROC originalWindowProc = g_helper()->qtWindowProcs.value(hwnd);
|
||||||
|
Q_ASSERT(originalWindowProc);
|
||||||
|
if (!originalWindowProc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(originalWindowProc)) == 0) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_helper()->qtWindowProcs.remove(hwnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
|
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
|
||||||
|
|
||||||
FramelessHelperWin::~FramelessHelperWin()
|
FramelessHelperWin::~FramelessHelperWin()
|
||||||
|
@ -62,17 +186,26 @@ void FramelessHelperWin::addWindow(QWindow *window)
|
||||||
if (!window) {
|
if (!window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QMutexLocker locker(&g_helper()->mutex);
|
const WId winId = window->winId();
|
||||||
|
g_helper()->mutex.lock();
|
||||||
|
if (g_helper()->acceptableWinIds.contains(winId)) {
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_helper()->acceptableWinIds.append(winId);
|
||||||
if (g_helper()->instance.isNull()) {
|
if (g_helper()->instance.isNull()) {
|
||||||
g_helper()->instance.reset(new FramelessHelperWin);
|
g_helper()->instance.reset(new FramelessHelperWin);
|
||||||
qApp->installNativeEventFilter(g_helper()->instance.data());
|
qApp->installNativeEventFilter(g_helper()->instance.data());
|
||||||
}
|
}
|
||||||
const WId winId = window->winId();
|
g_helper()->mutex.unlock();
|
||||||
Utilities::fixupQtInternals(winId);
|
Utilities::fixupQtInternals(winId);
|
||||||
Utilities::updateInternalWindowFrameMargins(window, true);
|
Utilities::updateInternalWindowFrameMargins(window, true);
|
||||||
Utilities::updateWindowFrameMargins(winId, false);
|
Utilities::updateWindowFrameMargins(winId, false);
|
||||||
const bool dark = Utilities::shouldAppsUseDarkMode();
|
const bool dark = Utilities::shouldAppsUseDarkMode();
|
||||||
Utilities::updateWindowFrameBorderColor(winId, dark);
|
Utilities::updateWindowFrameBorderColor(winId, dark);
|
||||||
|
if (!installWindowHook(winId)) {
|
||||||
|
qWarning() << "Failed to hook the window proc function.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramelessHelperWin::removeWindow(QWindow *window)
|
void FramelessHelperWin::removeWindow(QWindow *window)
|
||||||
|
@ -81,8 +214,18 @@ void FramelessHelperWin::removeWindow(QWindow *window)
|
||||||
if (!window) {
|
if (!window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const WId winId = window->winId();
|
||||||
|
g_helper()->mutex.lock();
|
||||||
|
if (!g_helper()->acceptableWinIds.contains(winId)) {
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
if (!uninstallWindowHook(winId)) {
|
||||||
|
qWarning() << "Failed to un-hook the window proc function.";
|
||||||
|
}
|
||||||
Utilities::updateInternalWindowFrameMargins(window, false);
|
Utilities::updateInternalWindowFrameMargins(window, false);
|
||||||
Utilities::updateWindowFrameMargins(window->winId(), true);
|
Utilities::updateWindowFrameMargins(winId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
@ -106,8 +249,15 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const WId winId = reinterpret_cast<WId>(msg->hwnd);
|
const WId winId = reinterpret_cast<WId>(msg->hwnd);
|
||||||
|
g_helper()->mutex.lock();
|
||||||
|
if (!g_helper()->acceptableWinIds.contains(winId)) {
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_helper()->mutex.unlock();
|
||||||
const FramelessManagerPrivate * const manager = FramelessManagerPrivate::instance();
|
const FramelessManagerPrivate * const manager = FramelessManagerPrivate::instance();
|
||||||
const QUuid id = manager->findIdByWinId(winId);
|
const QUuid id = manager->findIdByWinId(winId);
|
||||||
|
Q_ASSERT(!id.isNull());
|
||||||
if (id.isNull()) {
|
if (id.isNull()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
|
#include <QtGui/qguiapplication.h>
|
||||||
#include <QtCore/private/qsystemlibrary_p.h>
|
#include <QtCore/private/qsystemlibrary_p.h>
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||||
# include <QtCore/qoperatingsystemversion.h>
|
# include <QtCore/qoperatingsystemversion.h>
|
||||||
|
@ -32,7 +33,6 @@
|
||||||
#endif
|
#endif
|
||||||
#include <QtGui/qpa/qplatformwindow.h>
|
#include <QtGui/qpa/qplatformwindow.h>
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
# include <QtGui/qguiapplication.h>
|
|
||||||
# include <QtGui/qpa/qplatformnativeinterface.h>
|
# include <QtGui/qpa/qplatformnativeinterface.h>
|
||||||
#else
|
#else
|
||||||
# include <QtGui/qpa/qplatformwindow_p.h>
|
# include <QtGui/qpa/qplatformwindow_p.h>
|
||||||
|
@ -396,39 +396,41 @@ void Utilities::showSystemMenu(const WId winId, const QPointF &pos)
|
||||||
mii.cbSize = sizeof(mii);
|
mii.cbSize = sizeof(mii);
|
||||||
mii.fMask = MIIM_STATE;
|
mii.fMask = MIIM_STATE;
|
||||||
mii.fType = MFT_STRING;
|
mii.fType = MFT_STRING;
|
||||||
const auto setState = [&mii, menu](const UINT item, const bool enabled) -> bool {
|
const auto setState = [&mii, menu](const UINT item, const bool enabled, const bool highlight) -> bool {
|
||||||
mii.fState = (enabled ? MF_ENABLED : MF_DISABLED);
|
mii.fState = ((enabled ? MFS_ENABLED : MFS_DISABLED) | (highlight ? MFS_HILITE : 0));
|
||||||
if (SetMenuItemInfoW(menu, item, FALSE, &mii) == FALSE) {
|
if (SetMenuItemInfoW(menu, item, FALSE, &mii) == FALSE) {
|
||||||
|
Q_ASSERT(false);
|
||||||
qWarning() << getSystemErrorMessage(QStringLiteral("SetMenuItemInfoW"));
|
qWarning() << getSystemErrorMessage(QStringLiteral("SetMenuItemInfoW"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
const bool max = IsMaximized(hWnd);
|
const bool maxOrFull = (IsMaximized(hWnd) || isFullScreen(reinterpret_cast<WId>(hWnd)));
|
||||||
if (!setState(SC_RESTORE, max)) {
|
if (!setState(SC_RESTORE, maxOrFull, true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!setState(SC_MOVE, !max)) {
|
if (!setState(SC_MOVE, !maxOrFull, false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!setState(SC_SIZE, !max)) {
|
if (!setState(SC_SIZE, !maxOrFull, false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!setState(SC_MINIMIZE, true)) {
|
if (!setState(SC_MINIMIZE, true, false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!setState(SC_MAXIMIZE, !max)) {
|
if (!setState(SC_MAXIMIZE, !maxOrFull, false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!setState(SC_CLOSE, true)) {
|
if (!setState(SC_CLOSE, true, false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SetMenuDefaultItem(menu, UINT_MAX, FALSE) == FALSE) {
|
if (SetMenuDefaultItem(menu, SC_CLOSE, FALSE) == FALSE) {
|
||||||
qWarning() << getSystemErrorMessage(QStringLiteral("SetMenuDefaultItem"));
|
qWarning() << getSystemErrorMessage(QStringLiteral("SetMenuDefaultItem"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const QPoint roundedPos = pos.toPoint();
|
const QPoint roundedPos = pos.toPoint();
|
||||||
const auto ret = TrackPopupMenu(menu, TPM_RETURNCMD, roundedPos.x(), roundedPos.y(), 0, hWnd, nullptr);
|
const auto ret = TrackPopupMenu(menu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft()
|
||||||
|
? TPM_RIGHTALIGN : TPM_LEFTALIGN)), roundedPos.x(), roundedPos.y(), 0, hWnd, nullptr);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
if (PostMessageW(hWnd, WM_SYSCOMMAND, ret, 0) == FALSE) {
|
if (PostMessageW(hWnd, WM_SYSCOMMAND, ret, 0) == FALSE) {
|
||||||
qWarning() << getSystemErrorMessage(QStringLiteral("PostMessageW"));
|
qWarning() << getSystemErrorMessage(QStringLiteral("PostMessageW"));
|
||||||
|
@ -730,7 +732,7 @@ QColor Utilities::getFrameBorderColor(const bool active)
|
||||||
return (dark ? QColor(QStringLiteral("#4d4d4d")) : QColor(Qt::white));
|
return (dark ? QColor(QStringLiteral("#4d4d4d")) : QColor(Qt::white));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (dark ? QColor(QStringLiteral("#575959")) : QColor(QStringLiteral("#999999")));
|
return (dark ? QColor(QStringLiteral("#575959")) : QColor(QStringLiteral("#b3b3b3")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|