File: trianglemousefilter.cpp

package info (click to toggle)
plasma-workspace 4%3A5.27.5-2%2Bdeb12u2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 102,040 kB
  • sloc: cpp: 121,800; xml: 3,238; python: 645; perl: 586; sh: 254; javascript: 113; ruby: 62; makefile: 15; ansic: 13
file content (147 lines) | stat: -rw-r--r-- 5,455 bytes parent folder | download
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);
}