Compare commits

...

42 Commits

Author SHA1 Message Date
Altair Wei f4d20e5f06 commented out unused codes 2021-12-18 20:59:24 +08:00
Altair Wei 997ef5c508 apply #93 patch 2021-12-18 09:18:08 +08:00
Altair Wei 1ebead3148 sync with 1.x changes 2021-11-14 22:31:02 +08:00
Altair Wei 7b82375543 add APIs to disable Mac window std buttons 2021-11-13 00:15:13 +08:00
Altair Wei 57610634d6 fix close btn behavior on Mac 2021-11-12 22:43:54 +08:00
Altair Wei 7d29ee5b95 create widget-level APIs 2021-10-17 22:33:36 +08:00
Altair Wei dd0aa92035 Merge branch 'mac-window-button-location' into altairwei/2.0 2021-10-11 18:04:17 +08:00
Altair Wei dcb5fb1da4 make mac window buttons location settable 2021-10-11 18:03:44 +08:00
Altair Wei 92681dc9d9 copy WindowButtonsProxy from electron 2021-10-10 21:08:42 +08:00
Altair Wei 18912d5710 fix mouse behavior on resize handler region 2021-10-09 20:30:37 +08:00
Altair Wei fdf38d4c80 enable set window later 2021-10-07 20:46:13 +08:00
Altair Wei c8cfefb3ca init implementation of MacOS window btn location 2021-10-07 19:29:45 +08:00
Altair Wei 50dafa0c5e lower C++ std to c++11 and remove attributes 2021-10-07 15:23:59 +08:00
Altair Wei d8c137d11f mv source code into src folder 2021-10-07 15:13:34 +08:00
Altair Wei 1f1de5deb6 fix window maximization bugs on Windows 2021-10-07 11:27:56 +08:00
Altair Wei 0e63a053de fix examples on MacOS 2021-10-06 22:05:15 +08:00
Altair Wei 420d942762 fix examples on Windows 2021-10-06 20:53:24 +08:00
Altair Wei d680ad87b1 fix examples for Core API 2.0 2021-10-06 20:42:10 +08:00
Altair Wei af797125f8 fix bug when resizing on handler's edge on Linux 2021-10-06 17:02:15 +08:00
Altair Wei cefae7950d default titlebar height and resize border 2021-10-06 15:40:23 +08:00
Altair Wei 183aa9a730 integrate Windows implementation into 2.0 Core API 2021-10-06 15:10:53 +08:00
Altair Wei 5e96c3fec6 fix build error on Windows 2021-10-03 17:44:55 +08:00
Altair Wei c684d3c0ac
Merge pull request #85 from altairwei/2.0-macos
2.0 Core API for macos
2021-10-02 16:46:05 +08:00
Altair Wei de1d6abaf0 add showMacWindowButton 2021-10-02 15:50:22 +08:00
Altair Wei ce69d1a4c5 implement Core API on MacOS 2021-10-02 14:19:46 +08:00
Altair Wei 6a0dc5052c
Merge pull request #84 from altairwei/2.0-linux
framelesshelper v2.0 Core API Linux 端的实现
2021-10-02 11:56:12 +08:00
Altair Wei 6a6fea8ac9 improve resize handler db click behavior 2021-09-21 17:56:43 +08:00
Altair Wei 989de57f33 fix HiDPI position calculate error 2021-09-21 15:42:11 +08:00
Altair Wei 3a3e39c0c0 change X11 event impl 2021-09-21 10:17:42 +08:00
Altair Wei 9b2dc893b6 double click resize handler will change geometry 2021-09-20 20:40:03 +08:00
Altair Wei 5f58937588 add hitTestVisible logic 2021-09-20 19:41:24 +08:00
Altair Wei 5ab2024f54 only allow left button to move or resize 2021-09-20 16:54:52 +08:00
Altair Wei 0964483b20 enlarge the resize corner area 2021-09-20 16:45:30 +08:00
Altair Wei 96f49ded2f fix move and resize determination 2021-09-20 14:47:49 +08:00
Altair Wei 1aed38e882 double click make window maximized 2021-09-20 09:19:04 +08:00
Altair Wei 501ff149cf implemented X11 resizing 2021-09-20 09:10:04 +08:00
Altair Wei a365499455 implemented linux x11 moving 2021-09-20 00:08:43 +08:00
Altair Wei 93ac6f6ee3 minimal example that only depends on Core API 2021-09-19 22:24:00 +08:00
Altair Wei 678f903f5c update hovered states 2021-09-19 21:32:32 +08:00
Altair Wei da342753a2 change cursor according mouse state 2021-09-19 17:47:14 +08:00
Altair Wei 3784ef0e7f map position to frame section 2021-09-13 19:59:35 +08:00
Altair Wei 1e2598398d backbone of new FramelessHelper 2021-09-13 12:22:09 +08:00
44 changed files with 3504 additions and 892 deletions

3
.gitignore vendored
View File

@ -71,4 +71,5 @@ Thumbs.db
.qmake.conf
*.res
.vscode/
.vscode/
*/.DS_Store

View File

@ -5,9 +5,9 @@ project(FramelessHelper LANGUAGES CXX)
option(BUILD_EXAMPLES "Build examples." ON)
option(TEST_UNIX "Test UNIX version (from Win32)." OFF)
set(BUILD_SHARED_LIBS ON)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@ -19,90 +19,13 @@ set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Gui REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui REQUIRED)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
set(SOURCES
framelesshelper_global.h
framelesshelper.h
framelesshelper.cpp
framelesswindowsmanager.h
framelesswindowsmanager.cpp
utilities.h
utilities.cpp
)
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
list(APPEND SOURCES
framelessquickhelper.h
framelessquickhelper.cpp
)
endif()
if(WIN32)
list(APPEND SOURCES
framelesshelper_windows.h
utilities_win32.cpp
framelesshelper_win32.h
framelesshelper_win32.cpp
)
else()
if(MACOS)
list(APPEND SOURCES utilities_macos.mm)
else()
list(APPEND SOURCES utilities_linux.cpp)
endif()
endif()
if(WIN32 AND BUILD_SHARED_LIBS)
enable_language(RC)
list(APPEND SOURCES framelesshelper.rc)
endif()
add_library(${PROJECT_NAME} ${SOURCES})
add_library(wangwenx190::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(${PROJECT_NAME} PUBLIC
FRAMELESSHELPER_STATIC
)
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_KEYWORDS
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060100
FRAMELESSHELPER_BUILD_LIBRARY
)
if(TEST_UNIX)
target_compile_definitions(${PROJECT_NAME} PRIVATE
FRAMELESSHELPER_TEST_UNIX
)
endif()
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE
dwmapi
)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::GuiPrivate
)
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
)
add_subdirectory(src)
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
endif()

View File

@ -1,9 +1,12 @@
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets)
include_directories(../src)
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
add_subdirectory(widget)
add_subdirectory(mainwindow)
add_subdirectory(minimal)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>518</width>
<height>368</height>
<height>383</height>
</rect>
</property>
<property name="windowTitle">
@ -20,9 +20,16 @@
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_3">
<widget class="QPushButton" name="uninstallBtn">
<property name="text">
<string>PushButton</string>
<string>Uninstall Frameless Window</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="installBtn">
<property name="text">
<string>Install Frameless Window</string>
</property>
</widget>
</item>
@ -122,6 +129,15 @@
<height>25</height>
</size>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="nativeMenuBar">
<bool>true</bool>
</property>
<widget class="QMenu" name="menuItem_1">
<property name="title">
<string>Menu 1</string>

View File

@ -126,7 +126,7 @@
</property>
<property name="font">
<font>
<family>Segoe UI</family>
<family>Arial</family>
<pointsize>9</pointsize>
</font>
</property>
@ -283,6 +283,8 @@
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="../images.qrc"/>
<include location="../images.qrc"/>
<include location="../images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -24,7 +24,7 @@
#include "mainwindow.h"
#include <QtGui/qpainter.h>
#include "../../framelesswindowsmanager.h"
#include "core/utilities.h"
FRAMELESSHELPER_USE_NAMESPACE
@ -38,14 +38,13 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(par
appMainWindow = new Ui::MainWindow;
appMainWindow->setupUi(this);
const auto widget = new QWidget(this);
m_titleBar = new QWidget(this);
titleBarWidget = new Ui::TitleBar;
titleBarWidget->setupUi(widget);
titleBarWidget->setupUi(m_titleBar);
QMenuBar *mb = menuBar();
titleBarWidget->horizontalLayout->insertWidget(1, mb);
setMenuWidget(widget);
setMenuWidget(m_titleBar);
connect(this, &MainWindow::windowIconChanged, titleBarWidget->iconButton, &QPushButton::setIcon);
connect(this, &MainWindow::windowTitleChanged, titleBarWidget->titleLabel, &QLabel::setText);
@ -63,6 +62,13 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(par
titleBarWidget->maximizeButton->setToolTip(isMaximized() ? tr("Restore") : tr("Maximize"));
});
connect(appMainWindow->uninstallBtn, &QPushButton::clicked, [this]() {
this->m_helper->uninstall();
});
connect(appMainWindow->installBtn, &QPushButton::clicked, [this]() {
this->m_helper->install();
});
setWindowTitle(tr("Hello, World!"));
}
@ -85,18 +91,43 @@ void MainWindow::showEvent(QShowEvent *event)
if (!inited) {
const auto win = windowHandle();
if (win) {
FramelessWindowsManager::addWindow(win);
FramelessWindowsManager::setHitTestVisibleInChrome(win, titleBarWidget->iconButton, true);
FramelessWindowsManager::setHitTestVisibleInChrome(win, titleBarWidget->minimizeButton, true);
FramelessWindowsManager::setHitTestVisibleInChrome(win, titleBarWidget->maximizeButton, true);
FramelessWindowsManager::setHitTestVisibleInChrome(win, titleBarWidget->closeButton, true);
FramelessWindowsManager::setHitTestVisibleInChrome(win, appMainWindow->menubar, true);
m_helper = new FramelessHelper(win);
m_helper->setHitTestVisible(titleBarWidget->iconButton);
m_helper->setHitTestVisible(titleBarWidget->minimizeButton);
m_helper->setHitTestVisible(titleBarWidget->maximizeButton);
m_helper->setHitTestVisible(titleBarWidget->closeButton);
m_helper->setHitTestVisible(appMainWindow->menubar);
m_helper->setTitleBarHeight(m_titleBar->height());
m_helper->setResizeBorderThickness(4);
m_helper->install();
#ifndef Q_OS_MAC
setContentsMargins(1, 1, 1, 1);
#else // Q_OS_MAC
titleBarWidget->minimizeButton->hide();
titleBarWidget->maximizeButton->hide();
titleBarWidget->closeButton->hide();
Utilities::setStandardWindowButtonsVisibility(windowHandle(), true);
#endif // Q_OS_MAC
inited = true;
}
}
}
#ifdef Q_OS_WIN
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
if (!m_helper)
return QWidget::nativeEvent(eventType, message, result);
if (m_helper->handleNativeEvent(this->windowHandle(), eventType, message, result))
return true;
else
return QWidget::nativeEvent(eventType, message, result);
}
#endif // Q_OS_WIN
#ifndef Q_OS_MAC
void MainWindow::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
@ -136,8 +167,14 @@ void MainWindow::paintEvent(QPaintEvent *event)
{0, h, 0, 0}
};
painter.save();
painter.setPen({isActiveWindow() ? Qt::black : Qt::darkGray, 1});
const ColorizationArea area = Utilities::getColorizationArea();
const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder)
|| (area == ColorizationArea::AllArea));
const QColor borderColor = (isActiveWindow() ? (colorizedBorder ? Utilities::getColorizationColor() : Qt::black) : Qt::darkGray);
const auto borderThickness = static_cast<qreal>(Utilities::getWindowVisibleFrameBorderThickness(winId()));
painter.setPen({borderColor, qMax(borderThickness, devicePixelRatioF())});
painter.drawLines(lines);
painter.restore();
}
}
#endif // Q_OS_MAC

View File

@ -27,6 +27,7 @@
#include <QtWidgets/qmainwindow.h>
#include "ui_MainWindow.h"
#include "ui_TitleBar.h"
#include "core/framelesshelper.h"
class MainWindow : public QMainWindow
{
@ -38,13 +39,22 @@ public:
protected:
void showEvent(QShowEvent *event) override;
#ifndef Q_OS_MAC
void paintEvent(QPaintEvent *event) override;
void changeEvent(QEvent *event) override;
#endif // Q_OS_MAC
#ifdef Q_OS_WIN
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif // Q_OS_WIN
Q_SIGNALS:
void windowStateChanged();
private:
__flh_ns::FramelessHelper *m_helper = nullptr;
QWidget *m_titleBar = nullptr;
Ui::TitleBar *titleBarWidget = nullptr;
Ui::MainWindow *appMainWindow = nullptr;
};

View File

@ -0,0 +1,33 @@
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
main.cpp
flwindow.h
flwindow.cpp
)
add_executable(minimal ${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()

View File

@ -0,0 +1,77 @@
#include "flwindow.h"
#include "core/framelesshelper.h"
#include "core/utilities.h"
#include <QScreen>
#include <QVBoxLayout>
#include <QPushButton>
FRAMELESSHELPER_USE_NAMESPACE
FLWindow::FLWindow(QWidget *parent) : FramelessWindow<QWidget>(parent)
{
setWindowFlags(Qt::FramelessWindowHint);
setupUi();
move(screen()->geometry().center() - frameGeometry().center());
}
FLWindow::~FLWindow()
{
}
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);
setResizeBorderThickness(4);
setTitleBarHeight(m_titleBarWidget->height());
setResizable(true);
setHitTestVisible(m_minimizeButton);
setHitTestVisible(m_maximizeButton);
setHitTestVisible(m_closeButton);
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <QWidget>
#include "widget/framelesswindow.h"
class QPushButton;
class FLWindow : public __flh_ns::FramelessWindow<QWidget>
{
Q_OBJECT
public:
explicit FLWindow(QWidget *parent = nullptr);
~FLWindow() override;
private:
void setupUi();
private:
QWidget *m_titleBarWidget = nullptr;
QPushButton *m_minimizeButton = nullptr;
QPushButton *m_maximizeButton = nullptr;
QPushButton *m_closeButton = nullptr;
};

25
examples/minimal/main.cpp Normal file
View File

@ -0,0 +1,25 @@
#include <QtWidgets/qapplication.h>
#include "flwindow.h"
#include <QStyleFactory>
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);
application.setStyle(QStyleFactory::create(QStringLiteral("fusion")));
FLWindow win;
win.show();
return QApplication::exec();
}

View File

@ -22,7 +22,7 @@
* SOFTWARE.
*/
#include "../../framelessquickhelper.h"
#include "quick/framelessquickhelper.h"
#include <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQuickControls2/qquickstyle.h>

View File

@ -29,8 +29,8 @@
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qpushbutton.h>
#include "../../utilities.h"
#include "../../framelesswindowsmanager.h"
#include "core/utilities.h"
#include "core/framelesshelper.h"
FRAMELESSHELPER_USE_NAMESPACE
@ -98,12 +98,23 @@ void Widget::showEvent(QShowEvent *event)
qFatal("Failed to retrieve the window handle.");
return;
}
FramelessWindowsManager::addWindow(win);
FramelessWindowsManager::setHitTestVisibleInChrome(win, m_minimizeButton, true);
FramelessWindowsManager::setHitTestVisibleInChrome(win, m_maximizeButton, true);
FramelessWindowsManager::setHitTestVisibleInChrome(win, m_closeButton, true);
m_helper = new FramelessHelper(win);
m_helper->setHitTestVisible(m_minimizeButton);
m_helper->setHitTestVisible(m_maximizeButton);
m_helper->setHitTestVisible(m_closeButton);
m_helper->setResizeBorderThickness(4);
m_helper->setTitleBarHeight(m_titleBarWidget->height());
m_helper->install();
#ifndef Q_OS_MAC
const int margin = Utilities::getWindowVisibleFrameBorderThickness(winId());
setContentsMargins(margin, margin, margin, margin);
#else // Q_OS_MAC
m_minimizeButton->hide();
m_maximizeButton->hide();
m_closeButton->hide();
Utilities::setStandardWindowButtonsVisibility(windowHandle(), true);
#endif // Q_OS_MAC
}
}
@ -115,6 +126,7 @@ void Widget::timerEvent(QTimerEvent *event)
}
}
#ifndef Q_OS_MAC
void Widget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
@ -123,7 +135,6 @@ void Widget::changeEvent(QEvent *event)
const int margin = ((isMaximized() || isFullScreen()) ? 0 : Utilities::getWindowVisibleFrameBorderThickness(winId()));
setContentsMargins(margin, margin, margin, margin);
updateSystemButtonIcons();
updateTitleBarSize();
shouldUpdate = true;
} else if (event->type() == QEvent::ActivationChange) {
shouldUpdate = true;
@ -155,7 +166,7 @@ void Widget::paintEvent(QPaintEvent *event)
};
const ColorizationArea area = Utilities::getColorizationArea();
const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder)
|| (area == ColorizationArea::All));
|| (area == ColorizationArea::AllArea));
const QColor borderColor = (isActiveWindow() ? (colorizedBorder ? Utilities::getColorizationColor() : Qt::black) : Qt::darkGray);
const auto borderThickness = static_cast<qreal>(Utilities::getWindowVisibleFrameBorderThickness(winId()));
painter.setPen({borderColor, qMax(borderThickness, devicePixelRatioF())});
@ -163,15 +174,20 @@ void Widget::paintEvent(QPaintEvent *event)
painter.restore();
}
}
#endif // Q_OS_MAC
void Widget::setupUi()
{
setObjectName(QStringLiteral("MainWidget"));
setWindowTitle(tr("Hello, World!"));
resize(800, 600);
m_titleBarWidget = new QWidget(this);
m_titleBarWidget->setObjectName(QStringLiteral("TitleBarWidget"));
m_titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_titleBarWidget->setFixedHeight(28);
const QSize systemButtonSize = {45, 30};
m_windowTitleLabel = new QLabel(m_titleBarWidget);
m_windowTitleLabel->setObjectName(QStringLiteral("WindowTitleLabel"));
m_windowTitleLabel->setFrameShape(QFrame::NoFrame);
@ -180,11 +196,17 @@ void Widget::setupUi()
m_windowTitleLabel->setFont(titleFont);
m_windowTitleLabel->setText(windowTitle());
connect(this, &Widget::windowTitleChanged, m_windowTitleLabel, &QLabel::setText);
m_minimizeButton = new QPushButton(m_titleBarWidget);
m_minimizeButton->setObjectName(QStringLiteral("MinimizeButton"));
m_minimizeButton->setFixedSize(systemButtonSize);
m_minimizeButton->setIconSize(systemButtonSize);
connect(m_minimizeButton, &QPushButton::clicked, this, &Widget::showMinimized);
m_maximizeButton = new QPushButton(m_titleBarWidget);
m_maximizeButton->setObjectName(QStringLiteral("MaximizeButton"));
m_maximizeButton->setFixedSize(systemButtonSize);
m_maximizeButton->setIconSize(systemButtonSize);
connect(m_maximizeButton, &QPushButton::clicked, this, [this](){
if (isMaximized() || isFullScreen()) {
showNormal();
@ -193,21 +215,29 @@ void Widget::setupUi()
}
updateSystemButtonIcons();
});
m_closeButton = new QPushButton(m_titleBarWidget);
m_closeButton->setObjectName(QStringLiteral("CloseButton"));
m_closeButton->setFixedSize(systemButtonSize);
m_closeButton->setIconSize(systemButtonSize);
connect(m_closeButton, &QPushButton::clicked, this, &Widget::close);
updateSystemButtonIcons();
updateTitleBarSize();
const auto titleBarLayout = new QHBoxLayout(m_titleBarWidget);
titleBarLayout->setContentsMargins(0, 0, 0, 0);
titleBarLayout->setSpacing(0);
titleBarLayout->addSpacerItem(new QSpacerItem(10, 10));
#ifdef Q_OS_MAC
titleBarLayout->addStretch();
#endif // Q_OS_MAC
titleBarLayout->addWidget(m_windowTitleLabel);
titleBarLayout->addStretch();
titleBarLayout->addWidget(m_minimizeButton);
titleBarLayout->addWidget(m_maximizeButton);
titleBarLayout->addWidget(m_closeButton);
m_titleBarWidget->setLayout(titleBarLayout);
m_clockLabel = new QLabel(this);
m_clockLabel->setObjectName(QStringLiteral("ClockLabel"));
m_clockLabel->setFrameShape(QFrame::NoFrame);
@ -215,12 +245,14 @@ void Widget::setupUi()
clockFont.setBold(true);
clockFont.setPointSize(70);
m_clockLabel->setFont(clockFont);
const auto contentLayout = new QHBoxLayout;
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
contentLayout->addStretch();
contentLayout->addWidget(m_clockLabel);
contentLayout->addStretch();
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
@ -238,7 +270,7 @@ void Widget::updateStyleSheet()
const bool dark = Utilities::shouldAppsUseDarkMode();
const ColorizationArea area = Utilities::getColorizationArea();
const bool colorizedTitleBar = ((area == ColorizationArea::TitleBar_WindowBorder)
|| (area == ColorizationArea::All));
|| (area == ColorizationArea::AllArea));
const QColor colorizationColor = Utilities::getColorizationColor();
const QColor mainWidgetBackgroundColor = (dark ? systemDarkColor : systemLightColor);
const QColor titleBarWidgetBackgroundColor = [active, colorizedTitleBar, &colorizationColor, dark]{
@ -312,6 +344,15 @@ bool Widget::nativeEvent(const QByteArray &eventType, void *message, qintptr *re
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
#endif
{
#ifdef Q_OS_WIN
if (m_helper) {
if (m_helper->handleNativeEvent(this->windowHandle(), eventType, message, result))
return true;
else
return QWidget::nativeEvent(eventType, message, result);
}
#endif
if (message) {
if (Utilities::isThemeChanged(message)) {
updateStyleSheet();
@ -327,5 +368,6 @@ bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *resul
}
}
}
return QWidget::nativeEvent(eventType, message, result);
}

View File

@ -25,6 +25,7 @@
#pragma once
#include <QtWidgets/qwidget.h>
#include "core/framelesshelper.h"
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QLabel)
@ -42,8 +43,12 @@ public:
protected:
void showEvent(QShowEvent *event) override;
void timerEvent(QTimerEvent *event) override;
#ifndef Q_OS_MAC
void changeEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
#endif // Q_OS_MAC
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;
#else
@ -57,6 +62,7 @@ private:
void updateSystemButtonIcons();
private:
__flh_ns::FramelessHelper *m_helper = nullptr;
QWidget *m_titleBarWidget = nullptr;
QLabel *m_windowTitleLabel = nullptr;
QPushButton *m_minimizeButton = nullptr;

View File

@ -1,212 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelesshelper.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#include <QtCore/qdebug.h>
#include <QtGui/qevent.h>
#include <QtGui/qwindow.h>
#include "framelesswindowsmanager.h"
#include "utilities.h"
FRAMELESSHELPER_BEGIN_NAMESPACE
FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent) {}
void FramelessHelper::removeWindowFrame(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
window->setFlags(window->flags() | Qt::FramelessWindowHint);
window->installEventFilter(this);
window->setProperty(Constants::kFramelessModeFlag, true);
}
void FramelessHelper::bringBackWindowFrame(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
window->removeEventFilter(this);
window->setFlags(window->flags() & ~Qt::FramelessWindowHint);
window->setProperty(Constants::kFramelessModeFlag, false);
}
bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
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
static bool titlebarClicked = false;
if (type == QEvent::MouseButtonPress) {
if (isInTitlebarArea)
titlebarClicked = true;
else
titlebarClicked = false;
}
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) {
if (edges == Qt::Edges{}) {
if (isInTitlebarArea) {
if (!window->startSystemMove()) {
// ### FIXME: TO BE IMPLEMENTED!
qWarning() << "Current OS doesn't support QWindow::startSystemMove().";
}
}
}
}
} else if (type == QEvent::MouseButtonPress) {
if (edges != Qt::Edges{}) {
if ((window->windowState() == Qt::WindowState::WindowNoState) && !hitTestVisible && resizable) {
if (!window->startSystemResize(edges)) {
// ### FIXME: TO BE IMPLEMENTED!
qWarning() << "Current OS doesn't support QWindow::startSystemResize().";
}
}
}
}
return false;
}
FRAMELESSHELPER_END_NAMESPACE
#endif

View File

@ -1,57 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class FRAMELESSHELPER_API FramelessHelper : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessHelper)
public:
explicit FramelessHelper(QObject *parent = nullptr);
~FramelessHelper() override = default;
void removeWindowFrame(QWindow *window);
void bringBackWindowFrame(QWindow *window);
protected:
bool eventFilter(QObject *object, QEvent *event) override;
};
FRAMELESSHELPER_END_NAMESPACE
#endif

112
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,112 @@
set(SOURCES
framelesshelper_global.h
core/framelesshelper.h
core/framelesshelper.cpp
core/utilities.h
core/utilities.cpp
core/framelesswindowsmanager.h
core/framelesswindowsmanager.cpp
)
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
list(APPEND SOURCES
widget/framelesswindow.h
)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
list(APPEND SOURCES
quick/framelessquickhelper.h
quick/framelessquickhelper.cpp
)
endif()
if(WIN32)
list(APPEND SOURCES
core/framelesshelper_windows.h
core/utilities_win32.cpp
core/framelesshelper_win32.h
core/framelesshelper_win32.cpp
)
else()
if(APPLE)
list(APPEND SOURCES
core/utilities_macos.mm
core/nswindow_proxy.h
core/nswindow_proxy.mm
core/window_buttons_proxy.h
core/window_buttons_proxy.mm
core/scoped_nsobject.h
)
else()
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
list(APPEND SOURCES core/utilities_linux.cpp)
endif()
endif()
if(WIN32 AND BUILD_SHARED_LIBS)
enable_language(RC)
list(APPEND SOURCES framelesshelper.rc)
endif()
add_library(${PROJECT_NAME} ${SOURCES})
add_library(wangwenx190::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(${PROJECT_NAME} PUBLIC
FRAMELESSHELPER_STATIC
)
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
QT_NO_KEYWORDS
QT_DEPRECATED_WARNINGS
QT_DISABLE_DEPRECATED_BEFORE=0x060100
FRAMELESSHELPER_BUILD_LIBRARY
)
if(TEST_UNIX)
target_compile_definitions(${PROJECT_NAME} PRIVATE
FRAMELESSHELPER_TEST_UNIX
)
endif()
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE
dwmapi
)
else()
if(APPLE)
target_link_libraries(${PROJECT_NAME} PRIVATE
"-framework Cocoa -framework Carbon"
)
else()
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::X11Extras
X11
)
endif()
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::GuiPrivate
)
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::WidgetsPrivate
)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
)

1147
src/core/framelesshelper.cpp Normal file

File diff suppressed because it is too large Load Diff

118
src/core/framelesshelper.h Normal file
View File

@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qsize.h>
QT_BEGIN_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QWindow)
QT_FORWARD_DECLARE_CLASS(QMouseEvent)
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class FRAMELESSHELPER_API FramelessHelper : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessHelper)
public:
explicit FramelessHelper(QWindow *window = nullptr);
~FramelessHelper() override = default;
void install();
void uninstall();
void setWindow(QWindow *w);
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();
void setTitleBarHeight(int height);
QRect titleBarRect();
QRegion titleBarRegion();
int resizeBorderThickness();
void setResizeBorderThickness(int 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);
#ifdef Q_OS_WIN
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, qintptr *result);
#else
bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result);
#endif
#endif // Q_OS_WIN
protected:
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

View File

@ -32,31 +32,9 @@
FRAMELESSHELPER_BEGIN_NAMESPACE
[[nodiscard]] static inline bool shouldHaveWindowFrame()
{
if (Utilities::shouldUseNativeTitleBar()) {
// We have to use the original window frame unconditionally if we
// want to use the native title bar.
return true;
}
const bool should = qEnvironmentVariableIsSet(Constants::kPreserveNativeFrameFlag);
const bool force = qEnvironmentVariableIsSet(Constants::kForcePreserveNativeFrameFlag);
if (should || force) {
if (force) {
return true;
}
if (should) {
// If you preserve the window frame on Win7~8.1,
// the window will have a terrible appearance.
return Utilities::isWin10OrGreater();
}
}
return false;
}
struct FramelessHelperWinData
{
[[nodiscard]] bool create() {
bool create() {
if (!m_instance.isNull()) {
return false;
}
@ -64,18 +42,18 @@ struct FramelessHelperWinData
return !m_instance.isNull();
}
[[nodiscard]] bool release() {
bool release() {
if (!m_instance.isNull()) {
m_instance.reset();
}
return m_instance.isNull();
}
[[nodiscard]] bool isNull() const {
bool isNull() const {
return m_instance.isNull();
}
[[nodiscard]] bool install() {
bool install() {
if (isInstalled()) {
return true;
}
@ -89,7 +67,7 @@ struct FramelessHelperWinData
return true;
}
[[nodiscard]] bool uninstall() {
bool uninstall() {
if (!isInstalled()) {
return true;
}
@ -101,7 +79,7 @@ struct FramelessHelperWinData
return true;
}
[[nodiscard]] bool isInstalled() const {
bool isInstalled() const {
return m_installed;
}
@ -247,29 +225,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// preserve the four window borders. So we just remove the whole
// window frame, otherwise the code will become much more complex.
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
if (msg->wParam == FALSE) {
if (static_cast<BOOL>(msg->wParam) == FALSE) {
*result = 0;
return true;
}
const auto clientRect = &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]);
if (shouldHaveWindowFrame()) {
// Store the original top before the default window proc
// applies the default frame.
const LONG originalTop = clientRect->top;
// Apply the default frame
const LRESULT ret = DefWindowProcW(msg->hwnd, WM_NCCALCSIZE, TRUE, msg->lParam);
if (ret != 0) {
*result = ret;
return true;
}
// Re-apply the original top from before the size of the
// default frame was applied.
clientRect->top = originalTop;
}
bool nonClientAreaExists = false;
// We don't need this correction when we're fullscreen. We will
// have the WS_POPUP size, so we don't have to worry about
@ -283,11 +243,9 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// a window when it's maximized unless you restore it).
const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true);
clientRect->top += resizeBorderThickness;
if (!shouldHaveWindowFrame()) {
clientRect->bottom -= resizeBorderThickness;
clientRect->left += resizeBorderThickness;
clientRect->right -= resizeBorderThickness;
}
clientRect->bottom -= resizeBorderThickness;
clientRect->left += resizeBorderThickness;
clientRect->right -= resizeBorderThickness;
nonClientAreaExists = true;
}
// Attempt to detect if there's an autohide taskbar, and if
@ -427,17 +385,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// area.
case WM_NCUAHDRAWCAPTION:
case WM_NCUAHDRAWFRAME: {
if (shouldHaveWindowFrame()) {
break;
} else {
*result = 0;
return true;
}
*result = 0;
return true;
}
case WM_NCPAINT: {
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
if (!Utilities::isDwmCompositionAvailable() && !shouldHaveWindowFrame()) {
if (!Utilities::isDwmCompositionAvailable()) {
// Only block WM_NCPAINT when DWM composition is disabled. If
// it's blocked when DWM composition is enabled, the frame
// shadow won't be drawn.
@ -448,26 +402,22 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
}
}
case WM_NCACTIVATE: {
if (shouldHaveWindowFrame()) {
break;
if (Utilities::isDwmCompositionAvailable()) {
// DefWindowProc won't repaint the window border if lParam
// (normally a HRGN) is -1. See the following link's "lParam"
// section:
// https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate
// Don't use "*result = 0" otherwise the window won't respond
// to the window active state change.
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
} else {
if (Utilities::isDwmCompositionAvailable()) {
// DefWindowProc won't repaint the window border if lParam
// (normally a HRGN) is -1. See the following link's "lParam"
// section:
// https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate
// Don't use "*result = 0" otherwise the window won't respond
// to the window active state change.
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
if (static_cast<BOOL>(msg->wParam) == FALSE) {
*result = TRUE;
} else {
if (msg->wParam == FALSE) {
*result = TRUE;
} else {
*result = FALSE;
}
*result = FALSE;
}
return true;
}
return true;
}
case WM_NCHITTEST: {
// 原生Win32窗口只有顶边是在窗口内部resize的其余三边都是在窗口
@ -535,10 +485,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// another branch, if you are interested in it, you can give it a
// try.
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient"));
@ -557,99 +503,76 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) {
isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight)
&& (localMouse.x() >= 0) && (localMouse.x() <= windowWidth)
&& !Utilities::isHitTestVisibleInChrome(window);
&& !Utilities::isHitTestVisible(window);
}
if (window->windowState() == Qt::WindowNoState) {
isTitleBar = (localMouse.y() > resizeBorderThickness) && (localMouse.y() <= titleBarHeight)
&& (localMouse.x() > resizeBorderThickness) && (localMouse.x() < (windowWidth - resizeBorderThickness))
&& !Utilities::isHitTestVisibleInChrome(window);
&& !Utilities::isHitTestVisible(window);
}
const bool isTop = localMouse.y() <= resizeBorderThickness;
if (shouldHaveWindowFrame()) {
// This will handle the left, right and bottom parts of the frame
// because we didn't change them.
const LRESULT originalRet = DefWindowProcW(msg->hwnd, WM_NCHITTEST, 0, msg->lParam);
if (originalRet != HTCLIENT) {
*result = originalRet;
return true;
}
// At this point, we know that the cursor is inside the client area
// so it has to be either the little border at the top of our custom
// title bar or the drag bar. Apparently, it must be the drag bar or
// the little border at the top which the user can use to move or
// resize the window.
if (!IsMaximized(msg->hwnd) && isTop) {
*result = HTTOP;
return true;
}
if (isTitleBar) {
*result = HTCAPTION;
return true;
}
*result = HTCLIENT;
return true;
} else {
const LRESULT hitTestResult = [clientRect, msg, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window]{
if (IsMaximized(msg->hwnd)) {
if (isTitleBar) {
return HTCAPTION;
}
return HTCLIENT;
}
const LONG windowHeight = clientRect.bottom;
const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness));
// Make the border a little wider to let the user easy to resize on corners.
const qreal factor = (isTop || isBottom) ? 2.0 : 1.0;
const bool isLeft = (localMouse.x() <= qRound(static_cast<qreal>(resizeBorderThickness) * factor));
const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast<qreal>(resizeBorderThickness) * factor)));
const bool fixedSize = Utilities::isWindowFixedSize(window);
const auto getBorderValue = [fixedSize](int value) -> int {
return fixedSize ? HTCLIENT : value;
};
if (isTop) {
if (isLeft) {
return getBorderValue(HTTOPLEFT);
}
if (isRight) {
return getBorderValue(HTTOPRIGHT);
}
return getBorderValue(HTTOP);
}
if (isBottom) {
if (isLeft) {
return getBorderValue(HTBOTTOMLEFT);
}
if (isRight) {
return getBorderValue(HTBOTTOMRIGHT);
}
return getBorderValue(HTBOTTOM);
}
if (isLeft) {
return getBorderValue(HTLEFT);
}
if (isRight) {
return getBorderValue(HTRIGHT);
}
const LRESULT hitTestResult = [clientRect, msg, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window]{
if (IsMaximized(msg->hwnd)) {
if (isTitleBar) {
return HTCAPTION;
}
return HTCLIENT;
}();
*result = hitTestResult;
return true;
}
}
const LONG windowHeight = clientRect.bottom;
const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness));
// Make the border a little wider to let the user easy to resize on corners.
const qreal factor = (isTop || isBottom) ? 2.0 : 1.0;
const bool isLeft = (localMouse.x() <= qRound(static_cast<qreal>(resizeBorderThickness) * factor));
const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast<qreal>(resizeBorderThickness) * factor)));
const bool fixedSize = Utilities::isWindowFixedSize(window);
const auto getBorderValue = [fixedSize](int value) -> int {
return fixedSize ? HTCLIENT : value;
};
if (isTop) {
if (isLeft) {
return getBorderValue(HTTOPLEFT);
}
if (isRight) {
return getBorderValue(HTTOPRIGHT);
}
return getBorderValue(HTTOP);
}
if (isBottom) {
if (isLeft) {
return getBorderValue(HTBOTTOMLEFT);
}
if (isRight) {
return getBorderValue(HTBOTTOMRIGHT);
}
return getBorderValue(HTBOTTOM);
}
if (isLeft) {
return getBorderValue(HTLEFT);
}
if (isRight) {
return getBorderValue(HTRIGHT);
}
if (isTitleBar) {
return HTCAPTION;
}
return HTCLIENT;
}();
*result = hitTestResult;
return true;
}
case WM_SETICON:
case WM_SETTEXT: {
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
// Disable painting while these messages are handled to prevent them
// from drawing a window caption over the client area.
SetLastError(ERROR_SUCCESS);
const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE);
if (oldStyle == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW"));
break;
}
// Prevent Windows from drawing the default title bar by temporarily
// toggling the WS_VISIBLE style.
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break;
@ -657,6 +580,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const auto winId = reinterpret_cast<WId>(msg->hwnd);
Utilities::triggerFrameChange(winId);
const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break;
@ -665,15 +589,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
*result = ret;
return true;
}
case WM_SIZE: {
const bool normal = (msg->wParam == SIZE_RESTORED);
const bool max = (msg->wParam == SIZE_MAXIMIZED);
const bool full = (window->windowState() == Qt::WindowFullScreen);
if (normal || max || full) {
Utilities::updateFrameMargins(reinterpret_cast<WId>(msg->hwnd), (max || full));
Utilities::updateQtFrameMargins(const_cast<QWindow *>(window), true);
}
} break;
default:
break;
}

View File

@ -90,11 +90,11 @@
#endif
#ifndef GET_X_LPARAM
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define GET_X_LPARAM(lp) (static_cast<int>(static_cast<short>(LOWORD(lp))))
#endif
#ifndef GET_Y_LPARAM
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
#define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(HIWORD(lp))))
#endif
#ifndef IsMinimized
@ -105,13 +105,13 @@
#define IsMaximized(window) (IsZoomed(window) != FALSE)
#endif
[[maybe_unused]] constexpr int kAutoHideTaskbarThickness = 2; // The thickness of an auto-hide taskbar in pixels
constexpr UINT kAutoHideTaskbarThickness = 2; // The thickness of an auto-hide taskbar in pixels
[[maybe_unused]] constexpr char kDwmRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)";
[[maybe_unused]] constexpr char kPersonalizeRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)";
constexpr char kDwmRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)";
constexpr char kPersonalizeRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)";
[[maybe_unused]] constexpr int kDefaultResizeBorderThicknessClassic = 4;
[[maybe_unused]] constexpr int kDefaultResizeBorderThicknessAero = 8;
[[maybe_unused]] constexpr int kDefaultCaptionHeight = 23;
constexpr UINT kDefaultResizeBorderThicknessClassic = 4;
constexpr UINT kDefaultResizeBorderThicknessAero = 8;
constexpr UINT kDefaultCaptionHeight = 23;
[[maybe_unused]] constexpr WORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;
constexpr DWORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;

View File

@ -38,7 +38,7 @@
FRAMELESSHELPER_BEGIN_NAMESPACE
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix)
//Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix)
#endif
void FramelessWindowsManager::addWindow(QWindow *window)
@ -51,7 +51,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
}
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
framelessHelperUnix()->removeWindowFrame(window);
//framelessHelperUnix()->removeWindowFrame(window);
#else
FramelessHelperWin::addFramelessWindow(window);
// Work-around a Win32 multi-monitor bug.
@ -62,7 +62,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
#endif
}
void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject *object, const bool value)
void FramelessWindowsManager::setHitTestVisible(QWindow *window, QObject *object, const bool value)
{
Q_ASSERT(window);
Q_ASSERT(object);
@ -73,7 +73,7 @@ void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject
qWarning() << object << "is not a QWidget or QQuickItem.";
return;
}
auto objList = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleInChromeFlag));
auto objList = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleFlag));
if (value) {
if (objList.isEmpty() || !objList.contains(object)) {
objList.append(object);
@ -83,7 +83,7 @@ void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject
objList.removeAll(object);
}
}
window->setProperty(Constants::kHitTestVisibleInChromeFlag, QVariant::fromValue(objList));
window->setProperty(Constants::kHitTestVisibleFlag, QVariant::fromValue(objList));
}
int FramelessWindowsManager::getResizeBorderThickness(const QWindow *window)
@ -165,7 +165,7 @@ void FramelessWindowsManager::removeWindow(QWindow *window)
return;
}
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION
framelessHelperUnix()->bringBackWindowFrame(window);
//framelessHelperUnix()->bringBackWindowFrame(window);
#else
FramelessHelperWin::removeFramelessWindow(window);
#endif

View File

@ -38,13 +38,13 @@ namespace FramelessWindowsManager
FRAMELESSHELPER_API void addWindow(QWindow *window);
FRAMELESSHELPER_API void removeWindow(QWindow *window);
[[nodiscard]] FRAMELESSHELPER_API bool isWindowFrameless(const QWindow *window);
FRAMELESSHELPER_API void setHitTestVisibleInChrome(QWindow *window, QObject *object, const bool value = true);
[[nodiscard]] FRAMELESSHELPER_API int getResizeBorderThickness(const QWindow *window);
FRAMELESSHELPER_API bool isWindowFrameless(const QWindow *window);
FRAMELESSHELPER_API void setHitTestVisible(QWindow *window, QObject *object, const bool value = true);
FRAMELESSHELPER_API int getResizeBorderThickness(const QWindow *window);
FRAMELESSHELPER_API void setResizeBorderThickness(QWindow *window, const int value);
[[nodiscard]] FRAMELESSHELPER_API int getTitleBarHeight(const QWindow *window);
FRAMELESSHELPER_API int getTitleBarHeight(const QWindow *window);
FRAMELESSHELPER_API void setTitleBarHeight(QWindow *window, const int value);
[[nodiscard]] FRAMELESSHELPER_API bool getResizable(const QWindow *window);
FRAMELESSHELPER_API bool getResizable(const QWindow *window);
FRAMELESSHELPER_API void setResizable(QWindow *window, const bool value = true);
}

67
src/core/nswindow_proxy.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef NSWINDOWPROXY_H
#define NSWINDOWPROXY_H
#include <objc/runtime.h>
#include <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
#include <Quartz/Quartz.h>
#include <QtCore/qpoint.h>
#include "framelesshelper.h"
#include "scoped_nsobject.h"
#include "window_buttons_proxy.h"
@class NSWindowProxyDelegate;
class NSWindowProxy
{
private:
NSWindow* m_window;
QWindow* m_qtwindow;
scoped_nsobject<WindowButtonsProxy> m_buttonProxy;
scoped_nsobject<NSWindowProxyDelegate> m_windowDelegate;
bool m_windowButtonVisibility;
QPoint m_trafficLightPosition;
public:
NSWindowProxy(NSWindow *window, QWindow *qtwindow);
~NSWindowProxy();
NSWindow* window() { return m_window; }
QPoint trafficLightPosition() { return m_trafficLightPosition; }
void setTrafficLightPosition(const QPoint &pos);
bool windowButtonVisibility() { return m_windowButtonVisibility; }
void setWindowButtonVisibility(bool visible);
void redrawTrafficLights();
bool isFullscreen() const;
void setTitle(const QString& title);
void notifyWindowEnterFullScreen();
void notifyWindowLeaveFullScreen();
void notifyWindowWillEnterFullScreen();
void notifyWindowWillLeaveFullScreen();
void notifyWindowCloseButtonClicked();
};
@interface NSWindowProxyDelegate : NSObject<NSWindowDelegate> {
@private
NSWindowProxy* m_windowProxy;
bool m_isZooming;
int m_level;
bool m_isResizable;
// Only valid during a live resize.
// Used to keep track of whether a resize is happening horizontally or
// vertically, even if physically the user is resizing in both directions.
bool m_resizingHorizontally;
}
- (id)initWithWindowProxy:(NSWindowProxy*)proxy;
@end
#endif // NSWINDOWPROXY_H

304
src/core/nswindow_proxy.mm Normal file
View File

@ -0,0 +1,304 @@
#include "nswindow_proxy.h"
#include <QtGui/qguiapplication.h>
#include <QtGui/qwindow.h>
static QList<NSWindow*> gFlsWindows;
static bool gNSWindowOverrode = false;
typedef void (*setStyleMaskType)(id, SEL, NSWindowStyleMask);
static setStyleMaskType gOrigSetStyleMask = nullptr;
static void __setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask)
{
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
{
styleMask = styleMask | NSWindowStyleMaskFullSizeContentView;
}
if (gOrigSetStyleMask != nullptr)
gOrigSetStyleMask(obj, sel, styleMask);
}
typedef void (*setTitlebarAppearsTransparentType)(id, SEL, BOOL);
static setTitlebarAppearsTransparentType gOrigSetTitlebarAppearsTransparent = nullptr;
static void __setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent)
{
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
transparent = true;
if (gOrigSetTitlebarAppearsTransparent != nullptr)
gOrigSetTitlebarAppearsTransparent(obj, sel, transparent);
}
typedef BOOL (*canBecomeKeyWindowType)(id, SEL);
static canBecomeKeyWindowType gOrigCanBecomeKeyWindow = nullptr;
static BOOL __canBecomeKeyWindow(id obj, SEL sel)
{
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
{
return true;
}
if (gOrigCanBecomeKeyWindow != nullptr)
return gOrigCanBecomeKeyWindow(obj, sel);
return true;
}
typedef BOOL (*canBecomeMainWindowType)(id, SEL);
static canBecomeMainWindowType gOrigCanBecomeMainWindow = nullptr;
static BOOL __canBecomeMainWindow(id obj, SEL sel)
{
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
{
return true;
}
if (gOrigCanBecomeMainWindow != nullptr)
return gOrigCanBecomeMainWindow(obj, sel);
return true;
}
typedef void (*sendEventType)(id, SEL, NSEvent*);
static sendEventType gOrigSendEvent = nullptr;
static void __sendEvent(id obj, SEL sel, NSEvent* event)
{
if (gOrigSendEvent != nullptr)
gOrigSendEvent(obj, sel, event);
if (!gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
return;
if (event.type == NSEventTypeLeftMouseDown)
QGuiApplication::processEvents();
}
typedef BOOL (*isFlippedType)(id, SEL);
static isFlippedType gOrigIsFlipped = nullptr;
static BOOL __isFlipped(id obj, SEL sel)
{
if (!gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
return true;
if (gOrigIsFlipped != nullptr)
return gOrigIsFlipped(obj, sel);
return false;
}
/*!
Replace origin method \a origSEL of class \a cls with new one \a newIMP ,
then return old method as function pointer.
*/
static void* replaceMethod(Class cls, SEL origSEL, IMP newIMP)
{
Method origMethod = class_getInstanceMethod(cls, origSEL);
void *funcPtr = (void *)method_getImplementation(origMethod);
if (!class_addMethod(cls, origSEL, newIMP, method_getTypeEncoding(origMethod))) {
method_setImplementation(origMethod, newIMP);
}
return funcPtr;
}
static void restoreMethod(Class cls, SEL origSEL, IMP oldIMP)
{
Method method = class_getInstanceMethod(cls, origSEL);
method_setImplementation(method, oldIMP);
}
static void overrideNSWindowMethods(NSWindow* window)
{
if (!gNSWindowOverrode) {
Class cls = [window class];
gOrigSetStyleMask = (setStyleMaskType) replaceMethod(
cls, @selector(setStyleMask:), (IMP) __setStyleMask);
gOrigSetTitlebarAppearsTransparent = (setTitlebarAppearsTransparentType) replaceMethod(
cls, @selector(setTitlebarAppearsTransparent:), (IMP) __setTitlebarAppearsTransparent);
gOrigCanBecomeKeyWindow = (canBecomeKeyWindowType) replaceMethod(
cls, @selector(canBecomeKeyWindow), (IMP) __canBecomeKeyWindow);
gOrigCanBecomeMainWindow = (canBecomeMainWindowType) replaceMethod(
cls, @selector(canBecomeMainWindow), (IMP) __canBecomeMainWindow);
gOrigSendEvent = (sendEventType) replaceMethod(
cls, @selector(sendEvent:), (IMP) __sendEvent);
//gOrigIsFlipped = (isFlippedType) replaceMethod(
// cls, @selector (isFlipped), (IMP) __isFlipped);
gNSWindowOverrode = true;
}
gFlsWindows.append(window);
}
static void restoreNSWindowMethods(NSWindow* window)
{
gFlsWindows.removeAll(window);
if (gFlsWindows.size() == 0) {
Class cls = [window class];
restoreMethod(cls, @selector(setStyleMask:), (IMP) gOrigSetStyleMask);
gOrigSetStyleMask = nullptr;
restoreMethod(cls, @selector(setTitlebarAppearsTransparent:), (IMP) gOrigSetTitlebarAppearsTransparent);
gOrigSetTitlebarAppearsTransparent = nullptr;
restoreMethod(cls, @selector(canBecomeKeyWindow), (IMP) gOrigCanBecomeKeyWindow);
gOrigCanBecomeKeyWindow = nullptr;
restoreMethod(cls, @selector(canBecomeMainWindow), (IMP) gOrigCanBecomeMainWindow);
gOrigCanBecomeMainWindow = nullptr;
restoreMethod(cls, @selector(sendEvent:), (IMP) gOrigSendEvent);
gOrigSendEvent = nullptr;
//restoreMethod(cls, @selector(isFlipped), (IMP) gOrigIsFlipped);
//gOrigIsFlipped = nullptr;
gNSWindowOverrode = false;
}
}
NSWindowProxy::NSWindowProxy(NSWindow *window, QWindow *qtwindow)
: m_windowButtonVisibility(false)
, m_buttonProxy(nullptr)
, m_window(window)
, m_qtwindow(qtwindow)
{
overrideNSWindowMethods(window);
m_buttonProxy.reset([[WindowButtonsProxy alloc] initWithWindow:window]);
m_windowDelegate.reset([[NSWindowProxyDelegate alloc] initWithWindowProxy:this]);
[m_window setDelegate:m_windowDelegate.get()];
}
NSWindowProxy::~NSWindowProxy()
{
restoreNSWindowMethods(m_window);
[m_buttonProxy release];
}
void NSWindowProxy::setTrafficLightPosition(const QPoint &pos) {
m_trafficLightPosition = pos;
if (m_buttonProxy) {
[m_buttonProxy setMargin:m_trafficLightPosition];
}
}
void NSWindowProxy::setWindowButtonVisibility(bool visible) {
m_windowButtonVisibility = visible;
// The visibility of window buttons are managed by |buttons_proxy_| if the
// style is customButtonsOnHover.
if (false /*title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover*/)
[m_buttonProxy setVisible:visible];
else {
[[m_window standardWindowButton:NSWindowCloseButton] setHidden:!visible];
[[m_window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
[[m_window standardWindowButton:NSWindowZoomButton] setHidden:!visible];
}
}
bool NSWindowProxy::isFullscreen() const {
return [m_window styleMask] & NSWindowStyleMaskFullScreen;
}
void NSWindowProxy::redrawTrafficLights() {
if (m_buttonProxy && !isFullscreen())
[m_buttonProxy redraw];
}
void NSWindowProxy::setTitle(const QString& title) {
[m_window setTitle:title.toNSString()];
if (m_buttonProxy)
[m_buttonProxy redraw];
}
void NSWindowProxy::notifyWindowEnterFullScreen() {
// Restore the window title under fullscreen mode.
if (m_buttonProxy) {
[m_window setTitleVisibility:NSWindowTitleVisible];
}
}
void NSWindowProxy::notifyWindowLeaveFullScreen() {
// Restore window buttons.
if (m_buttonProxy && m_windowButtonVisibility) {
[m_buttonProxy redraw];
[m_buttonProxy setVisible:YES];
}
}
void NSWindowProxy::notifyWindowWillEnterFullScreen() {
}
void NSWindowProxy::notifyWindowWillLeaveFullScreen() {
if (m_buttonProxy) {
// Hide window title when leaving fullscreen.
[m_window setTitleVisibility:NSWindowTitleHidden];
// Hide the container otherwise traffic light buttons jump.
[m_buttonProxy setVisible:NO];
}
}
void NSWindowProxy::notifyWindowCloseButtonClicked() {
// Call QWindow::close() when button clicked.
m_qtwindow->close();
}
@implementation NSWindowProxyDelegate
- (id)initWithWindowProxy:(NSWindowProxy*)proxy {
m_windowProxy = proxy;
return self;
}
- (void)windowDidBecomeMain:(NSNotification*)notification {
m_windowProxy->redrawTrafficLights();
}
- (void)windowDidResignMain:(NSNotification*)notification {
m_windowProxy->redrawTrafficLights();
}
- (void)windowDidBecomeKey:(NSNotification*)notification {
m_windowProxy->redrawTrafficLights();
}
- (void)windowDidResignKey:(NSNotification*)notification {
// If our app is still active and we're still the key window, ignore this
// message, since it just means that a menu extra (on the "system status bar")
// was activated; we'll get another |-windowDidResignKey| if we ever really
// lose key window status.
if ([NSApp isActive] && ([NSApp keyWindow] == [notification object]))
return;
m_windowProxy->redrawTrafficLights();
}
- (void)windowDidResize:(NSNotification*)notification {
m_windowProxy->redrawTrafficLights();
}
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
m_windowProxy->notifyWindowWillEnterFullScreen();
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
m_windowProxy->notifyWindowEnterFullScreen();
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
m_windowProxy->notifyWindowWillLeaveFullScreen();
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
m_windowProxy->notifyWindowLeaveFullScreen();
}
- (BOOL)windowShouldClose:(id)window {
// We will override default close behavior
m_windowProxy->notifyWindowCloseButtonClicked();
return NO;
}
@end

203
src/core/scoped_nsobject.h Normal file
View File

@ -0,0 +1,203 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SCOPED_NSOBJECT_H
#define SCOPED_NSOBJECT_H
// Include NSObject.h directly because Foundation.h pulls in many dependencies.
// (Approx 100k lines of code versus 1.5k for NSObject.h). scoped_nsobject gets
// singled out because it is most typically included from other header files.
#import <Foundation/NSObject.h>
#if !defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)
#error Unsupported compiler.
#endif
// Annotate a variable indicating it's ok if the variable is not used.
// (Typically used to silence a compiler warning when the assignment
// is important for some other reason.)
// Use like:
// int x = ...;
// FML_ALLOW_UNUSED_LOCAL(x);
#define FML_ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0
// Annotate a typedef or function indicating it's ok if it's not used.
// Use like:
// typedef Foo Bar ALLOW_UNUSED_TYPE;
#if defined(__GNUC__) || defined(__clang__)
#define FML_ALLOW_UNUSED_TYPE __attribute__((unused))
#else
#define FML_ALLOW_UNUSED_TYPE
#endif
#ifndef FML_USED_ON_EMBEDDER
#define FML_EMBEDDER_ONLY [[deprecated]]
#else // FML_USED_ON_EMBEDDER
#define FML_EMBEDDER_ONLY
#endif // FML_USED_ON_EMBEDDER
#define FML_DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete
#define FML_DISALLOW_ASSIGN(TypeName) \
TypeName& operator=(const TypeName&) = delete
#define FML_DISALLOW_MOVE(TypeName) \
TypeName(TypeName&&) = delete; \
TypeName& operator=(TypeName&&) = delete
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
TypeName& operator=(const TypeName&) = delete
#define FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName) \
TypeName(const TypeName&) = delete; \
TypeName(TypeName&&) = delete; \
TypeName& operator=(const TypeName&) = delete; \
TypeName& operator=(TypeName&&) = delete
#define FML_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
TypeName() = delete; \
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName)
@class NSAutoreleasePool;
// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership
// of an NSObject subclass object. Style deviations here are solely for
// compatibility with scoped_ptr<>'s interface, with which everyone is already
// familiar.
//
// scoped_nsobject<> takes ownership of an object (in the constructor or in
// reset()) by taking over the caller's existing ownership claim. The caller
// must own the object it gives to scoped_nsobject<>, and relinquishes an
// ownership claim to that object. scoped_nsobject<> does not call -retain,
// callers have to call this manually if appropriate.
//
// scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used
// with protocols.
//
// scoped_nsobject<> is not to be used for NSAutoreleasePools. For
// NSAutoreleasePools use ScopedNSAutoreleasePool from
// scoped_nsautorelease_pool.h instead.
// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile
// time with a template specialization (see below).
template <typename NST>
class scoped_nsprotocol {
public:
explicit scoped_nsprotocol(NST object = nil) : object_(object) {}
scoped_nsprotocol(const scoped_nsprotocol<NST>& that) : object_([that.object_ retain]) {}
template <typename NSU>
scoped_nsprotocol(const scoped_nsprotocol<NSU>& that) : object_([that.get() retain]) {}
~scoped_nsprotocol() { [object_ release]; }
scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
reset([that.get() retain]);
return *this;
}
void reset(NST object = nil) {
// We intentionally do not check that object != object_ as the caller must
// either already have an ownership claim over whatever it passes to this
// method, or call it with the |RETAIN| policy which will have ensured that
// the object is retained once more when reaching this point.
[object_ release];
object_ = object;
}
bool operator==(NST that) const { return object_ == that; }
bool operator!=(NST that) const { return object_ != that; }
operator NST() const { return object_; }
NST get() const { return object_; }
void swap(scoped_nsprotocol& that) {
NST temp = that.object_;
that.object_ = object_;
object_ = temp;
}
// Shift reference to the autorelease pool to be released later.
NST autorelease() { return [release() autorelease]; }
private:
NST object_;
// scoped_nsprotocol<>::release() is like scoped_ptr<>::release. It is NOT a
// wrapper for [object_ release]. To force a scoped_nsprotocol<> to call
// [object_ release], use scoped_nsprotocol<>::reset().
[[nodiscard]] NST release() {
NST temp = object_;
object_ = nil;
return temp;
}
};
// Free functions
template <class C>
void swap(scoped_nsprotocol<C>& p1, scoped_nsprotocol<C>& p2) {
p1.swap(p2);
}
template <class C>
bool operator==(C p1, const scoped_nsprotocol<C>& p2) {
return p1 == p2.get();
}
template <class C>
bool operator!=(C p1, const scoped_nsprotocol<C>& p2) {
return p1 != p2.get();
}
template <typename NST>
class scoped_nsobject : public scoped_nsprotocol<NST*> {
public:
explicit scoped_nsobject(NST* object = nil) : scoped_nsprotocol<NST*>(object) {}
scoped_nsobject(const scoped_nsobject<NST>& that) : scoped_nsprotocol<NST*>(that) {}
template <typename NSU>
scoped_nsobject(const scoped_nsobject<NSU>& that) : scoped_nsprotocol<NST*>(that) {}
scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
scoped_nsprotocol<NST*>::operator=(that);
return *this;
}
};
// Specialization to make scoped_nsobject<id> work.
template <>
class scoped_nsobject<id> : public scoped_nsprotocol<id> {
public:
explicit scoped_nsobject(id object = nil) : scoped_nsprotocol<id>(object) {}
scoped_nsobject(const scoped_nsobject<id>& that) : scoped_nsprotocol<id>(that) {}
template <typename NSU>
scoped_nsobject(const scoped_nsobject<NSU>& that) : scoped_nsprotocol<id>(that) {}
scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
scoped_nsprotocol<id>::operator=(that);
return *this;
}
};
// Do not use scoped_nsobject for NSAutoreleasePools, use
// ScopedNSAutoreleasePool instead. This is a compile time check. See details
// at top of header.
template <>
class scoped_nsobject<NSAutoreleasePool> {
private:
explicit scoped_nsobject(NSAutoreleasePool* object = nil);
FML_DISALLOW_COPY_AND_ASSIGN(scoped_nsobject);
};
#endif // SCOPED_NSOBJECT_H

View File

@ -49,11 +49,6 @@ QWindow *Utilities::findWindow(const WId winId)
return nullptr;
}
bool Utilities::shouldUseNativeTitleBar()
{
return qEnvironmentVariableIsSet(Constants::kUseNativeTitleBarFlag);
}
bool Utilities::isWindowFixedSize(const QWindow *window)
{
Q_ASSERT(window);
@ -73,13 +68,13 @@ bool Utilities::isWindowFixedSize(const QWindow *window)
return false;
}
bool Utilities::isHitTestVisibleInChrome(const QWindow *window)
bool Utilities::isHitTestVisible(const QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return false;
}
const auto objs = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleInChromeFlag));
const auto objs = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleFlag));
if (objs.isEmpty()) {
return false;
}

87
src/core/utilities.h Normal file
View File

@ -0,0 +1,87 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#include <QtGui/qwindow.h>
#include <QtCore/qsize.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
namespace Utilities
{
FRAMELESSHELPER_API int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue = false);
FRAMELESSHELPER_API QWindow *findWindow(const WId winId);
FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window);
FRAMELESSHELPER_API bool isHitTestVisible(const QWindow *window);
FRAMELESSHELPER_API QPointF mapOriginPointToWindow(const QObject *object);
FRAMELESSHELPER_API QColor getColorizationColor();
FRAMELESSHELPER_API int getWindowVisibleFrameBorderThickness(const WId winId);
FRAMELESSHELPER_API bool shouldAppsUseDarkMode();
FRAMELESSHELPER_API ColorizationArea getColorizationArea();
FRAMELESSHELPER_API bool isThemeChanged(const void *data);
FRAMELESSHELPER_API bool isSystemMenuRequested(const void *data, QPointF *pos);
FRAMELESSHELPER_API bool showSystemMenu(const WId winId, const QPointF &pos);
#ifdef Q_OS_WINDOWS
FRAMELESSHELPER_API bool isWin8OrGreater();
FRAMELESSHELPER_API bool isWin8Point1OrGreater();
FRAMELESSHELPER_API bool isWin10OrGreater();
FRAMELESSHELPER_API bool isDwmCompositionAvailable();
FRAMELESSHELPER_API void triggerFrameChange(const WId winId);
FRAMELESSHELPER_API void updateFrameMargins(const WId winId, const bool reset);
FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable);
FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function);
#endif // Q_OS_WINDOWS
#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 // Q_OS_LINUX
#ifdef Q_OS_MAC
FRAMELESSHELPER_API bool setMacWindowHook(QWindow* w);
FRAMELESSHELPER_API bool unsetMacWindowHook(QWindow* w);
FRAMELESSHELPER_API bool setMacWindowFrameless(QWindow* w);
FRAMELESSHELPER_API bool unsetMacWindowFrameless(QWindow* w);
FRAMELESSHELPER_API bool startMacDrag(QWindow* w, const QPoint& pos);
FRAMELESSHELPER_API Qt::MouseButtons getMacMouseButtons();
FRAMELESSHELPER_API bool setStandardWindowButtonsVisibility(QWindow *w, bool visible);
FRAMELESSHELPER_API bool setStandardWindowButtonsPosition(QWindow *w, const QPoint &pos);
FRAMELESSHELPER_API QSize standardWindowButtonsSize(QWindow *w);
FRAMELESSHELPER_API bool setCloseBtnEnabled(QWindow *w, bool enable = true);
FRAMELESSHELPER_API bool setMinBtnEnabled(QWindow *w, bool enable = true);
FRAMELESSHELPER_API bool setZoomBtnEnabled(QWindow *w, bool enable = true);
#endif // Q_OS_MAC
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,312 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "utilities.h"
#include <QtCore/qvariant.h>
#include <QtCore/qdebug.h>
#include <QtGui/qscreen.h>
#include <QtX11Extras/qx11info_x11.h>
#include <X11/Xlib.h>
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 kDefaultCaptionHeight = 23;
int Utilities::getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue)
{
Q_ASSERT(window);
if (!window) {
return 0;
}
const qreal devicePixelRatio = window->devicePixelRatio();
const qreal scaleFactor = (dpiScale ? devicePixelRatio : 1.0);
switch (metric) {
case SystemMetric::ResizeBorderThickness: {
// ### TO BE IMPLEMENTED: Retrieve system value through official API
if (dpiScale) {
return qRound(static_cast<qreal>(kDefaultResizeBorderThickness) * devicePixelRatio);
} else {
return kDefaultResizeBorderThickness;
}
}
case SystemMetric::CaptionHeight: {
// ### TO BE IMPLEMENTED: Retrieve system value through official API
if (dpiScale) {
return qRound(static_cast<qreal>(kDefaultCaptionHeight) * devicePixelRatio);
} else {
return kDefaultCaptionHeight;
}
}
case SystemMetric::TitleBarHeight: {
const int captionHeight = getSystemMetric(window, SystemMetric::CaptionHeight,
dpiScale, forceSystemValue);
const int resizeBorderThickness = getSystemMetric(window, SystemMetric::ResizeBorderThickness,
dpiScale, forceSystemValue);
return (((window->windowState() == Qt::WindowMaximized)
|| (window->windowState() == Qt::WindowFullScreen))
? captionHeight : (captionHeight + resizeBorderThickness));
}
}
return 0;
}
QColor Utilities::getColorizationColor()
{
// ### TO BE IMPLEMENTED
return Qt::darkGray;
}
int Utilities::getWindowVisibleFrameBorderThickness(const WId winId)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(winId);
return 1;
}
bool Utilities::shouldAppsUseDarkMode()
{
// ### TO BE IMPLEMENTED
return false;
}
ColorizationArea Utilities::getColorizationArea()
{
// ### TO BE IMPLEMENTED
return ColorizationArea::NoArea;
}
bool Utilities::isThemeChanged(const void *data)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(data);
return false;
}
bool Utilities::isSystemMenuRequested(const void *data, QPointF *pos)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(data);
Q_UNUSED(pos);
return false;
}
bool Utilities::showSystemMenu(const WId winId, const QPointF &pos)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(winId);
Q_UNUSED(pos);
return false;
}
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

296
src/core/utilities_macos.mm Normal file
View File

@ -0,0 +1,296 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "utilities.h"
#include <QtGui/qguiapplication.h>
#include <QtCore/qlist.h>
#include <QtCore/qdebug.h>
#include "nswindow_proxy.h"
FRAMELESSHELPER_BEGIN_NAMESPACE
namespace Utilities {
static constexpr int kDefaultResizeBorderThickness = 8;
static constexpr int kDefaultCaptionHeight = 23;
int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue)
{
Q_ASSERT(window);
if (!window) {
return 0;
}
// The Apple platforms implement scaling and coordinate system virtualization,
// so there is no need to scale again.
switch (metric) {
case SystemMetric::ResizeBorderThickness: {
// ### TO BE IMPLEMENTED: Retrieve system value through official API
return kDefaultResizeBorderThickness;
}
case SystemMetric::CaptionHeight: {
// ### TO BE IMPLEMENTED: Retrieve system value through official API
return kDefaultCaptionHeight;
}
case SystemMetric::TitleBarHeight: {
const int captionHeight = getSystemMetric(window, SystemMetric::CaptionHeight,
dpiScale, forceSystemValue);
const int resizeBorderThickness = getSystemMetric(window, SystemMetric::ResizeBorderThickness,
dpiScale, forceSystemValue);
return (((window->windowState() == Qt::WindowMaximized)
|| (window->windowState() == Qt::WindowFullScreen))
? captionHeight : (captionHeight + resizeBorderThickness));
}
}
return 0;
}
QColor getColorizationColor()
{
// ### TO BE IMPLEMENTED
return Qt::darkGray;
}
int getWindowVisibleFrameBorderThickness(const WId winId)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(winId);
return 1;
}
bool shouldAppsUseDarkMode()
{
// ### TO BE IMPLEMENTED
return false;
}
ColorizationArea getColorizationArea()
{
// ### TO BE IMPLEMENTED
return ColorizationArea::NoArea;
}
bool isThemeChanged(const void *data)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(data);
return false;
}
bool isSystemMenuRequested(const void *data, QPointF *pos)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(data);
Q_UNUSED(pos);
return false;
}
bool showSystemMenu(const WId winId, const QPointF &pos)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(winId);
Q_UNUSED(pos);
return false;
}
static QHash<QWindow*, NSWindowProxy*> gQWindowToNSWindow;
static NSWindow* getNSWindow(QWindow* w)
{
NSView* view = reinterpret_cast<NSView *>(w->winId());
if (view == nullptr) {
qWarning() << "Unable to get NSView.";
return nullptr;
}
NSWindow* nswindow = [view window];
if (nswindow == nullptr) {
qWarning() << "Unable to get NSWindow.";
return nullptr;
}
return nswindow;
}
bool setMacWindowHook(QWindow* w)
{
NSWindow* nswindow = getNSWindow(w);
if (nswindow == nullptr)
return false;
NSWindowProxy *obj = new NSWindowProxy(nswindow, w);
gQWindowToNSWindow.insert(w, obj);
return true;
}
bool unsetMacWindowHook(QWindow* w)
{
if (!gQWindowToNSWindow.contains(w))
return false;
NSWindowProxy* obj = gQWindowToNSWindow[w];
gQWindowToNSWindow.remove(w);
delete obj;
return true;
}
bool setMacWindowFrameless(QWindow* w)
{
NSView* view = reinterpret_cast<NSView *>(w->winId());
if (view == nullptr)
return false;
NSWindow* nswindow = [view window];
if (nswindow == nullptr)
return false;
view.wantsLayer = YES;
nswindow.styleMask = nswindow.styleMask | NSWindowStyleMaskFullSizeContentView;
nswindow.titlebarAppearsTransparent = true;
nswindow.titleVisibility = NSWindowTitleHidden;
nswindow.hasShadow = true;
nswindow.movableByWindowBackground = false;
nswindow.movable = false;
nswindow.showsToolbarButton = false;
[nswindow standardWindowButton:NSWindowCloseButton].hidden = true;
[nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = true;
[nswindow standardWindowButton:NSWindowZoomButton].hidden = true;
[nswindow makeKeyWindow];
return true;
}
bool unsetMacWindowFrameless(QWindow* w)
{
NSView* view = reinterpret_cast<NSView *>(w->winId());
if (view == nullptr)
return false;
NSWindow* nswindow = [view window];
if (nswindow == nullptr)
return false;
view.wantsLayer = NO;
nswindow.styleMask = nswindow.styleMask & ~NSWindowStyleMaskFullSizeContentView;
nswindow.titlebarAppearsTransparent = false;
nswindow.titleVisibility = NSWindowTitleVisible;
nswindow.hasShadow = true;
nswindow.movableByWindowBackground = false;
nswindow.movable = true;
nswindow.showsToolbarButton = true;
[nswindow standardWindowButton:NSWindowCloseButton].hidden = false;
[nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = false;
[nswindow standardWindowButton:NSWindowZoomButton].hidden = false;
return true;
}
bool startMacDrag(QWindow* w, const QPoint& pos)
{
NSWindow* nswindow = getNSWindow(w);
if (nswindow == nullptr)
return false;
CGEventRef clickDown = CGEventCreateMouseEvent(
NULL, kCGEventLeftMouseDown, CGPointMake(pos.x(), pos.y()), kCGMouseButtonLeft);
NSEvent *nsevent = [NSEvent eventWithCGEvent:clickDown];
[nswindow performWindowDragWithEvent:nsevent];
CFRelease(clickDown);
return true;
}
Qt::MouseButtons getMacMouseButtons()
{
return static_cast<Qt::MouseButtons>((uint)(NSEvent.pressedMouseButtons & Qt::MouseButtonMask));
}
bool setStandardWindowButtonsVisibility(QWindow *w, bool visible)
{
NSWindowProxy* obj = gQWindowToNSWindow[w];
obj->setWindowButtonVisibility(visible);
return true;
}
/*! The origin of \a pos is top-left of window. */
bool setStandardWindowButtonsPosition(QWindow *w, const QPoint &pos)
{
NSWindowProxy* obj = gQWindowToNSWindow[w];
obj->setWindowButtonVisibility(true);
obj->setTrafficLightPosition(pos);
return true;
}
QSize standardWindowButtonsSize(QWindow *w)
{
NSWindow* nswindow = getNSWindow(w);
if (nswindow == nullptr)
return QSize();
NSButton* left = [nswindow standardWindowButton:NSWindowCloseButton];
NSButton* right = [nswindow standardWindowButton:NSWindowZoomButton];
float height = NSHeight(left.frame);
float width = NSMaxX(right.frame) - NSMinX(left.frame);
return QSize(width, height);
}
bool setStandardWindowButtonEnabled(QWindow *w, NSWindowButton name, bool enable = true)
{
NSWindow* nswindow = getNSWindow(w);
if (nswindow == nullptr)
return false;
NSButton* btn = [nswindow standardWindowButton:name];
[btn setEnabled: enable ? YES : NO];
return true;
}
bool setCloseBtnEnabled(QWindow *w, bool enable)
{
return setStandardWindowButtonEnabled(w, NSWindowCloseButton, enable);
}
bool setMinBtnEnabled(QWindow *w, bool enable)
{
return setStandardWindowButtonEnabled(w, NSWindowMiniaturizeButton, enable);
}
bool setZoomBtnEnabled(QWindow *w, bool enable)
{
return setStandardWindowButtonEnabled(w, NSWindowZoomButton, enable);
}
} // namespace Utilities
FRAMELESSHELPER_END_NAMESPACE

View File

@ -43,13 +43,13 @@ Q_DECLARE_METATYPE(QMargins)
FRAMELESSHELPER_BEGIN_NAMESPACE
[[nodiscard]] static inline QPointF extractMousePositionFromLParam(const LPARAM lParam)
static inline QPointF extractMousePositionFromLParam(const LPARAM lParam)
{
const POINT nativePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
return QPointF(static_cast<qreal>(nativePos.x), static_cast<qreal>(nativePos.y));
}
[[nodiscard]] static inline bool isWin10RS1OrGreater()
static inline bool isWin10RS1OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393));
@ -59,7 +59,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
return result;
}
[[nodiscard]] static inline bool isWin1019H1OrGreater()
static inline bool isWin1019H1OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
static const bool result = (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 18362));
@ -69,6 +69,39 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
return result;
}
static inline QString __getSystemErrorMessage(const QString &function, const DWORD code)
{
Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) {
return {};
}
if (code == ERROR_SUCCESS) {
return {};
}
LPWSTR buf = nullptr;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&buf), 0, nullptr) == 0) {
return {};
}
const QString message = QStringLiteral("Function %1() failed with error code %2: %3.")
.arg(function, QString::number(code), QString::fromWCharArray(buf));
LocalFree(buf);
return message;
}
static inline QString __getSystemErrorMessage(const QString &function, const HRESULT hr)
{
Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) {
return {};
}
if (SUCCEEDED(hr)) {
return {};
}
const DWORD dwError = HRESULT_CODE(hr);
return __getSystemErrorMessage(function, dwError);
}
bool Utilities::isWin8OrGreater()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
@ -110,7 +143,7 @@ bool Utilities::isDwmCompositionAvailable()
if (SUCCEEDED(hr)) {
return (enabled != FALSE);
} else {
qWarning() << getSystemErrorMessage(QStringLiteral("DwmIsCompositionEnabled"), hr);
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmIsCompositionEnabled"), hr);
const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat);
bool ok = false;
const DWORD value = registry.value(QStringLiteral("Composition"), 0).toUInt(&ok);
@ -128,64 +161,49 @@ int Utilities::getSystemMetric(const QWindow *window, const SystemMetric metric,
const qreal scaleFactor = (dpiScale ? devicePixelRatio : 1.0);
switch (metric) {
case SystemMetric::ResizeBorderThickness: {
const int resizeBorderThickness = window->property(Constants::kResizeBorderThicknessFlag).toInt();
if ((resizeBorderThickness > 0) && !forceSystemValue) {
return qRound(static_cast<qreal>(resizeBorderThickness) * scaleFactor);
} else {
const int result = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
if (result > 0) {
if (dpiScale) {
return result;
} else {
return qRound(static_cast<qreal>(result) / devicePixelRatio);
}
const int result = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
if (result > 0) {
if (dpiScale) {
return result;
} else {
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMetrics"));
// The padded border will disappear if DWM composition is disabled.
const int defaultResizeBorderThickness = (isDwmCompositionAvailable() ? kDefaultResizeBorderThicknessAero : kDefaultResizeBorderThicknessClassic);
if (dpiScale) {
return qRound(static_cast<qreal>(defaultResizeBorderThickness) * devicePixelRatio);
} else {
return defaultResizeBorderThickness;
}
return qRound(static_cast<qreal>(result) / devicePixelRatio);
}
} else {
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMetrics"));
// The padded border will disappear if DWM composition is disabled.
const int defaultResizeBorderThickness = (isDwmCompositionAvailable() ? kDefaultResizeBorderThicknessAero : kDefaultResizeBorderThicknessClassic);
if (dpiScale) {
return qRound(static_cast<qreal>(defaultResizeBorderThickness) * devicePixelRatio);
} else {
return defaultResizeBorderThickness;
}
}
}
case SystemMetric::CaptionHeight: {
const int captionHeight = window->property(Constants::kCaptionHeightFlag).toInt();
if ((captionHeight > 0) && !forceSystemValue) {
return qRound(static_cast<qreal>(captionHeight) * scaleFactor);
} else {
const int result = GetSystemMetrics(SM_CYCAPTION);
if (result > 0) {
if (dpiScale) {
return result;
} else {
return qRound(static_cast<qreal>(result) / devicePixelRatio);
}
const int result = GetSystemMetrics(SM_CYCAPTION);
if (result > 0) {
if (dpiScale) {
return result;
} else {
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMetrics"));
if (dpiScale) {
return qRound(static_cast<qreal>(kDefaultCaptionHeight) * devicePixelRatio);
} else {
return kDefaultCaptionHeight;
}
return qRound(static_cast<qreal>(result) / devicePixelRatio);
}
} else {
qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMetrics"));
if (dpiScale) {
return qRound(static_cast<qreal>(kDefaultCaptionHeight) * devicePixelRatio);
} else {
return kDefaultCaptionHeight;
}
}
}
case SystemMetric::TitleBarHeight: {
const int titleBarHeight = window->property(Constants::kTitleBarHeightFlag).toInt();
if ((titleBarHeight > 0) && !forceSystemValue) {
return qRound(static_cast<qreal>(titleBarHeight) * scaleFactor);
} else {
const int captionHeight = getSystemMetric(window,SystemMetric::CaptionHeight,
dpiScale, forceSystemValue);
const int resizeBorderThickness = getSystemMetric(window, SystemMetric::ResizeBorderThickness,
dpiScale, forceSystemValue);
return (((window->windowState() == Qt::WindowMaximized)
|| (window->windowState() == Qt::WindowFullScreen))
? captionHeight : (captionHeight + resizeBorderThickness));
}
const int captionHeight = getSystemMetric(
window, SystemMetric::CaptionHeight, dpiScale, forceSystemValue);
const int resizeBorderThickness = getSystemMetric(
window, SystemMetric::ResizeBorderThickness, dpiScale, forceSystemValue);
return (((window->windowState() == Qt::WindowMaximized)
|| (window->windowState() == Qt::WindowFullScreen))
? captionHeight : (captionHeight + resizeBorderThickness));
}
}
return 0;
@ -219,7 +237,7 @@ void Utilities::updateFrameMargins(const WId winId, const bool reset)
const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
const HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins);
if (FAILED(hr)) {
qWarning() << getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr);
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr);
}
}
@ -255,53 +273,29 @@ void Utilities::updateQtFrameMargins(QWindow *window, const bool enable)
#endif
}
QString Utilities::getSystemErrorMessage(const QString &function, const HRESULT hr)
{
Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) {
return {};
}
if (SUCCEEDED(hr)) {
return QStringLiteral("Operation succeeded.");
}
const DWORD dwError = HRESULT_CODE(hr);
LPWSTR buf = nullptr;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 0, nullptr) == 0) {
return QStringLiteral("Failed to retrieve the error message from system.");
}
const QString message = QStringLiteral("%1 failed with error %2: %3.")
.arg(function, QString::number(dwError), QString::fromWCharArray(buf));
LocalFree(buf);
return message;
}
QString Utilities::getSystemErrorMessage(const QString &function)
{
Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) {
return {};
}
const HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
if (SUCCEEDED(hr)) {
return QStringLiteral("Operation succeeded.");
const DWORD code = GetLastError();
if (code == ERROR_SUCCESS) {
return {};
}
return getSystemErrorMessage(function, hr);
return __getSystemErrorMessage(function, code);
}
QColor Utilities::getColorizationColor()
{
COLORREF color = RGB(0, 0, 0);
DWORD color = 0;
BOOL opaque = FALSE;
const HRESULT hr = DwmGetColorizationColor(&color, &opaque);
if (FAILED(hr)) {
qWarning() << getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr);
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr);
const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat);
bool ok = false;
color = registry.value(QStringLiteral("ColorizationColor"), 0).toUInt(&ok);
if (!ok || (color == 0)) {
color = RGB(128, 128, 128); // Dark gray
}
}
return QColor::fromRgba(color);
}
@ -373,7 +367,7 @@ bool Utilities::shouldAppsUseDarkMode()
ColorizationArea Utilities::getColorizationArea()
{
if (!isWin10OrGreater()) {
return ColorizationArea::None;
return ColorizationArea::NoArea;
}
const QString keyName = QStringLiteral("ColorPrevalence");
const QSettings themeRegistry(QString::fromUtf8(kPersonalizeRegistryKey), QSettings::NativeFormat);
@ -383,13 +377,13 @@ ColorizationArea Utilities::getColorizationArea()
const bool theme = (themeValue != 0);
const bool dwm = (dwmValue != 0);
if (theme && dwm) {
return ColorizationArea::All;
return ColorizationArea::AllArea;
} else if (theme) {
return ColorizationArea::StartMenu_TaskBar_ActionCenter;
} else if (dwm) {
return ColorizationArea::TitleBar_WindowBorder;
}
return ColorizationArea::None;
return ColorizationArea::NoArea;
}
bool Utilities::isThemeChanged(const void *data)

View File

@ -0,0 +1,59 @@
// Copyright (c) 2021 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_
#define SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_
#include <memory>
#import <Cocoa/Cocoa.h>
#include <QtCore/qpoint.h>
#include "scoped_nsobject.h"
@class WindowButtonsProxy;
// A helper view that floats above the window buttons.
@interface ButtonsAreaHoverView : NSView {
@private
WindowButtonsProxy* proxy_;
}
- (id)initWithProxy:(WindowButtonsProxy*)proxy;
@end
// Manipulating the window buttons.
@interface WindowButtonsProxy : NSObject {
@private
NSWindow* window_;
// Current left-top margin of buttons.
QPoint margin_;
// The default left-top margin.
QPoint default_margin_;
// Track mouse moves above window buttons.
BOOL show_on_hover_;
BOOL mouse_inside_;
scoped_nsobject<NSTrackingArea> tracking_area_;
scoped_nsobject<ButtonsAreaHoverView> hover_view_;
}
- (id)initWithWindow:(NSWindow*)window;
- (void)setVisible:(BOOL)visible;
- (BOOL)isVisible;
// Only show window buttons when mouse moves above them.
- (void)setShowOnHover:(BOOL)yes;
// Set left-top margin of the window buttons..
- (void)setMargin:(const QPoint&)margin;
// Return the bounds of all 3 buttons, with margin on all sides.
- (NSRect)getButtonsContainerBounds;
- (void)redraw;
- (void)updateTrackingAreas;
@end
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_

View File

@ -0,0 +1,220 @@
// Copyright (c) 2021 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "window_buttons_proxy.h"
@implementation ButtonsAreaHoverView : NSView
- (id)initWithProxy:(WindowButtonsProxy*)proxy {
if ((self = [super init])) {
proxy_ = proxy;
}
return self;
}
// Ignore all mouse events.
- (NSView*)hitTest:(NSPoint)aPoint {
return nil;
}
- (void)updateTrackingAreas {
[proxy_ updateTrackingAreas];
}
@end
@implementation WindowButtonsProxy
- (id)initWithWindow:(NSWindow*)window {
window_ = window;
show_on_hover_ = NO;
mouse_inside_ = NO;
// Remember the default margin.
margin_ = default_margin_ = [self getCurrentMargin];
return self;
}
- (void)dealloc {
if (hover_view_)
[hover_view_ removeFromSuperview];
[super dealloc];
}
- (void)setVisible:(BOOL)visible {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return;
[titleBarContainer setHidden:!visible];
}
- (BOOL)isVisible {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return YES;
return ![titleBarContainer isHidden];
}
- (void)setShowOnHover:(BOOL)yes {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return;
show_on_hover_ = yes;
// Put a transparent view above the window buttons so we can track mouse
// events when mouse enter/leave the window buttons.
if (show_on_hover_) {
hover_view_.reset([[ButtonsAreaHoverView alloc] initWithProxy:self]);
[hover_view_ setFrame:[self getButtonsBounds]];
[titleBarContainer addSubview:hover_view_.get()];
} else {
[hover_view_ removeFromSuperview];
hover_view_.reset();
}
[self updateButtonsVisibility];
}
- (void)setMargin:(const QPoint&)margin {
if (!margin.isNull())
margin_ = margin;
else
margin_ = default_margin_;
[self redraw];
}
- (NSRect)getButtonsContainerBounds {
return NSInsetRect([self getButtonsBounds], -margin_.x(), -margin_.y());
}
- (void)redraw {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return;
NSView* left = [self leftButton];
NSView* middle = [self middleButton];
NSView* right = [self rightButton];
float button_width = NSWidth(left.frame);
float button_height = NSHeight(left.frame);
float padding = NSMinX(middle.frame) - NSMaxX(left.frame);
float start;
if (false /*base::i18n::IsRTL()*/)
start =
NSWidth(window_.frame) - 3 * button_width - 2 * padding - margin_.x();
else
start = margin_.x();
NSRect cbounds = titleBarContainer.frame;
cbounds.size.height = button_height + 2 * margin_.y();
cbounds.origin.y = NSHeight(window_.frame) - NSHeight(cbounds);
[titleBarContainer setFrame:cbounds];
[left setFrameOrigin:NSMakePoint(start, margin_.y())];
start += button_width + padding;
[middle setFrameOrigin:NSMakePoint(start, margin_.y())];
start += button_width + padding;
[right setFrameOrigin:NSMakePoint(start, margin_.y())];
if (hover_view_)
[hover_view_ setFrame:[self getButtonsBounds]];
}
- (void)updateTrackingAreas {
if (tracking_area_)
[hover_view_ removeTrackingArea:tracking_area_.get()];
tracking_area_.reset([[NSTrackingArea alloc]
initWithRect:NSZeroRect
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
NSTrackingInVisibleRect
owner:self
userInfo:nil]);
[hover_view_ addTrackingArea:tracking_area_.get()];
}
- (void)mouseEntered:(NSEvent*)event {
mouse_inside_ = YES;
[self updateButtonsVisibility];
}
- (void)mouseExited:(NSEvent*)event {
mouse_inside_ = NO;
[self updateButtonsVisibility];
}
- (void)updateButtonsVisibility {
NSArray* buttons = @[
[window_ standardWindowButton:NSWindowCloseButton],
[window_ standardWindowButton:NSWindowMiniaturizeButton],
[window_ standardWindowButton:NSWindowZoomButton],
];
// Show buttons when mouse hovers above them.
BOOL hidden = show_on_hover_ && !mouse_inside_;
// Always show buttons under fullscreen.
if ([window_ styleMask] & NSWindowStyleMaskFullScreen)
hidden = NO;
for (NSView* button in buttons) {
[button setHidden:hidden];
[button setNeedsDisplay:YES];
}
}
// Return the bounds of all 3 buttons.
- (NSRect)getButtonsBounds {
NSView* left = [self leftButton];
NSView* right = [self rightButton];
return NSMakeRect(NSMinX(left.frame), NSMinY(left.frame),
NSMaxX(right.frame) - NSMinX(left.frame),
NSHeight(left.frame));
}
// Compute margin from position of current buttons.
- (QPoint)getCurrentMargin {
QPoint result;
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return result;
NSView* left = [self leftButton];
NSView* right = [self rightButton];
result.setX((NSHeight(titleBarContainer.frame) - NSHeight(left.frame)) / 2);
if (false /*base::i18n::IsRTL()*/)
result.setX(NSWidth(window_.frame) - NSMaxX(right.frame));
else
result.setX(NSMinX(left.frame));
return result;
}
// Receive the titlebar container, which might be nil if the window does not
// have the NSWindowStyleMaskTitled style.
- (NSView*)titleBarContainer {
NSView* left = [self leftButton];
if (!left.superview)
return nil;
return left.superview.superview;
}
// Receive the window buttons, note that the buttons might be removed and
// re-added on the fly so we should not cache them.
- (NSButton*)leftButton {
if (false /*base::i18n::IsRTL()*/)
return [window_ standardWindowButton:NSWindowZoomButton];
else
return [window_ standardWindowButton:NSWindowCloseButton];
}
- (NSButton*)middleButton {
return [window_ standardWindowButton:NSWindowMiniaturizeButton];
}
- (NSButton*)rightButton {
if (false /*base::i18n::IsRTL()*/)
return [window_ standardWindowButton:NSWindowCloseButton];
else
return [window_ standardWindowButton:NSWindowZoomButton];
}
@end

View File

@ -86,20 +86,21 @@
FRAMELESSHELPER_BEGIN_NAMESPACE
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
Q_NAMESPACE_EXPORT(FRAMELESSHELPER_API)
#else
Q_NAMESPACE
#endif
namespace Constants
{
[[maybe_unused]] constexpr char kFramelessModeFlag[] = "_FRAMELESSHELPER_FRAMELESS_MODE";
[[maybe_unused]] constexpr char kResizeBorderThicknessFlag[] = "_FRAMELESSHELPER_RESIZE_BORDER_THICKNESS";
[[maybe_unused]] constexpr char kCaptionHeightFlag[] = "_FRAMELESSHELPER_CAPTION_HEIGHT";
[[maybe_unused]] constexpr char kTitleBarHeightFlag[] = "_FRAMELESSHELPER_TITLE_BAR_HEIGHT";
[[maybe_unused]] constexpr char kHitTestVisibleInChromeFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE_IN_CHROME";
[[maybe_unused]] constexpr char kUseNativeTitleBarFlag[] = "_FRAMELESSHELPER_USE_NATIVE_TITLE_BAR";
[[maybe_unused]] constexpr char kPreserveNativeFrameFlag[] = "_FRAMELESSHELPER_PRESERVE_NATIVE_WINDOW_FRAME";
[[maybe_unused]] constexpr char kForcePreserveNativeFrameFlag[] = "_FRAMELESSHELPER_FORCE_PRESERVE_NATIVE_WINDOW_FRAME";
[[maybe_unused]] constexpr char kWindowFixedSizeFlag[] = "_FRAMELESSHELPER_WINDOW_FIXED_SIZE";
constexpr char kFramelessModeFlag[] = "_FRAMELESSHELPER_FRAMELESS_MODE";
constexpr char kResizeBorderThicknessFlag[] = "_FRAMELESSHELPER_RESIZE_BORDER_THICKNESS";
constexpr char kCaptionHeightFlag[] = "_FRAMELESSHELPER_CAPTION_HEIGHT";
constexpr char kTitleBarHeightFlag[] = "_FRAMELESSHELPER_TITLE_BAR_HEIGHT";
constexpr char kHitTestVisibleFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE";
constexpr char kWindowFixedSizeFlag[] = "_FRAMELESSHELPER_WINDOW_FIXED_SIZE";
}
@ -113,10 +114,10 @@ Q_ENUM_NS(SystemMetric)
enum class ColorizationArea : int
{
None = 0,
NoArea = 0, // 'None' has been defined as a macro in X11 headers.
StartMenu_TaskBar_ActionCenter,
TitleBar_WindowBorder,
All
AllArea
};
Q_ENUM_NS(ColorizationArea)

View File

@ -23,7 +23,7 @@
*/
#include "framelessquickhelper.h"
#include "framelesswindowsmanager.h"
#include "core/framelesswindowsmanager.h"
#include <QtQuick/qquickwindow.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -86,7 +86,7 @@ void FramelessQuickHelper::setHitTestVisibleInChrome(QQuickItem *item, const boo
if (!item) {
return;
}
FramelessWindowsManager::setHitTestVisibleInChrome(window(), item, visible);
FramelessWindowsManager::setHitTestVisible(window(), item, visible);
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,76 @@
#pragma once
#include <QtWidgets/qwidget.h>
#include "core/framelesshelper.h"
FRAMELESSHELPER_BEGIN_NAMESPACE
template <typename T>
class FramelessWindow : public T
{
static_assert(std::is_base_of<QWidget, T>::value, "T must inherit from QWidget");
public:
explicit FramelessWindow(QWidget *parent = nullptr)
: T(parent), m_helper(new FramelessHelper) {}
~FramelessWindow() {
delete m_helper;
}
FramelessHelper *helper() { return m_helper; }
void setResizable(bool reziable)
{
m_helper->setResizable(reziable);
}
void setHitTestVisible(QObject *obj)
{
m_helper->setHitTestVisible(obj);
}
void setResizeBorderThickness(int thickness) {
m_helper->setResizeBorderThickness(thickness);
}
void setTitleBarHeight(int height)
{
m_helper->setTitleBarHeight(height);
}
protected:
void showEvent(QShowEvent *event) override
{
T::showEvent(event);
if (!m_initied) {
const auto win = this->windowHandle();
if (win) {
m_helper->setWindow(win);
m_helper->install();
m_initied = true;
}
}
}
#ifdef Q_OS_WIN
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override
{
if (!m_helper)
return T::nativeEvent(eventType, message, result);
if (m_helper->handleNativeEvent(this->windowHandle(), eventType, message, result))
return true;
else
return T::nativeEvent(eventType, message, result);
}
#endif // Q_OS_WIN
private:
FramelessHelper *m_helper;
bool m_initied = false;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -1,63 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelper_global.h"
#include <QtGui/qwindow.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
namespace Utilities
{
[[nodiscard]] FRAMELESSHELPER_API int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue = false);
[[nodiscard]] FRAMELESSHELPER_API QWindow *findWindow(const WId winId);
[[nodiscard]] FRAMELESSHELPER_API bool shouldUseNativeTitleBar();
[[nodiscard]] FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window);
[[nodiscard]] FRAMELESSHELPER_API bool isHitTestVisibleInChrome(const QWindow *window);
[[nodiscard]] FRAMELESSHELPER_API QPointF mapOriginPointToWindow(const QObject *object);
[[nodiscard]] FRAMELESSHELPER_API QColor getColorizationColor();
[[nodiscard]] FRAMELESSHELPER_API int getWindowVisibleFrameBorderThickness(const WId winId);
[[nodiscard]] FRAMELESSHELPER_API bool shouldAppsUseDarkMode();
[[nodiscard]] FRAMELESSHELPER_API ColorizationArea getColorizationArea();
[[nodiscard]] FRAMELESSHELPER_API bool isThemeChanged(const void *data);
[[nodiscard]] FRAMELESSHELPER_API bool isSystemMenuRequested(const void *data, QPointF *pos);
[[nodiscard]] FRAMELESSHELPER_API bool showSystemMenu(const WId winId, const QPointF &pos);
#ifdef Q_OS_WINDOWS
[[nodiscard]] FRAMELESSHELPER_API bool isWin8OrGreater();
[[nodiscard]] FRAMELESSHELPER_API bool isWin8Point1OrGreater();
[[nodiscard]] FRAMELESSHELPER_API bool isWin10OrGreater();
[[nodiscard]] FRAMELESSHELPER_API bool isDwmCompositionAvailable();
FRAMELESSHELPER_API void triggerFrameChange(const WId winId);
FRAMELESSHELPER_API void updateFrameMargins(const WId winId, const bool reset);
FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable);
[[nodiscard]] FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function, const HRESULT hr);
[[nodiscard]] FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function);
#endif
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -1,135 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "utilities.h"
#include <QtCore/qvariant.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
static constexpr int kDefaultResizeBorderThickness = 8;
static constexpr int kDefaultCaptionHeight = 23;
int Utilities::getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue)
{
Q_ASSERT(window);
if (!window) {
return 0;
}
const qreal devicePixelRatio = window->devicePixelRatio();
const qreal scaleFactor = (dpiScale ? devicePixelRatio : 1.0);
switch (metric) {
case SystemMetric::ResizeBorderThickness: {
const int resizeBorderThickness = window->property(Constants::kResizeBorderThicknessFlag).toInt();
if ((resizeBorderThickness > 0) && !forceSystemValue) {
return qRound(static_cast<qreal>(resizeBorderThickness) * scaleFactor);
} else {
// ### TO BE IMPLEMENTED: Retrieve system value through official API
if (dpiScale) {
return qRound(static_cast<qreal>(kDefaultResizeBorderThickness) * devicePixelRatio);
} else {
return kDefaultResizeBorderThickness;
}
}
}
case SystemMetric::CaptionHeight: {
const int captionHeight = window->property(Constants::kCaptionHeightFlag).toInt();
if ((captionHeight > 0) && !forceSystemValue) {
return qRound(static_cast<qreal>(captionHeight) * scaleFactor);
} else {
// ### TO BE IMPLEMENTED: Retrieve system value through official API
if (dpiScale) {
return qRound(static_cast<qreal>(kDefaultCaptionHeight) * devicePixelRatio);
} else {
return kDefaultCaptionHeight;
}
}
}
case SystemMetric::TitleBarHeight: {
const int titleBarHeight = window->property(Constants::kTitleBarHeightFlag).toInt();
if ((titleBarHeight > 0) && !forceSystemValue) {
return qRound(static_cast<qreal>(titleBarHeight) * scaleFactor);
} else {
const int captionHeight = getSystemMetric(window,SystemMetric::CaptionHeight,
dpiScale, forceSystemValue);
const int resizeBorderThickness = getSystemMetric(window, SystemMetric::ResizeBorderThickness,
dpiScale, forceSystemValue);
return (((window->windowState() == Qt::WindowMaximized)
|| (window->windowState() == Qt::WindowFullScreen))
? captionHeight : (captionHeight + resizeBorderThickness));
}
}
}
return 0;
}
QColor Utilities::getColorizationColor()
{
// ### TO BE IMPLEMENTED
return Qt::darkGray;
}
int Utilities::getWindowVisibleFrameBorderThickness(const WId winId)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(winId);
return 1;
}
bool Utilities::shouldAppsUseDarkMode()
{
// ### TO BE IMPLEMENTED
return false;
}
ColorizationArea Utilities::getColorizationArea()
{
// ### TO BE IMPLEMENTED
return ColorizationArea::None;
}
bool Utilities::isThemeChanged(const void *data)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(data);
return false;
}
bool Utilities::isSystemMenuRequested(const void *data, QPointF *pos)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(data);
Q_UNUSED(pos);
return false;
}
bool Utilities::showSystemMenu(const WId winId, const QPointF &pos)
{
// ### TO BE IMPLEMENTED
Q_UNUSED(winId);
Q_UNUSED(pos);
return false;
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -1,25 +0,0 @@
/*
* MIT License
*
* Copyright (C) 2021 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "utilities.h"