focus navigation improved

This commit is contained in:
Uwe Rathmann 2018-01-24 10:14:50 +01:00
parent 3f0075f616
commit 5d91175d05
7 changed files with 124 additions and 15 deletions

View File

@ -42,6 +42,11 @@ static inline void qskSendEventTo( QObject* object, QEvent::Type type )
QCoreApplication::sendEvent( object, &event );
}
bool qskIsItemComplete( const QQuickItem* item )
{
return QQuickItemPrivate::get( item )->componentComplete;
}
bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child )
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
@ -413,6 +418,12 @@ public:
bool isWheelEnabled : 1;
};
static void qskUpdateControlFlags( QskControl::Flags flags, QskControl* control )
{
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
d->updateControlFlags( flags );
}
QskControl::QskControl( QQuickItem* parent ):
Inherited( *( new QskControlPrivate() ), parent )
{
@ -443,16 +454,18 @@ QskControl::QskControl( QQuickItem* parent ):
qskRegistry->insert( this );
}
static void qskUpdateControlFlags( QskControl::Flags flags, QskControl* control )
{
auto d = static_cast< QskControlPrivate* >( QQuickItemPrivate::get( control ) );
d->updateControlFlags( flags );
}
QskControl::~QskControl()
{
if ( qskRegistry )
qskRegistry->remove( this );
/*
We set componentComplete to false, so that operations
that are triggered by detaching the item from its parent
can be aware of the about-to-delete state.
*/
Q_D( QskControl );
d->componentComplete = false;
}
const char* QskControl::className() const
@ -1089,8 +1102,11 @@ bool QskControl::event( QEvent* event )
if ( auto focusItem = nextItemInFocusChain( true ) )
{
if ( qskIsAncestorOf( this, focusItem ) )
if ( qskIsItemComplete( focusItem )
&& qskIsAncestorOf( this, focusItem ) )
{
focusItem->setFocus( true );
}
}
}
break;

View File

@ -241,6 +241,7 @@ inline QSizeF QskControl::sizeHint() const
return effectiveConstraint( Qt::PreferredSize );
}
QSK_EXPORT bool qskIsItemComplete( const QQuickItem* item );
QSK_EXPORT bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem *child );
QSK_EXPORT bool qskIsTransparentForPositioner( const QQuickItem* );
QSK_EXPORT bool qskIsTabFence( const QQuickItem* );

View File

@ -21,6 +21,23 @@ static inline bool qskIsUpdateBlocked( const QQuickItem* item )
if ( const auto control = qobject_cast< const QskControl* >( item ) )
return control->testControlFlag( QskControl::DeferredUpdate );
}
#if 0
/*
Blocking items, that are outside the window would be easy,
but we have not yet found a performant way to send update notifications
when an item enters/leaves the window. TODO ...
*/
else if ( const auto control = qobject_cast< const QskControl* >( item ) )
{
const QRectF itemRect( item->mapToScene( QPointF() ), item->size() );
const QRectF sceneRect( 0, 0, item->window()->width(), item->window()->height() );
return !itemRect.intersects( sceneRect );
}
}
#endif
return false;
}

View File

@ -143,12 +143,13 @@ QRectF QskFocusIndicator::focusRect() const
{
if ( window() && parentItem() )
{
const QQuickItem* focusItem = window()->activeFocusItem();
if ( focusItem && ( focusItem != this )
&& ( focusItem != window()->contentItem() ) )
const QQuickItem* item = window()->activeFocusItem();
if ( item && ( item != this ) && item->isVisible()
&& ( item != window()->contentItem() ) )
{
const auto rect = qskFocusIndicatorRect( focusItem );
return parentItem()->mapRectFromItem( focusItem, rect );
const auto rect = qskFocusIndicatorRect( item );
return parentItem()->mapRectFromItem( item, rect );
}
}

View File

@ -331,8 +331,11 @@ QQuickItem* QskPopup::focusSuccessor() const
{
auto child = *it;
if ( child != this && child->isFocusScope() )
if ( ( child != this ) && child->isFocusScope()
&& child->activeFocusOnTab() && child->isVisible() )
{
return child;
}
}
}

View File

@ -24,12 +24,29 @@ QSK_QT_PRIVATE_END
static void qskResolveLocale( QskWindow* );
static bool qskEnforcedSkin = false;
static void qskSendEventTo( QObject* object, QEvent::Type type )
static inline void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
static QQuickItem* qskDefaultFocusItem( QQuickWindow* window )
{
const auto children = qskPaintOrderChildItems( window->contentItem() );
for ( auto it = children.crbegin(); it != children.crend(); ++it)
{
auto child = *it;
if ( child->isFocusScope() && child->isVisible()
&& child->isEnabled() && child->activeFocusOnTab() )
{
return child;
}
}
return window->contentItem()->nextItemInFocusChain( true );
}
namespace
{
class ChildListener final : public QQuickItemChangeListener
@ -187,7 +204,6 @@ bool QskWindow::autoLayoutChildren() const
return d->autoLayoutChildren;
}
void QskWindow::addItem( QQuickItem* item )
{
if ( item == nullptr )
@ -246,9 +262,49 @@ bool QskWindow::event( QEvent* event )
return Inherited::event( event );
}
void QskWindow::keyPressEvent( QKeyEvent* event )
{
if ( !( event->modifiers() & ( Qt::ControlModifier | Qt::AltModifier ) ) )
{
if ( ( event->key() == Qt::Key_Backtab ) || ( event->key() == Qt::Key_Tab ) )
{
auto focusItem = activeFocusItem();
if ( focusItem == nullptr || focusItem == contentItem() )
{
/*
The Qt/Quick implementation for navigating along the
focus tab chain gives unsufficient results, when the
starting point is the root item. In this specific
situation we also have to include all items being
tab fences into consideration.
In certain situations Qt/Quick gets even stuck in a non
terminating loop: see Qt-Bug 65943
So we better block the focus navigation and find the
next focus item on our own.
*/
ensureFocus( Qt::TabFocusReason );
event->accept();
return;
}
}
}
Inherited::keyPressEvent( event );
}
void QskWindow::keyReleaseEvent( QKeyEvent* event )
{
Inherited::keyReleaseEvent( event );
}
void QskWindow::exposeEvent( QExposeEvent* event )
{
ensureFocus( Qt::OtherFocusReason );
layoutItems();
Inherited::exposeEvent( event );
}
@ -391,6 +447,18 @@ void QskWindow::layoutItems()
}
}
void QskWindow::ensureFocus( Qt::FocusReason reason )
{
auto focusItem = contentItem()->scopedFocusItem();
if ( focusItem == nullptr )
{
focusItem = qskDefaultFocusItem( this );
if ( focusItem )
focusItem->setFocus( true, reason );
}
}
void QskWindow::setCustomRenderMode( const char* mode )
{
class RenderJob final : public QRunnable

View File

@ -82,8 +82,11 @@ protected:
virtual bool event( QEvent* ) override;
virtual void resizeEvent( QResizeEvent* ) override;
virtual void exposeEvent( QExposeEvent* ) override;
virtual void keyPressEvent(QKeyEvent *) override;
virtual void keyReleaseEvent(QKeyEvent *) override;
virtual void layoutItems();
virtual void ensureFocus( Qt::FocusReason );
private:
void resizeFramebuffer();