Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-03-13 12:14:08 +08:00
parent d0e19a2b1e
commit 556741cfb1
18 changed files with 355 additions and 284 deletions

View File

@ -2,11 +2,8 @@ cmake_minimum_required(VERSION 3.20)
project(FramelessHelper LANGUAGES CXX) project(FramelessHelper LANGUAGES CXX)
option(BUILD_EXAMPLES "Build examples." ON) option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF)
option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." ON)
if(NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
endif()
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -20,8 +17,8 @@ set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED)
#find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick) find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
#find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
set(SOURCES set(SOURCES
framelesshelper_global.h framelesshelper_global.h
@ -34,12 +31,12 @@ set(SOURCES
utilities.cpp utilities.cpp
) )
#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick) if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
list(APPEND SOURCES list(APPEND SOURCES
framelessquickhelper.h framelessquickhelper.h
framelessquickhelper.cpp framelessquickhelper.cpp
) )
endif()]] endif()
if(WIN32) if(WIN32)
list(APPEND SOURCES list(APPEND SOURCES
@ -56,15 +53,18 @@ elseif(UNIX)
list(APPEND SOURCES utilities_linux.cpp) list(APPEND SOURCES utilities_linux.cpp)
endif() endif()
if(WIN32 AND BUILD_SHARED_LIBS) if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
enable_language(RC) enable_language(RC)
list(APPEND SOURCES framelesshelper.rc) list(APPEND SOURCES framelesshelper.rc)
endif() endif()
add_library(${PROJECT_NAME} ${SOURCES}) if(FRAMELESSHELPER_BUILD_STATIC)
add_library(wangwenx190::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) add_library(${PROJECT_NAME} STATIC ${SOURCES})
else()
add_library(${PROJECT_NAME} SHARED ${SOURCES})
endif()
if(NOT BUILD_SHARED_LIBS) if(FRAMELESSHELPER_BUILD_STATIC)
target_compile_definitions(${PROJECT_NAME} PUBLIC target_compile_definitions(${PROJECT_NAME} PUBLIC
FRAMELESSHELPER_STATIC FRAMELESSHELPER_STATIC
) )
@ -85,16 +85,16 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::GuiPrivate Qt${QT_VERSION_MAJOR}::GuiPrivate
) )
#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick) if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
target_link_libraries(${PROJECT_NAME} PRIVATE target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Quick
) )
endif()]] endif()
target_include_directories(${PROJECT_NAME} PUBLIC target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
) )
if(BUILD_EXAMPLES) if(FRAMELESSHELPER_BUILD_EXAMPLES)
add_subdirectory(examples) add_subdirectory(examples)
endif() endif()

View File

@ -7,5 +7,5 @@ if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
endif() endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Quick) if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
#add_subdirectory(quick) add_subdirectory(quick)
endif() endif()

View File

@ -25,7 +25,7 @@ add_executable(MainWindow WIN32 ${SOURCES})
target_link_libraries(MainWindow PRIVATE target_link_libraries(MainWindow PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
wangwenx190::FramelessHelper FramelessHelper
) )
target_compile_definitions(MainWindow PRIVATE target_compile_definitions(MainWindow PRIVATE

View File

@ -23,7 +23,7 @@ add_executable(Quick WIN32 ${SOURCES})
target_link_libraries(Quick PRIVATE target_link_libraries(Quick PRIVATE
Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::QuickControls2 Qt${QT_VERSION_MAJOR}::QuickControls2
wangwenx190::FramelessHelper FramelessHelper
) )
target_compile_definitions(Quick PRIVATE target_compile_definitions(Quick PRIVATE

View File

@ -22,77 +22,20 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include "../../utilities.h"
#include "../../framelessquickhelper.h"
#include <QtGui/qguiapplication.h> #include <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h> #include <QtQml/qqmlapplicationengine.h>
#include <QtQuickControls2/qquickstyle.h> #include <QtQuickControls2/qquickstyle.h>
#include <framelessquickhelper.h>
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
static constexpr const char qtquicknamespace[] = "wangwenx190.Utils"; static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper";
class UtilFunctions : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(UtilFunctions)
Q_PROPERTY(bool isWindowsHost READ isWindowsHost CONSTANT)
Q_PROPERTY(bool isWindows10OrGreater READ isWindows10OrGreater CONSTANT)
Q_PROPERTY(bool isWindows11OrGreater READ isWindows11OrGreater CONSTANT)
Q_PROPERTY(QColor activeFrameBorderColor READ activeFrameBorderColor CONSTANT)
Q_PROPERTY(QColor inactiveFrameBorderColor READ inactiveFrameBorderColor CONSTANT)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT)
public:
explicit UtilFunctions(QObject *parent = nullptr) : QObject(parent) {}
~UtilFunctions() override = default;
inline bool isWindowsHost() const {
#ifdef Q_OS_WINDOWS
return true;
#else
return false;
#endif
}
inline bool isWindows10OrGreater() const {
#ifdef Q_OS_WINDOWS
return Utilities::isWin10OrGreater();
#else
return false;
#endif
}
inline bool isWindows11OrGreater() const {
#ifdef Q_OS_WINDOWS
return Utilities::isWin11OrGreater();
#else
return false;
#endif
}
inline QColor activeFrameBorderColor() const {
const ColorizationArea area = Utilities::getColorizationArea();
const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder)
|| (area == ColorizationArea::All));
return (colorizedBorder ? Utilities::getColorizationColor() : Qt::black);
}
inline QColor inactiveFrameBorderColor() const {
return Qt::darkGray;
}
inline qreal frameBorderThickness() const {
return 1.0;
}
};
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
@ -100,6 +43,9 @@ int main(int argc, char *argv[])
QGuiApplication application(argc, argv); QGuiApplication application(argc, argv);
QScopedPointer<FramelessQuickHelper> framelessHelper(new FramelessQuickHelper);
QScopedPointer<FramelessQuickUtils> framelessUtils(new FramelessQuickUtils);
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -108,14 +54,10 @@ int main(int argc, char *argv[])
QQuickStyle::setStyle(QStringLiteral("Default")); QQuickStyle::setStyle(QStringLiteral("Default"));
#endif #endif
qmlRegisterSingletonType<UtilFunctions>(qtquicknamespace, 1, 0, "Utils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { qmlRegisterSingletonInstance(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessHelper", framelessHelper.data());
Q_UNUSED(engine); qmlRegisterSingletonInstance(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessUtils", framelessUtils.data());
Q_UNUSED(scriptEngine);
return new UtilFunctions();
});
qmlRegisterType<FramelessQuickHelper>(qtquicknamespace, 1, 0, "FramelessHelper");
const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/main.qml")); const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/MainWindow.qml"));
const QMetaObject::Connection connection = QObject::connect( const QMetaObject::Connection connection = QObject::connect(
&engine, &engine,
&QQmlApplicationEngine::objectCreated, &QQmlApplicationEngine::objectCreated,
@ -124,17 +66,16 @@ int main(int argc, char *argv[])
if (url != mainQmlUrl) { if (url != mainQmlUrl) {
return; return;
} }
if (!object) { if (object) {
QGuiApplication::exit(-1);
} else {
QObject::disconnect(connection); QObject::disconnect(connection);
} else {
QCoreApplication::exit(-1);
} }
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
engine.load(mainQmlUrl); engine.load(mainQmlUrl);
return QGuiApplication::exec(); return QCoreApplication::exec();
} }
#include "main.moc"

View File

@ -1,6 +1,6 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>qml/main.qml</file> <file>qml/MainWindow.qml</file>
<file>qml/MinimizeButton.qml</file> <file>qml/MinimizeButton.qml</file>
<file>qml/MaximizeButton.qml</file> <file>qml/MaximizeButton.qml</file>
<file>qml/CloseButton.qml</file> <file>qml/CloseButton.qml</file>

View File

@ -24,25 +24,33 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import org.wangwenx190.FramelessHelper 1.0
Button { Button {
id: button id: button
implicitWidth: 45
implicitHeight: 30 implicitHeight: 30
implicitWidth: implicitHeight * 1.5
ToolTip.visible: hovered && !down ToolTip {
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval visible: button.hovered && !button.down
ToolTip.text: qsTr("Close") delay: Qt.styleHints.mousePressAndHoldInterval
text: qsTr("Close")
}
contentItem: Image { contentItem: Item {
anchors.fill: parent implicitWidth: 16
source: button.down implicitHeight: implicitWidth
|| button.hovered ? "qrc:/images/button_close_white.svg" : "qrc:/images/button_close_black.svg"
Image {
anchors.centerIn: parent
source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-close.svg" : "qrc:/images/dark/chrome-close.svg"
}
} }
background: Rectangle { background: Rectangle {
visible: button.down || button.hovered visible: button.hovered
color: button.down ? "#8c0a15" : (button.hovered ? "#e81123" : "transparent") color: "red"
opacity: 0.5
} }
} }

View File

@ -22,10 +22,10 @@
* SOFTWARE. * SOFTWARE.
*/ */
import QtQuick 2.0 import QtQuick 2.15
import QtQuick.Window 2.0 import QtQuick.Window 2.15
import QtQuick.Controls 2.0 import QtQuick.Controls 2.15
import wangwenx190.Utils 1.0 import org.wangwenx190.FramelessHelper 1.0
Window { Window {
id: window id: window
@ -33,14 +33,7 @@ Window {
width: 800 width: 800
height: 600 height: 600
title: qsTr("Hello, World!") title: qsTr("Hello, World!")
color: "#f0f0f0" color: FramelessUtils.darkModeEnabled ? "#202020" : "#f0f0f0"
property real _flh_margin: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen)) ? 0 : (Utils.frameBorderThickness / Screen.devicePixelRatio)
property var _win_prev_state: null
FramelessHelper {
id: framelessHelper
}
Timer { Timer {
id: timer id: timer
@ -52,35 +45,46 @@ Window {
Rectangle { Rectangle {
id: titleBar id: titleBar
height: framelessHelper.titleBarHeight height: 30
color: "white" color: window.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor :
(FramelessUtils.darkModeEnabled ? "white" : "black")) :
(FramelessUtils.darkModeEnabled ? "#202020" : "white")
anchors { anchors {
top: parent.top top: parent.top
topMargin: window._flh_margin topMargin: windowTopBorder.height
left: parent.left left: parent.left
leftMargin: window._flh_margin
right: parent.right right: parent.right
rightMargin: window._flh_margin
} }
Text { Text {
id: titleBarText id: titleBarText
text: window.title text: window.title
font.pointSize: 13 font.pointSize: 13
color: window.active ? "black" : "gray" color: window.active ? (FramelessUtils.darkModeEnabled ? "white" : "black") : "darkGray"
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 15 anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
MouseArea {
anchors.fill: parent
anchors.rightMargin: 30 * 1.5 * 3
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onDoubleClicked: maximizeButton.clicked()
onPositionChanged: {
if (containsPress && (window.visibility !== Window.FullScreen)) {
FramelessUtils.startSystemMove2(window);
}
}
}
Row { Row {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
MinimizeButton { MinimizeButton {
id: minimizeButton onClicked: FramelessUtils.showMinimized2(window)
onClicked: framelessHelper.showMinimized()
Component.onCompleted: framelessHelper.setHitTestVisible(minimizeButton, true)
} }
MaximizeButton { MaximizeButton {
@ -88,18 +92,15 @@ Window {
maximized: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen)) maximized: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen))
onClicked: { onClicked: {
if (maximized) { if (maximized) {
window.showNormal() window.showNormal();
} else { } else {
window.showMaximized() window.showMaximized();
} }
} }
Component.onCompleted: framelessHelper.setHitTestVisible(maximizeButton, true)
} }
CloseButton { CloseButton {
id: closeButton
onClicked: window.close() onClicked: window.close()
Component.onCompleted: framelessHelper.setHitTestVisible(closeButton, true)
} }
} }
} }
@ -111,41 +112,19 @@ Window {
pointSize: 70 pointSize: 70
bold: true bold: true
} }
} color: FramelessUtils.darkModeEnabled ? "white" : "black"
Button {
id: fullScreenButton
anchors {
horizontalCenter: parent.horizontalCenter
top: timeLabel.bottom
topMargin: 15
}
property bool _full: window.visibility === Window.FullScreen
text: _full ? qsTr("Exit FullScreen") : qsTr("Enter FullScreen")
onClicked: {
if (_full) {
if (_win_prev_state == Window.Maximized) {
window.showMaximized()
} else if (_win_prev_state == Window.Windowed) {
window.showNormal()
}
} else {
_win_prev_state = window.visibility
window.showFullScreen()
}
}
} }
Rectangle { Rectangle {
id: windowFrame id: windowTopBorder
anchors.fill: parent anchors {
color: "transparent" top: parent.top
visible: !Utils.isWindows11OrGreater left: parent.left
border { right: parent.right
color: window.active ? Utils.activeFrameBorderColor : Utils.inactiveFrameBorderColor
width: window._flh_margin
} }
height: ((window.visibility === Window.Windowed) && FramelessUtils.frameBorderVisible) ? 1 : 0
color: window.active ? FramelessUtils.frameBorderActiveColor : FramelessUtils.frameBorderInactiveColor
} }
Component.onCompleted: framelessHelper.removeWindowFrame() Component.onCompleted: FramelessHelper.addWindow(window)
} }

View File

@ -24,26 +24,37 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import org.wangwenx190.FramelessHelper 1.0
Button { Button {
id: button id: button
implicitWidth: 45
implicitHeight: 30 implicitHeight: 30
implicitWidth: implicitHeight * 1.5
property bool maximized: false property bool maximized: false
ToolTip.visible: hovered && !down ToolTip {
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval visible: button.hovered && !button.down
ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize") delay: Qt.styleHints.mousePressAndHoldInterval
text: button.maximized ? qsTr("Restore") : qsTr("Maximize")
}
contentItem: Image { contentItem: Item {
anchors.fill: parent implicitWidth: 16
source: maximized ? "qrc:/images/button_restore_black.svg" : "qrc:/images/button_maximize_black.svg" implicitHeight: implicitWidth
Image {
anchors.centerIn: parent
source: button.maximized ?
(FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-restore.svg" : "qrc:/images/dark/chrome-restore.svg") :
(FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-maximize.svg" : "qrc:/images/dark/chrome-maximize.svg")
}
} }
background: Rectangle { background: Rectangle {
visible: button.down || button.hovered visible: button.hovered
color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent") color: "gray"
opacity: 0.5
} }
} }

View File

@ -24,24 +24,33 @@
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Controls 2.0 import QtQuick.Controls 2.0
import org.wangwenx190.FramelessHelper 1.0
Button { Button {
id: button id: button
implicitWidth: 45
implicitHeight: 30 implicitHeight: 30
implicitWidth: implicitHeight * 1.5
ToolTip.visible: hovered && !down ToolTip {
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval visible: button.hovered && !button.down
ToolTip.text: qsTr("Minimize") delay: Qt.styleHints.mousePressAndHoldInterval
text: qsTr("Minimize")
}
contentItem: Image { contentItem: Item {
anchors.fill: parent implicitWidth: 16
source: "qrc:/images/button_minimize_black.svg" implicitHeight: implicitWidth
Image {
anchors.centerIn: parent
source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-minimize.svg" : "qrc:/images/dark/chrome-minimize.svg"
}
} }
background: Rectangle { background: Rectangle {
visible: button.down || button.hovered visible: button.hovered
color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent") color: "gray"
opacity: 0.5
} }
} }

View File

@ -23,7 +23,7 @@ add_executable(Widget WIN32 ${SOURCES})
target_link_libraries(Widget PRIVATE target_link_libraries(Widget PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Widgets
wangwenx190::FramelessHelper FramelessHelper
) )
target_compile_definitions(Widget PRIVATE target_compile_definitions(Widget PRIVATE

View File

@ -35,7 +35,7 @@
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
static const QColor systemLightColor = QStringLiteral("#f0f0f0"); static const QColor systemLightColor = QStringLiteral("#f0f0f0");
static const QColor systemDarkColor = QColor::fromRgb(32, 32, 32); static const QColor systemDarkColor = QStringLiteral("#202020");
static const QString mainStyleSheet = QStringLiteral(R"(#MainWidget { static const QString mainStyleSheet = QStringLiteral(R"(#MainWidget {
background-color: %1; background-color: %1;
@ -132,7 +132,7 @@ 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 = Utilities::getFrameBorderThickness(winId(), false); const int frameBorderThickness = 1;
pen.setWidth(frameBorderThickness); pen.setWidth(frameBorderThickness);
painter.setPen(pen); painter.setPen(pen);
painter.drawLine(0, frameBorderThickness, width(), frameBorderThickness); painter.drawLine(0, frameBorderThickness, width(), frameBorderThickness);
@ -174,7 +174,7 @@ void Widget::initFramelessHelperOnce()
void Widget::setupUi() void Widget::setupUi()
{ {
const int titleBarHeight = /*Utilities::getTitleBarHeight(winId(), false)*/30; const int titleBarHeight = 30;
const QSize systemButtonSize = {int(qRound(qreal(titleBarHeight) * 1.5)), titleBarHeight}; const QSize systemButtonSize = {int(qRound(qreal(titleBarHeight) * 1.5)), titleBarHeight};
const QSize systemIconSize = {16, 16}; const QSize systemIconSize = {16, 16};
setObjectName(QStringLiteral("MainWidget")); setObjectName(QStringLiteral("MainWidget"));
@ -327,7 +327,7 @@ void Widget::resetContentsMargins()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (Utilities::isWin10OrGreater()) { if (Utilities::isWin10OrGreater()) {
const int frameBorderThickness = Utilities::getFrameBorderThickness(winId(), false); const int frameBorderThickness = 1;
setContentsMargins(0, frameBorderThickness, 0, 0); setContentsMargins(0, frameBorderThickness, 0, 0);
} }
#endif #endif

View File

@ -66,8 +66,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
} }
const QEvent::Type type = event->type(); const QEvent::Type type = event->type();
// We are only interested in mouse events. // We are only interested in mouse events.
if ((type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseButtonPress) if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseMove)) {
&& (type != QEvent::MouseMove)) {
return false; return false;
} }
const auto window = qobject_cast<QWindow *>(object); const auto window = qobject_cast<QWindow *>(object);

View File

@ -67,7 +67,7 @@ void FramelessHelperWin::addWindow(QWindow *window)
qApp->installNativeEventFilter(g_helper()->instance.data()); qApp->installNativeEventFilter(g_helper()->instance.data());
} }
const WId winId = window->winId(); const WId winId = window->winId();
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();

View File

@ -23,85 +23,180 @@
*/ */
#include "framelessquickhelper.h" #include "framelessquickhelper.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))
# include <QtGui/qpa/qplatformtheme.h>
# include <QtGui/private/qguiapplication_p.h>
#endif
#include "framelesswindowsmanager.h" #include "framelesswindowsmanager.h"
#include <QtQuick/qquickwindow.h> #include "utilities.h"
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
#include "framelesshelper_windows.h" # include <QtCore/qt_windows.h>
#endif #endif
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent) FramelessQuickHelper::FramelessQuickHelper(QObject *parent) : QObject(parent) {}
{
}
qreal FramelessQuickHelper::resizeBorderThickness() const FramelessQuickHelper::~FramelessQuickHelper() = default;
{
return FramelessWindowsManager::getResizeBorderThickness(window());
}
void FramelessQuickHelper::setResizeBorderThickness(const qreal val) void FramelessQuickHelper::addWindow(QWindow *window)
{ {
FramelessWindowsManager::setResizeBorderThickness(window(), qRound(val)); Q_ASSERT(window);
Q_EMIT resizeBorderThicknessChanged(val); if (!window) {
}
qreal FramelessQuickHelper::titleBarHeight() const
{
return FramelessWindowsManager::getTitleBarHeight(window());
}
void FramelessQuickHelper::setTitleBarHeight(const qreal val)
{
FramelessWindowsManager::setTitleBarHeight(window(), qRound(val));
Q_EMIT titleBarHeightChanged(val);
}
bool FramelessQuickHelper::resizable() const
{
return FramelessWindowsManager::getResizable(window());
}
void FramelessQuickHelper::setResizable(const bool val)
{
FramelessWindowsManager::setResizable(window(), val);
Q_EMIT resizableChanged(val);
}
void FramelessQuickHelper::removeWindowFrame()
{
FramelessWindowsManager::addWindow(window());
}
void FramelessQuickHelper::bringBackWindowFrame()
{
FramelessWindowsManager::removeWindow(window());
}
bool FramelessQuickHelper::isWindowFrameless() const
{
return FramelessWindowsManager::isWindowFrameless(window());
}
void FramelessQuickHelper::setHitTestVisible(QQuickItem *item, const bool visible)
{
Q_ASSERT(item);
if (!item) {
return; return;
} }
FramelessWindowsManager::setHitTestVisible(window(), item, visible); FramelessWindowsManager::addWindow(window);
} }
void FramelessQuickHelper::showMinimized() void FramelessQuickHelper::removeWindow(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
FramelessWindowsManager::removeWindow(window);
}
FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
{
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::themeChanged, this, [this](){
Q_EMIT frameBorderActiveColorChanged();
Q_EMIT frameBorderInactiveColorChanged();
Q_EMIT darkModeEnabledChanged();
Q_EMIT systemAccentColorChanged();
Q_EMIT titleBarColorVisibleChanged();
});
}
FramelessQuickUtils::~FramelessQuickUtils() = default;
qreal FramelessQuickUtils::titleBarHeight()
{
return 30;
}
bool FramelessQuickUtils::frameBorderVisible()
{ {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
// Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711 return (Utilities::isWin10OrGreater() && !Utilities::isWin11OrGreater());
// Don't use "SW_SHOWMINIMIZED" because it will activate the current
// window instead of the next window in the Z order, that's not the
// native behavior of Windows applications.
ShowWindow(reinterpret_cast<HWND>(window()->winId()), SW_MINIMIZE);
#else #else
window()->showMinimized(); return false;
#endif
}
qreal FramelessQuickUtils::frameBorderThickness()
{
return 1;
}
QColor FramelessQuickUtils::frameBorderActiveColor()
{
#ifdef Q_OS_WINDOWS
return Utilities::getFrameBorderColor(true);
#else
return {};
#endif
}
QColor FramelessQuickUtils::frameBorderInactiveColor()
{
#ifdef Q_OS_WINDOWS
return Utilities::getFrameBorderColor(false);
#else
return {};
#endif
}
bool FramelessQuickUtils::darkModeEnabled()
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))
if (const QPlatformTheme * const theme = QGuiApplicationPrivate::platformTheme()) {
return (theme->appearance() == QPlatformTheme::Appearance::Dark);
}
return false;
#else
# ifdef Q_OS_WINDOWS
return Utilities::shouldAppsUseDarkMode();
# else
return false;
# endif
#endif
}
QColor FramelessQuickUtils::systemAccentColor()
{
#ifdef Q_OS_WINDOWS
return Utilities::getDwmColorizationColor();
#else
return {};
#endif
}
bool FramelessQuickUtils::titleBarColorVisible()
{
#ifdef Q_OS_WINDOWS
if (!Utilities::isWin10OrGreater()) {
return false;
}
const DwmColorizationArea area = Utilities::getDwmColorizationArea();
return ((area == DwmColorizationArea::TitleBar_WindowBorder) || (area == DwmColorizationArea::All));
#else
return false;
#endif
}
void FramelessQuickUtils::showMinimized2(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
// Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711
// Don't use "SW_SHOWMINIMIZED" because it will activate the current window
// instead of the next window in the Z order, which is not the default behavior
// of native Win32 applications.
ShowWindow(reinterpret_cast<HWND>(window->winId()), SW_MINIMIZE);
#else
window->showMinimized();
#endif
}
void FramelessQuickUtils::showSystemMenu(const QPointF &pos)
{
}
void FramelessQuickUtils::startSystemMove2(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemMove();
#else
# ifdef Q_OS_WINDOWS
Utilities::startSystemMove(window);
# endif
#endif
}
void FramelessQuickUtils::startSystemResize2(QWindow *window, const Qt::Edges edges)
{
Q_ASSERT(window);
if (!window) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemResize(edges);
#else
# ifdef Q_OS_WINDOWS
Utilities::startSystemResize(window, edges);
# endif
#endif #endif
} }

View File

@ -25,46 +25,72 @@
#pragma once #pragma once
#include "framelesshelper_global.h" #include "framelesshelper_global.h"
#include <QtQuick/qquickitem.h> #include <QtCore/qobject.h>
#include <QtGui/qwindow.h>
#include <QtQml/qqmlregistration.h>
QT_BEGIN_NAMESPACE
class QWindow;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
class FRAMELESSHELPER_API FramelessQuickHelper : public QQuickItem class FRAMELESSHELPER_API FramelessQuickHelper : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickHelper) Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
#ifdef QML_NAMED_ELEMENT #ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessHelper) QML_NAMED_ELEMENT(FramelessHelper)
#endif #endif
Q_PROPERTY(qreal resizeBorderThickness READ resizeBorderThickness WRITE setResizeBorderThickness NOTIFY resizeBorderThicknessChanged)
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged)
public: public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr); explicit FramelessQuickHelper(QObject *parent = nullptr);
~FramelessQuickHelper() override; ~FramelessQuickHelper() override;
Q_NODISCARD qreal resizeBorderThickness() const; Q_INVOKABLE static void addWindow(QWindow *window);
void setResizeBorderThickness(const qreal val); Q_INVOKABLE static void removeWindow(QWindow *window);
};
Q_NODISCARD qreal titleBarHeight() const; class FRAMELESSHELPER_API FramelessQuickUtils : public QObject
void setTitleBarHeight(const qreal val); {
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickUtils)
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessUtils)
#endif
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL)
Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL)
Q_PROPERTY(QColor frameBorderActiveColor READ frameBorderActiveColor NOTIFY frameBorderActiveColorChanged FINAL)
Q_PROPERTY(QColor frameBorderInactiveColor READ frameBorderInactiveColor NOTIFY frameBorderInactiveColorChanged FINAL)
Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL)
Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL)
Q_PROPERTY(bool titleBarColorVisible READ titleBarColorVisible NOTIFY titleBarColorVisibleChanged FINAL)
Q_NODISCARD bool resizable() const; public:
void setResizable(const bool val); explicit FramelessQuickUtils(QObject *parent = nullptr);
~FramelessQuickUtils() override;
Q_NODISCARD Q_INVOKABLE bool isWindowFrameless() const; Q_NODISCARD static qreal titleBarHeight();
Q_NODISCARD static bool frameBorderVisible();
Q_NODISCARD static qreal frameBorderThickness();
Q_NODISCARD static QColor frameBorderActiveColor();
Q_NODISCARD static QColor frameBorderInactiveColor();
Q_NODISCARD static bool darkModeEnabled();
Q_NODISCARD static QColor systemAccentColor();
Q_NODISCARD static bool titleBarColorVisible();
public Q_SLOTS: Q_INVOKABLE static void showMinimized2(QWindow *window);
void removeWindowFrame(); Q_INVOKABLE static void showSystemMenu(const QPointF &pos);
void bringBackWindowFrame(); Q_INVOKABLE static void startSystemMove2(QWindow *window);
void setHitTestVisible(QQuickItem *item, const bool visible); Q_INVOKABLE static void startSystemResize2(QWindow *window, const Qt::Edges edges);
void showMinimized();
Q_SIGNALS: Q_SIGNALS:
void resizeBorderThicknessChanged(qreal); void frameBorderActiveColorChanged();
void titleBarHeightChanged(qreal); void frameBorderInactiveColorChanged();
void resizableChanged(bool); void darkModeEnabledChanged();
void systemAccentColorChanged();
void titleBarColorVisibleChanged();
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -158,6 +158,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
g_managerPrivate()->qtFramelessHelpers.insert(uuid, qtFramelessHelper); g_managerPrivate()->qtFramelessHelpers.insert(uuid, qtFramelessHelper);
} }
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (!g_usePureQtImplementation) {
// Work-around Win32 multi-monitor artifacts. // Work-around Win32 multi-monitor artifacts.
const QMetaObject::Connection workaroundConnection = const QMetaObject::Connection workaroundConnection =
connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){ connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){
@ -172,6 +173,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
window->resize(window->size()); window->resize(window->size());
}); });
g_managerPrivate()->win32WorkaroundConnections.insert(uuid, workaroundConnection); g_managerPrivate()->win32WorkaroundConnections.insert(uuid, workaroundConnection);
}
#endif #endif
g_managerPrivate()->mutex.unlock(); g_managerPrivate()->mutex.unlock();
if (g_usePureQtImplementation) { if (g_usePureQtImplementation) {

View File

@ -49,6 +49,7 @@ public:
Q_SIGNALS: Q_SIGNALS:
void themeChanged(); void themeChanged();
void systemMenuRequested(const QPointF &);
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE