forked from github_mirror/framelesshelper
Compare commits
42 Commits
main
...
altairwei/
Author | SHA1 | Date |
---|---|---|
|
f4d20e5f06 | |
|
997ef5c508 | |
|
1ebead3148 | |
|
7b82375543 | |
|
57610634d6 | |
|
7d29ee5b95 | |
|
dd0aa92035 | |
|
dcb5fb1da4 | |
|
92681dc9d9 | |
|
18912d5710 | |
|
fdf38d4c80 | |
|
c8cfefb3ca | |
|
50dafa0c5e | |
|
d8c137d11f | |
|
1f1de5deb6 | |
|
0e63a053de | |
|
420d942762 | |
|
d680ad87b1 | |
|
af797125f8 | |
|
cefae7950d | |
|
183aa9a730 | |
|
5e96c3fec6 | |
|
c684d3c0ac | |
|
de1d6abaf0 | |
|
ce69d1a4c5 | |
|
6a0dc5052c | |
|
6a6fea8ac9 | |
|
989de57f33 | |
|
3a3e39c0c0 | |
|
9b2dc893b6 | |
|
5f58937588 | |
|
5ab2024f54 | |
|
0964483b20 | |
|
96f49ded2f | |
|
1aed38e882 | |
|
501ff149cf | |
|
a365499455 | |
|
93ac6f6ee3 | |
|
678f903f5c | |
|
da342753a2 | |
|
3784ef0e7f | |
|
1e2598398d |
|
@ -71,4 +71,5 @@ Thumbs.db
|
|||
.qmake.conf
|
||||
*.res
|
||||
|
||||
.vscode/
|
||||
.vscode/
|
||||
*/.DS_Store
|
|
@ -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()
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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()
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../../framelessquickhelper.h"
|
||||
#include "quick/framelessquickhelper.h"
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtQml/qqmlapplicationengine.h>
|
||||
#include <QtQuickControls2/qquickstyle.h>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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}>"
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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_
|
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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
|
|
@ -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
|
63
utilities.h
63
utilities.h
|
@ -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
|
|
@ -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
|
|
@ -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"
|
Loading…
Reference in New Issue