/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include #include #include #include #include "debugging/debugging.h" class DeferredMotion { QMouseEvent m_mouseMoveEvent; QTimer m_timer; public: template DeferredMotion( Functor func ) : m_mouseMoveEvent( QEvent::MouseMove, QPointF(), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifiers() ) { m_timer.setSingleShot( true ); m_timer.callOnTimeout( [this, func](){ func( m_mouseMoveEvent ); } ); } void motion( const QMouseEvent *event ){ m_mouseMoveEvent = *event; if( !m_timer.isActive() ) m_timer.start(); } }; class DeferredMotion2 { QMouseEvent m_mouseMoveEvent; const std::function m_func; public: template DeferredMotion2( Functor func ) : m_mouseMoveEvent( QEvent::MouseMove, QPointF(), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifiers() ), m_func( func ) { } void motion( const QMouseEvent& event ){ m_mouseMoveEvent = event; } void invoke(){ m_func( m_mouseMoveEvent ); } typedef MemberCaller InvokeCaller; }; class DeferredMotionDelta { QMouseEvent m_mouseMoveEvent; QTimer m_timer; int m_delta_x = 0; int m_delta_y = 0; public: template DeferredMotionDelta( Functor func ) : m_mouseMoveEvent( QEvent::MouseMove, QPointF(), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifiers() ) { m_timer.setSingleShot( true ); m_timer.callOnTimeout( [this, func](){ func( m_delta_x, m_delta_y, m_mouseMoveEvent ); m_delta_x = 0; m_delta_y = 0; } ); } void flush(){ m_timer.stop(); //. ? deferred_motion( this ); } void motion_delta( int x, int y, const QMouseEvent *event ){ m_delta_x += x; m_delta_y += y; m_mouseMoveEvent = *event; if( !m_timer.isActive() ) m_timer.start(); } }; class DeferredMotionDelta2 { QMouseEvent m_mouseMoveEvent; std::function m_func; int m_delta_x = 0; int m_delta_y = 0; public: template DeferredMotionDelta2( Functor func ) : m_mouseMoveEvent( QEvent::MouseMove, QPointF(), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifiers() ), m_func( func ) { } void motion_delta( int x, int y, const QMouseEvent *event ){ m_delta_x += x; m_delta_y += y; m_mouseMoveEvent = *event; } void invoke(){ m_func( m_delta_x, m_delta_y, m_mouseMoveEvent ); m_delta_x = 0; m_delta_y = 0; } typedef MemberCaller InvokeCaller; }; class FreezePointer : public QObject { QWidget* m_widget; //global coordinates QPoint m_initial_pos; bool m_trackingEstablished; std::function m_motion_delta_function; std::function m_focus_out_function; QTimer m_rescueTimer; QPoint getCenter() const { return m_widget->mapToGlobal( m_widget->rect().center() ); } protected: bool eventFilter( QObject *obj, QEvent *event ) override { if( event->type() == QEvent::MouseMove ) { QMouseEvent *mouseEvent = static_cast( event ); const QPoint center = getCenter(); /* QCursor::setPos( center ) effect may happen not immediately; suspend processing till then, otherwise start dash will happen */ if( !m_trackingEstablished ){ m_trackingEstablished = mouseEvent->globalPos() == center; QCursor::setPos( center ); } else if( mouseEvent->globalPos() != center ){ const QPoint delta = mouseEvent->globalPos() - center; m_motion_delta_function( delta.x(), delta.y(), mouseEvent ); QCursor::setPos( center ); } // handle runaways with released buttons; FIXME: need more elegant way to persistently grab in this case if( !m_widget->rect().contains( mouseEvent->pos() ) ){ // bomb cursor via timer to get it back to the widget if( !m_rescueTimer.isActive() ){ m_rescueTimer.disconnect(); // disconnect everything m_rescueTimer.callOnTimeout( [center = center](){ QCursor::setPos( center ); } ); m_rescueTimer.start( 33 ); } } else{ m_rescueTimer.stop(); } } else if( event->type() == QEvent::FocusOut ) { m_focus_out_function(); } return QObject::eventFilter( obj, event ); // standard event processing } public: FreezePointer() : m_widget( nullptr ){ } void freeze_pointer( QWidget* widget, decltype( m_motion_delta_function ) motion_delta_function, decltype( m_focus_out_function ) focus_out_function ){ ASSERT_MESSAGE( m_widget == nullptr, "can't freeze pointer: already frozen" ); m_widget = widget; m_initial_pos = QCursor::pos(); m_trackingEstablished = false; m_motion_delta_function = motion_delta_function; m_focus_out_function = focus_out_function; m_widget->installEventFilter( this ); m_widget->grabMouse( Qt::CursorShape::BlankCursor ); // is only needed for released mouse freezing, prevents certain glitches /* using center for tracking for max safety */ QCursor::setPos( getCenter() ); } void unfreeze_pointer( bool centerize ){ ASSERT_MESSAGE( m_widget != nullptr, "can't unfreeze pointer: is not frozen" ); m_rescueTimer.stop(); m_widget->releaseMouse(); m_widget->removeEventFilter( this ); if( centerize ) QCursor::setPos( getCenter() ); else QCursor::setPos( m_initial_pos ); m_widget = nullptr; } }; class DeferredAdjustment { int m_value; QTimer m_timer; public: template DeferredAdjustment( Functor func ) { m_timer.setSingleShot( true ); m_timer.callOnTimeout( [this, func](){ func( m_value ); } ); } void value_changed( int value ){ m_value = value; if( !m_timer.isActive() ) m_timer.start(); } };