general improvements

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-05-12 15:07:57 +08:00
parent bebd20f01e
commit 8cb24c61b3
14 changed files with 251 additions and 119 deletions

View File

@ -57,7 +57,7 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate
## Requiredments ## Requiredments
- Compiler: a modern compiler which supports C++17 at least. Tested on MSVC 2022 (Windows), GCC 11 (Linux) and Clang 14 (macOS). - Compiler: a modern compiler which supports C++17 at least. Tested on MSVC 2022 (Windows), GCC 11 (Linux) and Clang 13 (macOS).
- Qt version: using the latest stable version of Qt is highly recommended, the minimum supported version is Qt 5.6. However, if you are using some old Qt versions (such as older than 5.12), some features may not be available. - Qt version: using the latest stable version of Qt is highly recommended, the minimum supported version is Qt 5.6. However, if you are using some old Qt versions (such as older than 5.12), some features may not be available.
- Qt modules: QtCore and QtGui for the core module; QtWidgets for the widgets module; QtQuick, QtQuickControls2 and QtQuickTemplates2 for the quick module. - Qt modules: QtCore and QtGui for the core module; QtWidgets for the widgets module; QtQuick, QtQuickControls2 and QtQuickTemplates2 for the quick module.
- CMake & ninja: the newer, the better. Other build systems are not tested. - CMake & ninja: the newer, the better. Other build systems are not tested.
@ -66,7 +66,7 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate
- Windows: Windows 7, Windows 8, Windows 8.1, Windows 10, Windows 11 - Windows: Windows 7, Windows 8, Windows 8.1, Windows 10, Windows 11
- Linux: any modern Linux distros should work, but only tested on Ubuntu 20.04 and Ubuntu 22.04 - Linux: any modern Linux distros should work, but only tested on Ubuntu 20.04 and Ubuntu 22.04
- macOS: only tested on macOS 12 due to lack of Apple devices - macOS: only tested on macOS 12.3 due to lack of Apple devices
There are some additional restrictions for each platform, please refer to the _Platform notes_ section below. There are some additional restrictions for each platform, please refer to the _Platform notes_ section below.
@ -86,12 +86,12 @@ cmake --build . --config Release --target all --parallel
### Qt Widgets ### Qt Widgets
To customize the window frame of a QWidget, you need to instantiate a `FramelessWidgetsHelper` object and then attach it to the widget's top level widget, and then `FramelessWidgetsHelper` will do all the rest work for you: the window frame will be removed automatically once it has been attached to the top level widget successfully. In theory you can instantiate multiple `FramelessWidgetsHelper` instances for a same widget, in this case there will be only one instance that keeps functional, all other instances will become a wrapper of that one. But to make sure everything goes smoothly and normally, you should not do that in any case. The simplest way to instantiate a `FramelessWidgetsHelper` To customize the window frame of a QWidget, you need to instantiate a `FramelessWidgetsHelper` object and then attach it to the widget's top level widget, and then `FramelessWidgetsHelper` will do all the rest work for you: the window frame will be removed automatically once it has been attached to the top level widget successfully. In theory you can instantiate multiple `FramelessWidgetsHelper` objects for a same widget, in this case there will be only one object that keeps functional, all other objects will become a wrapper of that one. But to make sure everything goes smoothly and normally, you should not do that in any case. The simplest way to instantiate a `FramelessWidgetsHelper`
instance is to call the static method `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)`. It will return the handle of the previously instantiated instance if any, or it will instantiate a new instance if it can't find one. It's safe to call this method multiple times for a same widget, it won't instantiate any new instances if there is one already. It also does not matter when and where you call that function as long as the top level widget is the same. The internally created instance will always be parented to the top level widget. Once you get the handle of the `FramelessWidgetsHelper` instance, you can call `void FramelessWidgetsHelper::extendsContentIntoTitleBar()` to let it hide the default title bar provided by the operating system. In order to make sure `FramelessWidgetsHelper` can find the correct top level widget, you should call the `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)` function on a widget which has a complete parent-chain whose root parent is the top level widget. To make the frameless window draggable, you should provide a homemade title bar widget yourself, the title bar widget doesn't need to be in rectangular shape, it also doesn't need to be placed on the first row of the window. Call `void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)` to let `FramelessHelper` know what's your title bar widget. By default, all the widgets in the title bar area won't be responsible to any mouse and keyboard events due to they have been intercepted by FramelessHelper. To make them recover the responsible state, you should make them visible to hit test. Call `void FramelessWidgetsHelper::setHitTestVisible(QWidget* )` to do that. You can of course call it on a widget that is not inside the title bar at all, it won't have any effect though. Due to Qt's own limitations, you need to make sure your widget has a complete parent-chain whose root parent is the top level widget. Do not ever try to delete the `FramelessWidgetsHelper` instance, it may still be monitoring and controlling your widget, and Qt will delete it for you automatically. No need to worry about memory leaks. object is to call the static method `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)`. It will return the handle of the previously instantiated object if any, or it will instantiate a new object if it can't find one. It's safe to call this method multiple times for a same widget, it won't instantiate any new objects if there is one already. It also does not matter when and where you call that function as long as the top level widget is the same. The internally created objects will always be parented to the top level widget. Once you get the handle of the `FramelessWidgetsHelper` object, you can call `void FramelessWidgetsHelper::extendsContentIntoTitleBar()` to let it hide the default title bar provided by the operating system. In order to make sure `FramelessWidgetsHelper` can find the correct top level widget, you should call the `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)` function on a widget which has a complete parent-chain whose root parent is the top level widget. To make the frameless window draggable, you should provide a homemade title bar widget yourself, the title bar widget doesn't need to be in rectangular shape, it also doesn't need to be placed on the first row of the window. Call `void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)` to let `FramelessHelper` know what's your title bar widget. By default, all the widgets in the title bar area won't be responsible to any mouse and keyboard events due to they have been intercepted by FramelessHelper. To make them recover the responsible state, you should make them visible to hit test. Call `void FramelessWidgetsHelper::setHitTestVisible(QWidget* )` to do that. You can of course call it on a widget that is not inside the title bar at all, it won't have any effect though. Due to Qt's own limitations, you need to make sure your widget has a complete parent-chain whose root parent is the top level widget. Do not ever try to delete the `FramelessWidgetsHelper` object, it may still be monitoring and controlling your widget, and Qt will delete it for you automatically. No need to worry about memory leaks.
There are also two classes called `FramelessWidget` and `FramelessMainWindow`, they are only simple wrappers of `FramelessWidgetsHelper`, which just saves the call of the `void FramelessWidgetsHelper::extendsContentIntoTitleBar()` function for you. You can absolutely use plain `QWidget` instead. There are also two classes called `FramelessWidget` and `FramelessMainWindow`, they are only simple wrappers of `FramelessWidgetsHelper`, which just saves the call of the `void FramelessWidgetsHelper::extendsContentIntoTitleBar()` function for you. You can absolutely use plain `QWidget` instead.
#### Code #### Code snippet
First of all, call `void FramelessHelper::Core::initialize()` in your `main` function in a very early stage: First of all, call `void FramelessHelper::Core::initialize()` in your `main` function in a very early stage:
@ -140,7 +140,7 @@ void MyWidget::myFunction2()
### Qt Quick ### Qt Quick
#### Code #### Code snippet
First of all, you should call `void FramelessHelper::Core::initialize()` in your `main` function in a very early stage: First of all, you should call `void FramelessHelper::Core::initialize()` in your `main` function in a very early stage:
@ -212,10 +212,14 @@ Window {
} }
``` ```
In theory it's possible to instantiate multiple `FramelessHelper` instances for a same `Window`, in this case only one of them will keep functional, all other instances will become a wrapper of it, but doing so is not recommended and may cause unexpected behavior or bugs, so please avoid trying to do that in any case. In theory it's possible to instantiate multiple `FramelessHelper` objects for a same `Window`, in this case only one of them will keep functional, all other objects will become a wrapper of it, but doing so is not recommended and may cause unexpected behavior or bugs, so please avoid trying to do that in any case.
If you find any of `FramelessHelper` functions have no effect after calling, the most possible reason is by the time you call the function/change the property of `FramelessHelper`, the root window has not finished its initialization process and thus `FramelessHelper` can't get the handle of it, so any action from the user will be ignored until the root window finished initialization.
There's also a QML type called `FramelessWindow`, it's only a simple wrapper of `FramelessHelper`, you can absolutely use plain `Window` instead. There's also a QML type called `FramelessWindow`, it's only a simple wrapper of `FramelessHelper`, you can absolutely use plain `Window` instead.
### More
Please refer to the demo projects to see more detailed usages: [examples](./examples/) Please refer to the demo projects to see more detailed usages: [examples](./examples/)
### Title bar design guidance ### Title bar design guidance
@ -255,6 +259,7 @@ Please refer to the demo projects to see more detailed usages: [examples](./exam
- The frameless windows will appear in square corners instead of round corners. - The frameless windows will appear in square corners instead of round corners.
- The resize area is inside of the window. - The resize area is inside of the window.
- Some users reported that the window is not resizable on some old macOS versions.
## FAQs ## FAQs
@ -264,11 +269,11 @@ Please refer to the demo projects to see more detailed usages: [examples](./exam
### `When running on Wayland, dragging the title bar causes crash?` ### `When running on Wayland, dragging the title bar causes crash?`
You need to force Qt to use the **XCB** QPA backend when running on Wayland. Try setting the environment variable `QT_QPA_PLATFORM` to `xcb` before instantiating any `Q(Gui)Application` instances. Or just call `void FramelessHelper::Core::initialize()` in your `main` function, this function will take care of it for you. You need to force Qt to use the **XCB** QPA when running on Wayland. Try setting the environment variable `QT_QPA_PLATFORM` to `xcb` before instantiating any `Q(Gui)Application` instances. Or just call `void FramelessHelper::Core::initialize()` in your `main` function, this function will take care of it for you.
### `I can see the black background during window resizing?` ### `I can see the black background during window resizing?`
First of all, it's a Qt issue, not caused by FramelessHelper. And it should not be possible for Qt Widgets applications. It's a common issue for Qt Quick applications. Most of the time it's caused by D3D11/Vulkan/Metal because they are not good at dealing with texture resizing operations. If you really want to fix this issue, you can try to change Qt's RHI backend to **OpenGL** or **Software**. And please keep in mind that this issue is not fixable from outside of Qt. First of all, it's a Qt issue, not caused by FramelessHelper. And it should not be possible for Qt Widgets applications. It's a common issue for Qt Quick applications. Most of the time it's caused by D3D11/Vulkan/Metal because they are not good at dealing with texture resizing operations. If you really want to fix this issue, you can try to change Qt's RHI backend to **OpenGL** (be careful of the bug of your graphics card driver) or **Software** (if you don't care about performance). And please keep in mind that this issue is not fixable from outside of Qt.
## License ## License

View File

@ -26,7 +26,6 @@
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
#include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -51,7 +50,6 @@ public:
private Q_SLOTS: private Q_SLOTS:
void updateForeground(); void updateForeground();
void updateBackground(); void updateBackground();
void updateToolTip();
private: private:
void initialize(); void initialize();
@ -60,7 +58,6 @@ private:
QScopedPointer<QQuickItem> m_contentItem; QScopedPointer<QQuickItem> m_contentItem;
QScopedPointer<QQuickImage> m_image; QScopedPointer<QQuickImage> m_image;
QScopedPointer<QQuickRectangle> m_backgroundItem; QScopedPointer<QQuickRectangle> m_backgroundItem;
QPointer<QQuickToolTipAttached> m_tooltip = nullptr;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -27,7 +27,6 @@
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QQuickImage; class QQuickImage;
@ -55,7 +54,6 @@ public:
private Q_SLOTS: private Q_SLOTS:
void updateForeground(); void updateForeground();
void updateBackground(); void updateBackground();
void updateToolTip();
Q_SIGNALS: Q_SIGNALS:
void maximizedChanged(); void maximizedChanged();
@ -68,7 +66,6 @@ private:
QScopedPointer<QQuickItem> m_contentItem; QScopedPointer<QQuickItem> m_contentItem;
QScopedPointer<QQuickImage> m_image; QScopedPointer<QQuickImage> m_image;
QScopedPointer<QQuickRectangle> m_backgroundItem; QScopedPointer<QQuickRectangle> m_backgroundItem;
QPointer<QQuickToolTipAttached> m_tooltip = nullptr;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -27,7 +27,6 @@
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtQuickTemplates2/private/qquickbutton_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QQuickImage; class QQuickImage;
@ -51,7 +50,6 @@ public:
private Q_SLOTS: private Q_SLOTS:
void updateForeground(); void updateForeground();
void updateBackground(); void updateBackground();
void updateToolTip();
private: private:
void initialize(); void initialize();
@ -60,7 +58,6 @@ private:
QScopedPointer<QQuickItem> m_contentItem; QScopedPointer<QQuickItem> m_contentItem;
QScopedPointer<QQuickImage> m_image; QScopedPointer<QQuickImage> m_image;
QScopedPointer<QQuickRectangle> m_backgroundItem; QScopedPointer<QQuickRectangle> m_backgroundItem;
QPointer<QQuickToolTipAttached> m_tooltip = nullptr;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -27,9 +27,9 @@
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuickTemplates2/private/qquicklabel_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QQuickLabel;
class QQuickRow; class QQuickRow;
QT_END_NAMESPACE QT_END_NAMESPACE
@ -47,6 +47,7 @@ class FRAMELESSHELPER_QUICK_API QuickStandardTitleBar : public QQuickRectangle
#endif #endif
Q_DISABLE_COPY_MOVE(QuickStandardTitleBar) Q_DISABLE_COPY_MOVE(QuickStandardTitleBar)
Q_PROPERTY(Qt::Alignment titleLabelAlignment READ titleLabelAlignment WRITE setTitleLabelAlignment NOTIFY titleLabelAlignmentChanged FINAL) Q_PROPERTY(Qt::Alignment titleLabelAlignment READ titleLabelAlignment WRITE setTitleLabelAlignment NOTIFY titleLabelAlignmentChanged FINAL)
Q_PROPERTY(QQuickLabel* titleLabel READ titleLabel CONSTANT FINAL)
Q_PROPERTY(QuickStandardMinimizeButton* minimizeButton READ minimizeButton CONSTANT FINAL) Q_PROPERTY(QuickStandardMinimizeButton* minimizeButton READ minimizeButton CONSTANT FINAL)
Q_PROPERTY(QuickStandardMaximizeButton* maximizeButton READ maximizeButton CONSTANT FINAL) Q_PROPERTY(QuickStandardMaximizeButton* maximizeButton READ maximizeButton CONSTANT FINAL)
Q_PROPERTY(QuickStandardCloseButton* closeButton READ closeButton CONSTANT FINAL) Q_PROPERTY(QuickStandardCloseButton* closeButton READ closeButton CONSTANT FINAL)
@ -59,6 +60,7 @@ public:
Q_NODISCARD Qt::Alignment titleLabelAlignment() const; Q_NODISCARD Qt::Alignment titleLabelAlignment() const;
void setTitleLabelAlignment(const Qt::Alignment value); void setTitleLabelAlignment(const Qt::Alignment value);
Q_NODISCARD QQuickLabel *titleLabel() const;
Q_NODISCARD QuickStandardMinimizeButton *minimizeButton() const; Q_NODISCARD QuickStandardMinimizeButton *minimizeButton() const;
Q_NODISCARD QuickStandardMaximizeButton *maximizeButton() const; Q_NODISCARD QuickStandardMaximizeButton *maximizeButton() const;
Q_NODISCARD QuickStandardCloseButton *closeButton() const; Q_NODISCARD QuickStandardCloseButton *closeButton() const;
@ -68,6 +70,7 @@ public:
protected: protected:
void itemChange(const ItemChange change, const ItemChangeData &value) override; void itemChange(const ItemChange change, const ItemChangeData &value) override;
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
private Q_SLOTS: private Q_SLOTS:
void updateMaximizeButton(); void updateMaximizeButton();
@ -76,6 +79,7 @@ private Q_SLOTS:
void clickMinimizeButton(); void clickMinimizeButton();
void clickMaximizeButton(); void clickMaximizeButton();
void clickCloseButton(); void clickCloseButton();
void retranslateUi();
Q_SIGNALS: Q_SIGNALS:
void titleLabelAlignmentChanged(); void titleLabelAlignmentChanged();
@ -87,11 +91,11 @@ private:
private: private:
Qt::Alignment m_labelAlignment = {}; Qt::Alignment m_labelAlignment = {};
QScopedPointer<QQuickLabel> m_label; QScopedPointer<QQuickLabel> m_windowTitleLabel;
QScopedPointer<QQuickRow> m_row; QScopedPointer<QQuickRow> m_systemButtonsRow;
QScopedPointer<QuickStandardMinimizeButton> m_minBtn; QScopedPointer<QuickStandardMinimizeButton> m_minimizeButton;
QScopedPointer<QuickStandardMaximizeButton> m_maxBtn; QScopedPointer<QuickStandardMaximizeButton> m_maximizeButton;
QScopedPointer<QuickStandardCloseButton> m_closeBtn; QScopedPointer<QuickStandardCloseButton> m_closeButton;
QMetaObject::Connection m_windowStateChangeConnection = {}; QMetaObject::Connection m_windowStateChangeConnection = {};
QMetaObject::Connection m_windowActiveChangeConnection = {}; QMetaObject::Connection m_windowActiveChangeConnection = {};
QMetaObject::Connection m_windowTitleChangeConnection = {}; QMetaObject::Connection m_windowTitleChangeConnection = {};

View File

@ -30,6 +30,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QLabel; class QLabel;
class QSpacerItem;
QT_END_NAMESPACE QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
@ -50,12 +51,16 @@ public:
Q_NODISCARD static StandardTitleBarPrivate *get(StandardTitleBar *pub); Q_NODISCARD static StandardTitleBarPrivate *get(StandardTitleBar *pub);
Q_NODISCARD static const StandardTitleBarPrivate *get(const StandardTitleBar *pub); Q_NODISCARD static const StandardTitleBarPrivate *get(const StandardTitleBar *pub);
Q_NODISCARD Qt::Alignment titleLabelAlignment() const;
void setTitleLabelAlignment(const Qt::Alignment value);
Q_NODISCARD bool isExtended() const; Q_NODISCARD bool isExtended() const;
void setExtended(const bool value); void setExtended(const bool value);
public Q_SLOTS: public Q_SLOTS:
void updateMaximizeButton(); void updateMaximizeButton();
void updateTitleBarStyleSheet(); void updateTitleBarStyleSheet();
void retranslateUi();
protected: protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
@ -71,6 +76,9 @@ private:
QScopedPointer<StandardSystemButton> m_closeButton; QScopedPointer<StandardSystemButton> m_closeButton;
QPointer<QWidget> m_window = nullptr; QPointer<QWidget> m_window = nullptr;
bool m_extended = false; bool m_extended = false;
Qt::Alignment m_labelAlignment = {};
QSpacerItem *m_labelLeftStretch = nullptr;
QSpacerItem *m_labelRightStretch = nullptr;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -26,6 +26,7 @@
#include "framelesshelperwidgets_global.h" #include "framelesshelperwidgets_global.h"
#include <QtWidgets/qwidget.h> #include <QtWidgets/qwidget.h>
#include <QtWidgets/qlabel.h>
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
@ -37,6 +38,8 @@ class FRAMELESSHELPER_WIDGETS_API StandardTitleBar : public QWidget
Q_OBJECT Q_OBJECT
Q_DECLARE_PRIVATE(StandardTitleBar) Q_DECLARE_PRIVATE(StandardTitleBar)
Q_DISABLE_COPY_MOVE(StandardTitleBar) Q_DISABLE_COPY_MOVE(StandardTitleBar)
Q_PROPERTY(Qt::Alignment titleLabelAlignment READ titleLabelAlignment WRITE setTitleLabelAlignment NOTIFY titleLabelAlignmentChanged FINAL)
Q_PROPERTY(QLabel* titleLabel READ titleLabel CONSTANT FINAL)
Q_PROPERTY(StandardSystemButton* minimizeButton READ minimizeButton CONSTANT FINAL) Q_PROPERTY(StandardSystemButton* minimizeButton READ minimizeButton CONSTANT FINAL)
Q_PROPERTY(StandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL) Q_PROPERTY(StandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL)
Q_PROPERTY(StandardSystemButton* closeButton READ closeButton CONSTANT FINAL) Q_PROPERTY(StandardSystemButton* closeButton READ closeButton CONSTANT FINAL)
@ -46,6 +49,10 @@ public:
explicit StandardTitleBar(QWidget *parent = nullptr); explicit StandardTitleBar(QWidget *parent = nullptr);
~StandardTitleBar() override; ~StandardTitleBar() override;
Q_NODISCARD Qt::Alignment titleLabelAlignment() const;
void setTitleLabelAlignment(const Qt::Alignment value);
Q_NODISCARD QLabel *titleLabel() const;
Q_NODISCARD StandardSystemButton *minimizeButton() const; Q_NODISCARD StandardSystemButton *minimizeButton() const;
Q_NODISCARD StandardSystemButton *maximizeButton() const; Q_NODISCARD StandardSystemButton *maximizeButton() const;
Q_NODISCARD StandardSystemButton *closeButton() const; Q_NODISCARD StandardSystemButton *closeButton() const;
@ -58,6 +65,7 @@ protected:
Q_SIGNALS: Q_SIGNALS:
void extendedChanged(); void extendedChanged();
void titleLabelAlignmentChanged();
private: private:
QScopedPointer<StandardTitleBarPrivate> d_ptr; QScopedPointer<StandardTitleBarPrivate> d_ptr;

View File

@ -103,31 +103,60 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
const Win32HelperData data = g_win32Helper()->data.value(parentWindowId); const Win32HelperData data = g_win32Helper()->data.value(parentWindowId);
g_win32Helper()->mutex.unlock(); g_win32Helper()->mutex.unlock();
const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId); const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId);
// All mouse events: client area mouse events + non-client area mouse events.
// Hit-testing event should not be considered as a mouse event.
const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) ||
((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK)));
// We only use this drag bar window to activate the snap layouts feature, if the parent // We only use this drag bar window to activate the snap layouts feature, if the parent
// window is not resizable, the snap layouts feature should also be disabled at the same time, // window is not resizable, the snap layouts feature should also be disabled at the same time,
// hence forward everything to the parent window, we don't need to handle anything here. // hence forward everything to the parent window, we don't need to handle anything here.
if (data.params.isWindowFixedSize()) { if (data.params.isWindowFixedSize()) {
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); // Ask the parent window for the hit test result and returns it here, to
// let our homemade title bar still draggable.
if (uMsg == WM_NCHITTEST) {
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
}
// Forward all mouse events to the parent window to let the controls inside
// our homemade title bar still continue to work normally. But ignore these
// events in this drag bar window due to there are no controls in it.
if (isMouseEvent) {
SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
return 0;
}
// For all other events just use the default handling.
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
} }
const auto releaseButtons = [&data]() -> void { const auto releaseButtons = [&data](const std::optional<SystemButtonType> exclude) -> void {
static constexpr const auto defaultButtonState = ButtonState::Unspecified; static constexpr const auto defaultButtonState = ButtonState::Unspecified;
data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState); if (!exclude.has_value() || (exclude.value() != SystemButtonType::WindowIcon)) {
data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState); }
data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState); if (!exclude.has_value() || (exclude.value() != SystemButtonType::Help)) {
data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState); data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState);
data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState); }
if (!exclude.has_value() || (exclude.value() != SystemButtonType::Minimize)) {
data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState);
}
if (!exclude.has_value() || (exclude.value() != SystemButtonType::Maximize)) {
data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState);
}
if (!exclude.has_value() || (exclude.value() != SystemButtonType::Restore)) {
data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState);
}
if (!exclude.has_value() || (exclude.value() != SystemButtonType::Close)) {
data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState);
}
}; };
const auto hoverButton = [&releaseButtons, &data](const SystemButtonType button) -> void { const auto hoverButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
releaseButtons(); releaseButtons(button);
data.params.setSystemButtonState(button, ButtonState::Hovered); data.params.setSystemButtonState(button, ButtonState::Hovered);
}; };
const auto pressButton = [&releaseButtons, &data](const SystemButtonType button) -> void { const auto pressButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
releaseButtons(); releaseButtons(button);
data.params.setSystemButtonState(button, ButtonState::Pressed); data.params.setSystemButtonState(button, ButtonState::Pressed);
}; };
const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void { const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
releaseButtons(); releaseButtons(button);
data.params.setSystemButtonState(button, ButtonState::Clicked); data.params.setSystemButtonState(button, ButtonState::Clicked);
}; };
switch (uMsg) { switch (uMsg) {
@ -177,7 +206,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
switch (wParam) { switch (wParam) {
case HTTOP: case HTTOP:
case HTCAPTION: { case HTCAPTION: {
releaseButtons(); releaseButtons(std::nullopt);
// Pass caption-related nonclient messages to the parent window. // Pass caption-related nonclient messages to the parent window.
// Make sure to do this for the HTTOP, which is the top resize // Make sure to do this for the HTTOP, which is the top resize
// border, so we can resize the window on the top. // border, so we can resize the window on the top.
@ -199,7 +228,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
hoverButton(SystemButtonType::Close); hoverButton(SystemButtonType::Close);
break; break;
default: default:
releaseButtons(); releaseButtons(std::nullopt);
break; break;
} }
// If we haven't previously asked for mouse tracking, request mouse // If we haven't previously asked for mouse tracking, request mouse
@ -230,7 +259,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
case WM_NCMOUSELEAVE: case WM_NCMOUSELEAVE:
case WM_MOUSELEAVE: { case WM_MOUSELEAVE: {
// When the mouse leaves the drag rect, make sure to dismiss any hover. // When the mouse leaves the drag rect, make sure to dismiss any hover.
releaseButtons(); releaseButtons(std::nullopt);
QMutexLocker locker(&g_win32Helper()->mutex); QMutexLocker locker(&g_win32Helper()->mutex);
g_win32Helper()->data[parentWindowId].trackingMouse = false; g_win32Helper()->data[parentWindowId].trackingMouse = false;
} break; } break;
@ -314,11 +343,10 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
default: default:
break; break;
} }
// Forward all the mouse events we don't handled here to the parent window, // Forward all the mouse events we don't handle here to the parent window,
// this is a necessary step to make sure the child widgets/quick items can still // this is a necessary step to make sure the child widgets/quick items can still
// receive mouse events from our homemade title bar. // receive mouse events from our homemade title bar.
if (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) || if (isMouseEvent) {
((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))) {
SendMessageW(parentWindowHandle, uMsg, wParam, lParam); SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
} }
return DefWindowProcW(hWnd, uMsg, wParam, lParam); return DefWindowProcW(hWnd, uMsg, wParam, lParam);

View File

@ -29,6 +29,7 @@
#include <QtQuick/private/qquickimage_p.h> #include <QtQuick/private/qquickimage_p.h>
#include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
static inline void initResource() static inline void initResource()
{ {
@ -60,15 +61,11 @@ void QuickStandardCloseButton::updateForeground()
void QuickStandardCloseButton::updateBackground() void QuickStandardCloseButton::updateBackground()
{ {
static constexpr const auto button = SystemButtonType::Close; static constexpr const auto button = SystemButtonType::Close;
const ButtonState state = (isPressed() ? ButtonState::Pressed : ButtonState::Hovered); const bool hover = isHovered();
const bool visible = (isHovered() || isPressed()); const bool press = isPressed();
m_backgroundItem->setColor(Utils::calculateSystemButtonBackgroundColor(button, state)); m_backgroundItem->setColor(Utils::calculateSystemButtonBackgroundColor(button, (press ? ButtonState::Pressed : ButtonState::Hovered)));
m_backgroundItem->setVisible(visible); m_backgroundItem->setVisible(hover || press);
} qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(this))->setVisible(hover);
void QuickStandardCloseButton::updateToolTip()
{
m_tooltip->setVisible(isHovered() && !isPressed());
} }
void QuickStandardCloseButton::initialize() void QuickStandardCloseButton::initialize()
@ -93,15 +90,8 @@ void QuickStandardCloseButton::initialize()
connect(this, &QuickStandardCloseButton::hoveredChanged, this, &QuickStandardCloseButton::updateBackground); connect(this, &QuickStandardCloseButton::hoveredChanged, this, &QuickStandardCloseButton::updateBackground);
connect(this, &QuickStandardCloseButton::pressedChanged, this, &QuickStandardCloseButton::updateBackground); connect(this, &QuickStandardCloseButton::pressedChanged, this, &QuickStandardCloseButton::updateBackground);
m_tooltip = qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(this));
m_tooltip->setText(tr("Close"));
m_tooltip->setDelay(0);
connect(this, &QuickStandardCloseButton::hoveredChanged, this, &QuickStandardCloseButton::updateToolTip);
connect(this, &QuickStandardCloseButton::pressedChanged, this, &QuickStandardCloseButton::updateToolTip);
updateBackground(); updateBackground();
updateForeground(); updateForeground();
updateToolTip();
setContentItem(m_contentItem.data()); setContentItem(m_contentItem.data());
setBackground(m_backgroundItem.data()); setBackground(m_backgroundItem.data());

View File

@ -29,6 +29,7 @@
#include <QtQuick/private/qquickimage_p.h> #include <QtQuick/private/qquickimage_p.h>
#include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
static inline void initResource() static inline void initResource()
{ {
@ -76,16 +77,11 @@ void QuickStandardMaximizeButton::updateForeground()
void QuickStandardMaximizeButton::updateBackground() void QuickStandardMaximizeButton::updateBackground()
{ {
const SystemButtonType button = (m_max ? SystemButtonType::Restore : SystemButtonType::Maximize); const SystemButtonType button = (m_max ? SystemButtonType::Restore : SystemButtonType::Maximize);
const ButtonState state = (isPressed() ? ButtonState::Pressed : ButtonState::Hovered); const bool hover = isHovered();
const bool visible = (isHovered() || isPressed()); const bool press = isPressed();
m_backgroundItem->setColor(Utils::calculateSystemButtonBackgroundColor(button, state)); m_backgroundItem->setColor(Utils::calculateSystemButtonBackgroundColor(button, (press ? ButtonState::Pressed : ButtonState::Hovered)));
m_backgroundItem->setVisible(visible); m_backgroundItem->setVisible(hover || press);
} qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(this))->setVisible(hover);
void QuickStandardMaximizeButton::updateToolTip()
{
m_tooltip->setVisible(isHovered() && !isPressed());
m_tooltip->setText(m_max ? tr("Restore") : tr("Maximize"));
} }
void QuickStandardMaximizeButton::initialize() void QuickStandardMaximizeButton::initialize()
@ -109,15 +105,8 @@ void QuickStandardMaximizeButton::initialize()
connect(this, &QuickStandardMaximizeButton::hoveredChanged, this, &QuickStandardMaximizeButton::updateBackground); connect(this, &QuickStandardMaximizeButton::hoveredChanged, this, &QuickStandardMaximizeButton::updateBackground);
connect(this, &QuickStandardMaximizeButton::pressedChanged, this, &QuickStandardMaximizeButton::updateBackground); connect(this, &QuickStandardMaximizeButton::pressedChanged, this, &QuickStandardMaximizeButton::updateBackground);
m_tooltip = qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(this));
m_tooltip->setDelay(0);
connect(this, &QuickStandardMaximizeButton::hoveredChanged, this, &QuickStandardMaximizeButton::updateToolTip);
connect(this, &QuickStandardMaximizeButton::pressedChanged, this, &QuickStandardMaximizeButton::updateToolTip);
connect(this, &QuickStandardMaximizeButton::maximizedChanged, this, &QuickStandardMaximizeButton::updateToolTip);
updateBackground(); updateBackground();
updateForeground(); updateForeground();
updateToolTip();
setContentItem(m_contentItem.data()); setContentItem(m_contentItem.data());
setBackground(m_backgroundItem.data()); setBackground(m_backgroundItem.data());

View File

@ -29,6 +29,7 @@
#include <QtQuick/private/qquickimage_p.h> #include <QtQuick/private/qquickimage_p.h>
#include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p.h>
#include <QtQuickTemplates2/private/qquicktooltip_p.h>
static inline void initResource() static inline void initResource()
{ {
@ -60,15 +61,11 @@ void QuickStandardMinimizeButton::updateForeground()
void QuickStandardMinimizeButton::updateBackground() void QuickStandardMinimizeButton::updateBackground()
{ {
static constexpr const auto button = SystemButtonType::Minimize; static constexpr const auto button = SystemButtonType::Minimize;
const ButtonState state = (isPressed() ? ButtonState::Pressed : ButtonState::Hovered); const bool hover = isHovered();
const bool visible = (isHovered() || isPressed()); const bool press = isPressed();
m_backgroundItem->setColor(Utils::calculateSystemButtonBackgroundColor(button, state)); m_backgroundItem->setColor(Utils::calculateSystemButtonBackgroundColor(button, (press ? ButtonState::Pressed : ButtonState::Hovered)));
m_backgroundItem->setVisible(visible); m_backgroundItem->setVisible(hover || press);
} qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(this))->setVisible(hover);
void QuickStandardMinimizeButton::updateToolTip()
{
m_tooltip->setVisible(isHovered() && !isPressed());
} }
void QuickStandardMinimizeButton::initialize() void QuickStandardMinimizeButton::initialize()
@ -91,15 +88,8 @@ void QuickStandardMinimizeButton::initialize()
connect(this, &QuickStandardMinimizeButton::hoveredChanged, this, &QuickStandardMinimizeButton::updateBackground); connect(this, &QuickStandardMinimizeButton::hoveredChanged, this, &QuickStandardMinimizeButton::updateBackground);
connect(this, &QuickStandardMinimizeButton::pressedChanged, this, &QuickStandardMinimizeButton::updateBackground); connect(this, &QuickStandardMinimizeButton::pressedChanged, this, &QuickStandardMinimizeButton::updateBackground);
m_tooltip = qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(this));
m_tooltip->setText(tr("Minimize"));
m_tooltip->setDelay(0);
connect(this, &QuickStandardMinimizeButton::hoveredChanged, this, &QuickStandardMinimizeButton::updateToolTip);
connect(this, &QuickStandardMinimizeButton::pressedChanged, this, &QuickStandardMinimizeButton::updateToolTip);
updateBackground(); updateBackground();
updateForeground(); updateForeground();
updateToolTip();
setContentItem(m_contentItem.data()); setContentItem(m_contentItem.data());
setBackground(m_backgroundItem.data()); setBackground(m_backgroundItem.data());

View File

@ -33,7 +33,7 @@
#include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p.h>
#include <QtQuick/private/qquickanchors_p_p.h> #include <QtQuick/private/qquickanchors_p_p.h>
#include <QtQuick/private/qquickpositioners_p.h> #include <QtQuick/private/qquickpositioners_p.h>
#include <QtQuickTemplates2/private/qquicklabel_p.h> #include <QtQuickTemplates2/private/qquicktooltip_p.h>
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
@ -57,7 +57,7 @@ void QuickStandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value)
return; return;
} }
m_labelAlignment = value; m_labelAlignment = value;
QQuickAnchors * const labelAnchors = QQuickItemPrivate::get(m_label.data())->anchors(); QQuickAnchors * const labelAnchors = QQuickItemPrivate::get(m_windowTitleLabel.data())->anchors();
const QQuickItemPrivate * const titleBarPriv = QQuickItemPrivate::get(this); const QQuickItemPrivate * const titleBarPriv = QQuickItemPrivate::get(this);
if (m_labelAlignment & Qt::AlignTop) { if (m_labelAlignment & Qt::AlignTop) {
labelAnchors->setTop(titleBarPriv->top()); labelAnchors->setTop(titleBarPriv->top());
@ -72,7 +72,7 @@ void QuickStandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value)
labelAnchors->setLeftMargin(kDefaultTitleBarContentsMargin); labelAnchors->setLeftMargin(kDefaultTitleBarContentsMargin);
} }
if (m_labelAlignment & Qt::AlignRight) { if (m_labelAlignment & Qt::AlignRight) {
labelAnchors->setRight(QQuickItemPrivate::get(m_row.data())->left()); labelAnchors->setRight(QQuickItemPrivate::get(m_systemButtonsRow.data())->left());
labelAnchors->setRightMargin(kDefaultTitleBarContentsMargin); labelAnchors->setRightMargin(kDefaultTitleBarContentsMargin);
} }
if (m_labelAlignment & Qt::AlignVCenter) { if (m_labelAlignment & Qt::AlignVCenter) {
@ -88,19 +88,24 @@ void QuickStandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value)
Q_EMIT titleLabelAlignmentChanged(); Q_EMIT titleLabelAlignmentChanged();
} }
QQuickLabel *QuickStandardTitleBar::titleLabel() const
{
return m_windowTitleLabel.data();
}
QuickStandardMinimizeButton *QuickStandardTitleBar::minimizeButton() const QuickStandardMinimizeButton *QuickStandardTitleBar::minimizeButton() const
{ {
return m_minBtn.data(); return m_minimizeButton.data();
} }
QuickStandardMaximizeButton *QuickStandardTitleBar::maximizeButton() const QuickStandardMaximizeButton *QuickStandardTitleBar::maximizeButton() const
{ {
return m_maxBtn.data(); return m_maximizeButton.data();
} }
QuickStandardCloseButton *QuickStandardTitleBar::closeButton() const QuickStandardCloseButton *QuickStandardTitleBar::closeButton() const
{ {
return m_closeBtn.data(); return m_closeButton.data();
} }
bool QuickStandardTitleBar::isExtended() const bool QuickStandardTitleBar::isExtended() const
@ -124,7 +129,15 @@ void QuickStandardTitleBar::updateMaximizeButton()
if (!w) { if (!w) {
return; return;
} }
m_maxBtn->setMaximized(w->visibility() == QQuickWindow::Maximized); m_maximizeButton->setMaximized(w->visibility() == QQuickWindow::Maximized);
qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(m_maximizeButton.data()))->setText([this]() -> QString {
if (const QQuickWindow * const w = window()) {
if (w->visibility() == QQuickWindow::Maximized) {
return tr("Restore");
}
}
return tr("Maximize");
}());
} }
void QuickStandardTitleBar::updateTitleLabelText() void QuickStandardTitleBar::updateTitleLabelText()
@ -133,7 +146,7 @@ void QuickStandardTitleBar::updateTitleLabelText()
if (!w) { if (!w) {
return; return;
} }
m_label->setText(w->title()); m_windowTitleLabel->setText(w->title());
} }
void QuickStandardTitleBar::updateTitleBarColor() void QuickStandardTitleBar::updateTitleBarColor()
@ -174,7 +187,7 @@ void QuickStandardTitleBar::updateTitleBarColor()
foregroundColor = kDefaultDarkGrayColor; foregroundColor = kDefaultDarkGrayColor;
} }
setColor(backgroundColor); setColor(backgroundColor);
m_label->setColor(foregroundColor); m_windowTitleLabel->setColor(foregroundColor);
} }
void QuickStandardTitleBar::clickMinimizeButton() void QuickStandardTitleBar::clickMinimizeButton()
@ -208,6 +221,20 @@ void QuickStandardTitleBar::clickCloseButton()
w->close(); w->close();
} }
void QuickStandardTitleBar::retranslateUi()
{
qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(m_minimizeButton.data()))->setText(tr("Minimize"));
qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(m_maximizeButton.data()))->setText([this]() -> QString {
if (const QQuickWindow * const w = window()) {
if (w->visibility() == QQuickWindow::Maximized) {
return tr("Restore");
}
}
return tr("Maximize");
}());
qobject_cast<QQuickToolTipAttached *>(qmlAttachedPropertiesObject<QQuickToolTip>(m_closeButton.data()))->setText(tr("Close"));
}
void QuickStandardTitleBar::initialize() void QuickStandardTitleBar::initialize()
{ {
QQuickPen * const b = border(); QQuickPen * const b = border();
@ -215,27 +242,27 @@ void QuickStandardTitleBar::initialize()
b->setColor(kDefaultTransparentColor); b->setColor(kDefaultTransparentColor);
setHeight(kDefaultTitleBarHeight); setHeight(kDefaultTitleBarHeight);
m_label.reset(new QQuickLabel(this)); m_windowTitleLabel.reset(new QQuickLabel(this));
QFont f = m_label->font(); QFont f = m_windowTitleLabel->font();
f.setPointSize(kDefaultTitleBarFontPointSize); f.setPointSize(kDefaultTitleBarFontPointSize);
m_label->setFont(f); m_windowTitleLabel->setFont(f);
m_row.reset(new QQuickRow(this)); m_systemButtonsRow.reset(new QQuickRow(this));
QQuickAnchors * const rowAnchors = QQuickItemPrivate::get(m_row.data())->anchors(); QQuickAnchors * const rowAnchors = QQuickItemPrivate::get(m_systemButtonsRow.data())->anchors();
const QQuickItemPrivate * const thisPriv = QQuickItemPrivate::get(this); const QQuickItemPrivate * const thisPriv = QQuickItemPrivate::get(this);
rowAnchors->setTop(thisPriv->top()); rowAnchors->setTop(thisPriv->top());
rowAnchors->setRight(thisPriv->right()); rowAnchors->setRight(thisPriv->right());
m_minBtn.reset(new QuickStandardMinimizeButton(m_row.data())); m_minimizeButton.reset(new QuickStandardMinimizeButton(m_systemButtonsRow.data()));
connect(m_minBtn.data(), &QuickStandardMinimizeButton::clicked, this, &QuickStandardTitleBar::clickMinimizeButton); connect(m_minimizeButton.data(), &QuickStandardMinimizeButton::clicked, this, &QuickStandardTitleBar::clickMinimizeButton);
m_maxBtn.reset(new QuickStandardMaximizeButton(m_row.data())); m_maximizeButton.reset(new QuickStandardMaximizeButton(m_systemButtonsRow.data()));
connect(m_maxBtn.data(), &QuickStandardMaximizeButton::clicked, this, &QuickStandardTitleBar::clickMaximizeButton); connect(m_maximizeButton.data(), &QuickStandardMaximizeButton::clicked, this, &QuickStandardTitleBar::clickMaximizeButton);
m_closeBtn.reset(new QuickStandardCloseButton(m_row.data())); m_closeButton.reset(new QuickStandardCloseButton(m_systemButtonsRow.data()));
connect(m_closeBtn.data(), &QuickStandardCloseButton::clicked, this, &QuickStandardTitleBar::clickCloseButton); connect(m_closeButton.data(), &QuickStandardCloseButton::clicked, this, &QuickStandardTitleBar::clickCloseButton);
connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &QuickStandardTitleBar::updateTitleBarColor); connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &QuickStandardTitleBar::updateTitleBarColor);
setTitleLabelAlignment(Qt::AlignLeft | Qt::AlignVCenter); setTitleLabelAlignment(Qt::AlignLeft | Qt::AlignVCenter);
retranslateUi();
updateAll(); updateAll();
} }
@ -245,20 +272,32 @@ void QuickStandardTitleBar::itemChange(const ItemChange change, const ItemChange
if ((change == ItemSceneChange) && value.window) { if ((change == ItemSceneChange) && value.window) {
if (m_windowStateChangeConnection) { if (m_windowStateChangeConnection) {
disconnect(m_windowStateChangeConnection); disconnect(m_windowStateChangeConnection);
m_windowStateChangeConnection = {};
} }
if (m_windowActiveChangeConnection) { if (m_windowActiveChangeConnection) {
disconnect(m_windowActiveChangeConnection); disconnect(m_windowActiveChangeConnection);
m_windowActiveChangeConnection = {};
} }
if (m_windowTitleChangeConnection) { if (m_windowTitleChangeConnection) {
disconnect(m_windowTitleChangeConnection); disconnect(m_windowTitleChangeConnection);
m_windowTitleChangeConnection = {};
} }
m_windowStateChangeConnection = connect(value.window, &QQuickWindow::visibilityChanged, this, &QuickStandardTitleBar::updateMaximizeButton); m_windowStateChangeConnection = connect(value.window, &QQuickWindow::visibilityChanged, this, &QuickStandardTitleBar::updateMaximizeButton);
m_windowActiveChangeConnection = connect(value.window, &QQuickWindow::activeChanged, this, &QuickStandardTitleBar::updateTitleBarColor); m_windowActiveChangeConnection = connect(value.window, &QQuickWindow::activeChanged, this, &QuickStandardTitleBar::updateTitleBarColor);
m_windowTitleChangeConnection = connect(value.window, &QQuickWindow::windowTitleChanged, this, &QuickStandardTitleBar::updateTitleLabelText); m_windowTitleChangeConnection = connect(value.window, &QQuickWindow::windowTitleChanged, this, &QuickStandardTitleBar::updateTitleLabelText);
updateAll(); updateAll();
value.window->installEventFilter(this);
} }
} }
bool QuickStandardTitleBar::eventFilter(QObject *object, QEvent *event)
{
if (event && (event->type() == QEvent::LanguageChange)) {
retranslateUi();
}
return QQuickRectangle::eventFilter(object, event);
}
void QuickStandardTitleBar::updateAll() void QuickStandardTitleBar::updateAll()
{ {
updateMaximizeButton(); updateMaximizeButton();

View File

@ -26,6 +26,7 @@
#include "standardsystembutton_p.h" #include "standardsystembutton_p.h"
#include <QtCore/qvariant.h> #include <QtCore/qvariant.h>
#include <QtGui/qpainter.h> #include <QtGui/qpainter.h>
#include <QtWidgets/qtooltip.h>
#include <framelessmanager.h> #include <framelessmanager.h>
#include <utils.h> #include <utils.h>
@ -200,6 +201,16 @@ void StandardSystemButtonPrivate::setHovered(const bool value)
m_hovered = value; m_hovered = value;
Q_Q(StandardSystemButton); Q_Q(StandardSystemButton);
q->update(); q->update();
if (m_hovered) {
const QString toolTip = q->toolTip();
if (!toolTip.isEmpty() && !QToolTip::isVisible()) {
QToolTip::showText(q->mapToGlobal(QPoint(0, -(qRound(qreal(q->height()) * 1.3)))), toolTip, q, q->geometry());
}
} else {
if (QToolTip::isVisible()) {
QToolTip::hideText();
}
}
Q_EMIT q->hoveredChanged(); Q_EMIT q->hoveredChanged();
} }

View File

@ -71,6 +71,40 @@ const StandardTitleBarPrivate *StandardTitleBarPrivate::get(const StandardTitleB
return pub->d_func(); return pub->d_func();
} }
Qt::Alignment StandardTitleBarPrivate::titleLabelAlignment() const
{
return m_labelAlignment;
}
void StandardTitleBarPrivate::setTitleLabelAlignment(const Qt::Alignment value)
{
if (m_labelAlignment == value) {
return;
}
m_labelAlignment = value;
bool needsInvalidate = false;
if (m_labelAlignment & Qt::AlignLeft) {
m_labelLeftStretch->changeSize(0, 0);
m_labelRightStretch->changeSize(0, 0, QSizePolicy::Expanding);
needsInvalidate = true;
}
if (m_labelAlignment & Qt::AlignRight) {
m_labelLeftStretch->changeSize(0, 0, QSizePolicy::Expanding);
m_labelRightStretch->changeSize(0, 0);
needsInvalidate = true;
}
if (m_labelAlignment & Qt::AlignHCenter) {
m_labelLeftStretch->changeSize(0, 0, QSizePolicy::Expanding);
m_labelRightStretch->changeSize(0, 0, QSizePolicy::Expanding);
needsInvalidate = true;
}
Q_Q(StandardTitleBar);
if (needsInvalidate) {
q->layout()->invalidate();
}
Q_EMIT q->titleLabelAlignmentChanged();
}
bool StandardTitleBarPrivate::isExtended() const bool StandardTitleBarPrivate::isExtended() const
{ {
return m_extended; return m_extended;
@ -89,9 +123,9 @@ void StandardTitleBarPrivate::setExtended(const bool value)
void StandardTitleBarPrivate::updateMaximizeButton() void StandardTitleBarPrivate::updateMaximizeButton()
{ {
const bool zoomed = (m_window->isMaximized() || m_window->isFullScreen()); const bool max = m_window->isMaximized();
m_maximizeButton->setToolTip(zoomed ? tr("Restore") : tr("Maximize")); m_maximizeButton->setButtonType(max ? SystemButtonType::Restore : SystemButtonType::Maximize);
m_maximizeButton->setButtonType(zoomed ? SystemButtonType::Restore : SystemButtonType::Maximize); m_maximizeButton->setToolTip(max ? tr("Restore") : tr("Maximize"));
} }
void StandardTitleBarPrivate::updateTitleBarStyleSheet() void StandardTitleBarPrivate::updateTitleBarStyleSheet()
@ -133,6 +167,13 @@ void StandardTitleBarPrivate::updateTitleBarStyleSheet()
q->update(); q->update();
} }
void StandardTitleBarPrivate::retranslateUi()
{
m_minimizeButton->setToolTip(tr("Minimize"));
m_maximizeButton->setToolTip(m_window->isMaximized() ? tr("Restore") : tr("Maximize"));
m_closeButton->setToolTip(tr("Close"));
}
bool StandardTitleBarPrivate::eventFilter(QObject *object, QEvent *event) bool StandardTitleBarPrivate::eventFilter(QObject *object, QEvent *event)
{ {
Q_ASSERT(object); Q_ASSERT(object);
@ -154,6 +195,9 @@ bool StandardTitleBarPrivate::eventFilter(QObject *object, QEvent *event)
case QEvent::ActivationChange: case QEvent::ActivationChange:
updateTitleBarStyleSheet(); updateTitleBarStyleSheet();
break; break;
case QEvent::LanguageChange:
retranslateUi();
break;
default: default:
break; break;
} }
@ -174,7 +218,6 @@ void StandardTitleBarPrivate::initialize()
m_windowTitleLabel->setText(m_window->windowTitle()); m_windowTitleLabel->setText(m_window->windowTitle());
connect(m_window, &QWidget::windowTitleChanged, m_windowTitleLabel.data(), &QLabel::setText); connect(m_window, &QWidget::windowTitleChanged, m_windowTitleLabel.data(), &QLabel::setText);
m_minimizeButton.reset(new StandardSystemButton(SystemButtonType::Minimize, q)); m_minimizeButton.reset(new StandardSystemButton(SystemButtonType::Minimize, q));
m_minimizeButton->setToolTip(tr("Minimize"));
connect(m_minimizeButton.data(), &StandardSystemButton::clicked, m_window, &QWidget::showMinimized); connect(m_minimizeButton.data(), &StandardSystemButton::clicked, m_window, &QWidget::showMinimized);
m_maximizeButton.reset(new StandardSystemButton(SystemButtonType::Maximize, q)); m_maximizeButton.reset(new StandardSystemButton(SystemButtonType::Maximize, q));
updateMaximizeButton(); updateMaximizeButton();
@ -186,8 +229,15 @@ void StandardTitleBarPrivate::initialize()
} }
}); });
m_closeButton.reset(new StandardSystemButton(SystemButtonType::Close, q)); m_closeButton.reset(new StandardSystemButton(SystemButtonType::Close, q));
m_closeButton->setToolTip(tr("Close"));
connect(m_closeButton.data(), &StandardSystemButton::clicked, m_window, &QWidget::close); connect(m_closeButton.data(), &StandardSystemButton::clicked, m_window, &QWidget::close);
m_labelLeftStretch = new QSpacerItem(0, 0);
m_labelRightStretch = new QSpacerItem(0, 0);
const auto titleLabelLayout = new QHBoxLayout;
titleLabelLayout->setSpacing(0);
titleLabelLayout->setContentsMargins(0, 0, 0, 0);
titleLabelLayout->addSpacerItem(m_labelLeftStretch);
titleLabelLayout->addWidget(m_windowTitleLabel.data());
titleLabelLayout->addSpacerItem(m_labelRightStretch);
// According to the title bar design guidance, the system buttons should always be // According to the title bar design guidance, the system buttons should always be
// placed on the top-right corner of the window, so we need the following additional // placed on the top-right corner of the window, so we need the following additional
// layouts to ensure this. // layouts to ensure this.
@ -205,11 +255,12 @@ void StandardTitleBarPrivate::initialize()
const auto titleBarLayout = new QHBoxLayout(q); const auto titleBarLayout = new QHBoxLayout(q);
titleBarLayout->setContentsMargins(0, 0, 0, 0); titleBarLayout->setContentsMargins(0, 0, 0, 0);
titleBarLayout->setSpacing(0); titleBarLayout->setSpacing(0);
titleBarLayout->addSpacerItem(new QSpacerItem(kDefaultTitleBarContentsMargin, kDefaultTitleBarContentsMargin)); titleBarLayout->addSpacerItem(new QSpacerItem(kDefaultTitleBarContentsMargin, 0, QSizePolicy::Fixed, QSizePolicy::Fixed));
titleBarLayout->addWidget(m_windowTitleLabel.data()); titleBarLayout->addLayout(titleLabelLayout);
titleBarLayout->addStretch();
titleBarLayout->addLayout(systemButtonsOuterLayout); titleBarLayout->addLayout(systemButtonsOuterLayout);
q->setLayout(titleBarLayout); q->setLayout(titleBarLayout);
setTitleLabelAlignment(Qt::AlignLeft | Qt::AlignVCenter);
retranslateUi();
updateTitleBarStyleSheet(); updateTitleBarStyleSheet();
connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged,
this, &StandardTitleBarPrivate::updateTitleBarStyleSheet); this, &StandardTitleBarPrivate::updateTitleBarStyleSheet);
@ -223,6 +274,24 @@ StandardTitleBar::StandardTitleBar(QWidget *parent)
StandardTitleBar::~StandardTitleBar() = default; StandardTitleBar::~StandardTitleBar() = default;
Qt::Alignment StandardTitleBar::titleLabelAlignment() const
{
Q_D(const StandardTitleBar);
return d->titleLabelAlignment();
}
void StandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value)
{
Q_D(StandardTitleBar);
d->setTitleLabelAlignment(value);
}
QLabel *StandardTitleBar::titleLabel() const
{
Q_D(const StandardTitleBar);
return d->m_windowTitleLabel.data();
}
StandardSystemButton *StandardTitleBar::minimizeButton() const StandardSystemButton *StandardTitleBar::minimizeButton() const
{ {
Q_D(const StandardTitleBar); Q_D(const StandardTitleBar);