1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
/*
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2022 Derek Christ <christ.derek@gmail.com>
SPDX-FileCopyrightText: 2022 Bharadwaj Raju <bharadwaj.raju777@protonmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "trianglemousefilter.h"
#include <QPolygonF>
TriangleMouseFilter::TriangleMouseFilter(QQuickItem *parent)
: QQuickItem(parent)
, m_edgeLine()
, m_active(true)
, m_blockFirstEnter(false)
{
setFiltersChildMouseEvents(true);
m_resetTimer.setSingleShot(true);
connect(&m_resetTimer, &QTimer::timeout, this, [this]() {
m_interceptionPos.reset();
update();
if (!m_interceptedHoverItem) {
return;
}
if (m_interceptedHoverEnterPosition) {
const auto targetPosition = mapToItem(m_interceptedHoverItem, m_interceptedHoverEnterPosition.value());
QHoverEvent e(QEvent::HoverEnter, targetPosition, targetPosition);
qApp->sendEvent(m_interceptedHoverItem, &e);
m_interceptedHoverEnterPosition.reset();
}
const auto targetPosition = mapToItem(m_interceptedHoverItem, m_lastCursorPosition);
QHoverEvent e(QEvent::HoverMove, targetPosition, targetPosition);
qApp->sendEvent(m_interceptedHoverItem, &e);
});
};
bool TriangleMouseFilter::childMouseEventFilter(QQuickItem *item, QEvent *event)
{
update();
if (!m_active) {
event->setAccepted(false);
return false;
}
switch (event->type()) {
case QEvent::HoverLeave:
if (!m_interceptedHoverItem) {
return false;
}
if (item == m_interceptedHoverItem.data()) {
m_interceptedHoverItem.clear();
return false;
}
return true;
case QEvent::HoverEnter:
case QEvent::HoverMove: {
QHoverEvent *he = static_cast<QHoverEvent *>(event);
const QPointF position = item->mapToItem(this, he->posF());
if (filterContains(position)) {
if (event->type() == QEvent::HoverEnter) {
m_interceptedHoverEnterPosition = position;
m_interceptedHoverItem = item;
}
if (m_filterTimeout > 0) {
m_resetTimer.start(m_filterTimeout);
}
m_lastCursorPosition = position;
event->setAccepted(true);
return true;
} else {
if (m_blockFirstEnter && event->type() == QEvent::HoverEnter && !m_interceptionPos) {
// this clause means that we block focus when first entering a given position
// in the case of kickoff it's so that we can move the mouse from the bottom tabbar to the side view
m_interceptedHoverItem = item;
m_interceptedHoverEnterPosition = position;
if (m_filterTimeout > 0) {
m_resetTimer.start(m_filterTimeout);
}
event->setAccepted(true);
m_lastCursorPosition = position;
m_interceptionPos = position;
return true;
}
m_interceptionPos = position;
m_lastCursorPosition = position;
// if we are no longer inhibiting events and have previously intercepted a hover enter
// we manually send the hover enter to that item
if (event->type() == QEvent::HoverMove && m_interceptedHoverItem) {
const auto targetPosition = mapToItem(m_interceptedHoverItem, position);
QHoverEvent e(QEvent::HoverEnter, targetPosition, targetPosition);
qApp->sendEvent(m_interceptedHoverItem, &e);
m_interceptedHoverItem.clear();
}
event->setAccepted(false);
return false;
}
}
default:
return false;
}
}
bool TriangleMouseFilter::filterContains(const QPointF &p) const
{
if (!m_interceptionPos) {
return false;
}
// We add some jitter protection by extending our triangle out slight past the mouse position in the opposite direction of the edge;
const int jitterThreshold = 3;
// QPolygonF.contains returns false if we're on the edge, so we pad our main item
const QRectF shape = (m_edgeLine.size() == 4) ? QRect(m_edgeLine[0] - 1, m_edgeLine[1] - 1, width() + m_edgeLine[2] + 1, height() + m_edgeLine[3] + 1)
: QRect(-1, -1, width() + 1, height() + 1);
QPolygonF poly;
switch (m_edge) {
case Qt::RightEdge:
poly << m_interceptionPos.value() + QPointF(-jitterThreshold, 0) << shape.topRight() << shape.bottomRight();
break;
case Qt::TopEdge:
poly << m_interceptionPos.value() + QPointF(0, -jitterThreshold) << shape.topLeft() << shape.topRight();
break;
case Qt::LeftEdge:
poly << m_interceptionPos.value() + QPointF(jitterThreshold, 0) << shape.topLeft() << shape.bottomLeft();
break;
case Qt::BottomEdge:
poly << m_interceptionPos.value() + QPointF(0, jitterThreshold) << shape.bottomLeft() << shape.bottomRight();
}
bool firstCheck = poly.containsPoint(p, Qt::OddEvenFill);
poly.replace(0, m_secondaryPoint);
bool secondCheck = m_secondaryPoint != QPointF(0, 0) && poly.containsPoint(p, Qt::OddEvenFill);
return (firstCheck || secondCheck);
}
|