File indexing completed on 2025-02-02 14:19:59
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"