forked from github_mirror/framelesshelper
Merge pull request #84 from altairwei/2.0-linux
framelesshelper v2.0 Core API Linux 端的实现
This commit is contained in:
commit
6a0dc5052c
|
@ -50,6 +50,7 @@ else()
|
||||||
if(MACOS)
|
if(MACOS)
|
||||||
list(APPEND SOURCES utilities_macos.mm)
|
list(APPEND SOURCES utilities_macos.mm)
|
||||||
else()
|
else()
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
|
||||||
list(APPEND SOURCES utilities_linux.cpp)
|
list(APPEND SOURCES utilities_linux.cpp)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -87,6 +88,14 @@ if(WIN32)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
dwmapi
|
dwmapi
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
if(MACOS)
|
||||||
|
#TODO
|
||||||
|
else()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
|
Qt${QT_VERSION_MAJOR}::X11Extras
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
|
|
|
@ -4,6 +4,7 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets)
|
||||||
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
|
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
add_subdirectory(widget)
|
add_subdirectory(widget)
|
||||||
add_subdirectory(mainwindow)
|
add_subdirectory(mainwindow)
|
||||||
|
add_subdirectory(minimal)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
../images.qrc
|
||||||
|
main.cpp
|
||||||
|
flwindow.h
|
||||||
|
flwindow.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
enable_language(RC)
|
||||||
|
list(APPEND SOURCES ../example.rc ../example.manifest)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(minimal WIN32 ${SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(minimal PRIVATE
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
wangwenx190::FramelessHelper
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(minimal PRIVATE
|
||||||
|
QT_NO_CAST_FROM_ASCII
|
||||||
|
QT_NO_CAST_TO_ASCII
|
||||||
|
QT_NO_KEYWORDS
|
||||||
|
QT_DEPRECATED_WARNINGS
|
||||||
|
QT_DISABLE_DEPRECATED_BEFORE=0x060100
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(minimal PRIVATE dwmapi)
|
||||||
|
endif()
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include "flwindow.h"
|
||||||
|
#include "../../framelesshelper.h"
|
||||||
|
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
FRAMELESSHELPER_USE_NAMESPACE
|
||||||
|
|
||||||
|
FLWindow::FLWindow(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
setWindowFlags(Qt::FramelessWindowHint);
|
||||||
|
setupUi();
|
||||||
|
|
||||||
|
move(screen()->geometry().center() - frameGeometry().center());
|
||||||
|
}
|
||||||
|
|
||||||
|
FLWindow::~FLWindow()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLWindow::initFramelessWindow()
|
||||||
|
{
|
||||||
|
FramelessHelper* helper = new FramelessHelper(windowHandle());
|
||||||
|
helper->setResizeBorderThickness(4);
|
||||||
|
helper->setTitleBarHeight(m_titleBarWidget->height());
|
||||||
|
helper->setResizable(true);
|
||||||
|
helper->setHitTestVisible(m_minimizeButton);
|
||||||
|
helper->setHitTestVisible(m_maximizeButton);
|
||||||
|
helper->setHitTestVisible(m_closeButton);
|
||||||
|
helper->install();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLWindow::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
|
||||||
|
static bool inited = false;
|
||||||
|
if (!inited) {
|
||||||
|
inited = true;
|
||||||
|
initFramelessWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FLWindow::setupUi()
|
||||||
|
{
|
||||||
|
resize(800, 600);
|
||||||
|
|
||||||
|
m_titleBarWidget = new QWidget(this);
|
||||||
|
m_titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
m_titleBarWidget->setFixedHeight(40);
|
||||||
|
m_titleBarWidget->setStyleSheet(QString::fromLatin1("background:grey"));
|
||||||
|
|
||||||
|
m_minimizeButton = new QPushButton(m_titleBarWidget);
|
||||||
|
m_minimizeButton->setText(QStringLiteral("Min"));
|
||||||
|
m_minimizeButton->setObjectName(QStringLiteral("MinimizeButton"));
|
||||||
|
connect(m_minimizeButton, &QPushButton::clicked, this, &QWidget::showMinimized);
|
||||||
|
|
||||||
|
m_maximizeButton = new QPushButton(m_titleBarWidget);
|
||||||
|
m_maximizeButton->setText(QStringLiteral("Max"));
|
||||||
|
m_maximizeButton->setObjectName(QStringLiteral("MaximizeButton"));
|
||||||
|
connect(m_maximizeButton, &QPushButton::clicked, this, [this](){
|
||||||
|
if (isMaximized() || isFullScreen()) {
|
||||||
|
showNormal();
|
||||||
|
} else {
|
||||||
|
showMaximized();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_closeButton = new QPushButton(m_titleBarWidget);
|
||||||
|
m_closeButton->setText(QStringLiteral("Close"));
|
||||||
|
m_closeButton->setObjectName(QStringLiteral("CloseButton"));
|
||||||
|
connect(m_closeButton, &QPushButton::clicked, this, &QWidget::close);
|
||||||
|
|
||||||
|
const auto titleBarLayout = new QHBoxLayout(m_titleBarWidget);
|
||||||
|
titleBarLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
titleBarLayout->setSpacing(10);
|
||||||
|
titleBarLayout->addStretch();
|
||||||
|
titleBarLayout->addWidget(m_minimizeButton);
|
||||||
|
titleBarLayout->addWidget(m_maximizeButton);
|
||||||
|
titleBarLayout->addWidget(m_closeButton);
|
||||||
|
titleBarLayout->addStretch();
|
||||||
|
m_titleBarWidget->setLayout(titleBarLayout);
|
||||||
|
|
||||||
|
const auto mainLayout = new QVBoxLayout(this);
|
||||||
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
mainLayout->setSpacing(0);
|
||||||
|
mainLayout->addWidget(m_titleBarWidget);
|
||||||
|
mainLayout->addStretch();
|
||||||
|
setLayout(mainLayout);
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
|
class FLWindow : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit FLWindow(QWidget *parent = nullptr);
|
||||||
|
~FLWindow();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initFramelessWindow();
|
||||||
|
void setupUi();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget *m_titleBarWidget = nullptr;
|
||||||
|
QPushButton *m_minimizeButton = nullptr;
|
||||||
|
QPushButton *m_maximizeButton = nullptr;
|
||||||
|
QPushButton *m_closeButton = nullptr;
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include <QtWidgets/qapplication.h>
|
||||||
|
#include "flwindow.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||||
|
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
QApplication application(argc, argv);
|
||||||
|
|
||||||
|
FLWindow win;
|
||||||
|
win.show();
|
||||||
|
|
||||||
|
return QApplication::exec();
|
||||||
|
}
|
|
@ -31,6 +31,7 @@
|
||||||
#include <QtWidgets/qpushbutton.h>
|
#include <QtWidgets/qpushbutton.h>
|
||||||
#include "../../utilities.h"
|
#include "../../utilities.h"
|
||||||
#include "../../framelesswindowsmanager.h"
|
#include "../../framelesswindowsmanager.h"
|
||||||
|
#include "../../framelesshelper.h"
|
||||||
|
|
||||||
FRAMELESSHELPER_USE_NAMESPACE
|
FRAMELESSHELPER_USE_NAMESPACE
|
||||||
|
|
||||||
|
|
|
@ -29,182 +29,460 @@
|
||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
#include <QtGui/qevent.h>
|
#include <QtGui/qevent.h>
|
||||||
#include <QtGui/qwindow.h>
|
#include <QtGui/qwindow.h>
|
||||||
|
#include <QtGui/qscreen.h>
|
||||||
#include "framelesswindowsmanager.h"
|
#include "framelesswindowsmanager.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
|
||||||
FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) {}
|
FramelessHelper::FramelessHelper(QWindow *window)
|
||||||
|
: QObject(window)
|
||||||
void FramelessHelper::removeWindowFrame(QWindow *window)
|
, m_window(window)
|
||||||
|
, m_hoveredFrameSection(Qt::NoSection)
|
||||||
|
, m_clickedFrameSection(Qt::NoSection)
|
||||||
{
|
{
|
||||||
Q_ASSERT(window);
|
Q_ASSERT(window != nullptr && window->isTopLevel());
|
||||||
if (!window) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window->setFlags(window->flags() | Qt::FramelessWindowHint);
|
|
||||||
window->installEventFilter(this);
|
|
||||||
window->setProperty(Constants::kFramelessModeFlag, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramelessHelper::bringBackWindowFrame(QWindow *window)
|
/*!
|
||||||
|
Setup the window, make it frameless.
|
||||||
|
*/
|
||||||
|
void FramelessHelper::install()
|
||||||
{
|
{
|
||||||
Q_ASSERT(window);
|
QRect origRect = m_window->geometry();
|
||||||
if (!window) {
|
m_origWindowFlags = m_window->flags();
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
m_window->setFlags(Qt::Window);
|
||||||
|
#else
|
||||||
|
m_window->setFlags(m_origWindowFlags | Qt::FramelessWindowHint);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_window->setGeometry(origRect);
|
||||||
|
resizeWindow(origRect.size());
|
||||||
|
|
||||||
|
m_window->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Restore the window to its original state
|
||||||
|
*/
|
||||||
|
void FramelessHelper::uninstall()
|
||||||
|
{
|
||||||
|
m_window->setFlags(m_origWindowFlags);
|
||||||
|
m_origWindowFlags = Qt::WindowFlags();
|
||||||
|
resizeWindow(QSize());
|
||||||
|
|
||||||
|
m_window->removeEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Resize non-client area
|
||||||
|
*/
|
||||||
|
void FramelessHelper::resizeWindow(const QSize& windowSize)
|
||||||
|
{
|
||||||
|
if (windowSize == this->windowSize())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
setWindowSize(windowSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect FramelessHelper::titleBarRect()
|
||||||
|
{
|
||||||
|
return QRect(0, 0, windowSize().width(), titleBarHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QRegion FramelessHelper::titleBarRegion()
|
||||||
|
{
|
||||||
|
QRegion region(titleBarRect());
|
||||||
|
|
||||||
|
for (const auto obj : m_HTVObjects) {
|
||||||
|
if (!obj || !(obj->isWidgetType() || obj->inherits("QQuickItem"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj->property("visible").toBool()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
region -= getHTVObjectRect(obj);
|
||||||
}
|
}
|
||||||
window->removeEventFilter(this);
|
|
||||||
window->setFlags(window->flags() & ~Qt::FramelessWindowHint);
|
return region;
|
||||||
window->setProperty(Constants::kFramelessModeFlag, false);
|
}
|
||||||
|
|
||||||
|
QRect FramelessHelper::clientRect()
|
||||||
|
{
|
||||||
|
QRect rect(0, 0, windowSize().width(), windowSize().height());
|
||||||
|
rect = rect.adjusted(
|
||||||
|
resizeBorderThickness(), titleBarHeight(),
|
||||||
|
-resizeBorderThickness(), -resizeBorderThickness()
|
||||||
|
);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegion FramelessHelper::nonClientRegion()
|
||||||
|
{
|
||||||
|
QRegion region(QRect(QPoint(0, 0), windowSize()));
|
||||||
|
region -= clientRect();
|
||||||
|
|
||||||
|
for (const auto obj : m_HTVObjects) {
|
||||||
|
if (!obj || !(obj->isWidgetType() || obj->inherits("QQuickItem"))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj->property("visible").toBool()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
region -= getHTVObjectRect(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FramelessHelper::isInTitlebarArea(const QPoint& pos)
|
||||||
|
{
|
||||||
|
return titleBarRegion().contains(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int kCornerFactor = 2;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Determine window frame section by coordinates.
|
||||||
|
|
||||||
|
Returns the window frame section at position \a pos, or \c Qt::NoSection
|
||||||
|
if there is no window frame section at this position.
|
||||||
|
|
||||||
|
*/
|
||||||
|
Qt::WindowFrameSection FramelessHelper::mapPosToFrameSection(const QPoint& pos)
|
||||||
|
{
|
||||||
|
int border = 0;
|
||||||
|
|
||||||
|
// TODO: get system default resize border
|
||||||
|
const int sysBorder = Utilities::getSystemMetric(m_window, SystemMetric::ResizeBorderThickness, false);
|
||||||
|
|
||||||
|
Qt::WindowStates states = m_window->windowState();
|
||||||
|
// Resizing is disabled when WindowMaximized or WindowFullScreen
|
||||||
|
if (!(states & Qt::WindowMaximized) && !(states & Qt::WindowFullScreen))
|
||||||
|
{
|
||||||
|
border = resizeBorderThickness();
|
||||||
|
border = qMin(border, sysBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect windowRect(0, 0, windowSize().width(), windowSize().height());
|
||||||
|
|
||||||
|
if (windowRect.contains(pos))
|
||||||
|
{
|
||||||
|
QPoint mappedPos = pos - windowRect.topLeft();
|
||||||
|
|
||||||
|
// The corner is kCornerFactor times the size of the border
|
||||||
|
if (QRect(0, 0, border * kCornerFactor, border * kCornerFactor).contains(mappedPos))
|
||||||
|
return Qt::TopLeftSection;
|
||||||
|
|
||||||
|
if (QRect(border * kCornerFactor, 0, windowRect.width() - border * 2 * kCornerFactor, border).contains(mappedPos))
|
||||||
|
return Qt::TopSection;
|
||||||
|
|
||||||
|
if (QRect(windowRect.width() - border * kCornerFactor, 0, border * kCornerFactor, border * kCornerFactor).contains(mappedPos))
|
||||||
|
return Qt::TopRightSection;
|
||||||
|
|
||||||
|
if (QRect(windowRect.width() - border, border * kCornerFactor, border, windowRect.height() - border * 2 * kCornerFactor).contains(mappedPos))
|
||||||
|
return Qt::RightSection;
|
||||||
|
|
||||||
|
if (QRect(windowRect.width() - border * kCornerFactor, windowRect.height() - border * kCornerFactor, border * kCornerFactor, border * kCornerFactor).contains(mappedPos))
|
||||||
|
return Qt::BottomRightSection;
|
||||||
|
|
||||||
|
if (QRect(border * kCornerFactor, windowRect.height() - border, windowRect.width() - border * 2 * kCornerFactor, border).contains(mappedPos))
|
||||||
|
return Qt::BottomSection;
|
||||||
|
|
||||||
|
if (QRect(0, windowRect.height() - border * kCornerFactor, border * kCornerFactor, border * kCornerFactor).contains(mappedPos))
|
||||||
|
return Qt::BottomLeftSection;
|
||||||
|
|
||||||
|
if (QRect(0, border * kCornerFactor, border, windowRect.height() - border * 2 * kCornerFactor).contains(mappedPos))
|
||||||
|
return Qt::LeftSection;
|
||||||
|
|
||||||
|
// Determining window frame secion is the highest priority,
|
||||||
|
// so the determination of the title bar area can be simpler.
|
||||||
|
if (isInTitlebarArea(pos))
|
||||||
|
return Qt::TitleBarArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Qt::NoSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FramelessHelper::isHoverResizeHandler()
|
||||||
|
{
|
||||||
|
return m_hoveredFrameSection == Qt::LeftSection ||
|
||||||
|
m_hoveredFrameSection == Qt::RightSection ||
|
||||||
|
m_hoveredFrameSection == Qt::TopSection ||
|
||||||
|
m_hoveredFrameSection == Qt::BottomSection ||
|
||||||
|
m_hoveredFrameSection == Qt::TopLeftSection ||
|
||||||
|
m_hoveredFrameSection == Qt::TopRightSection ||
|
||||||
|
m_hoveredFrameSection == Qt::BottomLeftSection ||
|
||||||
|
m_hoveredFrameSection == Qt::BottomRightSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FramelessHelper::isClickResizeHandler()
|
||||||
|
{
|
||||||
|
return m_clickedFrameSection == Qt::LeftSection ||
|
||||||
|
m_clickedFrameSection == Qt::RightSection ||
|
||||||
|
m_clickedFrameSection == Qt::TopSection ||
|
||||||
|
m_clickedFrameSection == Qt::BottomSection ||
|
||||||
|
m_clickedFrameSection == Qt::TopLeftSection ||
|
||||||
|
m_clickedFrameSection == Qt::TopRightSection ||
|
||||||
|
m_clickedFrameSection == Qt::BottomLeftSection ||
|
||||||
|
m_clickedFrameSection == Qt::BottomRightSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCursor FramelessHelper::cursorForFrameSection(Qt::WindowFrameSection frameSection)
|
||||||
|
{
|
||||||
|
Qt::CursorShape cursor = Qt::ArrowCursor;
|
||||||
|
|
||||||
|
switch (frameSection)
|
||||||
|
{
|
||||||
|
case Qt::LeftSection:
|
||||||
|
case Qt::RightSection:
|
||||||
|
cursor = Qt::SizeHorCursor;
|
||||||
|
break;
|
||||||
|
case Qt::BottomSection:
|
||||||
|
case Qt::TopSection:
|
||||||
|
cursor = Qt::SizeVerCursor;
|
||||||
|
break;
|
||||||
|
case Qt::TopLeftSection:
|
||||||
|
case Qt::BottomRightSection:
|
||||||
|
cursor = Qt::SizeFDiagCursor;
|
||||||
|
break;
|
||||||
|
case Qt::TopRightSection:
|
||||||
|
case Qt::BottomLeftSection:
|
||||||
|
cursor = Qt::SizeBDiagCursor;
|
||||||
|
break;
|
||||||
|
case Qt::TitleBarArea:
|
||||||
|
cursor = Qt::ArrowCursor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::setCursor(const QCursor& cursor)
|
||||||
|
{
|
||||||
|
m_window->setCursor(cursor);
|
||||||
|
m_cursorChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::unsetCursor()
|
||||||
|
{
|
||||||
|
if (!m_cursorChanged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_window->unsetCursor();
|
||||||
|
m_cursorChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::updateCursor()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (isHoverResizeHandler()) {
|
||||||
|
Utilities::setX11CursorShape(m_window,
|
||||||
|
Utilities::getX11CursorForFrameSection(m_hoveredFrameSection));
|
||||||
|
m_cursorChanged = true;
|
||||||
|
} else {
|
||||||
|
if (!m_cursorChanged)
|
||||||
|
return;
|
||||||
|
Utilities::resetX1CursorShape(m_window);
|
||||||
|
m_cursorChanged = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (isHoverResizeHandler()) {
|
||||||
|
setCursor(cursorForFrameSection(m_hoveredFrameSection));
|
||||||
|
} else {
|
||||||
|
unsetCursor();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::updateMouse(const QPoint& pos)
|
||||||
|
{
|
||||||
|
updateHoverStates(pos);
|
||||||
|
updateCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::updateHoverStates(const QPoint& pos)
|
||||||
|
{
|
||||||
|
m_hoveredFrameSection = mapPosToFrameSection(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::startMove(const QPoint &globalPos)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
// On HiDPI screen, X11 ButtonRelease is likely to trigger
|
||||||
|
// a QEvent::MouseMove, so we reset m_clickedFrameSection in advance.
|
||||||
|
m_clickedFrameSection = Qt::NoSection;
|
||||||
|
Utilities::sendX11ButtonReleaseEvent(m_window, globalPos);
|
||||||
|
Utilities::startX11Moving(m_window, globalPos);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::startResize(const QPoint &globalPos, Qt::WindowFrameSection frameSection)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
// On HiDPI screen, X11 ButtonRelease is likely to trigger
|
||||||
|
// a QEvent::MouseMove, so we reset m_clickedFrameSection in advance.
|
||||||
|
m_clickedFrameSection = Qt::NoSection;
|
||||||
|
Utilities::sendX11ButtonReleaseEvent(m_window, globalPos);
|
||||||
|
Utilities::startX11Resizing(m_window, globalPos, frameSection);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::setHitTestVisible(QObject *obj)
|
||||||
|
{
|
||||||
|
m_HTVObjects.push_back(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FramelessHelper::isHitTestVisible(QObject *obj)
|
||||||
|
{
|
||||||
|
return m_HTVObjects.contains(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect FramelessHelper::getHTVObjectRect(QObject *obj)
|
||||||
|
{
|
||||||
|
const QPoint originPoint = m_window->mapFromGlobal(
|
||||||
|
Utilities::mapOriginPointToWindow(obj).toPoint());
|
||||||
|
const int width = obj->property("width").toInt();
|
||||||
|
const int height = obj->property("height").toInt();
|
||||||
|
|
||||||
|
return QRect(originPoint, QSize(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
|
bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
|
||||||
{
|
{
|
||||||
Q_ASSERT(object);
|
bool filterOut = false;
|
||||||
Q_ASSERT(event);
|
|
||||||
if (!object || !event) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Only monitor window events.
|
|
||||||
if (!object->isWindowType()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const QEvent::Type type = event->type();
|
|
||||||
// We are only interested in mouse events.
|
|
||||||
if ((type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseButtonPress)
|
|
||||||
&& (type != QEvent::MouseMove)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto window = qobject_cast<QWindow *>(object);
|
|
||||||
const int resizeBorderThickness = FramelessWindowsManager::getResizeBorderThickness(window);
|
|
||||||
const int titleBarHeight = FramelessWindowsManager::getTitleBarHeight(window);
|
|
||||||
const bool resizable = FramelessWindowsManager::getResizable(window);
|
|
||||||
const int windowWidth = window->width();
|
|
||||||
const auto mouseEvent = static_cast<QMouseEvent *>(event);
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
||||||
const QPoint localMousePosition = mouseEvent->position().toPoint();
|
|
||||||
#else
|
|
||||||
const QPoint localMousePosition = mouseEvent->windowPos().toPoint();
|
|
||||||
#endif
|
|
||||||
const Qt::Edges edges = [window, resizeBorderThickness, windowWidth, &localMousePosition] {
|
|
||||||
const int windowHeight = window->height();
|
|
||||||
if (localMousePosition.y() <= resizeBorderThickness) {
|
|
||||||
if (localMousePosition.x() <= resizeBorderThickness) {
|
|
||||||
return Qt::TopEdge | Qt::LeftEdge;
|
|
||||||
}
|
|
||||||
if (localMousePosition.x() >= (windowWidth - resizeBorderThickness)) {
|
|
||||||
return Qt::TopEdge | Qt::RightEdge;
|
|
||||||
}
|
|
||||||
return Qt::Edges{Qt::TopEdge};
|
|
||||||
}
|
|
||||||
if (localMousePosition.y() >= (windowHeight - resizeBorderThickness)) {
|
|
||||||
if (localMousePosition.x() <= resizeBorderThickness) {
|
|
||||||
return Qt::BottomEdge | Qt::LeftEdge;
|
|
||||||
}
|
|
||||||
if (localMousePosition.x() >= (windowWidth - resizeBorderThickness)) {
|
|
||||||
return Qt::BottomEdge | Qt::RightEdge;
|
|
||||||
}
|
|
||||||
return Qt::Edges{Qt::BottomEdge};
|
|
||||||
}
|
|
||||||
if (localMousePosition.x() <= resizeBorderThickness) {
|
|
||||||
return Qt::Edges{Qt::LeftEdge};
|
|
||||||
}
|
|
||||||
if (localMousePosition.x() >= (windowWidth - resizeBorderThickness)) {
|
|
||||||
return Qt::Edges{Qt::RightEdge};
|
|
||||||
}
|
|
||||||
return Qt::Edges{};
|
|
||||||
} ();
|
|
||||||
const bool hitTestVisible = Utilities::isHitTestVisibleInChrome(window);
|
|
||||||
bool isInTitlebarArea = false;
|
|
||||||
if ((window->windowState() == Qt::WindowMaximized)
|
|
||||||
|| (window->windowState() == Qt::WindowFullScreen)) {
|
|
||||||
isInTitlebarArea = (localMousePosition.y() >= 0)
|
|
||||||
&& (localMousePosition.y() <= titleBarHeight)
|
|
||||||
&& (localMousePosition.x() >= 0)
|
|
||||||
&& (localMousePosition.x() <= windowWidth)
|
|
||||||
&& !hitTestVisible;
|
|
||||||
}
|
|
||||||
if (window->windowState() == Qt::WindowNoState) {
|
|
||||||
isInTitlebarArea = (localMousePosition.y() > resizeBorderThickness)
|
|
||||||
&& (localMousePosition.y() <= titleBarHeight)
|
|
||||||
&& (localMousePosition.x() > resizeBorderThickness)
|
|
||||||
&& (localMousePosition.x() < (windowWidth - resizeBorderThickness))
|
|
||||||
&& !hitTestVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the mouse click occurred in the title bar
|
if (object == m_window) {
|
||||||
static bool titlebarClicked = false;
|
switch (event->type())
|
||||||
if (type == QEvent::MouseButtonPress) {
|
{
|
||||||
if (isInTitlebarArea)
|
case QEvent::Resize:
|
||||||
titlebarClicked = true;
|
{
|
||||||
else
|
QResizeEvent* re = static_cast<QResizeEvent *>(event);
|
||||||
titlebarClicked = false;
|
resizeWindow(re->size());
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (type == QEvent::MouseButtonDblClick) {
|
|
||||||
if (mouseEvent->button() != Qt::MouseButton::LeftButton) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isInTitlebarArea) {
|
|
||||||
if (window->windowState() == Qt::WindowState::WindowFullScreen) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (window->windowState() == Qt::WindowState::WindowMaximized) {
|
|
||||||
window->showNormal();
|
|
||||||
} else {
|
|
||||||
window->showMaximized();
|
|
||||||
}
|
|
||||||
window->setCursor(Qt::ArrowCursor);
|
|
||||||
}
|
|
||||||
} else if (type == QEvent::MouseMove) {
|
|
||||||
// Display resize indicators
|
|
||||||
static bool cursorChanged = false;
|
|
||||||
if ((window->windowState() == Qt::WindowState::WindowNoState) && resizable) {
|
|
||||||
if (((edges & Qt::TopEdge) && (edges & Qt::LeftEdge))
|
|
||||||
|| ((edges & Qt::BottomEdge) && (edges & Qt::RightEdge))) {
|
|
||||||
window->setCursor(Qt::SizeFDiagCursor);
|
|
||||||
cursorChanged = true;
|
|
||||||
} else if (((edges & Qt::TopEdge) && (edges & Qt::RightEdge))
|
|
||||||
|| ((edges & Qt::BottomEdge) && (edges & Qt::LeftEdge))) {
|
|
||||||
window->setCursor(Qt::SizeBDiagCursor);
|
|
||||||
cursorChanged = true;
|
|
||||||
} else if ((edges & Qt::TopEdge) || (edges & Qt::BottomEdge)) {
|
|
||||||
window->setCursor(Qt::SizeVerCursor);
|
|
||||||
cursorChanged = true;
|
|
||||||
} else if ((edges & Qt::LeftEdge) || (edges & Qt::RightEdge)) {
|
|
||||||
window->setCursor(Qt::SizeHorCursor);
|
|
||||||
cursorChanged = true;
|
|
||||||
} else {
|
|
||||||
if (cursorChanged) {
|
|
||||||
window->setCursor(Qt::ArrowCursor);
|
|
||||||
cursorChanged = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((mouseEvent->buttons() & Qt::LeftButton) && titlebarClicked) {
|
case QEvent::NonClientAreaMouseMove:
|
||||||
if (edges == Qt::Edges{}) {
|
case QEvent::MouseMove:
|
||||||
if (isInTitlebarArea) {
|
{
|
||||||
if (!window->startSystemMove()) {
|
auto ev = static_cast<QMouseEvent *>(event);
|
||||||
// ### FIXME: TO BE IMPLEMENTED!
|
updateMouse(ev->pos());
|
||||||
qWarning() << "Current OS doesn't support QWindow::startSystemMove().";
|
|
||||||
}
|
if (m_clickedFrameSection == Qt::TitleBarArea
|
||||||
}
|
&& isInTitlebarArea(ev->pos())) {
|
||||||
|
// Start system move
|
||||||
|
startMove(ev->globalPos());
|
||||||
|
ev->accept();
|
||||||
|
filterOut = true;
|
||||||
|
} else if (isClickResizeHandler() && isHoverResizeHandler()) {
|
||||||
|
// Start system resize
|
||||||
|
startResize(ev->globalPos(), m_hoveredFrameSection);
|
||||||
|
ev->accept();
|
||||||
|
filterOut = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This case takes into account that the mouse moves outside the window boundary
|
||||||
|
QRect windowRect(0, 0, windowSize().width(), windowSize().height());
|
||||||
|
if (isClickResizeHandler() && !windowRect.contains(ev->pos())) {
|
||||||
|
startResize(ev->globalPos(), m_clickedFrameSection);
|
||||||
|
ev->accept();
|
||||||
|
filterOut = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::Leave:
|
||||||
|
{
|
||||||
|
updateMouse(m_window->mapFromGlobal(QCursor::pos()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::NonClientAreaMouseButtonPress:
|
||||||
|
case QEvent::MouseButtonPress:
|
||||||
|
{
|
||||||
|
auto ev = static_cast<QMouseEvent *>(event);
|
||||||
|
|
||||||
|
if (ev->button() == Qt::LeftButton)
|
||||||
|
m_clickedFrameSection = m_hoveredFrameSection;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (type == QEvent::MouseButtonPress) {
|
case QEvent::NonClientAreaMouseButtonRelease:
|
||||||
if (edges != Qt::Edges{}) {
|
case QEvent::MouseButtonRelease:
|
||||||
if ((window->windowState() == Qt::WindowState::WindowNoState) && !hitTestVisible && resizable) {
|
{
|
||||||
if (!window->startSystemResize(edges)) {
|
m_clickedFrameSection = Qt::NoSection;
|
||||||
// ### FIXME: TO BE IMPLEMENTED!
|
break;
|
||||||
qWarning() << "Current OS doesn't support QWindow::startSystemResize().";
|
}
|
||||||
}
|
|
||||||
|
case QEvent::NonClientAreaMouseButtonDblClick:
|
||||||
|
case QEvent::MouseButtonDblClick:
|
||||||
|
{
|
||||||
|
auto ev = static_cast<QMouseEvent *>(event);
|
||||||
|
if (isHoverResizeHandler() && ev->button() == Qt::LeftButton) {
|
||||||
|
// double click resize handler
|
||||||
|
handleResizeHandlerDblClicked();
|
||||||
|
} else if (isInTitlebarArea(ev->pos()) && ev->button() == Qt::LeftButton) {
|
||||||
|
Qt::WindowStates states = m_window->windowState();
|
||||||
|
if (states & Qt::WindowMaximized)
|
||||||
|
m_window->showNormal();
|
||||||
|
else
|
||||||
|
m_window->showMaximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return filterOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FramelessHelper::handleResizeHandlerDblClicked()
|
||||||
|
{
|
||||||
|
QRect screenRect = m_window->screen()->availableGeometry();
|
||||||
|
QRect winRect = m_window->geometry();
|
||||||
|
|
||||||
|
switch (m_clickedFrameSection)
|
||||||
|
{
|
||||||
|
case Qt::TopSection:
|
||||||
|
m_window->setGeometry(winRect.x(), 0, winRect.width(), winRect.height() + winRect.y());
|
||||||
|
break;
|
||||||
|
case Qt::BottomSection:
|
||||||
|
m_window->setGeometry(winRect.x(), winRect.y(), winRect.width(), screenRect.height() - winRect.y());
|
||||||
|
break;
|
||||||
|
case Qt::LeftSection:
|
||||||
|
m_window->setGeometry(0, winRect.y(), winRect.x() + winRect.width(), winRect.height());
|
||||||
|
break;
|
||||||
|
case Qt::RightSection:
|
||||||
|
m_window->setGeometry(winRect.x(), winRect.y(), screenRect.width() - winRect.x(), winRect.height());
|
||||||
|
break;
|
||||||
|
case Qt::TopLeftSection:
|
||||||
|
m_window->setGeometry(0, 0, winRect.x() + winRect.width(), winRect.y() + winRect.height());
|
||||||
|
break;
|
||||||
|
case Qt::TopRightSection:
|
||||||
|
m_window->setGeometry(winRect.x(), 0, screenRect.width() - winRect.x(), winRect.y() + winRect.height());
|
||||||
|
break;
|
||||||
|
case Qt::BottomLeftSection:
|
||||||
|
m_window->setGeometry(0, winRect.y(), winRect.x() + winRect.width(), screenRect.height() - winRect.y());
|
||||||
|
break;
|
||||||
|
case Qt::BottomRightSection:
|
||||||
|
m_window->setGeometry(winRect.x(), winRect.y(), screenRect.width() - winRect.x(), screenRect.height() - winRect.y());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
|
@ -29,9 +29,11 @@
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||||
|
|
||||||
#include <QtCore/qobject.h>
|
#include <QtCore/qobject.h>
|
||||||
|
#include <QtCore/qsize.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
QT_FORWARD_DECLARE_CLASS(QWindow)
|
QT_FORWARD_DECLARE_CLASS(QWindow)
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QMouseEvent)
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
@ -42,14 +44,68 @@ class FRAMELESSHELPER_API FramelessHelper : public QObject
|
||||||
Q_DISABLE_COPY_MOVE(FramelessHelper)
|
Q_DISABLE_COPY_MOVE(FramelessHelper)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FramelessHelper(QObject *parent = nullptr);
|
explicit FramelessHelper(QWindow *window);
|
||||||
~FramelessHelper() override = default;
|
~FramelessHelper() override = default;
|
||||||
|
|
||||||
void removeWindowFrame(QWindow *window);
|
void install();
|
||||||
void bringBackWindowFrame(QWindow *window);
|
void uninstall();
|
||||||
|
|
||||||
|
QWindow *window() { return m_window; }
|
||||||
|
|
||||||
|
QSize windowSize() { return m_windowSize; }
|
||||||
|
void setWindowSize(const QSize& size) { m_windowSize = size; }
|
||||||
|
void resizeWindow(const QSize& windowSize);
|
||||||
|
|
||||||
|
int titleBarHeight() { return m_titleBarHeight; }
|
||||||
|
int setTitleBarHeight(int height) { m_titleBarHeight = height; }
|
||||||
|
QRect titleBarRect();
|
||||||
|
QRegion titleBarRegion();
|
||||||
|
|
||||||
|
int resizeBorderThickness() { return m_resizeBorderThickness; }
|
||||||
|
void setResizeBorderThickness(int thickness) { m_resizeBorderThickness = thickness; }
|
||||||
|
|
||||||
|
bool resizable() { return m_resizable; }
|
||||||
|
void setResizable(bool resizable) { m_resizable = resizable; }
|
||||||
|
|
||||||
|
QRect clientRect();
|
||||||
|
QRegion nonClientRegion();
|
||||||
|
|
||||||
|
bool isInTitlebarArea(const QPoint& pos);
|
||||||
|
Qt::WindowFrameSection mapPosToFrameSection(const QPoint& pos);
|
||||||
|
|
||||||
|
bool isHoverResizeHandler();
|
||||||
|
bool isClickResizeHandler();
|
||||||
|
|
||||||
|
QCursor cursorForFrameSection(Qt::WindowFrameSection frameSection);
|
||||||
|
void setCursor(const QCursor& cursor);
|
||||||
|
void unsetCursor();
|
||||||
|
void updateCursor();
|
||||||
|
|
||||||
|
void updateMouse(const QPoint& pos);
|
||||||
|
void updateHoverStates(const QPoint& pos);
|
||||||
|
|
||||||
|
void startMove(const QPoint &globalPos);
|
||||||
|
void startResize(const QPoint &globalPos, Qt::WindowFrameSection frameSection);
|
||||||
|
|
||||||
|
void setHitTestVisible(QObject *obj);
|
||||||
|
bool isHitTestVisible(QObject *obj);
|
||||||
|
QRect getHTVObjectRect(QObject *obj);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *object, QEvent *event) override;
|
bool eventFilter(QObject *object, QEvent *event) override;
|
||||||
|
void handleResizeHandlerDblClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWindow *m_window;
|
||||||
|
QSize m_windowSize;
|
||||||
|
int m_titleBarHeight;
|
||||||
|
int m_resizeBorderThickness;
|
||||||
|
bool m_resizable;
|
||||||
|
Qt::WindowFlags m_origWindowFlags;
|
||||||
|
bool m_cursorChanged;
|
||||||
|
Qt::WindowFrameSection m_hoveredFrameSection;
|
||||||
|
Qt::WindowFrameSection m_clickedFrameSection;
|
||||||
|
QList<QObject*> m_HTVObjects;
|
||||||
};
|
};
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||||
Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix)
|
//Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void FramelessWindowsManager::addWindow(QWindow *window)
|
void FramelessWindowsManager::addWindow(QWindow *window)
|
||||||
|
@ -51,7 +51,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
|
||||||
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||||
}
|
}
|
||||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||||
framelessHelperUnix()->removeWindowFrame(window);
|
//framelessHelperUnix()->removeWindowFrame(window);
|
||||||
#else
|
#else
|
||||||
FramelessHelperWin::addFramelessWindow(window);
|
FramelessHelperWin::addFramelessWindow(window);
|
||||||
// Work-around a Win32 multi-monitor bug.
|
// Work-around a Win32 multi-monitor bug.
|
||||||
|
@ -165,7 +165,7 @@ void FramelessWindowsManager::removeWindow(QWindow *window)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
|
||||||
framelessHelperUnix()->bringBackWindowFrame(window);
|
//framelessHelperUnix()->bringBackWindowFrame(window);
|
||||||
#else
|
#else
|
||||||
FramelessHelperWin::removeFramelessWindow(window);
|
FramelessHelperWin::removeFramelessWindow(window);
|
||||||
#endif
|
#endif
|
||||||
|
|
10
utilities.h
10
utilities.h
|
@ -58,6 +58,16 @@ FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable
|
||||||
[[nodiscard]] FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function);
|
[[nodiscard]] FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
FRAMELESSHELPER_API void sendX11ButtonReleaseEvent(QWindow *w, const QPoint &globalPos);
|
||||||
|
FRAMELESSHELPER_API void sendX11MoveResizeEvent(QWindow *w, const QPoint &globalPos, int section);
|
||||||
|
FRAMELESSHELPER_API void startX11Moving(QWindow *w, const QPoint &globalPos);
|
||||||
|
FRAMELESSHELPER_API void startX11Resizing(QWindow *w, const QPoint &globalPos, Qt::WindowFrameSection frameSection);
|
||||||
|
FRAMELESSHELPER_API void setX11CursorShape(QWindow *w, int cursorId);
|
||||||
|
FRAMELESSHELPER_API void resetX1CursorShape(QWindow *w);
|
||||||
|
FRAMELESSHELPER_API unsigned int getX11CursorForFrameSection(Qt::WindowFrameSection frameSection);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
|
@ -25,9 +25,26 @@
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#include <QtCore/qvariant.h>
|
#include <QtCore/qvariant.h>
|
||||||
|
#include <QtCore/qdebug.h>
|
||||||
|
#include <QtGui/qscreen.h>
|
||||||
|
#include <QtX11Extras/qx11info_x11.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
|
||||||
|
#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
|
||||||
|
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
|
||||||
|
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
|
||||||
|
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
|
||||||
|
|
||||||
static constexpr int kDefaultResizeBorderThickness = 8;
|
static constexpr int kDefaultResizeBorderThickness = 8;
|
||||||
static constexpr int kDefaultCaptionHeight = 23;
|
static constexpr int kDefaultCaptionHeight = 23;
|
||||||
|
|
||||||
|
@ -106,7 +123,8 @@ bool Utilities::shouldAppsUseDarkMode()
|
||||||
ColorizationArea Utilities::getColorizationArea()
|
ColorizationArea Utilities::getColorizationArea()
|
||||||
{
|
{
|
||||||
// ### TO BE IMPLEMENTED
|
// ### TO BE IMPLEMENTED
|
||||||
return ColorizationArea::None;
|
//return ColorizationArea::None; // ‘None’ has been defined as a macro in X11 headers.
|
||||||
|
return ColorizationArea::All;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utilities::isThemeChanged(const void *data)
|
bool Utilities::isThemeChanged(const void *data)
|
||||||
|
@ -132,4 +150,178 @@ bool Utilities::showSystemMenu(const WId winId, const QPointF &pos)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
void Utilities::sendX11ButtonReleaseEvent(QWindow *w, const QPoint &globalPos)
|
||||||
|
{
|
||||||
|
const QPoint pos = w->mapFromGlobal(globalPos);
|
||||||
|
const auto display = QX11Info::display();
|
||||||
|
const auto screen = QX11Info::appScreen();
|
||||||
|
|
||||||
|
XEvent xevent;
|
||||||
|
memset(&xevent, 0, sizeof(XEvent));
|
||||||
|
|
||||||
|
xevent.type = ButtonRelease;
|
||||||
|
xevent.xbutton.time = CurrentTime;
|
||||||
|
xevent.xbutton.button = 0;
|
||||||
|
xevent.xbutton.same_screen = True;
|
||||||
|
xevent.xbutton.send_event = True;
|
||||||
|
xevent.xbutton.window = w->winId();
|
||||||
|
xevent.xbutton.root = QX11Info::appRootWindow(screen);
|
||||||
|
xevent.xbutton.x = pos.x() * w->screen()->devicePixelRatio();
|
||||||
|
xevent.xbutton.y = pos.y() * w->screen()->devicePixelRatio();
|
||||||
|
xevent.xbutton.x_root = globalPos.x() * w->screen()->devicePixelRatio();
|
||||||
|
xevent.xbutton.y_root = globalPos.y() * w->screen()->devicePixelRatio();
|
||||||
|
xevent.xbutton.display = display;
|
||||||
|
|
||||||
|
if (XSendEvent(display, w->winId(), True, ButtonReleaseMask, &xevent) == 0)
|
||||||
|
qWarning() << "Failed to send ButtonRelease event.";
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utilities::sendX11MoveResizeEvent(QWindow *w, const QPoint &globalPos, int section)
|
||||||
|
{
|
||||||
|
const auto display = QX11Info::display();
|
||||||
|
const auto winId = w->winId();
|
||||||
|
const auto screen = QX11Info::appScreen();
|
||||||
|
|
||||||
|
XUngrabPointer(display, CurrentTime);
|
||||||
|
|
||||||
|
XEvent xev;
|
||||||
|
memset(&xev, 0x00, sizeof(xev));
|
||||||
|
const Atom netMoveResize = XInternAtom(display, "_NET_WM_MOVERESIZE", False);
|
||||||
|
xev.xclient.type = ClientMessage;
|
||||||
|
xev.xclient.message_type = netMoveResize;
|
||||||
|
xev.xclient.serial = 0;
|
||||||
|
xev.xclient.display = display;
|
||||||
|
xev.xclient.send_event = True;
|
||||||
|
xev.xclient.window = winId;
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
|
||||||
|
xev.xclient.data.l[0] = globalPos.x() * w->screen()->devicePixelRatio();
|
||||||
|
xev.xclient.data.l[1] = globalPos.y() * w->screen()->devicePixelRatio();
|
||||||
|
xev.xclient.data.l[2] = section;
|
||||||
|
xev.xclient.data.l[3] = Button1;
|
||||||
|
xev.xclient.data.l[4] = 0;
|
||||||
|
|
||||||
|
if(XSendEvent(display, QX11Info::appRootWindow(screen),
|
||||||
|
False, SubstructureRedirectMask | SubstructureNotifyMask, &xev) == 0)
|
||||||
|
qWarning("Failed to send Move or Resize event.");
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utilities::startX11Moving(QWindow *w, const QPoint &pos)
|
||||||
|
{
|
||||||
|
sendX11MoveResizeEvent(w, pos, _NET_WM_MOVERESIZE_MOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utilities::startX11Resizing(QWindow *w, const QPoint &pos, Qt::WindowFrameSection frameSection)
|
||||||
|
{
|
||||||
|
int section = -1;
|
||||||
|
|
||||||
|
switch (frameSection)
|
||||||
|
{
|
||||||
|
case Qt::LeftSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_LEFT;
|
||||||
|
break;
|
||||||
|
case Qt::TopLeftSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
|
||||||
|
break;
|
||||||
|
case Qt::TopSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_TOP;
|
||||||
|
break;
|
||||||
|
case Qt::TopRightSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
|
||||||
|
break;
|
||||||
|
case Qt::RightSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_RIGHT;
|
||||||
|
break;
|
||||||
|
case Qt::BottomRightSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
|
||||||
|
break;
|
||||||
|
case Qt::BottomSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_BOTTOM;
|
||||||
|
break;
|
||||||
|
case Qt::BottomLeftSection:
|
||||||
|
section = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section != -1)
|
||||||
|
sendX11MoveResizeEvent(w, pos, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class X11CursorType
|
||||||
|
{
|
||||||
|
kArrow = 2,
|
||||||
|
kTop = 138,
|
||||||
|
kTopRight = 136,
|
||||||
|
kRight = 96,
|
||||||
|
kBottomRight = 14,
|
||||||
|
kBottom = 16,
|
||||||
|
kBottomLeft = 12,
|
||||||
|
kLeft = 70,
|
||||||
|
kTopLeft = 134,
|
||||||
|
};
|
||||||
|
|
||||||
|
void Utilities::setX11CursorShape(QWindow *w, int cursorId)
|
||||||
|
{
|
||||||
|
const auto display = QX11Info::display();
|
||||||
|
const WId window_id = w->winId();
|
||||||
|
const Cursor cursor = XCreateFontCursor(display, cursorId);
|
||||||
|
if (!cursor) {
|
||||||
|
qWarning() << "Failed to set cursor.";
|
||||||
|
}
|
||||||
|
XDefineCursor(display, window_id, cursor);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utilities::resetX1CursorShape(QWindow *w)
|
||||||
|
{
|
||||||
|
const auto display = QX11Info::display();
|
||||||
|
const WId window_id = w->winId();
|
||||||
|
XUndefineCursor(display, window_id);
|
||||||
|
XFlush(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Utilities::getX11CursorForFrameSection(Qt::WindowFrameSection frameSection)
|
||||||
|
{
|
||||||
|
X11CursorType cursor = X11CursorType::kArrow;
|
||||||
|
|
||||||
|
switch (frameSection)
|
||||||
|
{
|
||||||
|
case Qt::LeftSection:
|
||||||
|
cursor = X11CursorType::kLeft;
|
||||||
|
break;
|
||||||
|
case Qt::RightSection:
|
||||||
|
cursor = X11CursorType::kRight;
|
||||||
|
break;
|
||||||
|
case Qt::BottomSection:
|
||||||
|
cursor = X11CursorType::kBottom;
|
||||||
|
break;
|
||||||
|
case Qt::TopSection:
|
||||||
|
cursor = X11CursorType::kTop;
|
||||||
|
break;
|
||||||
|
case Qt::TopLeftSection:
|
||||||
|
cursor = X11CursorType::kTopLeft;
|
||||||
|
break;
|
||||||
|
case Qt::BottomRightSection:
|
||||||
|
cursor = X11CursorType::kBottomRight;
|
||||||
|
break;
|
||||||
|
case Qt::TopRightSection:
|
||||||
|
cursor = X11CursorType::kTopRight;
|
||||||
|
break;
|
||||||
|
case Qt::BottomLeftSection:
|
||||||
|
cursor = X11CursorType::kBottomLeft;
|
||||||
|
break;
|
||||||
|
case Qt::TitleBarArea:
|
||||||
|
cursor = X11CursorType::kArrow;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (unsigned int)cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
Loading…
Reference in New Issue