netradiant-custom/libs/gtkutil/cursor.h
Garux 15ca706037 support DPI scaling by viewports rendering and input in certain scenarios
e.g. in mac, kde, wayland or after QT_SCALE_FACTOR=1.25 ./install/radiant
2022-11-07 19:02:40 +03:00

230 lines
6.3 KiB
C++

/*
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 <QMouseEvent>
#include <QTimer>
#include <QCursor>
#include <QWidget>
#include "debugging/debugging.h"
class DeferredMotion
{
QMouseEvent m_mouseMoveEvent;
QTimer m_timer;
public:
template<class Functor>
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<void( const QMouseEvent& )> m_func;
public:
template<class Functor>
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<DeferredMotion2, &DeferredMotion2::invoke> InvokeCaller;
};
class DeferredMotionDelta
{
QMouseEvent m_mouseMoveEvent;
QTimer m_timer;
int m_delta_x = 0;
int m_delta_y = 0;
public:
template<class Functor>
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<void( int, int, const QMouseEvent& )> m_func;
int m_delta_x = 0;
int m_delta_y = 0;
public:
template<class Functor>
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<DeferredMotionDelta2, &DeferredMotionDelta2::invoke> InvokeCaller;
};
class FreezePointer : public QObject
{
QWidget* m_widget;
//global coordinates
QPoint m_initial_pos;
bool m_trackingEstablished;
std::function<void(int, int, const QMouseEvent*)> m_motion_delta_function;
std::function<void()> 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<QMouseEvent *>( 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.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<class Functor>
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();
}
};