/************************************************************************* * Copyright (C) 2014 by Hugo Pereira Da Costa * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * *************************************************************************/ #include "adwaitasplitterproxy.h" #include "adwaita.h" #include "fakeadwaitastyleconfigdata.h" #include #include #include namespace Adwaita { //____________________________________________________________________ void SplitterFactory::setEnabled( bool value ) { if( _enabled != value ) { // store _enabled = value; // assign to existing splitters for( WidgetMap::iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter ) { if( iter.value() ) iter.value().data()->setEnabled( value ); } } } //____________________________________________________________________ bool SplitterFactory::registerWidget( QWidget *widget ) { // check widget type if( qobject_cast( widget ) ) { WidgetMap::iterator iter( _widgets.find( widget ) ); if( iter == _widgets.end() || !iter.value() ) { widget->installEventFilter( &_addEventFilter ); SplitterProxy* proxy( new SplitterProxy( widget, _enabled ) ); widget->removeEventFilter( &_addEventFilter ); widget->installEventFilter( proxy ); _widgets.insert( widget, proxy ); } else { widget->removeEventFilter( iter.value().data() ); widget->installEventFilter( iter.value().data() ); } return true; } else if( qobject_cast( widget ) ) { QWidget* window( widget->window() ); WidgetMap::iterator iter( _widgets.find( window ) ); if( iter == _widgets.end() || !iter.value() ) { window->installEventFilter( &_addEventFilter ); SplitterProxy* proxy( new SplitterProxy( window, _enabled ) ); window->removeEventFilter( &_addEventFilter ); widget->installEventFilter( proxy ); _widgets.insert( window, proxy ); } else { widget->removeEventFilter( iter.value().data() ); widget->installEventFilter( iter.value().data() ); } return true; } else return false; } //____________________________________________________________________ void SplitterFactory::unregisterWidget( QWidget *widget ) { WidgetMap::iterator iter( _widgets.find( widget ) ); if( iter != _widgets.end() ) { if( iter.value() ) iter.value().data()->deleteLater(); _widgets.erase( iter ); } } //____________________________________________________________________ SplitterProxy::SplitterProxy( QWidget* parent, bool enabled ): QWidget( parent ), _enabled( enabled ), _timerId( 0 ) { setAttribute( Qt::WA_TranslucentBackground, true ); setAttribute( Qt::WA_OpaquePaintEvent, false ); hide(); } //____________________________________________________________________ SplitterProxy::~SplitterProxy( void ) {} //____________________________________________________________________ void SplitterProxy::setEnabled( bool value ) { // make sure status has changed if( _enabled != value ) { _enabled = value; if( _enabled ) clearSplitter(); } } //____________________________________________________________________ bool SplitterProxy::eventFilter( QObject* object, QEvent* event ) { // do nothing if disabled if( !_enabled ) return false; // do nothing in case of mouse grab if( mouseGrabber() ) return false; switch( event->type() ) { case QEvent::HoverEnter: if( !isVisible() ) { // cast to splitter handle if( QSplitterHandle* handle = qobject_cast( object ) ) { setSplitter( handle ); } } return false; case QEvent::HoverMove: case QEvent::HoverLeave: return isVisible() && object == _splitter.data(); case QEvent::MouseMove: case QEvent::Timer: case QEvent::Move: return false; case QEvent::CursorChange: if( QWidget *window = qobject_cast( object ) ) { if( window->cursor().shape() == Qt::SplitHCursor || window->cursor().shape() == Qt::SplitVCursor ) { setSplitter( window ); } } return false; case QEvent::WindowDeactivate: case QEvent::MouseButtonRelease: clearSplitter(); return false; default: return false; } } //____________________________________________________________________ bool SplitterProxy::event( QEvent *event ) { switch( event->type() ) { #if 0 case QEvent::Paint: { QPainter painter( this ); painter.setClipRegion( static_cast( event )->region() ); painter.setRenderHints( QPainter::Antialiasing ); painter.setPen( Qt::red ); painter.drawRect( QRectF( rect() ).adjusted( 0.5, 0.5, -0.5, -0.5 ) ); return true; } #endif case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: { // check splitter if( !_splitter ) return false; event->accept(); // grab on mouse press if( event->type() == QEvent::MouseButtonPress) { grabMouse(); resize(1,1); } // cast to mouse event QMouseEvent *mouseEvent( static_cast( event ) ); // get relevant position to post mouse drag event to application if( event->type() == QEvent::MouseButtonPress ) { // use hook, to make sure splitter is properly dragged QMouseEvent copy( mouseEvent->type(), _hook, _splitter.data()->mapToGlobal(_hook), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers()); QCoreApplication::sendEvent( _splitter.data(), © ); } else { // map event position to current splitter and post. QMouseEvent copy( mouseEvent->type(), _splitter.data()->mapFromGlobal( mouseEvent->globalPos() ), mouseEvent->globalPos(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers()); QCoreApplication::sendEvent( _splitter.data(), © ); } // release grab on mouse-Release if( event->type() == QEvent::MouseButtonRelease && mouseGrabber() == this ) { releaseMouse(); } return true; } case QEvent::Timer: if( static_cast( event )->timerId() != _timerId ) { return QWidget::event( event ); } /* Fall through is intended. We somehow lost a QEvent::Leave before timeout. We fix it from here */ case QEvent::HoverLeave: case QEvent::Leave: { if( mouseGrabber() == this ) { return true; } // reset splitter if( isVisible() && !rect().contains( mapFromGlobal( QCursor::pos() ) ) ) { clearSplitter(); } return true; } default: return QWidget::event( event ); } } //____________________________________________________________________ void SplitterProxy::setSplitter( QWidget* widget ) { // check if changed if( _splitter.data() == widget ) return; // get cursor position QPoint position( QCursor::pos() ); // store splitter and hook _splitter = widget; _hook = _splitter.data()->mapFromGlobal( position ); // adjust rect QRect rect( 0, 0, 2*StyleConfigData::splitterProxyWidth(), 2*StyleConfigData::splitterProxyWidth() ); rect.moveCenter( parentWidget()->mapFromGlobal( position ) ); setGeometry( rect ); setCursor( _splitter.data()->cursor().shape() ); // show raise(); show(); // timer used to automatically hide proxy in case leave events are lost if( !_timerId ) _timerId = startTimer(150); } //____________________________________________________________________ void SplitterProxy::clearSplitter( void ) { // check if changed if( !_splitter ) return; // release mouse if( mouseGrabber() == this ) releaseMouse(); // hide parentWidget()->setUpdatesEnabled(false); hide(); parentWidget()->setUpdatesEnabled(true); // send hover event if( _splitter ) { QHoverEvent hoverEvent( qobject_cast(_splitter.data()) ? QEvent::HoverLeave : QEvent::HoverMove, _splitter.data()->mapFromGlobal(QCursor::pos()), _hook); QCoreApplication::sendEvent( _splitter.data(), &hoverEvent ); _splitter.clear(); } // kill timer if any if( _timerId ) { killTimer( _timerId ); _timerId = 0; } } }