redesign API

This commit is contained in:
Yuhang Zhao 2021-05-31 10:33:54 +08:00
parent 6f98dd8930
commit 4139cf0ab2
17 changed files with 266 additions and 258 deletions

View File

@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 3.18)
cmake_minimum_required(VERSION 3.15)
project(FramelessHelper LANGUAGES CXX)
option(BUILD_EXAMPLES "Build examples." ON)
option(TEST_UNIX "Test UNIX version (from Win32)." OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -14,7 +15,7 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
if(NOT CMAKE_DEBUG_POSTFIX)
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
if(WIN32)
set(CMAKE_DEBUG_POSTFIX d)
else()
@ -22,15 +23,23 @@ if(NOT CMAKE_DEBUG_POSTFIX)
endif()
endif()
if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
endif()
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
endif()
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
endif()
if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
endif()
@ -41,6 +50,8 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
set(SOURCES
framelesshelper_global.h
framelesshelper.h
framelesshelper.cpp
framelesswindowsmanager.h
framelesswindowsmanager.cpp
utilities.h
@ -61,10 +72,6 @@ if(WIN32)
framelesshelper_win32.cpp
)
else()
list(APPEND SOURCES
framelesshelper.h
framelesshelper.cpp
)
if(MACOS)
list(APPEND SOURCES utilities_macos.mm)
else()
@ -99,10 +106,14 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_TO_ASCII
QT_NO_KEYWORDS
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060000
QT_DISABLE_DEPRECATED_BEFORE=0x060100
FRAMELESSHELPER_BUILD_LIBRARY
)
if(TEST_UNIX)
target_compile_definitions(${PROJECT_NAME} PRIVATE FRAMELESSHELPER_TEST_UNIX)
endif()
if(WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE
WIN32_LEAN_AND_MEAN

View File

@ -79,11 +79,11 @@ void MainWindow::showEvent(QShowEvent *event)
const auto win = windowHandle();
if (win) {
FramelessWindowsManager::addWindow(win);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->iconButton);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->minimizeButton);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->maximizeButton);
FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->closeButton);
FramelessWindowsManager::addIgnoreObject(win, appMainWindow->menubar);
FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->iconButton, true);
FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->minimizeButton, true);
FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->maximizeButton, true);
FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->closeButton, true);
FramelessWindowsManager::setHitTestVisible(win, appMainWindow->menubar, true);
inited = true;
}
}

View File

@ -79,7 +79,7 @@ Window {
MinimizeButton {
id: minimizeButton
onClicked: window.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(minimizeButton)
Component.onCompleted: framelessHelper.setHitTestVisible(minimizeButton, true)
}
MaximizeButton {
@ -92,13 +92,13 @@ Window {
window.showMaximized()
}
}
Component.onCompleted: framelessHelper.addIgnoreObject(maximizeButton)
Component.onCompleted: framelessHelper.setHitTestVisible(maximizeButton, true)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.addIgnoreObject(closeButton)
Component.onCompleted: framelessHelper.setHitTestVisible(closeButton, true)
}
}
}

View File

@ -46,7 +46,11 @@ void Widget::showEvent(QShowEvent *event)
QWidget::showEvent(event);
static bool inited = false;
if (!inited) {
FramelessWindowsManager::addWindow(windowHandle());
QWindow *win = windowHandle();
FramelessWindowsManager::addWindow(win);
FramelessWindowsManager::setHitTestVisible(win, m_minimizeButton, true);
FramelessWindowsManager::setHitTestVisible(win, m_maximizeButton, true);
FramelessWindowsManager::setHitTestVisible(win, m_closeButton, true);
inited = true;
}
}
@ -134,9 +138,6 @@ void Widget::setupUi()
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);

View File

@ -63,44 +63,30 @@ void FramelessHelper::setTitleBarHeight(const int val)
m_titleBarHeight = val;
}
QObjectList FramelessHelper::getIgnoreObjects(const QWindow *window) const
{
Q_ASSERT(window);
QObjectList ret{};
const QObjectList objs = m_ignoreObjects.value(window);
if (!objs.isEmpty()) {
for (auto &&_obj : qAsConst(objs)) {
if (_obj) {
ret.append(_obj);
}
}
}
return ret;
}
void FramelessHelper::addIgnoreObject(const QWindow *window, QObject *val)
{
Q_ASSERT(window);
QObjectList objs = m_ignoreObjects[window];
objs.append(val);
m_ignoreObjects[window] = objs;
}
bool FramelessHelper::getResizable(const QWindow *window) const
{
Q_ASSERT(window);
if (!window) {
return false;
}
return !m_fixedSize.value(window);
}
void FramelessHelper::setResizable(const QWindow *window, const bool val)
{
Q_ASSERT(window);
m_fixedSize[window] = !val;
if (!window) {
return;
}
m_fixedSize.insert(window, !val);
}
void FramelessHelper::removeWindowFrame(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
// TODO: check whether these flags are correct for Linux and macOS.
window->setFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint
| Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
@ -112,6 +98,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (!object || !event) {
return false;
}
if (!object->isWindowType()) {
return false;
}
@ -165,22 +154,22 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
}
return Qt::CursorShape::ArrowCursor;
};
const auto isInTitlebarArea = [this](const QPointF &globalPoint,
const QPointF &point,
const QWindow *window) -> bool {
const auto isInTitlebarArea = [this](const QPointF &point, const QWindow *window) -> bool {
Q_ASSERT(window);
return (point.y() <= m_titleBarHeight)
&& !Utilities::isMouseInSpecificObjects(globalPoint, getIgnoreObjects(window));
if (!window) {
return false;
}
return (point.y() <= m_titleBarHeight) && !Utilities::isHitTestVisible(window);
};
const auto moveOrResize =
[this, &getWindowEdges, &isInTitlebarArea](const QPointF &globalPoint,
const QPointF &point,
QWindow *window) {
[this, &getWindowEdges, &isInTitlebarArea](const QPointF &point, QWindow *window) {
Q_ASSERT(window);
//const QPointF deltaPoint = globalPoint - m_pOldMousePos;
if (!window) {
return;
}
const Qt::Edges edges = getWindowEdges(point, window->width(), window->height());
if (edges == Qt::Edges{}) {
if (isInTitlebarArea(globalPoint, point, window)) {
if (isInTitlebarArea(point, window)) {
if (!window->startSystemMove()) {
// ### FIXME: TO BE IMPLEMENTED!
qWarning() << "Current OS doesn't support QWindow::startSystemMove().";
@ -188,8 +177,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
}
} else {
if ((window->windowState() == Qt::WindowState::WindowNoState)
&& !Utilities::isMouseInSpecificObjects(globalPoint, getIgnoreObjects(window))
&& getResizable(window)) {
&& !Utilities::isHitTestVisible(window) && getResizable(window)) {
if (!window->startSystemResize(edges)) {
// ### FIXME: TO BE IMPLEMENTED!
qWarning() << "Current OS doesn't support QWindow::startSystemResize().";
@ -199,6 +187,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
};
const auto getMousePos = [](const QMouseEvent *e, const bool global) -> QPointF {
Q_ASSERT(e);
if (!e) {
return {};
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
return global ? e->globalPosition() : e->scenePosition();
#else
@ -212,9 +203,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
if (mouseEvent->button() != Qt::MouseButton::LeftButton) {
break;
}
if (isInTitlebarArea(getMousePos(mouseEvent, true),
getMousePos(mouseEvent, false),
currentWindow)) {
if (isInTitlebarArea(getMousePos(mouseEvent, false), currentWindow)) {
if (currentWindow->windowState() == Qt::WindowState::WindowFullScreen) {
break;
}
@ -235,9 +224,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
}
m_bIsMRBPressed = true;
m_pOldMousePos = getMousePos(mouseEvent, true);
moveOrResize(getMousePos(mouseEvent, true),
getMousePos(mouseEvent, false),
currentWindow);
moveOrResize(getMousePos(mouseEvent, false), currentWindow);
}
} break;
case QEvent::MouseMove: {
@ -265,12 +252,11 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
case QEvent::TouchBegin:
case QEvent::TouchUpdate: {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const auto point = static_cast<QTouchEvent *>(event)->points().first();
moveOrResize(point.globalPosition(), point.position(), currentWindow);
const auto point = static_cast<QTouchEvent *>(event)->points().first().position();
#else
const auto point = static_cast<QTouchEvent *>(event)->touchPoints().first();
moveOrResize(point.screenPos(), point.pos(), currentWindow);
const auto point = static_cast<QTouchEvent *>(event)->touchPoints().first().pos();
#endif
moveOrResize(point, currentWindow);
} break;
default:
break;

View 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
@ -34,7 +34,7 @@ QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
class FRAMELESSHELPER_EXPORT FramelessHelper : public QObject
class FRAMELESSHELPER_API FramelessHelper : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessHelper)
@ -54,9 +54,6 @@ public:
int getTitleBarHeight() const;
void setTitleBarHeight(const int val);
void addIgnoreObject(const QWindow *window, QObject *val);
QObjectList getIgnoreObjects(const QWindow *window) const;
bool getResizable(const QWindow *window) const;
void setResizable(const QWindow *window, const bool val);
@ -68,7 +65,6 @@ private:
// the scale factor is 1.0. Don't know how to acquire these values on UNIX
// platforms through native API.
int m_borderWidth = 8, m_borderHeight = 8, m_titleBarHeight = 30;
QHash<const QWindow *, QObjectList> m_ignoreObjects = {};
QHash<const QWindow *, bool> m_fixedSize = {};
};
#endif

View 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

View File

@ -26,14 +26,14 @@
#include <QtCore/qglobal.h>
#ifndef FRAMELESSHELPER_EXPORT
#ifndef FRAMELESSHELPER_API
#ifdef FRAMELESSHELPER_STATIC
#define FRAMELESSHELPER_EXPORT
#define FRAMELESSHELPER_API
#else
#ifdef FRAMELESSHELPER_BUILD_LIBRARY
#define FRAMELESSHELPER_EXPORT Q_DECL_EXPORT
#define FRAMELESSHELPER_API Q_DECL_EXPORT
#else
#define FRAMELESSHELPER_EXPORT Q_DECL_IMPORT
#define FRAMELESSHELPER_API Q_DECL_IMPORT
#endif
#endif
#endif
@ -42,31 +42,31 @@
#define Q_OS_WINDOWS
#endif
#ifndef Q_DISABLE_MOVE
#define Q_DISABLE_MOVE(Class) \
#ifndef Q_DISABLE_COPY_MOVE
#define Q_DISABLE_COPY_MOVE(Class) \
Q_DISABLE_COPY(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
#if !defined(Q_OS_WINDOWS) || defined(FRAMELESSHELPER_TEST_UNIX)
#define FRAMELESSHELPER_USE_UNIX_VERSION
#endif
namespace _flh_global {
[[maybe_unused]] const char _flh_framelessEnabled_flag[] = "_FRAMELESSHELPER_FRAMELESS_MODE_ENABLED";
[[maybe_unused]] const char _flh_borderWidth_flag[] = "_FRAMELESSHELPER_WINDOW_BORDER_WIDTH";
[[maybe_unused]] const char _flh_borderHeight_flag[] = "_FRAMELESSHELPER_WINDOW_BORDER_HEIGHT";
[[maybe_unused]] const char _flh_titleBarHeight_flag[] = "_FRAMELESSHELPER_WINDOW_TITLE_BAR_HEIGHT";
[[maybe_unused]] const char _flh_ignoredObjects_flag[] = "_FRAMELESSHELPER_WINDOW_TITLE_BAR_IGNORED_OBJECTS";
[[maybe_unused]] const char _flh_hitTestVisible_flag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE";
[[maybe_unused]] const char _flh_useNativeTitleBar_flag[] = "_FRAMELESSHELPER_USE_NATIVE_TITLE_BAR";
[[maybe_unused]] const char _flh_preserveNativeFrame_flag[] = "_FRAMELESSHELPER_PRESERVE_NATIVE_WINDOW_FRAME";
[[maybe_unused]] const char _flh_forcePreserveNativeFrame_flag[] = "_FRAMELESSHELPER_FORCE_PRESERVE_NATIVE_WINDOW_FRAME";
[[maybe_unused]] const char _flh_nativeParent_flag[] = "_FRAMELESSHELPER_NATIVE_PARENT";
}

View File

@ -156,42 +156,13 @@ void FramelessHelperWin::removeFramelessWindow(QWindow *window)
installHelper(window, false);
}
void FramelessHelperWin::setIgnoredObjects(QWindow *window, const QObjectList &objects)
{
Q_ASSERT(window);
if (!window) {
return;
}
window->setProperty(_flh_global::_flh_ignoredObjects_flag, QVariant::fromValue(objects));
}
QObjectList FramelessHelperWin::getIgnoredObjects(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return {};
}
return qvariant_cast<QObjectList>(window->property(_flh_global::_flh_ignoredObjects_flag));
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
#else
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
// this, everyone is assuming it will never be null, including Microsoft,
// but according to Lucas, frameless applications crashed on many Win7
// machines because it's null. The temporary solution is also strange:
// upgrade drivers or switch to the basic theme.
if (!result) {
return false;
}
// The example code in Qt's documentation has this check. I don't know
// whether we really need this check or not, but adding this check won't
// bring us harm anyway.
if (eventType != "windows_generic_MSG") {
if ((eventType != QByteArrayLiteral("windows_generic_MSG")) || !message || !result) {
return false;
}
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
@ -200,13 +171,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
#else
const auto msg = static_cast<LPMSG>(message);
#endif
if (!msg || (msg && !msg->hwnd)) {
if (!msg->hwnd) {
// Why sometimes the window handle is null? Is it designed to be?
// Anyway, we should skip it in this case.
return false;
}
const QWindow *window = Utilities::findWindow(reinterpret_cast<WId>(msg->hwnd));
if (!window || (window && !window->property(_flh_global::_flh_framelessEnabled_flag).toBool())) {
if (!window || !window->property(_flh_global::_flh_framelessEnabled_flag).toBool()) {
return false;
}
switch (msg->message) {
@ -551,15 +522,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
break;
}
const qreal dpr = window->devicePixelRatio();
const QPointF globalMouse = QCursor::pos(window->screen()) * dpr;
const QPointF globalMouse = Utilities::getGlobalMousePosition(window);
POINT winLocalMouse = {qRound(globalMouse.x()), qRound(globalMouse.y())};
ScreenToClient(msg->hwnd, &winLocalMouse);
const QPointF localMouse = {static_cast<qreal>(winLocalMouse.x), static_cast<qreal>(winLocalMouse.y)};
const bool isInIgnoreObjects = Utilities::isMouseInSpecificObjects(globalMouse, getIgnoredObjects(window), dpr);
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 isTitleBar = (localMouse.y() <= tbh) && !Utilities::isHitTestVisible(window);
const bool isTop = localMouse.y() <= bh;
if (shouldHaveWindowFrame()) {
// This will handle the left, right and bottom parts of the frame

View File

@ -32,7 +32,7 @@ QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
class FRAMELESSHELPER_EXPORT FramelessHelperWin : public QAbstractNativeEventFilter
class FRAMELESSHELPER_API FramelessHelperWin : public QAbstractNativeEventFilter
{
Q_DISABLE_COPY_MOVE(FramelessHelperWin)
@ -44,9 +44,6 @@ public:
static bool isWindowFrameless(const QWindow *window);
static void removeFramelessWindow(QWindow *window);
static void setIgnoredObjects(QWindow *window, const QObjectList &objects);
static QObjectList getIgnoredObjects(const QWindow *window);
static void setBorderWidth(QWindow *window, const int bw);
static void setBorderHeight(QWindow *window, const int bh);
static void setTitleBarHeight(QWindow *window, const int tbh);

View File

@ -97,11 +97,11 @@ void FramelessQuickHelper::removeWindowFrame()
FramelessWindowsManager::addWindow(window());
}
void FramelessQuickHelper::addIgnoreObject(QQuickItem *val)
void FramelessQuickHelper::setHitTestVisible(QQuickItem *item, const bool visible)
{
Q_ASSERT(val);
if (!val) {
Q_ASSERT(item);
if (!item) {
return;
}
FramelessWindowsManager::addIgnoreObject(window(), val);
FramelessWindowsManager::setHitTestVisible(window(), item, visible);
}

View File

@ -27,11 +27,11 @@
#include "framelesshelper_global.h"
#include <QtQuick/qquickitem.h>
class FRAMELESSHELPER_EXPORT FramelessQuickHelper : public QQuickItem
class FRAMELESSHELPER_API FramelessQuickHelper : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessHelper)
#endif
Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged)
@ -60,7 +60,7 @@ public:
public Q_SLOTS:
void removeWindowFrame();
void addIgnoreObject(QQuickItem *val);
void setHitTestVisible(QQuickItem *item, const bool visible);
Q_SIGNALS:
void borderWidthChanged();

View File

@ -23,136 +23,141 @@
*/
#include "framelesswindowsmanager.h"
#include <QtCore/qvariant.h>
#include <QtGui/qwindow.h>
#include "utilities.h"
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
#include "framelesshelper.h"
#else
#include <QtGui/qscreen.h>
#include "framelesshelper_win32.h"
#else
#include "framelesshelper.h"
#endif
#ifndef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_GLOBAL_STATIC(FramelessHelper, framelessHelper)
#endif
FramelessWindowsManager::FramelessWindowsManager() = default;
void FramelessWindowsManager::addWindow(const QWindow *window)
void FramelessWindowsManager::addWindow(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
const auto win = const_cast<QWindow *>(window);
FramelessHelperWin::addFramelessWindow(win);
// Work-around a Win32 multi-monitor bug.
QObject::connect(win, &QWindow::screenChanged, [win](QScreen *screen){
Q_UNUSED(screen);
win->resize(win->size());
});
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
framelessHelper()->removeWindowFrame(window);
#else
framelessHelper()->removeWindowFrame(const_cast<QWindow *>(window));
FramelessHelperWin::addFramelessWindow(window);
// Work-around a Win32 multi-monitor bug.
QObject::connect(window, &QWindow::screenChanged, [window](QScreen *screen){
Q_UNUSED(screen);
window->resize(window->size());
});
#endif
}
void FramelessWindowsManager::addIgnoreObject(const QWindow *window, QObject *object)
void FramelessWindowsManager::setHitTestVisible(QWindow *window, QObject *object, const bool value)
{
Q_ASSERT(window);
if (!window) {
Q_ASSERT(object);
if (!window || !object) {
return;
}
#ifdef Q_OS_WINDOWS
QObjectList objects = FramelessHelperWin::getIgnoredObjects(window);
objects.append(object);
FramelessHelperWin::setIgnoredObjects(const_cast<QWindow *>(window), objects);
#else
framelessHelper()->addIgnoreObject(window, object);
#endif
if (!object->isWidgetType() && !object->inherits("QQuickItem")) {
qWarning() << object << "is not a QWidget or QQuickItem.";
return;
}
const QObject *nativeParent = Utilities::getNativeParent(object);
Q_ASSERT(nativeParent);
if (!nativeParent) {
return;
}
window->setProperty(_flh_global::_flh_nativeParent_flag, QVariant::fromValue(nativeParent));
object->setProperty(_flh_global::_flh_hitTestVisible_flag, value);
}
int FramelessWindowsManager::getBorderWidth(const QWindow *window)
{
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_UNUSED(window);
return framelessHelper()->getBorderWidth();
#else
Q_ASSERT(window);
if (!window) {
return 0;
}
return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderWidth, false);
#else
Q_UNUSED(window);
return framelessHelper()->getBorderWidth();
#endif
}
void FramelessWindowsManager::setBorderWidth(const QWindow *window, const int value)
void FramelessWindowsManager::setBorderWidth(QWindow *window, const int value)
{
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_UNUSED(window);
framelessHelper()->setBorderWidth(value);
#else
Q_ASSERT(window);
if (!window) {
return;
}
FramelessHelperWin::setBorderWidth(const_cast<QWindow *>(window), value);
#else
Q_UNUSED(window);
framelessHelper()->setBorderWidth(value);
FramelessHelperWin::setBorderWidth(window, value);
#endif
}
int FramelessWindowsManager::getBorderHeight(const QWindow *window)
{
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_UNUSED(window);
return framelessHelper()->getBorderHeight();
#else
Q_ASSERT(window);
if (!window) {
return 0;
}
return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderHeight, false);
#else
Q_UNUSED(window);
return framelessHelper()->getBorderHeight();
#endif
}
void FramelessWindowsManager::setBorderHeight(const QWindow *window, const int value)
void FramelessWindowsManager::setBorderHeight(QWindow *window, const int value)
{
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_UNUSED(window);
framelessHelper()->setBorderHeight(value);
#else
Q_ASSERT(window);
if (!window) {
return;
}
FramelessHelperWin::setBorderHeight(const_cast<QWindow *>(window), value);
#else
Q_UNUSED(window);
framelessHelper()->setBorderHeight(value);
FramelessHelperWin::setBorderHeight(window, value);
#endif
}
int FramelessWindowsManager::getTitleBarHeight(const QWindow *window)
{
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_UNUSED(window);
return framelessHelper()->getTitleBarHeight();
#else
Q_ASSERT(window);
if (!window) {
return 0;
}
return Utilities::getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, false);
#else
Q_UNUSED(window);
return framelessHelper()->getTitleBarHeight();
#endif
}
void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int value)
void FramelessWindowsManager::setTitleBarHeight(QWindow *window, const int value)
{
#ifdef Q_OS_WINDOWS
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_UNUSED(window);
framelessHelper()->setTitleBarHeight(value);
#else
Q_ASSERT(window);
if (!window) {
return;
}
FramelessHelperWin::setTitleBarHeight(const_cast<QWindow *>(window), value);
#else
Q_UNUSED(window);
framelessHelper()->setTitleBarHeight(value);
FramelessHelperWin::setTitleBarHeight(window, value);
#endif
}
@ -162,22 +167,22 @@ bool FramelessWindowsManager::getResizable(const QWindow *window)
if (!window) {
return false;
}
#ifdef Q_OS_WINDOWS
return !Utilities::isWindowFixedSize(window);
#else
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
return framelessHelper()->getResizable(window);
#else
return !Utilities::isWindowFixedSize(window);
#endif
}
void FramelessWindowsManager::setResizable(const QWindow *window, const bool value)
void FramelessWindowsManager::setResizable(QWindow *window, const bool value)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
const_cast<QWindow *>(window)->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value);
#else
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
framelessHelper()->setResizable(window, value);
#else
window->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value);
#endif
}

View 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
@ -32,7 +32,7 @@ QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
class FRAMELESSHELPER_EXPORT FramelessWindowsManager
class FRAMELESSHELPER_API FramelessWindowsManager
{
Q_DISABLE_COPY_MOVE(FramelessWindowsManager)
@ -40,19 +40,19 @@ public:
explicit FramelessWindowsManager();
~FramelessWindowsManager() = default;
static void addWindow(const QWindow *window);
static void addWindow(QWindow *window);
static void addIgnoreObject(const QWindow *window, QObject *object);
static void setHitTestVisible(QWindow *window, QObject *object, const bool value = true);
static int getBorderWidth(const QWindow *window);
static void setBorderWidth(const QWindow *window, const int value);
static void setBorderWidth(QWindow *window, const int value);
static int getBorderHeight(const QWindow *window);
static void setBorderHeight(const QWindow *window, const int value);
static void setBorderHeight(QWindow *window, const int value);
static int getTitleBarHeight(const QWindow *window);
static void setTitleBarHeight(const QWindow *window, const int value);
static void setTitleBarHeight(QWindow *window, const int value);
static bool getResizable(const QWindow *window);
static void setResizable(const QWindow *window, const bool value = true);
static void setResizable(QWindow *window, const bool value = true);
};

View File

@ -9,7 +9,7 @@ DEFINES += \
QT_NO_CAST_TO_ASCII \
QT_NO_KEYWORDS \
QT_DEPRECATED_WARNINGS \
QT_DISABLE_DEPRECATED_BEFORE=0x060000 \
QT_DISABLE_DEPRECATED_BEFORE=0x060100 \
FRAMELESSHELPER_BUILD_LIBRARY
HEADERS += \
framelesshelper_global.h \

View File

@ -67,50 +67,87 @@ bool Utilities::isWindowFixedSize(const QWindow *window)
return false;
}
bool Utilities::isMouseInSpecificObjects(const QPointF &mousePos, const QObjectList &objects, const qreal dpr)
QPointF Utilities::getGlobalMousePosition(const QWindow *window)
{
if (mousePos.isNull()) {
qWarning() << "Mouse position point is null.";
if (window) {
return (QCursor::pos(window->screen()) * window->devicePixelRatio());
} else {
const qreal dpr = 1.0; // TODO
return (QCursor::pos() * dpr);
}
}
bool Utilities::isHitTestVisible(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return false;
}
if (objects.isEmpty()) {
qWarning() << "Object list is empty.";
QObjectList objs = {};
const auto target = qvariant_cast<const QObject *>(window->property(_flh_global::_flh_nativeParent_flag));
objs = target ? target->findChildren<QObject *>() : window->findChildren<QObject *>();
if (objs.isEmpty()) {
return false;
}
for (auto &&object : qAsConst(objects)) {
if (!object) {
qWarning() << "Object pointer is null.";
for (auto &&obj : qAsConst(objs)) {
if (!obj || !(obj->isWidgetType() || obj->inherits("QQuickItem"))) {
continue;
}
if (!object->isWidgetType() && !object->inherits("QQuickItem")) {
qWarning() << object << "is not a QWidget or QQuickItem!";
if (!obj->property(_flh_global::_flh_hitTestVisible_flag).toBool() || !obj->property("visible").toBool()) {
continue;
}
if (!object->property("visible").toBool()) {
qDebug() << "Skipping invisible object" << object;
continue;
}
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()};
if (parent->isWindowType()) {
break;
}
}
return point;
};
const QPointF originPoint = mapOriginPointToWindow(object);
const qreal width = object->property("width").toReal();
const qreal height = object->property("height").toReal();
const QPointF originPoint = mapOriginPointToWindow(obj);
const qreal width = obj->property("width").toReal();
const qreal height = obj->property("height").toReal();
const qreal dpr = window->devicePixelRatio();
const QRectF rect = {originPoint.x() * dpr, originPoint.y() * dpr, width * dpr, height * dpr};
if (rect.contains(mousePos)) {
if (rect.contains(getGlobalMousePosition(window))) {
return true;
}
}
return false;
}
QObject *Utilities::getNativeParent(const QObject *object)
{
Q_ASSERT(object);
if (!object) {
return nullptr;
}
if (!object->isWidgetType() && !object->inherits("QQuickItem")) {
qWarning() << object << "is not a QWidget or a QQuickItem.";
return nullptr;
}
QObject *parent = object->parent();
while (parent) {
QObject *p = parent->parent();
if (!p) {
return parent;
}
if (p->isWindowType()) {
return parent;
}
parent = p;
}
return parent;
}
QPointF Utilities::mapOriginPointToWindow(const QObject *object)
{
Q_ASSERT(object);
if (!object) {
return {};
}
if (!object->isWidgetType() && !object->inherits("QQuickItem")) {
qWarning() << object << "is not a QWidget or a QQuickItem.";
return {};
}
QPointF point = {object->property("x").toReal(), object->property("y").toReal()};
for (QObject *parent = object->parent(); parent; parent = parent->parent()) {
point += {parent->property("x").toReal(), parent->property("y").toReal()};
if (parent->isWindowType()) {
break;
}
}
return point;
}

View File

@ -38,42 +38,48 @@ enum class SystemMetric
};
// Common
FRAMELESSHELPER_EXPORT int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue = false);
FRAMELESSHELPER_API int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue = false);
FRAMELESSHELPER_EXPORT bool isLightThemeEnabled();
FRAMELESSHELPER_EXPORT bool isDarkThemeEnabled();
FRAMELESSHELPER_API bool isLightThemeEnabled();
FRAMELESSHELPER_API bool isDarkThemeEnabled();
FRAMELESSHELPER_EXPORT QWindow *findWindow(const WId winId);
FRAMELESSHELPER_API QWindow *findWindow(const WId winId);
FRAMELESSHELPER_EXPORT bool shouldUseNativeTitleBar();
FRAMELESSHELPER_API bool shouldUseNativeTitleBar();
FRAMELESSHELPER_EXPORT bool isWindowFixedSize(const QWindow *window);
FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window);
FRAMELESSHELPER_EXPORT bool isMouseInSpecificObjects(const QPointF &mousePos, const QObjectList &objects, const qreal dpr = 1.0);
FRAMELESSHELPER_API QPointF getGlobalMousePosition(const QWindow *window);
FRAMELESSHELPER_EXPORT QColor getNativeWindowFrameColor(const bool isActive = true);
FRAMELESSHELPER_API bool isHitTestVisible(const QWindow *window);
FRAMELESSHELPER_API QColor getNativeWindowFrameColor(const bool isActive = true);
FRAMELESSHELPER_API QObject *getNativeParent(const QObject *object);
FRAMELESSHELPER_API QPointF mapOriginPointToWindow(const QObject *object);
#ifdef Q_OS_WINDOWS
// Windows specific
FRAMELESSHELPER_EXPORT bool isWin7OrGreater();
FRAMELESSHELPER_EXPORT bool isWin8OrGreater();
FRAMELESSHELPER_EXPORT bool isWin8Point1OrGreater();
FRAMELESSHELPER_EXPORT bool isWin10OrGreater();
FRAMELESSHELPER_EXPORT bool isWin10OrGreater(const int subVer);
FRAMELESSHELPER_API bool isWin7OrGreater();
FRAMELESSHELPER_API bool isWin8OrGreater();
FRAMELESSHELPER_API bool isWin8Point1OrGreater();
FRAMELESSHELPER_API bool isWin10OrGreater();
FRAMELESSHELPER_API bool isWin10OrGreater(const int subVer);
FRAMELESSHELPER_EXPORT bool isDwmBlurAvailable();
FRAMELESSHELPER_API bool isDwmBlurAvailable();
FRAMELESSHELPER_EXPORT bool isColorizationEnabled();
FRAMELESSHELPER_EXPORT QColor getColorizationColor();
FRAMELESSHELPER_API bool isColorizationEnabled();
FRAMELESSHELPER_API QColor getColorizationColor();
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_API void triggerFrameChange(const QWindow *window);
FRAMELESSHELPER_API void updateFrameMargins(const QWindow *window, const bool reset);
FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable);
FRAMELESSHELPER_EXPORT quint32 getWindowDpi(const QWindow *window);
FRAMELESSHELPER_EXPORT QMargins getWindowNativeFrameMargins(const QWindow *window);
FRAMELESSHELPER_API quint32 getWindowDpi(const QWindow *window);
FRAMELESSHELPER_API QMargins getWindowNativeFrameMargins(const QWindow *window);
FRAMELESSHELPER_EXPORT void displaySystemMenu(const QWindow *window, const QPoint &pos = {});
FRAMELESSHELPER_API void displaySystemMenu(const QWindow *window, const QPoint &pos = {});
#endif
}