diff --git a/examples/gallery/CMakeLists.txt b/examples/gallery/CMakeLists.txt index 3de7a2ba..e229a560 100644 --- a/examples/gallery/CMakeLists.txt +++ b/examples/gallery/CMakeLists.txt @@ -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) diff --git a/examples/gallery/FocusIndicator.cpp b/examples/gallery/FocusIndicator.cpp deleted file mode 100644 index 16c6565c..00000000 --- a/examples/gallery/FocusIndicator.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include "FocusIndicator.h" - -#include -#include - -#include -#include - -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" diff --git a/examples/gallery/FocusIndicator.h b/examples/gallery/FocusIndicator.h deleted file mode 100644 index 6a0238f8..00000000 --- a/examples/gallery/FocusIndicator.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -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; -}; diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index 18cba4f0..6861c778 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -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(); diff --git a/src/controls/QskFocusIndicator.cpp b/src/controls/QskFocusIndicator.cpp index 34380e34..8051de8b 100644 --- a/src/controls/QskFocusIndicator.cpp +++ b/src/controls/QskFocusIndicator.cpp @@ -5,11 +5,13 @@ #include "QskFocusIndicator.h" #include "QskAspect.h" +#include "QskAnimationHint.h" #include "QskEvent.h" #include "QskQuick.h" #include #include +#include QSK_QT_PRIVATE_BEGIN #include @@ -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 ) diff --git a/src/controls/QskFocusIndicator.h b/src/controls/QskFocusIndicator.h index 7c783d9e..63241aab 100644 --- a/src/controls/QskFocusIndicator.h +++ b/src/controls/QskFocusIndicator.h @@ -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* );