File indexing completed on 2024-05-05 05:35:25

0001 //////////////////////////////////////////////////////////////////////////////
0002 // oxygensplitterproxy.cpp
0003 // Extended hit area for Splitters
0004 // -------------------
0005 //
0006 // SPDX-FileCopyrightText: 2011 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0007 //
0008 // Based on Bespin splitterproxy code
0009 // SPDX-FileCopyrightText: 2011 Thomas Luebking <thomas.luebking@web.de>
0010 //
0011 //
0012 // SPDX-License-Identifier: LGPL-2.0-only
0013 //////////////////////////////////////////////////////////////////////////////
0014 
0015 #include "oxygensplitterproxy.h"
0016 #include "oxygenstyleconfigdata.h"
0017 
0018 #include <QCoreApplication>
0019 #include <QDebug>
0020 #include <QPainter>
0021 
0022 namespace Oxygen
0023 {
0024 //____________________________________________________________________
0025 void SplitterFactory::setEnabled(bool value)
0026 {
0027     if (_enabled != value) {
0028         // store
0029         _enabled = value;
0030 
0031         // assign to existing splitters
0032         for (WidgetMap::iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter) {
0033             if (iter.value())
0034                 iter.value().data()->setEnabled(value);
0035         }
0036     }
0037 }
0038 
0039 //____________________________________________________________________
0040 bool SplitterFactory::registerWidget(QWidget *widget)
0041 {
0042     // check widget type
0043     if (qobject_cast<QMainWindow *>(widget)) {
0044         WidgetMap::iterator iter(_widgets.find(widget));
0045         if (iter == _widgets.end() || !iter.value()) {
0046             widget->installEventFilter(&_addEventFilter);
0047             SplitterProxy *proxy(new SplitterProxy(widget, _enabled));
0048             widget->removeEventFilter(&_addEventFilter);
0049 
0050             widget->installEventFilter(proxy);
0051             _widgets.insert(widget, proxy);
0052 
0053         } else {
0054             widget->removeEventFilter(iter.value().data());
0055             widget->installEventFilter(iter.value().data());
0056         }
0057 
0058         return true;
0059 
0060     } else if (qobject_cast<QSplitterHandle *>(widget)) {
0061         QWidget *window(widget->window());
0062         WidgetMap::iterator iter(_widgets.find(window));
0063         if (iter == _widgets.end() || !iter.value()) {
0064             window->installEventFilter(&_addEventFilter);
0065             SplitterProxy *proxy(new SplitterProxy(window, _enabled));
0066             window->removeEventFilter(&_addEventFilter);
0067 
0068             widget->installEventFilter(proxy);
0069             _widgets.insert(window, proxy);
0070 
0071         } else {
0072             widget->removeEventFilter(iter.value().data());
0073             widget->installEventFilter(iter.value().data());
0074         }
0075 
0076         return true;
0077 
0078     } else
0079         return false;
0080 }
0081 
0082 //____________________________________________________________________
0083 void SplitterFactory::unregisterWidget(QWidget *widget)
0084 {
0085     WidgetMap::iterator iter(_widgets.find(widget));
0086     if (iter != _widgets.end()) {
0087         if (iter.value())
0088             iter.value().data()->deleteLater();
0089         _widgets.erase(iter);
0090     }
0091 }
0092 
0093 //____________________________________________________________________
0094 SplitterProxy::SplitterProxy(QWidget *parent, bool enabled)
0095     : QWidget(parent)
0096     , _enabled(enabled)
0097     , _timerId(0)
0098 {
0099     setAttribute(Qt::WA_TranslucentBackground, true);
0100     setAttribute(Qt::WA_OpaquePaintEvent, false);
0101     hide();
0102 }
0103 
0104 //____________________________________________________________________
0105 void SplitterProxy::setEnabled(bool value)
0106 {
0107     // make sure status has changed
0108     if (_enabled != value) {
0109         _enabled = value;
0110         if (_enabled)
0111             clearSplitter();
0112     }
0113 }
0114 
0115 //____________________________________________________________________
0116 bool SplitterProxy::eventFilter(QObject *object, QEvent *event)
0117 {
0118     // do nothing if disabled
0119     if (!_enabled)
0120         return false;
0121 
0122     // do nothing in case of mouse grab
0123     if (mouseGrabber())
0124         return false;
0125 
0126     switch (event->type()) {
0127     case QEvent::HoverEnter:
0128         if (!isVisible()) {
0129             // cast to splitter handle
0130             if (QSplitterHandle *handle = qobject_cast<QSplitterHandle *>(object)) {
0131                 setSplitter(handle);
0132             }
0133         }
0134 
0135         return false;
0136 
0137     case QEvent::HoverMove:
0138     case QEvent::HoverLeave:
0139         return isVisible() && object == _splitter.data();
0140 
0141     case QEvent::MouseMove:
0142     case QEvent::Timer:
0143     case QEvent::Move:
0144         return false;
0145 
0146     case QEvent::CursorChange:
0147         if (QWidget *window = qobject_cast<QMainWindow *>(object)) {
0148             if (window->cursor().shape() == Qt::SplitHCursor || window->cursor().shape() == Qt::SplitVCursor) {
0149                 setSplitter(window);
0150             }
0151         }
0152         return false;
0153 
0154     case QEvent::WindowDeactivate:
0155     case QEvent::MouseButtonRelease:
0156         clearSplitter();
0157         return false;
0158 
0159     default:
0160         return false;
0161     }
0162 }
0163 
0164 //____________________________________________________________________
0165 bool SplitterProxy::event(QEvent *event)
0166 {
0167     switch (event->type()) {
0168     case QEvent::MouseMove:
0169     case QEvent::MouseButtonPress:
0170     case QEvent::MouseButtonRelease: {
0171         // check splitter
0172         if (!_splitter)
0173             return false;
0174 
0175         event->accept();
0176 
0177         // grab on mouse press
0178         if (event->type() == QEvent::MouseButtonPress) {
0179             grabMouse();
0180             resize(1, 1);
0181         }
0182 
0183         // cast to mouse event
0184         QMouseEvent *mouseEvent(static_cast<QMouseEvent *>(event));
0185 
0186         // get relevant position to post mouse drag event to application
0187         if (event->type() == QEvent::MouseButtonPress) {
0188             // use hook, to make sure splitter is properly dragged
0189             QMouseEvent mouseEvent2(mouseEvent->type(), _hook, mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers());
0190 
0191             QCoreApplication::sendEvent(_splitter.data(), &mouseEvent2);
0192 
0193         } else {
0194             // map event position to current splitter and post.
0195             QMouseEvent mouseEvent2(mouseEvent->type(),
0196                                     _splitter.data()->mapFromGlobal(mouseEvent->globalPos()),
0197                                     mouseEvent->button(),
0198                                     mouseEvent->buttons(),
0199                                     mouseEvent->modifiers());
0200 
0201             QCoreApplication::sendEvent(_splitter.data(), &mouseEvent2);
0202         }
0203 
0204         // release grab on mouse-Release
0205         if (event->type() == QEvent::MouseButtonRelease && mouseGrabber() == this) {
0206             releaseMouse();
0207         }
0208 
0209         return true;
0210     }
0211 
0212     case QEvent::Timer:
0213         if (static_cast<QTimerEvent *>(event)->timerId() != _timerId) {
0214             return QWidget::event(event);
0215         }
0216 
0217         /*
0218         Fall through is intended.
0219         We somehow lost a QEvent::Leave before timeout. We fix it from here
0220         */
0221         Q_FALLTHROUGH();
0222 
0223     case QEvent::HoverLeave:
0224     case QEvent::Leave: {
0225         if (mouseGrabber() == this) {
0226             return true;
0227         }
0228 
0229         // reset splitter
0230         if (isVisible() && !rect().contains(mapFromGlobal(QCursor::pos()))) {
0231             clearSplitter();
0232         }
0233         return true;
0234     }
0235 
0236     default:
0237         return QWidget::event(event);
0238     }
0239 }
0240 
0241 //____________________________________________________________________
0242 void SplitterProxy::setSplitter(QWidget *widget)
0243 {
0244     // check if changed
0245     if (_splitter.data() == widget)
0246         return;
0247 
0248     // get cursor position
0249     const QPoint position(QCursor::pos());
0250 
0251     // store splitter and hook
0252     _splitter = widget;
0253     _hook = _splitter.data()->mapFromGlobal(position);
0254 
0255     // adjust rect
0256     QRect rect(0, 0, 2 * StyleConfigData::splitterProxyWidth(), 2 * StyleConfigData::splitterProxyWidth());
0257     rect.moveCenter(parentWidget()->mapFromGlobal(position));
0258     setGeometry(rect);
0259     setCursor(_splitter.data()->cursor().shape());
0260 
0261     // show
0262     raise();
0263     show();
0264 
0265     // timer used to automatically hide proxy in case leave events are lost
0266     if (!_timerId)
0267         _timerId = startTimer(150);
0268 }
0269 
0270 //____________________________________________________________________
0271 void SplitterProxy::clearSplitter(void)
0272 {
0273     // check if changed
0274     if (!_splitter)
0275         return;
0276 
0277     // release mouse
0278     if (mouseGrabber() == this)
0279         releaseMouse();
0280 
0281     // hide
0282     parentWidget()->setUpdatesEnabled(false);
0283     hide();
0284     parentWidget()->setUpdatesEnabled(true);
0285 
0286     // send hover event
0287     if (_splitter) {
0288         QHoverEvent hoverEvent(qobject_cast<QSplitterHandle *>(_splitter.data()) ? QEvent::HoverLeave : QEvent::HoverMove,
0289                                _splitter.data()->mapFromGlobal(QCursor::pos()),
0290                                _hook);
0291         QCoreApplication::sendEvent(_splitter.data(), &hoverEvent);
0292         _splitter.clear();
0293     }
0294 
0295     // kill timer if any
0296     if (_timerId) {
0297         killTimer(_timerId);
0298         _timerId = 0;
0299     }
0300 }
0301 }