refactor widgets interface

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-05-03 19:02:39 +08:00
parent 306ebfc717
commit 81e736570f
50 changed files with 1281 additions and 1782 deletions

View File

@ -44,19 +44,6 @@
![Dark](./doc/mac_dark.png)
## Roadmap
- 2.1
- [ ] All: Add cross-platform system menu for both Qt Widgets and Qt Quick. Support both light and dark theme. Can be triggered by right-clicking on the title bar area or pressing the system menu shortcut (ALT + SPACE).
- [ ] All: Add QtWebEngine demo applications for both Qt Widgets and Qt Quick.
- [ ] All: Make more settings and options configurable through environment variables and configuration files.
- [ ] Windows: Snap layout feature introduced in Windows 11.
- Future versions
- [ ] Linux: Support runtime theme switching.
- [ ] Linux: Move window resize area outside of the client area.
- [ ] macOS: Move window resize area outside of the client area.
- [ ] More feature requests are welcome!
## Requiredments
- Compiler: a modern compiler which supports C++17 at least.
@ -74,7 +61,7 @@ cmake -DCMAKE_PREFIX_PATH=<YOUR_QT_SDK_DIR_PATH> -DCMAKE_BUILD_TYPE=Release -GNi
cmake --build . --config Release --target all --parallel
```
**Note**: On Linux you need to install the GTK3 and X11 development packages first.
**Note**: On Linux you need to install the _GTK3_ and _X11_ development packages first.
## Use
@ -97,6 +84,12 @@ Please refer to the demo applications to see more detailed usages: [examples](./
- Force your application use pure software rendering instead of rendering through OpenGL.
- Or just don't use OpenGL at all, try to use Direct3D/Vulkan/Metal instead.
- Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least no older than Windows 10 1809. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11.
- To make the snap layout work as expected, there are some additional requirements for your homemade system buttons to follow:
- Make sure there are two public invokable functions (slot functions are always invokable): `void setHovered(bool)` and `void setPressed(bool)`. These two functions will be invoked by FramelessHelper when the button is being hovered or pressed. You should change the button's visual state inside these functions.
- Make sure there's a public signal: `void clicked()`. When the button is being clicked, that signal will be triggered by FramelessHelper. You should connect your event handler to that signal.
- Don't forget to call `setSystemButton()` for each button to let FramelessHelper know which is the minimize/maximize/close button.
- System buttons will not be able to receive any actual mouse events so there's no need to handle any mouse events inside these buttons. That's also why we need to set the button's visual state manually.
- I know this is making everything complicated but unfortunately we can't avoid this mess if we need to support the snap layout feature. Snap layout is really only designed for the original standard window frame, so if we want to forcely support it without a standard window frame, many black magic will be needed.
### Linux

View File

@ -23,13 +23,10 @@
]]
set(SOURCES
TitleBar.ui
MainWindow.ui
mainwindow.ui
mainwindow.h
mainwindow.cpp
main.cpp
systembutton.h
systembutton.cpp
)
if(WIN32)

View File

@ -1,276 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TitleBar</class>
<widget class="QWidget" name="TitleBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>552</width>
<height>30</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QMenuBar {
background-color: transparent;
height: 23px;
}
#iconButton, #minimizeButton, #maximizeButton, #closeButton {
border-style: none;
background-color: transparent;
}
#minimizeButton:hover, #maximizeButton:hover {
background-color: #c7c7c7;
}
#minimizeButton:pressed, #maximizeButton:pressed {
background-color: #808080;
}
#closeButton:hover {
background-color: #e81123;
}
#closeButton:pressed {
background-color: #8c0a15;
}</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="iconButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Segoe UI</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Hello, World!</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="SystemButton" name="minimizeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>30</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Minimize</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="SystemButton" name="maximizeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>30</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Maximize</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="SystemButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>45</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>30</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Close</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>SystemButton</class>
<extends>QPushButton</extends>
<header>systembutton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -23,9 +23,12 @@
*/
#include "mainwindow.h"
#include "ui_MainWindow.h"
#include "ui_TitleBar.h"
#include "ui_mainwindow.h"
#include <QtWidgets/qboxlayout.h>
#include <Utils>
#include <StandardTitleBar>
#include <StandardSystemButton>
#include <FramelessWidgetsHelper>
FRAMELESSHELPER_USE_NAMESPACE
@ -33,62 +36,31 @@ using namespace Global;
MainWindow::MainWindow(QWidget *parent, const Qt::WindowFlags flags) : FramelessMainWindow(parent, flags)
{
setupUi();
initialize();
}
MainWindow::~MainWindow() = default;
void MainWindow::changeEvent(QEvent *event)
void MainWindow::initialize()
{
FramelessMainWindow::changeEvent(event);
if (event->type() == QEvent::WindowStateChange) {
Q_EMIT windowStateChanged();
}
}
void MainWindow::setupUi()
{
mainWindow.reset(new Ui::MainWindow);
mainWindow->setupUi(this);
const auto titleBarWidget = new QWidget(this);
titleBar.reset(new Ui::TitleBar);
titleBar->setupUi(titleBarWidget);
const SystemTheme theme = SystemTheme::Light;
const ResourceType resource = ResourceType::Icon;
titleBar->minimizeButton->setIcon(qvariant_cast<QIcon>(Utils::getSystemButtonIconResource(SystemButtonType::Minimize, theme, resource)));
titleBar->maximizeButton->setIcon(qvariant_cast<QIcon>(Utils::getSystemButtonIconResource(SystemButtonType::Maximize, theme, resource)));
titleBar->closeButton->setIcon(qvariant_cast<QIcon>(Utils::getSystemButtonIconResource(SystemButtonType::Close, theme, resource)));
QMenuBar *mb = menuBar();
titleBar->horizontalLayout->insertWidget(1, mb);
// This call to the setMenuWidget() function is only needed by this example
// application to achieve some special effects, don't use it in your own
// code if you don't know what's it for!
setMenuWidget(titleBarWidget);
setTitleBarWidget(titleBarWidget);
setHitTestVisible(mb); // IMPORTANT!
setSystemButton(titleBar->minimizeButton, SystemButtonType::Minimize);
setSystemButton(titleBar->maximizeButton, SystemButtonType::Maximize);
setSystemButton(titleBar->closeButton, SystemButtonType::Close);
connect(titleBar->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized);
connect(titleBar->maximizeButton, &QPushButton::clicked, this, &MainWindow::toggleMaximized);
connect(titleBar->closeButton, &QPushButton::clicked, this, &MainWindow::close);
connect(this, &MainWindow::windowIconChanged, titleBar->iconButton, &QPushButton::setIcon);
connect(this, &MainWindow::windowTitleChanged, titleBar->titleLabel, &QLabel::setText);
connect(this, &MainWindow::windowStateChanged, this, [this](){
const bool zoomed = isZoomed();
const SystemTheme theme = SystemTheme::Light;
const SystemButtonType button = (zoomed ? SystemButtonType::Restore : SystemButtonType::Maximize);
const ResourceType resource = ResourceType::Icon;
titleBar->maximizeButton->setIcon(qvariant_cast<QIcon>(Utils::getSystemButtonIconResource(button, theme, resource)));
titleBar->maximizeButton->setToolTip(zoomed ? tr("Restore") : tr("Maximize"));
});
setWindowTitle(tr("Hello, World! - Qt MainWindow"));
m_titleBar.reset(new StandardTitleBar(this));
m_mainWindow.reset(new Ui::MainWindow);
m_mainWindow->setupUi(this);
QMenuBar * const mb = menuBar();
const auto titleBarLayout = static_cast<QHBoxLayout *>(m_titleBar->layout());
titleBarLayout->insertWidget(0, mb);
// "setMenuWidget()" will insert the menu widget on the window's top, just below
// system's title bar (if it's still there).
setMenuWidget(m_titleBar.data());
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar.data());
helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close);
helper->setHitTestVisible(mb); // IMPORTANT!
setWindowTitle(tr("FramelessHelper demo application - Qt MainWindow"));
}

View File

@ -26,9 +26,12 @@
#include <FramelessMainWindow>
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
namespace Ui
{
class TitleBar;
class MainWindow;
}
@ -41,16 +44,10 @@ public:
explicit MainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {});
~MainWindow() override;
protected:
void changeEvent(QEvent *event) override;
private:
void initialize();
private:
void setupUi();
Q_SIGNALS:
void windowStateChanged();
private:
QScopedPointer<Ui::TitleBar> titleBar;
QScopedPointer<Ui::MainWindow> mainWindow;
QScopedPointer<FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)> m_titleBar;
QScopedPointer<Ui::MainWindow> m_mainWindow;
};

View File

@ -39,8 +39,6 @@ set(SOURCES
mainwindow.h
mainwindow.cpp
main.cpp
systembutton.h
systembutton.cpp
)
if(WIN32)

View File

@ -24,9 +24,10 @@
#include "mainwindow.h"
#include "glwidget.h"
#include "systembutton.h"
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qlabel.h>
#include <FramelessWidgetsHelper>
#include <StandardTitleBar>
#include <StandardSystemButton>
FRAMELESSHELPER_USE_NAMESPACE
@ -34,58 +35,27 @@ using namespace Global;
MainWindow::MainWindow(QWidget *parent) : FramelessWidget(parent)
{
setupUi();
initialize();
}
MainWindow::~MainWindow() = default;
void MainWindow::updateMaximizeButton()
void MainWindow::initialize()
{
m_maxBtn->setText(isZoomed() ? tr("RESTORE") : tr("MAXIMIZE"));
}
void MainWindow::setupUi()
{
m_titleLabel = new QLabel(this);
QFont f = font();
f.setPointSize(kDefaultTitleBarFontPointSize);
m_titleLabel->setFont(f);
connect(this, &MainWindow::windowTitleChanged, m_titleLabel, &QLabel::setText);
m_minBtn = new SystemButton(this);
m_minBtn->setText(tr("MINIMIZE"));
connect(m_minBtn, &SystemButton::clicked, this, &MainWindow::showMinimized);
m_maxBtn = new SystemButton(this);
updateMaximizeButton();
connect(m_maxBtn, &SystemButton::clicked, this, &MainWindow::toggleMaximized);
connect(this, &MainWindow::zoomedChanged, this, &MainWindow::updateMaximizeButton);
m_closeBtn = new SystemButton(this);
m_closeBtn->setText(tr("CLOSE"));
connect(m_closeBtn, &SystemButton::clicked, this, &MainWindow::close);
m_titleBarWidget = new QWidget(this);
m_titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_titleBarWidget->setFixedHeight(kDefaultTitleBarHeight);
const auto titleBarLayout = new QHBoxLayout(m_titleBarWidget);
titleBarLayout->setSpacing(0);
titleBarLayout->setContentsMargins(0, 0, 0, 0);
titleBarLayout->addSpacerItem(new QSpacerItem(kDefaultTitleBarTitleLabelMargin, kDefaultTitleBarTitleLabelMargin));
titleBarLayout->addWidget(m_titleLabel);
titleBarLayout->addStretch();
titleBarLayout->addWidget(m_minBtn);
titleBarLayout->addWidget(m_maxBtn);
titleBarLayout->addWidget(m_closeBtn);
m_titleBarWidget->setLayout(titleBarLayout);
resize(800, 600);
setWindowTitle(tr("FramelessHelper demo application - QOpenGLWidget"));
m_titleBar = new StandardTitleBar(this);
m_glWidget = new GLWidget(this);
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(m_titleBarWidget);
mainLayout->addWidget(m_titleBar);
mainLayout->addWidget(m_glWidget);
setLayout(mainLayout);
setTitleBarWidget(m_titleBarWidget);
resize(800, 600);
setWindowTitle(tr("QOpenGLWidget demo"));
setSystemButton(m_minBtn, SystemButtonType::Minimize);
setSystemButton(m_maxBtn, SystemButtonType::Maximize);
setSystemButton(m_closeBtn, SystemButtonType::Close);
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar);
helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close);
}

View File

@ -24,14 +24,13 @@
#pragma once
#include <framelesswidget.h>
#include <FramelessWidget>
QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class GLWidget;
class SystemButton;
class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)
{
@ -42,17 +41,10 @@ public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
public Q_SLOTS:
void updateMaximizeButton();
private:
void initialize();
private:
void setupUi();
private:
QLabel *m_titleLabel = nullptr;
SystemButton *m_minBtn = nullptr;
SystemButton *m_maxBtn = nullptr;
SystemButton *m_closeBtn = nullptr;
QWidget *m_titleBarWidget = nullptr;
FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr;
GLWidget *m_glWidget = nullptr;
};

View File

@ -26,29 +26,21 @@
#include <QtCore/qdatetime.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qboxlayout.h>
#include <FramelessWindowsManager>
#include <Utils>
#include <FramelessWidgetsHelper>
#include <StandardTitleBar>
#include <StandardSystemButton>
FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
static const UserSettings settings =
Widget::Widget(QWidget *parent) : FramelessWidget(parent)
{
/* startupPosition */ QPoint(),
/* startupSize */ QSize(),
/* startupState */ Qt::WindowNoState,
/* options */ { Option::CreateStandardWindowLayout }, // Only needed by this demo application, you most probably don't need this option.
/* systemMenuOffset */ QPoint(),
/* minimizeButton */ nullptr,
/* maximizeButton */ nullptr,
/* closeButton */ nullptr
};
Widget::Widget(QWidget *parent) : FramelessWidget(parent, settings)
{
setupUi();
initialize();
startTimer(500);
connect(this, &Widget::systemThemeChanged, this, &Widget::updateStyleSheet);
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, &Widget::updateStyleSheet);
}
Widget::~Widget() = default;
@ -62,27 +54,36 @@ void Widget::timerEvent(QTimerEvent *event)
m_clockLabel->setText(QTime::currentTime().toString(FRAMELESSHELPER_STRING_LITERAL("hh:mm:ss")));
}
void Widget::setupUi()
void Widget::initialize()
{
setWindowTitle(tr("Hello, World! - Qt Widgets"));
setWindowTitle(tr("FramelessHelper demo application - Qt Widgets"));
resize(800, 600);
m_titleBar.reset(new StandardTitleBar(this));
m_clockLabel.reset(new QLabel(this));
m_clockLabel->setFrameShape(QFrame::NoFrame);
QFont clockFont = font();
clockFont.setBold(true);
clockFont.setPointSize(70);
m_clockLabel->setFont(clockFont);
const auto widget = new QWidget(this);
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
const auto contentLayout = new QHBoxLayout(widget);
const auto contentLayout = new QHBoxLayout;
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
contentLayout->addStretch();
contentLayout->addWidget(m_clockLabel.data());
contentLayout->addStretch();
widget->setLayout(contentLayout);
setContentWidget(widget);
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(m_titleBar.data());
mainLayout->addLayout(contentLayout);
setLayout(mainLayout);
updateStyleSheet();
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar.data());
helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close);
}
void Widget::updateStyleSheet()

View File

@ -30,6 +30,10 @@ QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class Widget : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)
{
Q_OBJECT
@ -43,11 +47,12 @@ protected:
void timerEvent(QTimerEvent *event) override;
private:
void setupUi();
void initialize();
private Q_SLOTS:
void updateStyleSheet();
private:
QScopedPointer<QLabel> m_clockLabel;
QScopedPointer<FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)> m_titleBar;
};

View File

@ -38,7 +38,7 @@ public:
explicit FramelessHelperQt(QObject *parent = nullptr);
~FramelessHelperQt() override;
static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
static void addWindow(const Global::SystemParameters &params);
protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -37,7 +37,7 @@ public:
explicit FramelessHelperWin();
~FramelessHelperWin() override;
static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
static void addWindow(const Global::SystemParameters &params);
Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override;
};

View File

@ -25,10 +25,9 @@
#pragma once
#include <QtCore/qglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qpoint.h>
#include <QtCore/qsize.h>
#include <QtCore/qpointer.h>
#include <QtCore/qobject.h>
#include <QtGui/qcolor.h>
#include <QtGui/qwindowdefs.h>
#include <functional>
@ -192,37 +191,6 @@ FRAMELESSHELPER_STRING_CONSTANT2(UsePureQtImplKeyPath, "Options/UsePureQtImpleme
FRAMELESSHELPER_STRING_CONSTANT2(ForceHideFrameBorderKeyPath, "Options/ForceHideFrameBorder")
FRAMELESSHELPER_STRING_CONSTANT2(ForceShowFrameBorderKeyPath, "Options/ForceShowFrameBorder")
enum class Option
{
ForceHideWindowFrameBorder = 0x00000001, // Windows only, force hide the window frame border even on Windows 10 and onwards.
ForceShowWindowFrameBorder = 0x00000002, // Windows only, force show the window frame border even on Windows 7 (~ 8.1).
DontDrawTopWindowFrameBorder = 0x00000004, // Windows only, don't draw the top window frame border even if the window frame border is visible.
DontForceSquareWindowCorners = 0x00000008, // Windows only, don't force the window corners to be square if the default corner style is not square.
TransparentWindowBackground = 0x00000010, // Make the window's background become transparent.
DisableWindowsSnapLayout = 0x00000020, // Windows only, don't enable the snap layout feature (available since Windows 11) unconditionally.
CreateStandardWindowLayout = 0x00000040, // Using this option will cause FramelessHelper create a homemade titlebar and a window layout to contain it. If your window has a layout already, the newly created layout will mess up your own layout.
BeCompatibleWithQtFramelessWindowHint = 0x00000080, // Windows only, make the code compatible with Qt::FramelessWindowHint. Don't use this option unless you really need that flag.
DontTouchQtInternals = 0x00000100, // Windows only, don't modify Qt's internal data.
DontTouchWindowFrameBorderColor = 0x00000200, // Windows only, don't change the window frame border color.
DontInstallSystemMenuHook = 0x00000400, // Windows only, don't install the system menu hook.
DisableSystemMenu = 0x00000800, // Windows only, don't open the system menu when right clicks the titlebar.
NoDoubleClickMaximizeToggle = 0x00001000, // Don't toggle the maximized state when user double clicks the titlebar.
DisableResizing = 0x00002000, // Disable resizing of the window.
DisableDragging = 0x00004000, // Disable dragging through the titlebar of the window.
DontTouchCursorShape = 0x00008000, // Don't change the cursor shape while the mouse is hovering above the window.
DontMoveWindowToDesktopCenter = 0x00010000, // Don't move the window to the desktop center before it's first shown.
DontTreatFullScreenAsZoomed = 0x00020000, // Don't treat fullscreen as zoomed (maximized).
DontTouchHighDpiScalingPolicy = 0x00040000, // Don't change Qt's default high DPI scaling policy. Qt5 default: disabled, Qt6 default: enabled.
DontTouchScaleFactorRoundingPolicy = 0x00080000, // Don't change Qt's default scale factor rounding policy. Qt5 default: round, Qt6 default: pass through.
DontTouchProcessDpiAwarenessLevel = 0x00100000, // Windows only, don't change the current process's DPI awareness level.
DontEnsureNonNativeWidgetSiblings = 0x00200000, // Don't ensure that siblings of native widgets stay non-native.
SyncNativeControlsThemeWithSystem = 0x00400000 // Windows only, sync the native Win32 controls' theme with system theme.
};
Q_ENUM_NS(Option)
Q_DECLARE_FLAGS(Options, Option)
Q_FLAG_NS(Options)
Q_DECLARE_OPERATORS_FOR_FLAGS(Options)
enum class SystemTheme
{
Unknown = -1,
@ -409,20 +377,6 @@ using ShouldIgnoreMouseEventsCallback = std::function<bool(const QPoint &)>;
using ShowSystemMenuCallback = std::function<void(const QPoint &)>;
struct UserSettings
{
QPoint startupPosition = {};
QSize startupSize = {};
Qt::WindowState startupState = Qt::WindowNoState;
Options options = {};
QPoint systemMenuOffset = {};
QPointer<QObject> windowIconButton = nullptr;
QPointer<QObject> contextHelpButton = nullptr;
QPointer<QObject> minimizeButton = nullptr;
QPointer<QObject> maximizeButton = nullptr;
QPointer<QObject> closeButton = nullptr;
};
struct SystemParameters
{
GetWindowFlagsCallback getWindowFlags = nullptr;
@ -528,11 +482,10 @@ static_assert((sizeof(WindowsVersions) / sizeof(WindowsVersions[0])) == (static_
namespace FramelessHelper::Core
{
FRAMELESSHELPER_CORE_API void initialize(const Global::Options options = {});
FRAMELESSHELPER_CORE_API void initialize();
} // namespace FramelessHelper::Core
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::UserSettings)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::SystemParameters)

View File

@ -51,7 +51,7 @@ public:
Q_NODISCARD QColor systemAccentColor() const;
public Q_SLOTS:
void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
void addWindow(const Global::SystemParameters &params);
Q_SIGNALS:
void systemThemeChanged();

View File

@ -70,9 +70,7 @@ FRAMELESSHELPER_CORE_API void syncWmPaintWithDwm();
FRAMELESSHELPER_CORE_API void showSystemMenu(
const WId windowId,
const QPoint &pos,
const QPoint &offset,
const bool selectFirstEntry,
const Global::Options options,
const Global::IsWindowFixedSizeCallback &isWindowFixedSize);
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getDwmColorizationColor();
[[nodiscard]] FRAMELESSHELPER_CORE_API Global::DwmColorizationArea getDwmColorizationArea();
@ -93,8 +91,6 @@ FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId windowId);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized();
FRAMELESSHELPER_CORE_API void installSystemMenuHook(
const WId windowId,
const Global::Options options,
const QPoint &offset,
const Global::IsWindowFixedSizeCallback &isWindowFixedSize,
const Global::IsInsideTitleBarDraggableAreaCallback &isInTitleBarArea,
const Global::GetWindowDevicePixelRatioCallback &getDevicePixelRatio);

View File

@ -76,36 +76,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
struct FRAMELESSHELPER_QUICK_API QuickGlobal
{
enum class Option
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, ForceHideWindowFrameBorder)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, ForceShowWindowFrameBorder)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontDrawTopWindowFrameBorder)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontForceSquareWindowCorners)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, TransparentWindowBackground)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DisableWindowsSnapLayout)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, CreateStandardWindowLayout)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, BeCompatibleWithQtFramelessWindowHint)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchQtInternals)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchWindowFrameBorderColor)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontInstallSystemMenuHook)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DisableSystemMenu)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, NoDoubleClickMaximizeToggle)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DisableResizing)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DisableDragging)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchCursorShape)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontMoveWindowToDesktopCenter)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTreatFullScreenAsZoomed)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchHighDpiScalingPolicy)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchScaleFactorRoundingPolicy)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchProcessDpiAwarenessLevel)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontEnsureNonNativeWidgetSiblings)
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, SyncNativeControlsThemeWithSystem)
};
Q_ENUM(Option)
Q_DECLARE_FLAGS(Options, Option)
Q_FLAG(Options)
enum class SystemTheme
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Unknown)
@ -175,6 +145,4 @@ private:
#endif
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QuickGlobal::Options)
FRAMELESSHELPER_END_NAMESPACE

View File

@ -42,7 +42,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem
Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL)
public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr, const Global::UserSettings &settings = {});
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
~FramelessQuickHelper() override;
Q_NODISCARD static FramelessQuickHelper *qmlAttachedProperties(QObject *parentObject);
@ -61,7 +61,7 @@ public Q_SLOTS:
void moveWindowToDesktopCenter();
void bringWindowToFront();
void setWindowFixedSize(const bool value, const bool force = false);
void setWindowFixedSize(const bool value);
protected:
void itemChange(const ItemChange change, const ItemChangeData &data) override;

View File

@ -44,10 +44,9 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow
Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL)
Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fullScreen READ isFullScreen NOTIFY fullScreenChanged FINAL)
Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL)
public:
explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::UserSettings &settings = {});
explicit FramelessQuickWindow(QWindow *parent = nullptr);
~FramelessQuickWindow() override;
Q_NODISCARD bool isHidden() const;
@ -55,7 +54,6 @@ public:
Q_NODISCARD bool isMinimized() const;
Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFullScreen() const;
Q_NODISCARD QColor frameBorderColor() const;
public Q_SLOTS:
void showMinimized2();
@ -69,7 +67,6 @@ Q_SIGNALS:
void minimizedChanged();
void zoomedChanged();
void fullScreenChanged();
void frameBorderColorChanged();
private:
QScopedPointer<FramelessQuickWindowPrivate> d_ptr;

View File

@ -0,0 +1 @@
#include <framelesswidgetshelper.h>

View File

@ -0,0 +1 @@
#include <standardtitlebar.h>

View File

@ -29,7 +29,7 @@
FRAMELESSHELPER_BEGIN_NAMESPACE
class FramelessWidgetsHelper;
class WidgetsSharedHelper;
class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow
{
@ -38,43 +38,26 @@ class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow
Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL)
Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL)
Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL)
Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
public:
explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}, const Global::UserSettings &settings = {});
explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {});
~FramelessMainWindow() override;
Q_NODISCARD bool isNormal() const;
Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFixedSize() const;
void setFixedSize(const bool value);
void setTitleBarWidget(QWidget *widget);
Q_NODISCARD QWidget *titleBarWidget() const;
public Q_SLOTS:
void setHitTestVisible(QWidget *widget);
void toggleMaximized();
void toggleFullScreen();
void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2(const QPoint &pos);
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void zoomedChanged();
void fixedSizeChanged();
void titleBarWidgetChanged();
void systemThemeChanged();
private:
QScopedPointer<FramelessWidgetsHelper> d_ptr;
QScopedPointer<WidgetsSharedHelper> m_helper;
Qt::WindowState m_savedWindowState = Qt::WindowNoState;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -29,7 +29,7 @@
FRAMELESSHELPER_BEGIN_NAMESPACE
class FramelessWidgetsHelper;
class WidgetsSharedHelper;
class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget
{
@ -38,48 +38,26 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget
Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL)
Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL)
Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL)
Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
Q_PROPERTY(QWidget* contentWidget READ contentWidget WRITE setContentWidget NOTIFY contentWidgetChanged FINAL)
public:
explicit FramelessWidget(QWidget *parent = nullptr, const Global::UserSettings &settings = {});
explicit FramelessWidget(QWidget *parent = nullptr);
~FramelessWidget() override;
Q_NODISCARD bool isNormal() const;
Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFixedSize() const;
void setFixedSize(const bool value);
void setTitleBarWidget(QWidget *widget);
Q_NODISCARD QWidget *titleBarWidget() const;
void setContentWidget(QWidget *widget);
Q_NODISCARD QWidget *contentWidget() const;
public Q_SLOTS:
void setHitTestVisible(QWidget *widget);
void toggleMaximized();
void toggleFullScreen();
void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2(const QPoint &pos);
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void zoomedChanged();
void fixedSizeChanged();
void titleBarWidgetChanged();
void contentWidgetChanged();
void systemThemeChanged();
private:
QScopedPointer<FramelessWidgetsHelper> d_ptr;
QScopedPointer<WidgetsSharedHelper> m_helper;
Qt::WindowState m_savedWindowState = Qt::WindowNoState;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,72 @@
/*
* MIT License
*
* Copyright (C) 2022 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 "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
class FramelessWidgetsHelperPrivate;
class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(FramelessWidgetsHelper)
Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper)
Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
public:
explicit FramelessWidgetsHelper(QObject *parent = nullptr);
~FramelessWidgetsHelper() override;
Q_NODISCARD static FramelessWidgetsHelper *get(QObject *object);
Q_NODISCARD QWidget *titleBarWidget() const;
Q_NODISCARD bool isWindowFixedSize() const;
public Q_SLOTS:
void attach();
void setTitleBarWidget(QWidget *widget);
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
void setHitTestVisible(QWidget *widget);
void showSystemMenu(const QPoint &pos);
void windowStartSystemMove2(const QPoint &pos);
void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos);
void moveWindowToDesktopCenter();
void bringWindowToFront();
void setWindowFixedSize(const bool value);
Q_SIGNALS:
void titleBarWidgetChanged();
private:
QScopedPointer<FramelessWidgetsHelperPrivate> d_ptr;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -22,22 +22,38 @@
* SOFTWARE.
*/
#include "systembutton.h"
#pragma once
SystemButton::SystemButton(QWidget *parent) : QPushButton(parent)
#include "framelesshelperwidgets_global.h"
#include <QtWidgets/qwidget.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBarPrivate;
class StandardSystemButton;
class FRAMELESSHELPER_WIDGETS_API StandardTitleBar : public QWidget
{
}
Q_OBJECT
Q_DECLARE_PRIVATE(StandardTitleBar)
Q_DISABLE_COPY_MOVE(StandardTitleBar)
Q_PROPERTY(StandardSystemButton* minimizeButton READ minimizeButton CONSTANT FINAL)
Q_PROPERTY(StandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL)
Q_PROPERTY(StandardSystemButton* closeButton READ closeButton CONSTANT FINAL)
SystemButton::~SystemButton() = default;
public:
explicit StandardTitleBar(QWidget *parent = nullptr);
~StandardTitleBar() override;
void SystemButton::setHovered(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}
Q_NODISCARD StandardSystemButton *minimizeButton() const;
Q_NODISCARD StandardSystemButton *maximizeButton() const;
Q_NODISCARD StandardSystemButton *closeButton() const;
void SystemButton::setPressed(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}
protected:
void paintEvent(QPaintEvent *event) override;
private:
QScopedPointer<StandardTitleBarPrivate> d_ptr;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -48,8 +48,6 @@ if(WIN32)
list(APPEND SOURCES
${INCLUDE_PREFIX}/framelesshelper_windows.h
${INCLUDE_PREFIX}/framelesshelper_win.h
qwinregistry_p.h
qwinregistry.cpp
utils_win.cpp
framelesshelper_win.cpp
)

View File

@ -36,7 +36,6 @@ using namespace Global;
struct QtHelperData
{
UserSettings settings = {};
SystemParameters params = {};
FramelessHelperQt *eventFilter = nullptr;
bool cursorShapeChanged = false;
@ -55,7 +54,7 @@ FramelessHelperQt::FramelessHelperQt(QObject *parent) : QObject(parent) {}
FramelessHelperQt::~FramelessHelperQt() = default;
void FramelessHelperQt::addWindow(const UserSettings &settings, const SystemParameters &params)
void FramelessHelperQt::addWindow(const SystemParameters &params)
{
Q_ASSERT(params.isValid());
if (!params.isValid()) {
@ -68,7 +67,6 @@ void FramelessHelperQt::addWindow(const UserSettings &settings, const SystemPara
return;
}
QtHelperData data = {};
data.settings = settings;
data.params = params;
QWindow *window = params.getWindowHandle();
// Give it a parent so that it can be deleted even if we forget to do so.
@ -148,7 +146,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
QMutexLocker locker(&g_qtHelper()->mutex);
g_qtHelper()->data[windowId].leftButtonPressed = false;
}
if ((button == Qt::RightButton) && !(data.settings.options & Option::DisableSystemMenu)) {
if (button == Qt::RightButton) {
if (!ignoreThisEvent && insideTitleBar) {
data.params.showSystemMenu(scenePos);
return true;
@ -156,8 +154,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
}
} break;
case QEvent::MouseButtonDblClick: {
if ((button == Qt::LeftButton) && !windowFixedSize && !ignoreThisEvent
&& insideTitleBar && !(data.settings.options & Option::NoDoubleClickMaximizeToggle)) {
if ((button == Qt::LeftButton) && !windowFixedSize && !ignoreThisEvent && insideTitleBar) {
Qt::WindowState newWindowState = Qt::WindowNoState;
if (data.params.getWindowState() != Qt::WindowMaximized) {
newWindowState = Qt::WindowMaximized;
@ -166,7 +163,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
}
} break;
case QEvent::MouseMove: {
if (!windowFixedSize && !(data.settings.options & Option::DontTouchCursorShape)) {
if (!windowFixedSize) {
const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos);
if (cs == Qt::ArrowCursor) {
if (data.cursorShapeChanged) {
@ -180,7 +177,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
g_qtHelper()->data[windowId].cursorShapeChanged = true;
}
}
if (data.leftButtonPressed && !(data.settings.options & Option::DisableDragging)) {
if (data.leftButtonPressed) {
if (!ignoreThisEvent && insideTitleBar) {
Utils::startSystemMove(window, globalPos);
return true;

View File

@ -41,7 +41,6 @@ using namespace Global;
struct Win32HelperData
{
UserSettings settings = {};
SystemParameters params = {};
bool trackingMouse = false;
WId dragBarWindowId = 0;
@ -353,9 +352,8 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
if (!parentWindowId) {
return false;
}
if (!Utils::isWindowsVersionOrGreater(WindowsVersion::_8)) {
qWarning() << "Our drag bar window needs the WS_EX_LAYERED style, however, "
"it's not supported for child windows until Windows 8.";
if (!Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1507)) {
qWarning() << "The drag bar window is only supported on Windows 10 and onwards.";
return false;
}
const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId);
@ -415,7 +413,7 @@ FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
FramelessHelperWin::~FramelessHelperWin() = default;
void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemParameters &params)
void FramelessHelperWin::addWindow(const SystemParameters &params)
{
Q_ASSERT(params.isValid());
if (!params.isValid()) {
@ -428,44 +426,27 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar
return;
}
Win32HelperData data = {};
data.settings = settings;
data.params = params;
if ((settings.options & Option::ForceHideWindowFrameBorder)
&& (settings.options & Option::ForceShowWindowFrameBorder)) {
data.settings.options &= ~(Option::ForceHideWindowFrameBorder | Option::ForceShowWindowFrameBorder);
qWarning() << "You can't use both \"Option::ForceHideWindowFrameBorder\" and "
"\"Option::ForceShowWindowFrameBorder\" at the same time.";
}
g_win32Helper()->data.insert(windowId, data);
if (g_win32Helper()->nativeEventFilter.isNull()) {
g_win32Helper()->nativeEventFilter.reset(new FramelessHelperWin);
qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data());
}
g_win32Helper()->mutex.unlock();
if (!(settings.options & Option::DontTouchQtInternals)) {
Utils::fixupQtInternals(windowId);
}
Utils::fixupQtInternals(windowId);
Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true);
Utils::updateWindowFrameMargins(windowId, false);
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_8)) {
if (!(settings.options & Option::DisableWindowsSnapLayout)) {
if (!createDragBarWindow(windowId)) {
qWarning() << "Failed to create the drag bar window.";
}
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1507)) {
if (!createDragBarWindow(windowId)) {
qWarning() << "Failed to create the drag bar window.";
}
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1607)) {
const bool dark = Utils::shouldAppsUseDarkMode();
if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) {
Utils::updateWindowFrameBorderColor(windowId, dark);
}
Utils::updateWindowFrameBorderColor(windowId, dark);
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1809)) {
if (settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
//Utils::updateGlobalWin32ControlsTheme(windowId, dark); // Causes some QtWidgets paint incorrectly.
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2)) {
if (!(settings.options & Option::DontForceSquareWindowCorners)) {
Utils::forceSquareCornersForWindow(windowId, true);
}
Utils::forceSquareCornersForWindow(windowId, true);
}
}
}
@ -499,15 +480,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
}
const Win32HelperData data = g_win32Helper()->data.value(windowId);
g_win32Helper()->mutex.unlock();
const bool frameBorderVisible = [&data]() -> bool {
if (data.settings.options & Option::ForceShowWindowFrameBorder) {
return true;
}
if (data.settings.options & Option::ForceHideWindowFrameBorder) {
return false;
}
return Utils::isWindowFrameBorderVisible();
}();
const bool frameBorderVisible = Utils::isWindowFrameBorderVisible();
const UINT uMsg = msg->message;
const WPARAM wParam = msg->wParam;
const LPARAM lParam = msg->lParam;
@ -832,8 +805,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const bool buttonSwapped = (GetSystemMetrics(SM_SWAPBUTTON) != FALSE);
const bool leftButtonPressed = (buttonSwapped ?
(GetAsyncKeyState(VK_RBUTTON) < 0) : (GetAsyncKeyState(VK_LBUTTON) < 0));
const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) &&
leftButtonPressed && !(data.settings.options & Option::DisableDragging));
const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) && leftButtonPressed);
if (frameBorderVisible) {
// This will handle the left, right and bottom parts of the frame
// because we didn't change them.
@ -1040,13 +1012,9 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
.compare(qThemeSettingChangeEventName, Qt::CaseInsensitive) == 0)) {
systemThemeChanged = true;
const bool dark = Utils::shouldAppsUseDarkMode();
if (!(data.settings.options & Option::DontTouchWindowFrameBorderColor)) {
Utils::updateWindowFrameBorderColor(windowId, dark);
}
Utils::updateWindowFrameBorderColor(windowId, dark);
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1809)) {
if (data.settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
//Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
}
}

View File

@ -117,7 +117,7 @@ QColor FramelessWindowsManagerPrivate::systemAccentColor() const
return m_accentColor;
}
void FramelessWindowsManagerPrivate::addWindow(const UserSettings &settings, const SystemParameters &params)
void FramelessWindowsManagerPrivate::addWindow(const SystemParameters &params)
{
Q_ASSERT(params.isValid());
if (!params.isValid()) {
@ -151,16 +151,14 @@ void FramelessWindowsManagerPrivate::addWindow(const UserSettings &settings, con
}
#endif
if (pureQt) {
FramelessHelperQt::addWindow(settings, params);
FramelessHelperQt::addWindow(params);
}
#ifdef Q_OS_WINDOWS
if (!pureQt) {
FramelessHelperWin::addWindow(settings, params);
}
if (!(settings.options & Option::DontInstallSystemMenuHook)) {
Utils::installSystemMenuHook(windowId, settings.options, settings.systemMenuOffset,
params.isWindowFixedSize, params.isInsideTitleBarDraggableArea, params.getWindowDevicePixelRatio);
FramelessHelperWin::addWindow(params);
}
Utils::installSystemMenuHook(windowId, params.isWindowFixedSize,
params.isInsideTitleBarDraggableArea, params.getWindowDevicePixelRatio);
#endif
}
@ -242,13 +240,13 @@ QColor FramelessWindowsManager::systemAccentColor() const
return d->systemAccentColor();
}
void FramelessWindowsManager::addWindow(const UserSettings &settings, const SystemParameters &params)
void FramelessWindowsManager::addWindow(const SystemParameters &params)
{
Q_D(FramelessWindowsManager);
d->addWindow(settings, params);
d->addWindow(params);
}
void FramelessHelper::Core::initialize(const Options options)
void FramelessHelper::Core::initialize()
{
static bool inited = false;
if (inited) {
@ -256,7 +254,7 @@ void FramelessHelper::Core::initialize(const Options options)
}
inited = true;
#ifdef Q_OS_LINUX
// Qt's Wayland experience is not good, so we force the X11 backend here.
// Qt's Wayland experience is not good, so we force the XCB backend here.
// TODO: Remove this hack once Qt's Wayland implementation is good enough.
// We are setting the preferred QPA backend, so we have to set it early
// enough, that is, before the construction of any Q(Gui)Application
@ -269,42 +267,33 @@ void FramelessHelper::Core::initialize(const Options options)
qputenv(MAC_LAYER_ENV_VAR, kValueOne);
#endif
#ifdef Q_OS_WINDOWS
if (!(options & Option::DontTouchProcessDpiAwarenessLevel)) {
// This is equivalent to set the "dpiAware" and "dpiAwareness" field in
// your manifest file. It works through out Windows Vista to Windows 11.
// It's highly recommended to enable the highest DPI awareness level
// (currently it's PerMonitor Version 2, or PMv2 for short) for any GUI
// applications, to allow your user interface scale to an appropriate
// size and still stay sharp, though you will have to do the calculation
// and resize by yourself.
Utils::tryToEnableHighestDpiAwarenessLevel();
}
// This is equivalent to set the "dpiAware" and "dpiAwareness" field in
// your manifest file. It works through out Windows Vista to Windows 11.
// It's highly recommended to enable the highest DPI awareness level
// (currently it's PerMonitor Version 2, or PMv2 for short) for any GUI
// applications, to allow your user interface scale to an appropriate
// size and still stay sharp, though you will have to do the calculation
// and resize by yourself.
Utils::tryToEnableHighestDpiAwarenessLevel();
#endif
if (!(options & Option::DontEnsureNonNativeWidgetSiblings)) {
// This attribute is known to be __NOT__ compatible with QGLWidget.
// Please consider migrating to the recommended QOpenGLWidget instead.
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
}
// This attribute is known to be __NOT__ compatible with QGLWidget.
// Please consider migrating to the recommended QOpenGLWidget instead.
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
if (!(options & Option::DontTouchHighDpiScalingPolicy)) {
// Enable high DPI scaling by default, but only for Qt5 applications,
// because this has become the default setting since Qt6 and it can't
// be changed from outside anymore (except for internal testing purposes).
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
}
// Enable high DPI scaling by default, but only for Qt5 applications,
// because this has become the default setting since Qt6 and it can't
// be changed from outside anymore (except for internal testing purposes).
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
if (!(options & Option::DontTouchScaleFactorRoundingPolicy)) {
// Non-integer scale factors will cause Qt have some painting defects
// for both Qt Widgets and Qt Quick applications, and it's still not
// totally fixed till now (Qt 6.4), so we round the scale factors to
// get a better looking. Non-integer scale factors will also cause
// flicker and jitter during window resizing.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
}
// Non-integer scale factors will cause Qt have some painting defects
// for both Qt Widgets and Qt Quick applications, and it's still not
// totally fixed till now (Qt 6.4), so we round the scale factors to
// get a better looking. Non-integer scale factors will also cause
// flicker and jitter during window resizing.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif
qRegisterMetaType<Option>();
qRegisterMetaType<SystemTheme>();
qRegisterMetaType<SystemButtonType>();
qRegisterMetaType<ResourceType>();
@ -313,7 +302,6 @@ void FramelessHelper::Core::initialize(const Options options)
qRegisterMetaType<ButtonState>();
qRegisterMetaType<WindowsVersion>();
qRegisterMetaType<VersionNumber>();
qRegisterMetaType<UserSettings>();
qRegisterMetaType<SystemParameters>();
}

View File

@ -48,9 +48,8 @@ public:
Q_NODISCARD Global::SystemTheme systemTheme() const;
Q_NODISCARD QColor systemAccentColor() const;
public Q_SLOTS:
static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
void notifySystemThemeHasChangedOrNot();
static void addWindow(const Global::SystemParameters &params);
Q_INVOKABLE void notifySystemThemeHasChangedOrNot();
private:
void initialize();

View File

@ -1,119 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwinregistry_p.h"
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
#include <QtCore/qvarlengtharray.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
QWinRegistryKey::QWinRegistryKey()
{
}
// Open a key with the specified permissions (KEY_READ/KEY_WRITE).
// "access" is to explicitly use the 32- or 64-bit branch.
QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions, REGSAM access)
{
if (RegOpenKeyExW(parentHandle, qUtf16Printable(subKey), 0,
permissions | access, &m_key) != ERROR_SUCCESS) {
m_key = nullptr;
}
}
QWinRegistryKey::~QWinRegistryKey()
{
close();
}
void QWinRegistryKey::close()
{
if (isValid()) {
RegCloseKey(m_key);
m_key = nullptr;
}
}
QString QWinRegistryKey::stringValue(QStringView subKey) const
{
QString result = {};
if (!isValid()) {
return result;
}
DWORD type = 0;
DWORD size = 0;
if ((RegQueryValueExW(m_key, qUtf16Printable(subKey), nullptr, &type, nullptr, &size) != ERROR_SUCCESS)
|| ((type != REG_SZ) && (type != REG_EXPAND_SZ)) || (size <= 2)) {
return result;
}
// Reserve more for rare cases where trailing '\0' are missing in registry.
// Rely on 0-termination since strings of size 256 padded with 0 have been
// observed (QTBUG-84455).
size += 2;
QVarLengthArray<unsigned char> buffer(static_cast<int>(size));
std::fill(buffer.data(), buffer.data() + size, 0u);
if (RegQueryValueExW(m_key, qUtf16Printable(subKey), nullptr, &type, buffer.data(), &size) == ERROR_SUCCESS) {
result = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(buffer.constData()));
}
return result;
}
QPair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
{
if (!isValid()) {
return qMakePair(0, false);
}
DWORD type = 0;
if ((RegQueryValueExW(m_key, qUtf16Printable(subKey), nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS)
|| (type != REG_DWORD)) {
return qMakePair(0, false);
}
DWORD value = 0;
DWORD size = sizeof(value);
const bool ok =
RegQueryValueExW(m_key, qUtf16Printable(subKey), nullptr, nullptr,
reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS;
return qMakePair(value, ok);
}
QT_END_NAMESPACE
#endif // QT_VERSION < QT_VERSION_CHECK(5, 14, 0)

View File

@ -1,95 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "framelesshelpercore_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
#include <QtCore/private/qwinregistry_p.h>
#else // QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
#include <QtCore/qpair.h>
#include <QtCore/qstring.h>
#include "framelesshelper_windows.h"
QT_BEGIN_NAMESPACE
class FRAMELESSHELPER_CORE_API QWinRegistryKey
{
Q_DISABLE_COPY(QWinRegistryKey)
public:
QWinRegistryKey();
explicit QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions = KEY_READ, REGSAM access = 0);
~QWinRegistryKey();
QWinRegistryKey(QWinRegistryKey &&other) noexcept
: m_key(std::exchange(other.m_key, nullptr)) {}
QWinRegistryKey &operator=(QWinRegistryKey &&other) noexcept {
QWinRegistryKey moved(std::move(other));
swap(moved);
return *this;
}
void swap(QWinRegistryKey &other) noexcept { std::swap(m_key, other.m_key); }
[[nodiscard]] bool isValid() const { return m_key != nullptr; }
[[nodiscard]] operator HKEY() const { return m_key; }
void close();
[[nodiscard]] QString stringValue(QStringView subKey) const;
[[nodiscard]] QPair<DWORD, bool> dwordValue(QStringView subKey) const;
private:
HKEY m_key = nullptr;
};
QT_END_NAMESPACE
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)

View File

@ -35,7 +35,6 @@
#else
# include <QtGui/qpa/qplatformwindow_p.h>
#endif
#include "qwinregistry_p.h"
#include "framelesswindowsmanager.h"
#include "framelesshelper_windows.h"
#include <atlbase.h>
@ -50,8 +49,6 @@ using namespace Global;
struct Win32UtilsHelperData
{
WNDPROC originalWindowProc = nullptr;
Options options = {};
QPoint offset = {};
IsWindowFixedSizeCallback isWindowFixedSize = nullptr;
IsInsideTitleBarDraggableAreaCallback isInTitleBarArea = nullptr;
GetWindowDevicePixelRatioCallback getDevicePixelRatio = nullptr;
@ -127,6 +124,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SetMenuDefaultItem)
FRAMELESSHELPER_STRING_CONSTANT(HiliteMenuItem)
FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen)
FRAMELESSHELPER_STRING_CONSTANT2(HKEY_CURRENT_USER, "HKEY_CURRENT_USER")
[[nodiscard]] static inline bool
doCompareWindowsVersion(const DWORD dwMajor, const DWORD dwMinor, const DWORD dwBuild)
@ -244,14 +242,13 @@ FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen)
const auto getNativePosFromMouse = [lParam]() -> QPoint {
return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
};
const auto getNativeGlobalPosFromKeyboard = [hWnd, windowId, &data]() -> QPoint {
const auto getNativeGlobalPosFromKeyboard = [hWnd, windowId]() -> QPoint {
RECT windowPos = {};
if (GetWindowRect(hWnd, &windowPos) == FALSE) {
qWarning() << Utils::getSystemErrorMessage(kGetWindowRect);
return {};
}
const bool maxOrFull = (IsMaximized(hWnd) ||
((data.options & Option::DontTreatFullScreenAsZoomed) ? false : Utils::isFullScreen(windowId)));
const bool maxOrFull = (IsMaximized(hWnd) || Utils::isFullScreen(windowId));
const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true);
const bool frameBorderVisible = Utils::isWindowFrameBorderVisible();
const int horizontalOffset = ((maxOrFull || !frameBorderVisible) ? 0 : frameSizeX);
@ -320,8 +317,7 @@ FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen)
break;
}
if (shouldShowSystemMenu) {
Utils::showSystemMenu(windowId, nativeGlobalPos, data.offset,
broughtByKeyboard, data.options, data.isWindowFixedSize);
Utils::showSystemMenu(windowId, nativeGlobalPos, broughtByKeyboard, data.isWindowFixedSize);
// QPA's internal code will handle system menu events separately, and its
// behavior is not what we would want to see because it doesn't know our
// window doesn't have any window frame now, so return early here to avoid
@ -351,9 +347,10 @@ bool Utils::isDwmCompositionEnabled()
return true;
}
const auto resultFromRegistry = []() -> bool {
const QWinRegistryKey registry(HKEY_CURRENT_USER, qDwmRegistryKey);
const auto result = registry.dwordValue(kComposition);
return (result.second && (result.first != 0));
const QSettings registry(kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey, QSettings::NativeFormat);
bool ok = false;
const DWORD value = registry.value(kComposition).toULongLong(&ok);
return (ok && (value != 0));
};
static const auto pDwmIsCompositionEnabled =
reinterpret_cast<decltype(&DwmIsCompositionEnabled)>(
@ -472,9 +469,10 @@ QString Utils::getSystemErrorMessage(const QString &function)
QColor Utils::getDwmColorizationColor()
{
const auto resultFromRegistry = []() -> QColor {
const QWinRegistryKey registry(HKEY_CURRENT_USER, qDwmRegistryKey);
const auto result = registry.dwordValue(kColorizationColor);
return (result.second ? QColor::fromRgba(result.first) : kDefaultDarkGrayColor);
const QSettings registry(kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey, QSettings::NativeFormat);
bool ok = false;
const DWORD value = registry.value(kColorizationColor).toULongLong(&ok);
return (ok ? QColor::fromRgba(value) : kDefaultDarkGrayColor);
};
static const auto pDwmGetColorizationColor =
reinterpret_cast<decltype(&DwmGetColorizationColor)>(
@ -498,12 +496,14 @@ DwmColorizationArea Utils::getDwmColorizationArea()
if (!isWindowsVersionOrGreater(WindowsVersion::_10_1507)) {
return DwmColorizationArea::None_;
}
const QWinRegistryKey themeRegistry(HKEY_CURRENT_USER, qPersonalizeRegistryKey);
const auto themeValue = themeRegistry.dwordValue(qDwmColorKeyName);
const QWinRegistryKey dwmRegistry(HKEY_CURRENT_USER, qDwmRegistryKey);
const auto dwmValue = dwmRegistry.dwordValue(qDwmColorKeyName);
const bool theme = themeValue.second && (themeValue.first != 0);
const bool dwm = dwmValue.second && (dwmValue.first != 0);
const QSettings themeRegistry(kHKEY_CURRENT_USER + u'\\' + qPersonalizeRegistryKey, QSettings::NativeFormat);
bool themeOk = false;
const DWORD themeValue = themeRegistry.value(qDwmColorKeyName).toULongLong(&themeOk);
const QSettings dwmRegistry(kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey, QSettings::NativeFormat);
bool dwmOk = false;
const DWORD dwmValue = dwmRegistry.value(qDwmColorKeyName).toULongLong(&dwmOk);
const bool theme = (themeOk && (themeValue != 0));
const bool dwm = (dwmOk && (dwmValue != 0));
if (theme && dwm) {
return DwmColorizationArea::All;
} else if (theme) {
@ -514,8 +514,7 @@ DwmColorizationArea Utils::getDwmColorizationArea()
return DwmColorizationArea::None_;
}
void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const QPoint &offset,
const bool selectFirstEntry, const Options options,
void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const bool selectFirstEntry,
const IsWindowFixedSizeCallback &isWindowFixedSize)
{
Q_ASSERT(windowId);
@ -531,8 +530,7 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const QPoint &
// as an error so just ignore it and return early.
return;
}
const bool maxOrFull = (IsMaximized(hWnd) ||
((options & Option::DontTreatFullScreenAsZoomed) ? false : isFullScreen(windowId)));
const bool maxOrFull = (IsMaximized(hWnd) || isFullScreen(windowId));
const bool fixedSize = isWindowFixedSize();
EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
// The first menu item should be selected by default if the menu is brought
@ -545,7 +543,7 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const QPoint &
// highlight bar to indicate the current selected menu item, which will make
// the menu look kind of weird. Currently I don't know how to fix this issue.
HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE)));
EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | ((!maxOrFull && !(options & Option::DisableDragging)) ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (!maxOrFull ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | MFS_ENABLED));
EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((!maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
@ -554,16 +552,8 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const QPoint &
// menu item per menu at most. Set the item ID to "UINT_MAX" (or simply "-1")
// can clear the default item for the given menu.
SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
// If you need to adjust the menu popup position (such as in a fully
// customized window frame), you should pass in the "offset" parameter.
// But it will not be needed when the window is maximized or fullscreen
// because in that case the menu should always align to the left edge
// of the screen.
const QPoint adjustment = (maxOrFull ? QPoint(0, 0) : offset);
const int xPos = (pos.x() + adjustment.x());
const int yPos = (pos.y() + adjustment.y());
const int result = TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft()
? TPM_RIGHTALIGN : TPM_LEFTALIGN)), xPos, yPos, 0, hWnd, nullptr);
? TPM_RIGHTALIGN : TPM_LEFTALIGN)), pos.x(), pos.y(), 0, hWnd, nullptr);
// TrackPopupMenu returns 0: the user cancelled the menu, or some error occurred.
if (result == 0) {
const DWORD dwError = GetLastError();
@ -1063,7 +1053,7 @@ bool Utils::isFrameBorderColorized()
return isTitleBarColorized();
}
void Utils::installSystemMenuHook(const WId windowId, const Options options, const QPoint &offset,
void Utils::installSystemMenuHook(const WId windowId,
const IsWindowFixedSizeCallback &isWindowFixedSize,
const IsInsideTitleBarDraggableAreaCallback &isInTitleBarArea,
const GetWindowDevicePixelRatioCallback &getDevicePixelRatio)
@ -1093,8 +1083,6 @@ void Utils::installSystemMenuHook(const WId windowId, const Options options, con
//triggerFrameChange(windowId); // Crash
Win32UtilsHelperData data = {};
data.originalWindowProc = originalWindowProc;
data.options = options;
data.offset = offset;
data.isWindowFixedSize = isWindowFixedSize;
data.isInTitleBarArea = isInTitleBarArea;
data.getDevicePixelRatio = getDevicePixelRatio;
@ -1302,9 +1290,10 @@ bool Utils::shouldAppsUseDarkMode_windows()
return false;
}
const auto resultFromRegistry = []() -> bool {
const QWinRegistryKey registry(HKEY_CURRENT_USER, qPersonalizeRegistryKey);
const auto result = registry.dwordValue(kAppsUseLightTheme);
return (result.second && (result.first == 0));
const QSettings registry(kHKEY_CURRENT_USER + u'\\' + qPersonalizeRegistryKey, QSettings::NativeFormat);
bool ok = false;
const DWORD value = registry.value(kAppsUseLightTheme).toULongLong(&ok);
return (ok && (value == 0));
};
static const auto pShouldAppsUseDarkMode =
reinterpret_cast<BOOL(WINAPI *)(VOID)>(

View File

@ -25,7 +25,11 @@
#include "framelessquickhelper.h"
#include "framelessquickhelper_p.h"
#include <QtCore/qmutex.h>
#include <QtGui/qpa/qplatformwindow.h> // For QWINDOWSIZE_MAX
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include <QtGui/qpa/qplatformwindow.h> // For QWINDOWSIZE_MAX
#else
# include <QtGui/private/qwindow_p.h> // For QWINDOWSIZE_MAX
#endif
#include <QtQuick/qquickwindow.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <framelesswindowsmanager.h>
@ -38,10 +42,14 @@ using namespace Global;
struct QuickHelperData
{
bool attached = false;
UserSettings settings = {};
SystemParameters params = {};
QPointer<QQuickItem> titleBarItem = nullptr;
QList<QQuickItem *> hitTestVisibleItems = {};
QPointer<QQuickItem> windowIconButton = nullptr;
QPointer<QQuickItem> contextHelpButton = nullptr;
QPointer<QQuickItem> minimizeButton = nullptr;
QPointer<QQuickItem> maximizeButton = nullptr;
QPointer<QQuickItem> closeButton = nullptr;
};
struct QuickHelper
@ -52,17 +60,15 @@ struct QuickHelper
Q_GLOBAL_STATIC(QuickHelper, g_quickHelper)
static constexpr const char QTQUICK_ITEM_CLASS_NAME[] = "QQuickItem";
static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton";
FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q, const UserSettings &settings) : QObject(q)
FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q) : QObject(q)
{
Q_ASSERT(q);
if (!q) {
return;
}
q_ptr = q;
m_cachedSettings = settings;
}
FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate() = default;
@ -124,9 +130,6 @@ void FramelessQuickHelperPrivate::attachToWindow()
return;
}
UserSettings settings = m_cachedSettings;
m_cachedSettings = {};
SystemParameters params = {};
params.getWindowId = [window]() -> WId { return window->winId(); };
params.getWindowFlags = [window]() -> Qt::WindowFlags { return window->flags(); };
@ -163,7 +166,7 @@ void FramelessQuickHelperPrivate::attachToWindow()
data->attached = true;
g_quickHelper()->mutex.unlock();
FramelessWindowsManager::instance()->addWindow(settings, params);
FramelessWindowsManager::instance()->addWindow(params);
}
void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
@ -180,20 +183,20 @@ void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickG
Q_ASSERT(false);
break;
case QuickGlobal::SystemButtonType::WindowIcon:
data->settings.windowIconButton = item;
data->windowIconButton = item;
break;
case QuickGlobal::SystemButtonType::Help:
data->settings.contextHelpButton = item;
data->contextHelpButton = item;
break;
case QuickGlobal::SystemButtonType::Minimize:
data->settings.minimizeButton = item;
data->minimizeButton = item;
break;
case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore:
data->settings.maximizeButton = item;
data->maximizeButton = item;
break;
case QuickGlobal::SystemButtonType::Close:
data->settings.closeButton = item;
data->closeButton = item;
break;
}
}
@ -226,7 +229,7 @@ void FramelessQuickHelperPrivate::showSystemMenu(const QPoint &pos)
}
const QPoint globalPos = window->mapToGlobal(pos);
const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint();
Utils::showSystemMenu(window->winId(), nativePos, {}, false, {}, [this]() -> bool { return isWindowFixedSize(); });
Utils::showSystemMenu(window->winId(), nativePos, false, [this]() -> bool { return isWindowFixedSize(); });
#else
Q_UNUSED(pos);
#endif
@ -287,10 +290,6 @@ void FramelessQuickHelperPrivate::bringWindowToFront()
bool FramelessQuickHelperPrivate::isWindowFixedSize() const
{
const QuickHelperData data = getWindowData();
if (data.settings.options & Option::DisableResizing) {
return true;
}
Q_Q(const FramelessQuickHelper);
const QQuickWindow * const window = q->window();
if (!window) {
@ -307,16 +306,16 @@ bool FramelessQuickHelperPrivate::isWindowFixedSize() const
return false;
}
void FramelessQuickHelperPrivate::setWindowFixedSize(const bool value, const bool force)
void FramelessQuickHelperPrivate::setWindowFixedSize(const bool value)
{
if ((isWindowFixedSize() == value) && !force) {
return;
}
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
if (isWindowFixedSize() == value) {
return;
}
if (value) {
const QSize size = window->size();
window->setMinimumSize(size);
@ -355,37 +354,32 @@ bool FramelessQuickHelperPrivate::isInSystemButtons(const QPoint &pos, QuickGlob
}
*button = QuickGlobal::SystemButtonType::Unknown;
const QuickHelperData data = getWindowData();
if (data.settings.windowIconButton && data.settings.windowIconButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto iconBtn = qobject_cast<QQuickItem *>(data.settings.windowIconButton);
if (mapItemGeometryToScene(iconBtn).contains(pos)) {
if (data.windowIconButton) {
if (mapItemGeometryToScene(data.windowIconButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::WindowIcon;
return true;
}
}
if (data.settings.contextHelpButton && data.settings.contextHelpButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto helpBtn = qobject_cast<QQuickItem *>(data.settings.contextHelpButton);
if (mapItemGeometryToScene(helpBtn).contains(pos)) {
if (data.contextHelpButton) {
if (mapItemGeometryToScene(data.contextHelpButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Help;
return true;
}
}
if (data.settings.minimizeButton && data.settings.minimizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto minBtn = qobject_cast<QQuickItem *>(data.settings.minimizeButton);
if (mapItemGeometryToScene(minBtn).contains(pos)) {
if (data.minimizeButton) {
if (mapItemGeometryToScene(data.minimizeButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Minimize;
return true;
}
}
if (data.settings.maximizeButton && data.settings.maximizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto maxBtn = qobject_cast<QQuickItem *>(data.settings.maximizeButton);
if (mapItemGeometryToScene(maxBtn).contains(pos)) {
if (data.maximizeButton) {
if (mapItemGeometryToScene(data.maximizeButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Maximize;
return true;
}
}
if (data.settings.closeButton && data.settings.closeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto closeBtn = qobject_cast<QQuickItem *>(data.settings.closeButton);
if (mapItemGeometryToScene(closeBtn).contains(pos)) {
if (data.closeButton) {
if (mapItemGeometryToScene(data.closeButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Close;
return true;
}
@ -400,12 +394,11 @@ bool FramelessQuickHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos) c
return false;
}
QRegion region = mapItemGeometryToScene(data.titleBarItem);
const auto systemButtons = {data.settings.windowIconButton, data.settings.contextHelpButton,
data.settings.minimizeButton, data.settings.maximizeButton, data.settings.closeButton};
const auto systemButtons = {data.windowIconButton, data.contextHelpButton,
data.minimizeButton, data.maximizeButton, data.closeButton};
for (auto &&button : qAsConst(systemButtons)) {
if (button && button->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto quickButton = qobject_cast<QQuickItem *>(button);
region -= mapItemGeometryToScene(quickButton);
if (button) {
region -= mapItemGeometryToScene(button);
}
}
if (!data.hitTestVisibleItems.isEmpty()) {
@ -455,29 +448,29 @@ void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::System
Q_ASSERT(false);
} break;
case QuickGlobal::SystemButtonType::WindowIcon: {
if (data.settings.windowIconButton && data.settings.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.windowIconButton);
if (data.windowIconButton && data.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.windowIconButton);
}
} break;
case QuickGlobal::SystemButtonType::Help: {
if (data.settings.contextHelpButton && data.settings.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.contextHelpButton);
if (data.contextHelpButton && data.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.contextHelpButton);
}
} break;
case QuickGlobal::SystemButtonType::Minimize: {
if (data.settings.minimizeButton && data.settings.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.minimizeButton);
if (data.minimizeButton && data.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.minimizeButton);
}
} break;
case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore: {
if (data.settings.maximizeButton && data.settings.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.maximizeButton);
if (data.maximizeButton && data.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.maximizeButton);
}
} break;
case QuickGlobal::SystemButtonType::Close: {
if (data.settings.closeButton && data.settings.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.closeButton);
if (data.closeButton && data.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.closeButton);
}
} break;
}
@ -546,8 +539,8 @@ QuickHelperData *FramelessQuickHelperPrivate::getWindowDataMutable() const
return &g_quickHelper()->data[windowId];
}
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent, const UserSettings &settings)
: QQuickItem(parent), d_ptr(new FramelessQuickHelperPrivate(this, settings))
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent)
: QQuickItem(parent), d_ptr(new FramelessQuickHelperPrivate(this))
{
}
@ -645,16 +638,16 @@ void FramelessQuickHelper::bringWindowToFront()
d->bringWindowToFront();
}
void FramelessQuickHelper::setWindowFixedSize(const bool value, const bool force)
void FramelessQuickHelper::setWindowFixedSize(const bool value)
{
Q_D(FramelessQuickHelper);
d->setWindowFixedSize(value, force);
d->setWindowFixedSize(value);
}
void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeData &data)
{
QQuickItem::itemChange(change, data);
if ((change == FramelessQuickHelper::ItemSceneChange) && data.window) {
if ((change == ItemSceneChange) && data.window) {
Q_D(FramelessQuickHelper);
d->attachToWindow();
}

View File

@ -43,7 +43,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject
Q_DISABLE_COPY_MOVE(FramelessQuickHelperPrivate)
public:
explicit FramelessQuickHelperPrivate(FramelessQuickHelper *q, const Global::UserSettings &settings = {});
explicit FramelessQuickHelperPrivate(FramelessQuickHelper *q);
~FramelessQuickHelperPrivate() override;
Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub);
@ -63,7 +63,7 @@ public:
void bringWindowToFront();
Q_NODISCARD bool isWindowFixedSize() const;
void setWindowFixedSize(const bool value, const bool force = false);
void setWindowFixedSize(const bool value);
private:
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;
@ -76,7 +76,6 @@ private:
private:
FramelessQuickHelper *q_ptr = nullptr;
Global::UserSettings m_cachedSettings = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -58,7 +58,6 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
return;
}
inited = true;
qRegisterMetaType<QuickGlobal::Options>();
qRegisterMetaType<QuickGlobal::SystemTheme>();
qRegisterMetaType<QuickGlobal::SystemButtonType>();
qRegisterMetaType<QuickGlobal::ResourceType>();

View File

@ -34,14 +34,13 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const UserSettings &settings) : QObject(q)
FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q) : QObject(q)
{
Q_ASSERT(q);
if (!q) {
return;
}
q_ptr = q;
m_settings = settings;
initialize();
}
@ -88,8 +87,7 @@ bool FramelessQuickWindowPrivate::isZoomed() const
Q_Q(const FramelessQuickWindow);
const FramelessQuickWindow::Visibility visibility = q->visibility();
return ((visibility == FramelessQuickWindow::Maximized) ||
((m_settings.options & Option::DontTreatFullScreenAsZoomed)
? false : (visibility == FramelessQuickWindow::FullScreen)));
(visibility == FramelessQuickWindow::FullScreen));
}
bool FramelessQuickWindowPrivate::isFullScreen() const
@ -228,9 +226,6 @@ void FramelessQuickWindowPrivate::toggleFullScreen()
void FramelessQuickWindowPrivate::initialize()
{
Q_Q(FramelessQuickWindow);
if (m_settings.options & Option::TransparentWindowBackground) {
q->setColor(kDefaultTransparentColor);
}
QQuickItem * const rootItem = q->contentItem();
const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem);
m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
@ -254,17 +249,13 @@ void FramelessQuickWindowPrivate::initialize()
Q_EMIT q->fullScreenChanged();
});
connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor);
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this, q](){
updateTopBorderColor();
Q_EMIT q->frameBorderColorChanged();
});
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor);
}
bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const
{
#ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2)
&& !(m_settings.options & Option::DontDrawTopWindowFrameBorder));
return (Utils::isWindowFrameBorderVisible() && !Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2));
#else
return false;
#endif
@ -291,8 +282,8 @@ void FramelessQuickWindowPrivate::updateTopBorderHeight()
#endif
}
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings)
: QQuickWindow(parent), d_ptr(new FramelessQuickWindowPrivate(this, settings))
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent)
: QQuickWindow(parent), d_ptr(new FramelessQuickWindowPrivate(this))
{
}
@ -328,12 +319,6 @@ bool FramelessQuickWindow::isFullScreen() const
return d->isFullScreen();
}
QColor FramelessQuickWindow::frameBorderColor() const
{
Q_D(const FramelessQuickWindow);
return d->getFrameBorderColor();
}
void FramelessQuickWindow::snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor)
{
Q_ASSERT(item);

View File

@ -44,7 +44,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindowPrivate : public QObject
Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate)
public:
explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::UserSettings &settings = {});
explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q);
~FramelessQuickWindowPrivate() override;
Q_NODISCARD static FramelessQuickWindowPrivate *get(FramelessQuickWindow *pub);
@ -83,8 +83,6 @@ private:
QScopedPointer<QQuickRectangle> m_topBorderRectangle;
QScopedPointer<QQuickAnchors> m_topBorderAnchors;
QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed;
Global::UserSettings m_settings = {};
bool m_windowExposed = false;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -31,12 +31,18 @@ set(SOURCES
${INCLUDE_PREFIX}/framelesswidget.h
${INCLUDE_PREFIX}/framelessmainwindow.h
${INCLUDE_PREFIX}/standardsystembutton.h
${INCLUDE_PREFIX}/framelesswidgetshelper.h
${INCLUDE_PREFIX}/standardtitlebar.h
framelesswidgetshelper_p.h
standardsystembutton_p.h
standardtitlebar_p.h
framelessmainwindow.cpp
framelesswidgetshelper.cpp
framelesswidget.cpp
standardsystembutton.cpp
standardtitlebar.cpp
widgetssharedhelper_p.h
widgetssharedhelper.cpp
)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)

View File

@ -23,92 +23,50 @@
*/
#include "framelessmainwindow.h"
#include "framelesswidgetshelper_p.h"
#include "framelesswidgetshelper.h"
#include "widgetssharedhelper_p.h"
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags, const UserSettings &settings) : QMainWindow(parent, flags)
FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags) : QMainWindow(parent, flags)
{
d_ptr.reset(new FramelessWidgetsHelper(this, settings));
FramelessWidgetsHelper::get(this)->attach();
m_helper.reset(new WidgetsSharedHelper(this));
m_helper->setup(this);
}
FramelessMainWindow::~FramelessMainWindow() = default;
bool FramelessMainWindow::isNormal() const
{
return d_ptr->isNormal();
return (Utils::windowStatesToWindowState(windowState()) == Qt::WindowNoState);
}
bool FramelessMainWindow::isZoomed() const
{
return d_ptr->isZoomed();
}
bool FramelessMainWindow::isFixedSize() const
{
return d_ptr->isFixedSize();
}
void FramelessMainWindow::setFixedSize(const bool value)
{
d_ptr->setFixedSize(value);
}
void FramelessMainWindow::setTitleBarWidget(QWidget *widget)
{
d_ptr->setTitleBarWidget(widget);
}
QWidget *FramelessMainWindow::titleBarWidget() const
{
return d_ptr->getTitleBarWidget();
}
void FramelessMainWindow::setHitTestVisible(QWidget *widget)
{
d_ptr->setHitTestVisible(widget);
return (isMaximized() || isFullScreen());
}
void FramelessMainWindow::toggleMaximized()
{
d_ptr->toggleMaximized();
if (isMaximized()) {
showNormal();
} else {
showMaximized();
}
}
void FramelessMainWindow::toggleFullScreen()
{
d_ptr->toggleFullScreen();
}
void FramelessMainWindow::moveToDesktopCenter()
{
d_ptr->moveToDesktopCenter();
}
void FramelessMainWindow::bringToFront()
{
d_ptr->bringToFront();
}
void FramelessMainWindow::showSystemMenu(const QPoint &pos)
{
d_ptr->showSystemMenu(pos);
}
void FramelessMainWindow::startSystemMove2(const QPoint &pos)
{
d_ptr->startSystemMove2(pos);
}
void FramelessMainWindow::startSystemResize2(const Qt::Edges edges, const QPoint &pos)
{
d_ptr->startSystemResize2(edges, pos);
}
void FramelessMainWindow::setSystemButton(QWidget *widget, const SystemButtonType buttonType)
{
d_ptr->setSystemButton(widget, buttonType);
if (isFullScreen()) {
setWindowState(m_savedWindowState);
} else {
m_savedWindowState = Utils::windowStatesToWindowState(windowState());
showFullScreen();
}
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -23,102 +23,50 @@
*/
#include "framelesswidget.h"
#include "framelesswidgetshelper_p.h"
#include "framelesswidgetshelper.h"
#include "widgetssharedhelper_p.h"
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FramelessWidget::FramelessWidget(QWidget *parent, const UserSettings &settings) : QWidget(parent)
FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent)
{
d_ptr.reset(new FramelessWidgetsHelper(this, settings));
FramelessWidgetsHelper::get(this)->attach();
m_helper.reset(new WidgetsSharedHelper(this));
m_helper->setup(this);
}
FramelessWidget::~FramelessWidget() = default;
bool FramelessWidget::isNormal() const
{
return d_ptr->isNormal();
return (Utils::windowStatesToWindowState(windowState()) == Qt::WindowNoState);
}
bool FramelessWidget::isZoomed() const
{
return d_ptr->isZoomed();
}
bool FramelessWidget::isFixedSize() const
{
return d_ptr->isFixedSize();
}
void FramelessWidget::setFixedSize(const bool value)
{
d_ptr->setFixedSize(value);
}
void FramelessWidget::setTitleBarWidget(QWidget *widget)
{
d_ptr->setTitleBarWidget(widget);
}
QWidget *FramelessWidget::titleBarWidget() const
{
return d_ptr->getTitleBarWidget();
}
void FramelessWidget::setContentWidget(QWidget *widget)
{
d_ptr->setContentWidget(widget);
}
QWidget *FramelessWidget::contentWidget() const
{
return d_ptr->getContentWidget();
}
void FramelessWidget::setHitTestVisible(QWidget *widget)
{
d_ptr->setHitTestVisible(widget);
return (isMaximized() || isFullScreen());
}
void FramelessWidget::toggleMaximized()
{
d_ptr->toggleMaximized();
if (isMaximized()) {
showNormal();
} else {
showMaximized();
}
}
void FramelessWidget::toggleFullScreen()
{
d_ptr->toggleFullScreen();
}
void FramelessWidget::moveToDesktopCenter()
{
d_ptr->moveToDesktopCenter();
}
void FramelessWidget::bringToFront()
{
d_ptr->bringToFront();
}
void FramelessWidget::showSystemMenu(const QPoint &pos)
{
d_ptr->showSystemMenu(pos);
}
void FramelessWidget::startSystemMove2(const QPoint &pos)
{
d_ptr->startSystemMove2(pos);
}
void FramelessWidget::startSystemResize2(const Qt::Edges edges, const QPoint &pos)
{
d_ptr->startSystemResize2(edges, pos);
}
void FramelessWidget::setSystemButton(QWidget *widget, const SystemButtonType buttonType)
{
d_ptr->setSystemButton(widget, buttonType);
if (isFullScreen()) {
setWindowState(m_savedWindowState);
} else {
m_savedWindowState = Utils::windowStatesToWindowState(windowState());
showFullScreen();
}
}
FRAMELESSHELPER_END_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@ -22,19 +22,4 @@
* SOFTWARE.
*/
#pragma once
#include <QtWidgets/qpushbutton.h>
class SystemButton : public QPushButton
{
Q_OBJECT
public:
explicit SystemButton(QWidget *parent = nullptr);
~SystemButton() override;
public Q_SLOTS:
void setHovered(const bool value);
void setPressed(const bool value);
};
#include "../../include/FramelessHelper/Widgets/framelesswidgetshelper.h"

View File

@ -26,92 +26,53 @@
#include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtGui/qwindow.h>
QT_BEGIN_NAMESPACE
class QLabel;
class QVBoxLayout;
class QShowEvent;
class QPaintEvent;
class QMouseEvent;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardSystemButton;
struct WidgetsHelperData;
class FramelessWidgetsHelper;
class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject
class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelperPrivate : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper)
Q_DECLARE_PUBLIC(FramelessWidgetsHelper)
Q_DISABLE_COPY_MOVE(FramelessWidgetsHelperPrivate)
public:
explicit FramelessWidgetsHelper(QWidget *q, const Global::UserSettings &settings = {});
~FramelessWidgetsHelper() override;
explicit FramelessWidgetsHelperPrivate(FramelessWidgetsHelper *q);
~FramelessWidgetsHelperPrivate() override;
Q_NODISCARD static FramelessWidgetsHelper *get(QWidget *pub);
Q_NODISCARD static const FramelessWidgetsHelper *get(const QWidget *pub);
Q_NODISCARD static FramelessWidgetsHelperPrivate *get(FramelessWidgetsHelper *pub);
Q_NODISCARD static const FramelessWidgetsHelperPrivate *get(const FramelessWidgetsHelper *pub);
Q_NODISCARD Q_INVOKABLE bool isNormal() const;
Q_NODISCARD Q_INVOKABLE bool isZoomed() const;
Q_NODISCARD Q_INVOKABLE bool isFixedSize() const;
Q_INVOKABLE void setFixedSize(const bool value, const bool force = false);
Q_NODISCARD QWidget *getTitleBarWidget() const;
void setTitleBarWidget(QWidget *widget);
Q_INVOKABLE void setTitleBarWidget(QWidget *widget);
Q_NODISCARD Q_INVOKABLE QWidget *getTitleBarWidget() const;
Q_INVOKABLE void setContentWidget(QWidget *widget);
Q_NODISCARD Q_INVOKABLE QWidget *getContentWidget() const;
Q_INVOKABLE void showEventHandler(QShowEvent *event);
Q_INVOKABLE void changeEventHandler(QEvent *event);
Q_INVOKABLE void paintEventHandler(QPaintEvent *event);
public Q_SLOTS:
void setHitTestVisible(QWidget *widget);
void toggleMaximized();
void toggleFullScreen();
void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2(const QPoint &pos);
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
void attachToWindow();
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
void setHitTestVisible(QWidget *widget);
void showSystemMenu(const QPoint &pos);
void windowStartSystemMove2(const QPoint &pos);
void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos);
protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
void moveWindowToDesktopCenter();
void bringWindowToFront();
Q_NODISCARD bool isWindowFixedSize() const;
void setWindowFixedSize(const bool value);
private:
void initialize();
void createSystemTitleBar();
void createUserContentContainer();
void setupInitialUi();
Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldDrawFrameBorder() const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
void setSystemButtonState(const Global::SystemButtonType button, const Global::ButtonState state);
private Q_SLOTS:
void updateContentsMargins();
void updateSystemTitleBarStyleSheet();
void updateSystemMaximizeButton();
Q_NODISCARD QWidget *getWindow() const;
Q_NODISCARD WidgetsHelperData getWindowData() const;
Q_NODISCARD WidgetsHelperData *getWindowDataMutable() const;
private:
QPointer<QWidget> q = nullptr;
QScopedPointer<QWidget> m_systemTitleBarWidget;
QScopedPointer<QLabel> m_systemWindowTitleLabel;
QPointer<QWidget> m_userTitleBarWidget = nullptr;
QPointer<QWidget> m_userContentWidget = nullptr;
QScopedPointer<QVBoxLayout> m_mainLayout;
QWidgetList m_hitTestVisibleWidgets = {};
QScopedPointer<QWidget> m_userContentContainerWidget;
QScopedPointer<QVBoxLayout> m_userContentContainerLayout;
Qt::WindowState m_savedWindowState = {};
Global::UserSettings m_settings = {};
Global::SystemParameters m_params = {};
bool m_windowExposed = false;
FramelessWidgetsHelper *q_ptr = nullptr;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -340,7 +340,7 @@ SystemButtonType StandardSystemButton::buttonType()
return d->getButtonType();
}
void StandardSystemButton::setButtonType(const Global::SystemButtonType value)
void StandardSystemButton::setButtonType(const SystemButtonType value)
{
Q_D(StandardSystemButton);
d->setButtonType(value);

View File

@ -37,7 +37,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardSystemButton;
class StandardSystemButtonPrivate : public QObject
class FRAMELESSHELPER_WIDGETS_API StandardSystemButtonPrivate : public QObject
{
Q_OBJECT
Q_DECLARE_PUBLIC(StandardSystemButton)

View File

@ -0,0 +1,235 @@
/*
* MIT License
*
* Copyright (C) 2022 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 "standardtitlebar.h"
#include "standardtitlebar_p.h"
#include "standardsystembutton.h"
#include <QtCore/qcoreevent.h>
#include <QtGui/qpainter.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qstyle.h>
#include <QtWidgets/qstyleoption.h>
#include <framelesswindowsmanager.h>
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetColorTemplate, "color: %1;")
FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetBackgroundColorTemplate, "background-color: %1;")
StandardTitleBarPrivate::StandardTitleBarPrivate(StandardTitleBar *q) : QObject(q)
{
Q_ASSERT(q);
if (!q) {
return;
}
q_ptr = q;
initialize();
}
StandardTitleBarPrivate::~StandardTitleBarPrivate() = default;
StandardTitleBarPrivate *StandardTitleBarPrivate::get(StandardTitleBar *pub)
{
Q_ASSERT(pub);
if (!pub) {
return nullptr;
}
return pub->d_func();
}
const StandardTitleBarPrivate *StandardTitleBarPrivate::get(const StandardTitleBar *pub)
{
Q_ASSERT(pub);
if (!pub) {
return nullptr;
}
return pub->d_func();
}
void StandardTitleBarPrivate::updateMaximizeButton()
{
const bool zoomed = (m_window->isMaximized() || m_window->isFullScreen());
m_maximizeButton->setToolTip(zoomed ? tr("Restore") : tr("Maximize"));
m_maximizeButton->setButtonType(zoomed ? SystemButtonType::Restore : SystemButtonType::Maximize);
}
void StandardTitleBarPrivate::updateTitleBarStyleSheet()
{
const bool active = m_window->isActiveWindow();
const bool dark = Utils::shouldAppsUseDarkMode();
const bool colorizedTitleBar = Utils::isTitleBarColorized();
const QColor titleBarBackgroundColor = [active, colorizedTitleBar, dark]() -> QColor {
if (active) {
if (colorizedTitleBar) {
#ifdef Q_OS_WINDOWS
return Utils::getDwmColorizationColor();
#endif
#ifdef Q_OS_LINUX
return Utils::getWmThemeColor();
#endif
#ifdef Q_OS_MACOS
return Utils::getControlsAccentColor();
#endif
} else {
if (dark) {
return kDefaultBlackColor;
} else {
return kDefaultWhiteColor;
}
}
} else {
if (dark) {
return kDefaultSystemDarkColor;
} else {
return kDefaultWhiteColor;
}
}
}();
const QColor windowTitleLabelTextColor = (active ? ((dark || colorizedTitleBar) ? kDefaultWhiteColor : kDefaultBlackColor) : kDefaultDarkGrayColor);
m_windowTitleLabel->setStyleSheet(kStyleSheetColorTemplate.arg(windowTitleLabelTextColor.name()));
Q_Q(StandardTitleBar);
q->setStyleSheet(kStyleSheetBackgroundColorTemplate.arg(titleBarBackgroundColor.name()));
q->update();
}
bool StandardTitleBarPrivate::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (!object || !event) {
return false;
}
if (!object->isWidgetType()) {
return QObject::eventFilter(object, event);
}
const auto widget = qobject_cast<QWidget *>(object);
if (!widget->isWindow() || (widget != m_window)) {
return QObject::eventFilter(object, event);
}
switch (event->type()) {
case QEvent::WindowStateChange:
updateMaximizeButton();
break;
case QEvent::ActivationChange:
updateTitleBarStyleSheet();
break;
default:
break;
}
return QObject::eventFilter(object, event);
}
void StandardTitleBarPrivate::initialize()
{
Q_Q(StandardTitleBar);
m_window = q->window();
q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
q->setFixedHeight(kDefaultTitleBarHeight);
m_windowTitleLabel.reset(new QLabel(q));
m_windowTitleLabel->setFrameShape(QFrame::NoFrame);
QFont windowTitleFont = q->font();
windowTitleFont.setPointSize(kDefaultTitleBarFontPointSize);
m_windowTitleLabel->setFont(windowTitleFont);
m_windowTitleLabel->setText(m_window->windowTitle());
connect(m_window, &QWidget::windowTitleChanged, m_windowTitleLabel.data(), &QLabel::setText);
m_minimizeButton.reset(new StandardSystemButton(SystemButtonType::Minimize, q));
m_minimizeButton->setFixedSize(kDefaultSystemButtonSize);
m_minimizeButton->setIconSize(kDefaultSystemButtonIconSize);
m_minimizeButton->setToolTip(tr("Minimize"));
connect(m_minimizeButton.data(), &StandardSystemButton::clicked, m_window, &QWidget::showMinimized);
m_maximizeButton.reset(new StandardSystemButton(SystemButtonType::Maximize, q));
m_maximizeButton->setFixedSize(kDefaultSystemButtonSize);
m_maximizeButton->setIconSize(kDefaultSystemButtonIconSize);
updateMaximizeButton();
connect(m_maximizeButton.data(), &StandardSystemButton::clicked, this, [this](){
if (m_window->isMaximized() || m_window->isFullScreen()) {
m_window->showNormal();
} else {
m_window->showMaximized();
}
});
m_closeButton.reset(new StandardSystemButton(SystemButtonType::Close, q));
m_closeButton->setFixedSize(kDefaultSystemButtonSize);
m_closeButton->setIconSize(kDefaultSystemButtonIconSize);
m_closeButton->setToolTip(tr("Close"));
connect(m_closeButton.data(), &StandardSystemButton::clicked, m_window, &QWidget::close);
const auto titleBarLayout = new QHBoxLayout(q);
titleBarLayout->setContentsMargins(0, 0, 0, 0);
titleBarLayout->setSpacing(0);
titleBarLayout->addSpacerItem(new QSpacerItem(kDefaultTitleBarTitleLabelMargin, kDefaultTitleBarTitleLabelMargin));
titleBarLayout->addWidget(m_windowTitleLabel.data());
titleBarLayout->addStretch();
titleBarLayout->addWidget(m_minimizeButton.data());
titleBarLayout->addWidget(m_maximizeButton.data());
titleBarLayout->addWidget(m_closeButton.data());
q->setLayout(titleBarLayout);
updateTitleBarStyleSheet();
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged,
this, &StandardTitleBarPrivate::updateTitleBarStyleSheet);
m_window->installEventFilter(this);
}
StandardTitleBar::StandardTitleBar(QWidget *parent)
: QWidget(parent), d_ptr(new StandardTitleBarPrivate(this))
{
}
StandardTitleBar::~StandardTitleBar() = default;
StandardSystemButton *StandardTitleBar::minimizeButton() const
{
Q_D(const StandardTitleBar);
return d->m_minimizeButton.data();
}
StandardSystemButton *StandardTitleBar::maximizeButton() const
{
Q_D(const StandardTitleBar);
return d->m_maximizeButton.data();
}
StandardSystemButton *StandardTitleBar::closeButton() const
{
Q_D(const StandardTitleBar);
return d->m_closeButton.data();
}
void StandardTitleBar::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
// This block of code ensures that our widget applies the stylesheet correctly.
// Enabling the "Qt::WA_StyledBackground" attribute can also achieve the same
// effect, but since it's documented as only for internal uses, we use the
// public way to do that instead.
QStyleOption option;
option.initFrom(this);
QPainter painter(this);
style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -22,19 +22,4 @@
* SOFTWARE.
*/
#pragma once
#include <QtWidgets/qpushbutton.h>
class SystemButton : public QPushButton
{
Q_OBJECT
public:
explicit SystemButton(QWidget *parent = nullptr);
~SystemButton() override;
public Q_SLOTS:
void setHovered(const bool value);
void setPressed(const bool value);
};
#include "../../include/FramelessHelper/Widgets/standardtitlebar.h"

View File

@ -0,0 +1,72 @@
/*
* MIT License
*
* Copyright (C) 2022 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 "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
class StandardSystemButton;
class FRAMELESSHELPER_WIDGETS_API StandardTitleBarPrivate : public QObject
{
Q_OBJECT
Q_DECLARE_PUBLIC(StandardTitleBar)
Q_DISABLE_COPY_MOVE(StandardTitleBarPrivate)
public:
explicit StandardTitleBarPrivate(StandardTitleBar *q);
~StandardTitleBarPrivate() override;
Q_NODISCARD static StandardTitleBarPrivate *get(StandardTitleBar *pub);
Q_NODISCARD static const StandardTitleBarPrivate *get(const StandardTitleBar *pub);
public Q_SLOTS:
void updateMaximizeButton();
void updateTitleBarStyleSheet();
protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
private:
void initialize();
private:
StandardTitleBar *q_ptr = nullptr;
QScopedPointer<QLabel> m_windowTitleLabel;
QScopedPointer<StandardSystemButton> m_minimizeButton;
QScopedPointer<StandardSystemButton> m_maximizeButton;
QScopedPointer<StandardSystemButton> m_closeButton;
QPointer<QWidget> m_window = nullptr;
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,144 @@
/*
* MIT License
*
* Copyright (C) 2022 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 "widgetssharedhelper_p.h"
#include <QtCore/qcoreevent.h>
#include <QtGui/qevent.h>
#include <QtGui/qpainter.h>
#include <QtWidgets/qwidget.h>
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
WidgetsSharedHelper::WidgetsSharedHelper(QObject *parent) : QObject(parent)
{
}
WidgetsSharedHelper::~WidgetsSharedHelper() = default;
void WidgetsSharedHelper::setup(QWidget *widget)
{
Q_ASSERT(widget);
if (!widget) {
return;
}
if (m_targetWidget == widget) {
return;
}
m_targetWidget = widget;
m_targetWidget->installEventFilter(this);
updateContentsMargins();
m_targetWidget->update();
}
bool WidgetsSharedHelper::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (!object || !event) {
return false;
}
if (!object->isWidgetType()) {
return QObject::eventFilter(object, event);
}
const auto widget = qobject_cast<QWidget *>(object);
if (widget != m_targetWidget) {
return QObject::eventFilter(object, event);
}
switch (event->type()) {
case QEvent::Paint: {
const auto paintEvent = static_cast<QPaintEvent *>(event);
paintEventHandler(paintEvent);
} break;
case QEvent::WindowStateChange: {
changeEventHandler(event);
} break;
default:
break;
}
return QObject::eventFilter(object, event);
}
void WidgetsSharedHelper::changeEventHandler(QEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (event->type() != QEvent::WindowStateChange) {
return;
}
updateContentsMargins();
QMetaObject::invokeMethod(m_targetWidget, "hiddenChanged");
QMetaObject::invokeMethod(m_targetWidget, "normalChanged");
QMetaObject::invokeMethod(m_targetWidget, "zoomedChanged");
}
void WidgetsSharedHelper::paintEventHandler(QPaintEvent *event)
{
#ifdef Q_OS_WINDOWS
Q_ASSERT(event);
if (!event) {
return;
}
if (!shouldDrawFrameBorder()) {
return;
}
QPainter painter(m_targetWidget);
painter.save();
QPen pen = {};
pen.setColor(Utils::getFrameBorderColor(m_targetWidget->isActiveWindow()));
pen.setWidth(1);
painter.setPen(pen);
// We should use "m_targetWidget->width() - 1" actually but we can't because
// Qt's drawing system have some rounding error internally and
// if we minus one here we'll get a one pixel gap, so sad. But
// drawing a line with extra pixels won't hurt anyway.
painter.drawLine(0, 0, m_targetWidget->width(), 0);
painter.restore();
#else
Q_UNUSED(event);
#endif
}
bool WidgetsSharedHelper::shouldDrawFrameBorder() const
{
#ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2)
&& (Utils::windowStatesToWindowState(m_targetWidget->windowState()) == Qt::WindowNoState));
#else
return false;
#endif
}
void WidgetsSharedHelper::updateContentsMargins()
{
#ifdef Q_OS_WINDOWS
m_targetWidget->setContentsMargins(0, (shouldDrawFrameBorder() ? 1 : 0), 0, 0);
#endif
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -22,22 +22,42 @@
* SOFTWARE.
*/
#include "systembutton.h"
#pragma once
SystemButton::SystemButton(QWidget *parent) : QPushButton(parent)
#include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
class QPaintEvent;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class FRAMELESSHELPER_WIDGETS_API WidgetsSharedHelper : public QObject
{
}
Q_OBJECT
Q_DISABLE_COPY_MOVE(WidgetsSharedHelper)
SystemButton::~SystemButton() = default;
public:
explicit WidgetsSharedHelper(QObject *parent = nullptr);
~WidgetsSharedHelper() override;
void SystemButton::setHovered(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}
void setup(QWidget *widget);
void SystemButton::setPressed(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}
protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
private Q_SLOTS:
void updateContentsMargins();
private:
void changeEventHandler(QEvent *event);
void paintEventHandler(QPaintEvent *event);
Q_NODISCARD bool shouldDrawFrameBorder() const;
private:
QPointer<QWidget> m_targetWidget = nullptr;
};
FRAMELESSHELPER_END_NAMESPACE