forked from github_mirror/framelesshelper
quick: emulate Windows behavior more, just like widget
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
1b36587f14
commit
47dcb8f032
|
@ -66,6 +66,7 @@ public Q_SLOTS:
|
|||
void setTitleBarItem(QQuickItem *value);
|
||||
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
|
||||
void setHitTestVisible(QQuickItem *item, const bool visible = true);
|
||||
void setHitTestVisible(const QRect &rect, const bool visible = true);
|
||||
|
||||
void showSystemMenu(const QPoint &pos);
|
||||
void windowStartSystemMove2(const QPoint &pos);
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
void attachToWindow();
|
||||
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
|
||||
void setHitTestVisible(QQuickItem *item, const bool visible = true);
|
||||
void setHitTestVisible(const QRect &rect, const bool visible = true);
|
||||
void showSystemMenu(const QPoint &pos);
|
||||
void windowStartSystemMove2(const QPoint &pos);
|
||||
void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos);
|
||||
|
|
|
@ -113,6 +113,10 @@ Q_SIGNALS:
|
|||
private:
|
||||
void initialize();
|
||||
void updateAll();
|
||||
void mouseEventHandler(const QMouseEvent *event);
|
||||
Q_NODISCARD QRect windowIconRect() const;
|
||||
Q_NODISCARD bool isInTitleBarIconArea(const QPoint &pos) const;
|
||||
Q_NODISCARD bool windowIconVisible_real() const;
|
||||
|
||||
private:
|
||||
Qt::Alignment m_labelAlignment = {};
|
||||
|
@ -128,6 +132,7 @@ private:
|
|||
bool m_extended = false;
|
||||
bool m_hideWhenClose = false;
|
||||
QScopedPointer<QuickChromePalette> m_chromePalette;
|
||||
bool m_closeTriggered = false;
|
||||
};
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -61,6 +61,7 @@ struct QuickHelperData
|
|||
QPointer<QQuickItem> minimizeButton = nullptr;
|
||||
QPointer<QQuickItem> maximizeButton = nullptr;
|
||||
QPointer<QQuickItem> closeButton = nullptr;
|
||||
QList<QRect> hitTestVisibleRects = {};
|
||||
};
|
||||
|
||||
struct QuickHelper
|
||||
|
@ -279,6 +280,26 @@ void FramelessQuickHelperPrivate::setHitTestVisible(QQuickItem *item, const bool
|
|||
}
|
||||
}
|
||||
|
||||
void FramelessQuickHelperPrivate::setHitTestVisible(const QRect &rect, const bool visible)
|
||||
{
|
||||
Q_ASSERT(rect.isValid());
|
||||
if (!rect.isValid()) {
|
||||
return;
|
||||
}
|
||||
const QMutexLocker locker(&g_quickHelper()->mutex);
|
||||
QuickHelperData *data = getWindowDataMutable();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const bool exists = data->hitTestVisibleRects.contains(rect);
|
||||
if (visible && !exists) {
|
||||
data->hitTestVisibleRects.append(rect);
|
||||
}
|
||||
if (!visible && exists) {
|
||||
data->hitTestVisibleRects.removeAll(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void FramelessQuickHelperPrivate::showSystemMenu(const QPoint &pos)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
|
@ -620,6 +641,13 @@ bool FramelessQuickHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos) c
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!data.hitTestVisibleRects.isEmpty()) {
|
||||
for (auto &&rect : qAsConst(data.hitTestVisibleRects)) {
|
||||
if (rect.isValid()) {
|
||||
region -= rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
return region.contains(pos);
|
||||
}
|
||||
|
||||
|
@ -856,6 +884,16 @@ void FramelessQuickHelper::setHitTestVisible(QQuickItem *item, const bool visibl
|
|||
d->setHitTestVisible(item, visible);
|
||||
}
|
||||
|
||||
void FramelessQuickHelper::setHitTestVisible(const QRect &rect, const bool visible)
|
||||
{
|
||||
Q_ASSERT(rect.isValid());
|
||||
if (!rect.isValid()) {
|
||||
return;
|
||||
}
|
||||
Q_D(FramelessQuickHelper);
|
||||
d->setHitTestVisible(rect, visible);
|
||||
}
|
||||
|
||||
void FramelessQuickHelper::showSystemMenu(const QPoint &pos)
|
||||
{
|
||||
Q_D(FramelessQuickHelper);
|
||||
|
|
|
@ -25,8 +25,11 @@
|
|||
#include "quickstandardtitlebar_p.h"
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
#include "quickimageitem.h"
|
||||
#include "framelessquickhelper.h"
|
||||
#include "quickstandardsystembutton_p.h"
|
||||
#include "framelessquickwindow_p.h"
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtGui/qevent.h>
|
||||
#include <QtQuick/private/qquickitem_p.h>
|
||||
#include <QtQuick/private/qquickanchors_p.h>
|
||||
#include <QtQuick/private/qquickanchors_p_p.h>
|
||||
|
@ -181,7 +184,6 @@ void QuickStandardTitleBar::setWindowIconSize(const QSizeF &value)
|
|||
m_windowIcon->setWidth(value.width());
|
||||
m_windowIcon->setHeight(value.height());
|
||||
#endif
|
||||
Q_EMIT windowIconSizeChanged();
|
||||
}
|
||||
|
||||
bool QuickStandardTitleBar::windowIconVisible() const
|
||||
|
@ -201,6 +203,7 @@ void QuickStandardTitleBar::setWindowIconVisible(const bool value)
|
|||
} else {
|
||||
labelAnchors->setLeft(QQuickItemPrivate::get(this)->left());
|
||||
}
|
||||
FramelessQuickHelper::get(this)->setHitTestVisible(windowIconRect(), windowIconVisible_real());
|
||||
}
|
||||
|
||||
QVariant QuickStandardTitleBar::windowIcon() const
|
||||
|
@ -354,6 +357,93 @@ void QuickStandardTitleBar::updateWindowIcon()
|
|||
m_windowIcon->setSource(icon);
|
||||
}
|
||||
|
||||
void QuickStandardTitleBar::mouseEventHandler(const QMouseEvent *event)
|
||||
{
|
||||
Q_ASSERT(event);
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
const Qt::MouseButton button = event->button();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
const QPoint scenePos = event->scenePosition().toPoint();
|
||||
#else
|
||||
const QPoint scenePos = event->windowPos().toPoint();
|
||||
#endif
|
||||
const bool interestArea = isInTitleBarIconArea(scenePos);
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseButtonRelease:
|
||||
if (interestArea) {
|
||||
// Sadly the mouse release events are always triggered before the
|
||||
// mouse double click events, and if we intercept the mouse release
|
||||
// events here, we'll never get the double click events afterwards,
|
||||
// so we have to wait for a little bit to make sure the double
|
||||
// click events are handled first, before we actually handle the
|
||||
// mouse release events here.
|
||||
// We need to wait long enough because the time interval between these
|
||||
// events is really really short, if the delay time is not long enough,
|
||||
// we still can't trigger the double click event due to we have handled
|
||||
// the mouse release events here already. But we also can't wait too
|
||||
// long, otherwise the system menu will show up too late, which is not
|
||||
// a good user experience. In my experiments, I found that 150ms is
|
||||
// the minimum value we can use here.
|
||||
// We need a copy of the "scenePos" variable here, otherwise it will
|
||||
// soon fall out of scope when the lambda function actually runs.
|
||||
QTimer::singleShot(150, this, [this, button, scenePos](){
|
||||
// The close event is already triggered, don't try to show the
|
||||
// system menu anymore, otherwise it will prevent our window
|
||||
// from closing.
|
||||
if (m_closeTriggered) {
|
||||
return;
|
||||
}
|
||||
FramelessQuickHelper::get(this)->showSystemMenu([this, button, &scenePos]() -> QPoint {
|
||||
if (button == Qt::LeftButton) {
|
||||
return {0, qRound(height())};
|
||||
}
|
||||
return scenePos;
|
||||
}());
|
||||
});
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonDblClick:
|
||||
if (QQuickWindow * const w = window()) {
|
||||
if ((button == Qt::LeftButton) && interestArea) {
|
||||
m_closeTriggered = true;
|
||||
w->close();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QRect QuickStandardTitleBar::windowIconRect() const
|
||||
{
|
||||
#if 0
|
||||
const QSizeF size = windowIconSize();
|
||||
const qreal y = ((height() - size.height()) / qreal(2));
|
||||
return QRectF(QPointF(kDefaultTitleBarContentsMargin, y), size).toRect();
|
||||
#else
|
||||
return QRectF(QPointF(m_windowIcon->x(), m_windowIcon->y()), windowIconSize()).toRect();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QuickStandardTitleBar::windowIconVisible_real() const
|
||||
{
|
||||
if (m_windowIcon.isNull() || !m_windowIcon->isVisible() || !m_windowIcon->source().isValid()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuickStandardTitleBar::isInTitleBarIconArea(const QPoint &pos) const
|
||||
{
|
||||
if (!windowIconVisible_real()) {
|
||||
return false;
|
||||
}
|
||||
return windowIconRect().contains(pos);
|
||||
}
|
||||
|
||||
void QuickStandardTitleBar::initialize()
|
||||
{
|
||||
setSmooth(true);
|
||||
|
@ -380,6 +470,9 @@ void QuickStandardTitleBar::initialize()
|
|||
iconAnchors->setVerticalCenter(thisPriv->verticalCenter());
|
||||
connect(m_windowIcon.data(), &QuickImageItem::visibleChanged, this, &QuickStandardTitleBar::windowIconVisibleChanged);
|
||||
connect(m_windowIcon.data(), &QuickImageItem::sourceChanged, this, &QuickStandardTitleBar::windowIconChanged);
|
||||
// ### TODO: QuickImageItem::sizeChanged()
|
||||
connect(m_windowIcon.data(), &QuickImageItem::widthChanged, this, &QuickStandardTitleBar::windowIconSizeChanged);
|
||||
connect(m_windowIcon.data(), &QuickImageItem::heightChanged, this, &QuickStandardTitleBar::windowIconSizeChanged);
|
||||
|
||||
m_windowTitleLabel.reset(new QQuickLabel(this));
|
||||
QFont f = m_windowTitleLabel->font();
|
||||
|
@ -428,13 +521,30 @@ void QuickStandardTitleBar::itemChange(const ItemChange change, const ItemChange
|
|||
m_windowTitleChangeConnection = connect(value.window, &QQuickWindow::windowTitleChanged, this, &QuickStandardTitleBar::updateTitleLabelText);
|
||||
updateAll();
|
||||
value.window->installEventFilter(this);
|
||||
FramelessQuickHelper::get(this)->setHitTestVisible(windowIconRect(), windowIconVisible_real());
|
||||
}
|
||||
}
|
||||
|
||||
bool QuickStandardTitleBar::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
if (event && (event->type() == QEvent::LanguageChange)) {
|
||||
Q_ASSERT(object);
|
||||
Q_ASSERT(event);
|
||||
if (!object || !event) {
|
||||
return false;
|
||||
}
|
||||
if (!object->isWindowType()) {
|
||||
return QQuickRectangle::eventFilter(object, event);
|
||||
}
|
||||
const auto w = qobject_cast<QQuickWindow *>(object);
|
||||
if (w != window()) {
|
||||
return QQuickRectangle::eventFilter(object, event);
|
||||
}
|
||||
const QEvent::Type type = event->type();
|
||||
if (type == QEvent::LanguageChange) {
|
||||
retranslateUi();
|
||||
} else if ((type >= QEvent::MouseButtonPress) && (type <= QEvent::MouseMove)) {
|
||||
const auto mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
mouseEventHandler(mouseEvent);
|
||||
}
|
||||
return QQuickRectangle::eventFilter(object, event);
|
||||
}
|
||||
|
|
|
@ -278,6 +278,13 @@ void StandardTitleBarPrivate::mouseEventHandler(const QMouseEvent *event)
|
|||
// so we have to wait for a little bit to make sure the double
|
||||
// click events are handled first, before we actually handle the
|
||||
// mouse release events here.
|
||||
// We need to wait long enough because the time interval between these
|
||||
// events is really really short, if the delay time is not long enough,
|
||||
// we still can't trigger the double click event due to we have handled
|
||||
// the mouse release events here already. But we also can't wait too
|
||||
// long, otherwise the system menu will show up too late, which is not
|
||||
// a good user experience. In my experiments, I found that 150ms is
|
||||
// the minimum value we can use here.
|
||||
// We need a copy of the "scenePos" variable here, otherwise it will
|
||||
// soon fall out of scope when the lambda function actually runs.
|
||||
QTimer::singleShot(150, this, [this, button, q, scenePos](){
|
||||
|
|
Loading…
Reference in New Issue