QskFocusIndicator::duration introduced

This commit is contained in:
Uwe Rathmann 2023-12-02 12:48:20 +01:00
parent 6fa8cd9dc3
commit 2419183397
6 changed files with 214 additions and 257 deletions

View File

@ -12,7 +12,6 @@ set(SOURCES
dialog/DialogPage.h dialog/DialogPage.cpp
listbox/ListBoxPage.h listbox/ListBoxPage.cpp
Page.h Page.cpp
FocusIndicator.h FocusIndicator.cpp
main.cpp
)
qt_add_resources(SOURCES icons.qrc)

View File

@ -1,218 +0,0 @@
#include "FocusIndicator.h"
#include <QskEvent.h>
#include <QskAnimationHint.h>
#include <qquickwindow.h>
#include <qbasictimer.h>
static inline bool qskIsEnablingKey( const QKeyEvent* event )
{
// what keys do we want have here ???
return qskIsButtonPressKey( event ) || qskFocusChainIncrement( event );
}
class FocusIndicator::PrivateData
{
public:
inline bool isAutoDisabling() const { return duration > 0; }
inline bool isAutoEnabling() const { return false; }
int duration = 0;
QBasicTimer timer;
bool blockAutoRepeatKeyEvents = false;
};
FocusIndicator::FocusIndicator( QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData() )
{
setDuration( 4500 );
}
FocusIndicator::~FocusIndicator()
{
}
void FocusIndicator::setDuration( int ms )
{
ms = std::max( ms, 0 );
if ( ms == m_data->duration )
return;
m_data->duration = ms;
if ( m_data->isAutoDisabling() )
{
if ( auto w = window() )
w->installEventFilter( this );
if ( isEnabled() )
{
if ( isInitiallyPainted() )
m_data->timer.start( m_data->duration, this );
else
setEnabled( false );
}
connect( this, &QQuickItem::enabledChanged,
this, &FocusIndicator::resetTimer );
}
else
{
if ( auto w = window() )
w->removeEventFilter( this );
setEnabled( true );
disconnect( this, &QQuickItem::enabledChanged,
this, &FocusIndicator::resetTimer );
}
Q_EMIT durationChanged( ms );
}
int FocusIndicator::duration() const
{
return m_data->duration;
}
void FocusIndicator::maybeEnable( bool on )
{
if ( !m_data->isAutoEnabling() )
return;
if ( on )
{
if ( auto w = window() )
{
if ( w->isExposed() && w->isActive() )
setEnabled( true );
}
}
else
{
setEnabled( false );
}
}
void FocusIndicator::resetTimer()
{
if ( m_data->isAutoDisabling() )
{
if ( isEnabled() )
{
const auto hint = animationHint( Panel | QskAspect::Color );
m_data->timer.start( m_data->duration + hint.duration, this );
}
else
{
m_data->timer.stop();
}
}
}
bool FocusIndicator::eventFilter( QObject* object, QEvent* event )
{
if( ( object != window() ) || !m_data->isAutoDisabling() )
return Inherited::eventFilter( object, event );
switch( static_cast< int >( event->type() ) )
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::ShortcutOverride:
{
if ( m_data->timer.isActive() )
{
// renew the exposed period
m_data->timer.start( m_data->duration, this );
}
break;
}
}
switch( static_cast< int >( event->type() ) )
{
case QEvent::KeyPress:
{
const auto keyEvent = static_cast< QKeyEvent* >( event );
if( keyEvent->isAutoRepeat() && m_data->blockAutoRepeatKeyEvents )
{
/*
We swallow all auto repeated events to avoid running along
the tab chain by accident.
*/
return true;
}
if ( !isEnabled() && qskIsEnablingKey( keyEvent ) )
{
setEnabled( true );
m_data->blockAutoRepeatKeyEvents = true;
return true;
}
m_data->blockAutoRepeatKeyEvents = false;
break;
}
case QEvent::KeyRelease:
{
if( m_data->blockAutoRepeatKeyEvents )
{
if( !static_cast< QKeyEvent* >( event )->isAutoRepeat() )
m_data->blockAutoRepeatKeyEvents = false;
return true;
}
break;
}
case QEvent::Expose:
case QEvent::FocusIn:
case QEvent::FocusOut:
{
maybeEnable( event->type() != QEvent::FocusOut );
break;
}
}
return Inherited::eventFilter( object, event );
}
void FocusIndicator::windowChangeEvent( QskWindowChangeEvent* event )
{
Inherited::windowChangeEvent( event );
if ( m_data->isAutoDisabling() )
{
if ( auto w = event->oldWindow() )
w->removeEventFilter( this );
if( auto w = event->window() )
{
w->installEventFilter( this );
maybeEnable( true );
}
}
}
void FocusIndicator::timerEvent( QTimerEvent* event )
{
if ( m_data->isAutoDisabling() )
{
if( event->timerId() == m_data->timer.timerId() )
{
setEnabled( false );
return;
}
}
Inherited::timerEvent( event );
}
#include "moc_FocusIndicator.cpp"

View File

@ -1,36 +0,0 @@
#pragma once
#include <QskFocusIndicator.h>
class FocusIndicator : public QskFocusIndicator
{
Q_OBJECT
Q_PROPERTY( int duration READ duration
WRITE setDuration NOTIFY durationChanged )
using Inherited = QskFocusIndicator;
public:
FocusIndicator( QQuickItem* parent = nullptr );
~FocusIndicator() override;
bool eventFilter( QObject*, QEvent* ) override;
void setDuration( int ms );
int duration() const;
Q_SIGNALS:
void durationChanged( int );
protected:
void windowChangeEvent( QskWindowChangeEvent* ) override;
void timerEvent( QTimerEvent* ) override;
private:
void resetTimer();
void maybeEnable( bool );
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};

View File

@ -3,7 +3,6 @@
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "FocusIndicator.h"
#include "label/LabelPage.h"
#include "progressbar/ProgressBarPage.h"
#include "inputs/InputPage.h"
@ -297,7 +296,7 @@ int main( int argc, char* argv[] )
QskWindow window;
window.addItem( mainView );
window.addItem( new FocusIndicator() );
window.addItem( new QskFocusIndicator() );
window.resize( 800, 600 );
window.show();

View File

@ -5,11 +5,13 @@
#include "QskFocusIndicator.h"
#include "QskAspect.h"
#include "QskAnimationHint.h"
#include "QskEvent.h"
#include "QskQuick.h"
#include <qpointer.h>
#include <qquickwindow.h>
#include <qbasictimer.h>
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
@ -52,6 +54,12 @@ static inline QskAspect::Section qskItemSection( const QQuickItem* item )
return QskAspect::Body;
}
static inline bool qskIsEnablingKey( const QKeyEvent* event )
{
// what keys do we want have here ???
return qskIsButtonPressKey( event ) || qskFocusChainIncrement( event );
}
class QskFocusIndicator::PrivateData
{
public:
@ -63,8 +71,16 @@ class QskFocusIndicator::PrivateData
connections.clear();
}
inline bool isAutoDisabling() const { return duration > 0; }
inline bool isAutoEnabling() const { return false; }
QPointer< QQuickItem > clippingItem;
QVector< QMetaObject::Connection > connections;
int duration = 0;
QBasicTimer timer;
bool blockAutoRepeatKeyEvents = false;
};
QskFocusIndicator::QskFocusIndicator( QQuickItem* parent )
@ -73,12 +89,177 @@ QskFocusIndicator::QskFocusIndicator( QQuickItem* parent )
{
setPlacementPolicy( QskPlacementPolicy::Ignore );
connectWindow( window(), true );
setDuration( 4500 );
}
QskFocusIndicator::~QskFocusIndicator()
{
}
void QskFocusIndicator::setDuration( int ms )
{
ms = std::max( ms, 0 );
if ( ms == m_data->duration )
return;
m_data->duration = ms;
if ( m_data->isAutoDisabling() )
{
if ( auto w = window() )
w->installEventFilter( this );
if ( isEnabled() )
{
if ( isInitiallyPainted() )
m_data->timer.start( m_data->duration, this );
else
setEnabled( false );
}
connect( this, &QQuickItem::enabledChanged,
this, &QskFocusIndicator::resetTimer );
}
else
{
if ( auto w = window() )
w->removeEventFilter( this );
setEnabled( true );
disconnect( this, &QQuickItem::enabledChanged,
this, &QskFocusIndicator::resetTimer );
}
Q_EMIT durationChanged( ms );
}
int QskFocusIndicator::duration() const
{
return m_data->duration;
}
void QskFocusIndicator::maybeEnable( bool on )
{
if ( !m_data->isAutoEnabling() )
return;
if ( on )
{
if ( auto w = window() )
{
if ( w->isExposed() && w->isActive() )
setEnabled( true );
}
}
else
{
setEnabled( false );
}
}
void QskFocusIndicator::resetTimer()
{
if ( m_data->isAutoDisabling() )
{
if ( isEnabled() )
{
const auto hint = animationHint( Panel | QskAspect::Color );
m_data->timer.start( m_data->duration + hint.duration, this );
}
else
{
m_data->timer.stop();
}
}
}
bool QskFocusIndicator::eventFilter( QObject* object, QEvent* event )
{
if( ( object != window() ) || !m_data->isAutoDisabling() )
return Inherited::eventFilter( object, event );
switch( static_cast< int >( event->type() ) )
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::ShortcutOverride:
{
if ( m_data->timer.isActive() )
{
// renew the exposed period
m_data->timer.start( m_data->duration, this );
}
break;
}
}
switch( static_cast< int >( event->type() ) )
{
case QEvent::KeyPress:
{
const auto keyEvent = static_cast< QKeyEvent* >( event );
if( keyEvent->isAutoRepeat() && m_data->blockAutoRepeatKeyEvents )
{
/*
We swallow all auto repeated events to avoid running along
the tab chain by accident.
*/
return true;
}
if ( !isEnabled() && qskIsEnablingKey( keyEvent ) )
{
setEnabled( true );
m_data->blockAutoRepeatKeyEvents = true;
return true;
}
m_data->blockAutoRepeatKeyEvents = false;
break;
}
case QEvent::KeyRelease:
{
if( m_data->blockAutoRepeatKeyEvents )
{
if( !static_cast< QKeyEvent* >( event )->isAutoRepeat() )
m_data->blockAutoRepeatKeyEvents = false;
return true;
}
break;
}
case QEvent::Expose:
case QEvent::FocusIn:
case QEvent::FocusOut:
{
maybeEnable( event->type() != QEvent::FocusOut );
break;
}
}
return Inherited::eventFilter( object, event );
}
void QskFocusIndicator::timerEvent( QTimerEvent* event )
{
if ( m_data->isAutoDisabling() )
{
if( event->timerId() == m_data->timer.timerId() )
{
setEnabled( false );
return;
}
}
Inherited::timerEvent( event );
}
bool QskFocusIndicator::contains( const QPointF& ) const
{
// so that tools like Squish do not see it
@ -213,6 +394,18 @@ void QskFocusIndicator::windowChangeEvent( QskWindowChangeEvent* event )
connectWindow( event->window(), true );
onFocusItemChanged();
if ( m_data->isAutoDisabling() )
{
if ( auto w = event->oldWindow() )
w->removeEventFilter( this );
if( auto w = event->window() )
{
w->installEventFilter( this );
maybeEnable( true );
}
}
}
void QskFocusIndicator::connectWindow( const QQuickWindow* window, bool on )

View File

@ -14,17 +14,34 @@ class QSK_EXPORT QskFocusIndicator : public QskControl
using Inherited = QskControl;
Q_PROPERTY( int duration READ duration
WRITE setDuration NOTIFY durationChanged )
public:
QSK_SUBCONTROLS( Panel )
QskFocusIndicator( QQuickItem* parent = nullptr );
~QskFocusIndicator() override;
/*
duration until the indicator goes into disabled state after being enabled.
A duration of 0 disables automatic state changes.
*/
void setDuration( int ms );
int duration() const;
bool contains( const QPointF& ) const override;
QRectF clipRect() const override;
bool eventFilter( QObject*, QEvent* ) override;
Q_SIGNALS:
void durationChanged( int );
protected:
void windowChangeEvent( QskWindowChangeEvent* ) override;
void timerEvent( QTimerEvent* ) override;
virtual QRectF focusRect() const;
void updateFocusFrame();
@ -35,6 +52,9 @@ class QSK_EXPORT QskFocusIndicator : public QskControl
void onFocusItemChanged();
void onFocusItemDestroyed();
void resetTimer();
void maybeEnable( bool );
void connectWindow( const QQuickWindow*, bool on );
QVector< QMetaObject::Connection > connectItem( const QQuickItem* );