6#include "QskFocusIndicator.h"
8#include "QskAnimationHint.h"
13#include <qquickwindow.h>
14#include <qbasictimer.h>
17#include <private/qquickitem_p.h>
22static inline QRectF qskFocusIndicatorRect(
const QQuickItem* item )
24 if (
auto control = qskControlCast( item ) )
25 return control->focusIndicatorRect();
27 const QVariant v = item->property(
"focusIndicatorRect" );
28 if ( v.canConvert< QRectF >() )
31 return qskItemRect( item );
34static inline QRectF qskFocusIndicatorClipRect(
const QQuickItem* item )
36 QRectF rect( 0.0, 0.0, -1.0, -1.0 );
40 if (
auto control = qskControlCast( item ) )
41 rect = control->focusIndicatorClipRect();
43 rect = item->clipRect();
49static inline QskAspect::Section qskItemSection(
const QQuickItem* item )
51 if (
auto control = qskControlCast( item ) )
52 return control->section();
54 return QskAspect::Body;
57static inline bool qskIsEnablingKey(
const QKeyEvent* event )
60 return qskIsButtonPressKey( event ) || qskFocusChainIncrement( event );
63class QskFocusIndicator::PrivateData
66 void resetConnections()
68 for (
const auto& connection : std::as_const( connections ) )
69 QObject::disconnect( connection );
74 inline bool isAutoDisabling()
const {
return duration > 0; }
75 inline bool isAutoEnabling()
const {
return false; }
77 QPointer< QQuickItem > clippingItem;
78 QVector< QMetaObject::Connection > connections;
83 bool blockAutoRepeatKeyEvents =
false;
86QskFocusIndicator::QskFocusIndicator( QQuickItem* parent )
88 , m_data( new PrivateData() )
91 connectWindow( window(),
true );
96QskFocusIndicator::~QskFocusIndicator()
100void QskFocusIndicator::setDuration(
int ms )
102 ms = std::max( ms, 0 );
103 if ( ms == m_data->duration )
106 m_data->duration = ms;
108 if ( m_data->isAutoDisabling() )
110 if (
auto w = window() )
111 w->installEventFilter(
this );
116 m_data->timer.start( m_data->duration,
this );
121 connect(
this, &QQuickItem::enabledChanged,
122 this, &QskFocusIndicator::resetTimer );
126 if (
auto w = window() )
127 w->removeEventFilter(
this );
131 disconnect(
this, &QQuickItem::enabledChanged,
132 this, &QskFocusIndicator::resetTimer );
135 Q_EMIT durationChanged( ms );
138int QskFocusIndicator::duration()
const
140 return m_data->duration;
143void QskFocusIndicator::maybeEnable(
bool on )
145 if ( !m_data->isAutoEnabling() )
150 if (
auto w = window() )
152 if ( w->isExposed() && w->isActive() )
162void QskFocusIndicator::resetTimer()
164 if ( m_data->isAutoDisabling() )
169 m_data->timer.start( m_data->duration + hint.duration,
this );
173 m_data->timer.stop();
178bool QskFocusIndicator::eventFilter( QObject*
object, QEvent* event )
180 if( (
object != window() ) || !m_data->isAutoDisabling() )
181 return Inherited::eventFilter(
object, event );
183 switch(
static_cast< int >( event->type() ) )
185 case QEvent::KeyPress:
186 case QEvent::KeyRelease:
187 case QEvent::ShortcutOverride:
189 if ( m_data->timer.isActive() )
192 m_data->timer.start( m_data->duration,
this );
198 switch(
static_cast< int >( event->type() ) )
200 case QEvent::KeyPress:
202 const auto keyEvent =
static_cast< QKeyEvent*
>( event );
204 if( keyEvent->isAutoRepeat() && m_data->blockAutoRepeatKeyEvents )
213 if ( !isEnabled() && qskIsEnablingKey( keyEvent ) )
216 m_data->blockAutoRepeatKeyEvents =
true;
220 m_data->blockAutoRepeatKeyEvents =
false;
224 case QEvent::KeyRelease:
226 if( m_data->blockAutoRepeatKeyEvents )
228 if( !
static_cast< QKeyEvent*
>( event )->isAutoRepeat() )
229 m_data->blockAutoRepeatKeyEvents =
false;
238 case QEvent::FocusIn:
239 case QEvent::FocusOut:
241 maybeEnable( event->type() != QEvent::FocusOut );
246 return Inherited::eventFilter(
object, event );
249void QskFocusIndicator::timerEvent( QTimerEvent* event )
251 if ( m_data->isAutoDisabling() )
253 if( event->timerId() == m_data->timer.timerId() )
260 Inherited::timerEvent( event );
263bool QskFocusIndicator::contains(
const QPointF& )
const
269QRectF QskFocusIndicator::clipRect()
const
271 if ( m_data->clippingItem )
273 auto rect = qskFocusIndicatorClipRect( m_data->clippingItem );
274 rect = mapRectFromItem( m_data->clippingItem,
rect );
279 return Inherited::clipRect();
282void QskFocusIndicator::onFocusItemGeometryChanged()
287void QskFocusIndicator::onWindowSizeChanged(
int )
292void QskFocusIndicator::onFocusItemDestroyed()
294 m_data->resetConnections();
298void QskFocusIndicator::onFocusItemChanged()
300 m_data->resetConnections();
302 if ( !( window() && window()->contentItem() ) )
306 setParentItem( window()->contentItem() );
309 const auto focusItem = window()->activeFocusItem();
310 QQuickItem* clippingItem =
nullptr;
312 if ( focusItem && ( focusItem != window()->contentItem() ) )
314 setSection( qskItemSection( focusItem ) );
316 auto item = focusItem;
317 m_data->connections += connectItem( item );
319 while (
auto itemParent = item->parentItem() )
321 m_data->connections += connectItem( itemParent );
323 if ( clippingItem ==
nullptr && itemParent->clip() )
324 clippingItem = itemParent;
330 m_data->clippingItem = clippingItem;
334void QskFocusIndicator::updateFocusFrame()
336 QRectF r = focusRect();
337 setVisible( !r.isEmpty() );
343 if (
auto w = window() )
345 QRectF clipRect( 0, 0, w->width(), w->height() );
346 clipRect = parentItem()->mapRectFromScene( clipRect );
348 r = r.intersected( clipRect );
353 const auto clipRect = qskFocusIndicatorClipRect( m_data->clippingItem );
354 setClip( !clipRect.isEmpty() );
365 QQuickItemPrivate::get(
this )->dirty( QQuickItemPrivate::Size );
372QRectF QskFocusIndicator::focusRect()
const
374 if ( window() && parentItem() )
376 const auto item = window()->activeFocusItem();
378 if ( item && ( item !=
this ) && item->isVisible() &&
379 ( item != window()->contentItem() ) )
381 const auto rect = qskFocusIndicatorRect( item );
382 return parentItem()->mapRectFromItem( item,
rect );
393 connectWindow( event->oldWindow(),
false );
394 connectWindow( event->window(),
true );
396 onFocusItemChanged();
398 if ( m_data->isAutoDisabling() )
400 if (
auto w = event->oldWindow() )
401 w->removeEventFilter(
this );
403 if(
auto w = event->window() )
405 w->installEventFilter(
this );
411void QskFocusIndicator::connectWindow(
const QQuickWindow* window,
bool on )
413 if ( window ==
nullptr )
418 connect( window, &QQuickWindow::activeFocusItemChanged,
419 this, &QskFocusIndicator::onFocusItemChanged );
421 connect( window, &QQuickWindow::widthChanged,
422 this, &QskFocusIndicator::onWindowSizeChanged );
424 connect( window, &QQuickWindow::heightChanged,
425 this, &QskFocusIndicator::onWindowSizeChanged );
429 disconnect( window, &QQuickWindow::activeFocusItemChanged,
430 this, &QskFocusIndicator::onFocusItemChanged );
432 disconnect( window, &QQuickWindow::widthChanged,
433 this, &QskFocusIndicator::onWindowSizeChanged );
435 disconnect( window, &QQuickWindow::heightChanged,
436 this, &QskFocusIndicator::onWindowSizeChanged );
440QVector< QMetaObject::Connection > QskFocusIndicator::connectItem(
const QQuickItem* sender )
442 QVector< QMetaObject::Connection > c;
445 c += QObject::connect( sender, &QObject::destroyed,
446 this, &QskFocusIndicator::onFocusItemDestroyed );
448 const auto method = &QskFocusIndicator::onFocusItemGeometryChanged;
450 c += QObject::connect( sender, &QQuickItem::xChanged,
this, method );
451 c += QObject::connect( sender, &QQuickItem::yChanged,
this, method );
452 c += QObject::connect( sender, &QQuickItem::widthChanged,
this, method );
453 c += QObject::connect( sender, &QQuickItem::heightChanged,
this, method );
454 c += QObject::connect( sender, &QQuickItem::visibleChanged,
this, method );
456 if (
const auto control = qskControlCast( sender ) )
462 if ( sender->metaObject()->indexOfSignal(
"focusIndicatorRectChanged()" ) >= 0 )
465 this, SLOT(onFocusItemGeometryChanged()) );
472#include "moc_QskFocusIndicator.cpp"
void focusIndicatorRectChanged()
void windowChangeEvent(QskWindowChangeEvent *) override
bool isInitiallyPainted() const
virtual void windowChangeEvent(QskWindowChangeEvent *)
void setGeometry(qreal x, qreal y, qreal width, qreal height)
QMarginsF paddingHint(QskAspect, QskSkinHintStatus *=nullptr) const
Retrieves a padding hint.
QskAnimationHint animationHint(QskAspect, QskSkinHintStatus *=nullptr) const