File indexing completed on 2024-04-28 03:59:02

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1998 Kurt Granroth <granroth@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "kcursor.h"
0009 #include "kcursor_p.h"
0010 
0011 #include <QAbstractScrollArea>
0012 #include <QCursor>
0013 #include <QEvent>
0014 #include <QTimer>
0015 #include <QWidget>
0016 
0017 void KCursor::setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter)
0018 {
0019     KCursorPrivate::self()->setAutoHideCursor(w, enable, customEventFilter);
0020 }
0021 
0022 void KCursor::autoHideEventFilter(QObject *o, QEvent *e)
0023 {
0024     KCursorPrivate::self()->eventFilter(o, e);
0025 }
0026 
0027 void KCursor::setHideCursorDelay(int ms)
0028 {
0029     KCursorPrivate::self()->hideCursorDelay = ms;
0030 }
0031 
0032 int KCursor::hideCursorDelay()
0033 {
0034     return KCursorPrivate::self()->hideCursorDelay;
0035 }
0036 
0037 // **************************************************************************
0038 
0039 KCursorPrivateAutoHideEventFilter::KCursorPrivateAutoHideEventFilter(QWidget *widget)
0040     : m_widget(widget)
0041     , m_wasMouseTracking(m_widget->hasMouseTracking())
0042     , m_isCursorHidden(false)
0043     , m_isOwnCursor(false)
0044 {
0045     mouseWidget()->setMouseTracking(true);
0046     connect(&m_autoHideTimer, &QTimer::timeout, this, &KCursorPrivateAutoHideEventFilter::hideCursor);
0047 }
0048 
0049 KCursorPrivateAutoHideEventFilter::~KCursorPrivateAutoHideEventFilter()
0050 {
0051     if (m_widget != nullptr) {
0052         mouseWidget()->setMouseTracking(m_wasMouseTracking);
0053     }
0054 }
0055 
0056 void KCursorPrivateAutoHideEventFilter::resetWidget()
0057 {
0058     m_widget = nullptr;
0059 }
0060 
0061 void KCursorPrivateAutoHideEventFilter::hideCursor()
0062 {
0063     m_autoHideTimer.stop();
0064 
0065     if (m_isCursorHidden) {
0066         return;
0067     }
0068 
0069     m_isCursorHidden = true;
0070 
0071     QWidget *w = mouseWidget();
0072 
0073     m_isOwnCursor = w->testAttribute(Qt::WA_SetCursor);
0074     if (m_isOwnCursor) {
0075         m_oldCursor = w->cursor();
0076     }
0077 
0078     w->setCursor(QCursor(Qt::BlankCursor));
0079 }
0080 
0081 void KCursorPrivateAutoHideEventFilter::unhideCursor()
0082 {
0083     m_autoHideTimer.stop();
0084 
0085     if (!m_isCursorHidden) {
0086         return;
0087     }
0088 
0089     m_isCursorHidden = false;
0090 
0091     QWidget *w = mouseWidget();
0092 
0093     if (w->cursor().shape() != Qt::BlankCursor) { // someone messed with the cursor already
0094         return;
0095     }
0096 
0097     if (m_isOwnCursor) {
0098         w->setCursor(m_oldCursor);
0099     } else {
0100         w->unsetCursor();
0101     }
0102 }
0103 
0104 // The widget which gets mouse events, and that shows the cursor
0105 // (that is the viewport, for a QAbstractScrollArea)
0106 QWidget *KCursorPrivateAutoHideEventFilter::mouseWidget() const
0107 {
0108     QWidget *w = m_widget;
0109 
0110     // Is w a QAbstractScrollArea ? Call setCursor on the viewport in that case.
0111     QAbstractScrollArea *sv = qobject_cast<QAbstractScrollArea *>(w);
0112     if (sv) {
0113         w = sv->viewport();
0114     }
0115 
0116     return w;
0117 }
0118 
0119 bool KCursorPrivateAutoHideEventFilter::eventFilter(QObject *o, QEvent *e)
0120 {
0121     Q_UNUSED(o);
0122     // o is m_widget or its viewport
0123     // Q_ASSERT( o == m_widget );
0124 
0125     switch (e->type()) {
0126     case QEvent::Leave:
0127     case QEvent::FocusOut:
0128     case QEvent::WindowDeactivate:
0129         unhideCursor();
0130         break;
0131     case QEvent::KeyPress:
0132     case QEvent::ShortcutOverride:
0133         hideCursor();
0134         break;
0135     case QEvent::Enter:
0136     case QEvent::FocusIn:
0137     case QEvent::MouseButtonPress:
0138     case QEvent::MouseButtonRelease:
0139     case QEvent::MouseButtonDblClick:
0140     case QEvent::MouseMove:
0141     case QEvent::Show:
0142     case QEvent::Hide:
0143     case QEvent::Wheel:
0144         unhideCursor();
0145         if (m_widget->hasFocus()) {
0146             m_autoHideTimer.setSingleShot(true);
0147             m_autoHideTimer.start(KCursorPrivate::self()->hideCursorDelay);
0148         }
0149         break;
0150     default:
0151         break;
0152     }
0153 
0154     return false;
0155 }
0156 
0157 KCursorPrivate *KCursorPrivate::s_self = nullptr;
0158 
0159 KCursorPrivate *KCursorPrivate::self()
0160 {
0161     if (!s_self) {
0162         s_self = new KCursorPrivate;
0163     }
0164     // WABA: Don't delete KCursorPrivate, it serves no real purpose.
0165     // Even worse it causes crashes because it seems to get deleted
0166     // during ~QApplication and ~QApplication doesn't seem to like it
0167     // when we delete a QCursor. No idea if that is a bug itself.
0168 
0169     return s_self;
0170 }
0171 
0172 KCursorPrivate::KCursorPrivate()
0173 {
0174     hideCursorDelay = 5000; // 5s default value
0175     enabled = true;
0176 }
0177 
0178 KCursorPrivate::~KCursorPrivate()
0179 {
0180 }
0181 
0182 void KCursorPrivate::setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter)
0183 {
0184     if (!w || !enabled) {
0185         return;
0186     }
0187 
0188     QWidget *viewport = nullptr;
0189     QAbstractScrollArea *sv = qobject_cast<QAbstractScrollArea *>(w);
0190     if (sv) {
0191         viewport = sv->viewport();
0192     }
0193 
0194     if (enable) {
0195         if (m_eventFilters.contains(w)) {
0196             return;
0197         }
0198         KCursorPrivateAutoHideEventFilter *filter = new KCursorPrivateAutoHideEventFilter(w);
0199         m_eventFilters.insert(w, filter);
0200         if (viewport) {
0201             m_eventFilters.insert(viewport, filter);
0202             connect(viewport, &QObject::destroyed, this, &KCursorPrivate::slotViewportDestroyed);
0203         }
0204         if (!customEventFilter) {
0205             w->installEventFilter(filter); // for key events
0206             if (viewport) {
0207                 viewport->installEventFilter(filter); // for mouse events
0208             }
0209         }
0210         connect(w, &QObject::destroyed, this, &KCursorPrivate::slotWidgetDestroyed);
0211     } else {
0212         KCursorPrivateAutoHideEventFilter *filter = m_eventFilters.take(w);
0213         if (filter == nullptr) {
0214             return;
0215         }
0216         w->removeEventFilter(filter);
0217         if (viewport) {
0218             m_eventFilters.remove(viewport);
0219             disconnect(viewport, &QObject::destroyed, this, &KCursorPrivate::slotViewportDestroyed);
0220             viewport->removeEventFilter(filter);
0221         }
0222         delete filter;
0223         disconnect(w, &QObject::destroyed, this, &KCursorPrivate::slotWidgetDestroyed);
0224     }
0225 }
0226 
0227 bool KCursorPrivate::eventFilter(QObject *o, QEvent *e)
0228 {
0229     if (!enabled || e->type() == QEvent::ChildAdded) {
0230         return false;
0231     }
0232 
0233     KCursorPrivateAutoHideEventFilter *filter = m_eventFilters.value(o);
0234 
0235     Q_ASSERT(filter != nullptr);
0236     if (filter == nullptr) {
0237         return false;
0238     }
0239 
0240     return filter->eventFilter(o, e);
0241 }
0242 
0243 void KCursorPrivate::slotViewportDestroyed(QObject *o)
0244 {
0245     m_eventFilters.remove(o);
0246 }
0247 
0248 void KCursorPrivate::slotWidgetDestroyed(QObject *o)
0249 {
0250     KCursorPrivateAutoHideEventFilter *filter = m_eventFilters.take(o);
0251 
0252     Q_ASSERT(filter != nullptr);
0253 
0254     filter->resetWidget(); // so that dtor doesn't access it
0255     delete filter;
0256 }
0257 
0258 #include "moc_kcursor_p.cpp"