improving focus handling after closing a popup

This commit is contained in:
Uwe Rathmann 2018-01-21 14:10:12 +01:00
parent 8cb65fefa6
commit 3f0075f616
4 changed files with 37 additions and 75 deletions

View File

@ -114,6 +114,14 @@ QQuickItem* qskNearestFocusScope( const QQuickItem* item )
return nullptr;
}
QList<QQuickItem *> qskPaintOrderChildItems( const QQuickItem* item )
{
if ( item )
return QQuickItemPrivate::get( item )->paintOrderChildItems();
return QList<QQuickItem *>();
}
const QSGNode* qskItemNode( const QQuickItem* item )
{
if ( item == nullptr )

View File

@ -247,6 +247,7 @@ QSK_EXPORT bool qskIsTabFence( const QQuickItem* );
QSK_EXPORT bool qskIsShortcutScope( const QQuickItem* );
QSK_EXPORT QQuickItem* qskNearestFocusScope( const QQuickItem* );
QSK_EXPORT QList<QQuickItem *> qskPaintOrderChildItems( const QQuickItem* );
QSK_EXPORT const QSGNode* qskItemNode( const QQuickItem* );
QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* );

View File

@ -6,8 +6,6 @@
#include "QskPopup.h"
#include "QskAspect.h"
#include <QQuickWindow>
#include <QGuiApplication>
#include <QStyleHints>
#include <QtMath>
QSK_QT_PRIVATE_BEGIN
@ -17,7 +15,7 @@ QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskPopup, Overlay )
static void qskSetFocusInScope( QQuickItem* item, bool on )
static void qskSetFocus( QQuickItem* item, bool on )
{
if ( item->window() == nullptr )
return;
@ -39,71 +37,6 @@ static void qskSetFocusInScope( QQuickItem* item, bool on )
}
}
static QQuickItem* qskNextFocusItem( const QskPopup* popup )
{
if ( popup == nullptr || popup->parentItem() == nullptr )
return nullptr;
const auto children = popup->parentItem()->childItems();
if ( children.count() <= 1 )
return nullptr;
QskPopup* modalPopup = nullptr;
for ( auto child : children )
{
if ( ( child != popup ) && child->isVisible() )
{
if ( auto otherPopup = qobject_cast< QskPopup* >( child ) )
{
if ( !otherPopup->isModal() || ( modalPopup != nullptr ) )
{
/*
We can't decide, wether to give the focus to
one of the popups or the top level item
*/
return nullptr;
}
modalPopup = otherPopup;
}
}
}
if ( modalPopup )
{
// Exactly one popup, that is modal.
return modalPopup;
}
const auto tabFocusBehavior = QGuiApplication::styleHints()->tabFocusBehavior();
int i = 0;
while( children[i] != popup )
i++;
for ( int j = i - 1; j != i; j-- )
{
auto item = children[j];
if ( item->isEnabled() && item->isVisible() )
{
if ( item->activeFocusOnTab() )
{
if ( ( tabFocusBehavior == Qt::TabFocusAllControls ) ||
QQuickItemPrivate::canAcceptTabFocus( item ) )
{
return item;
}
}
}
if ( j == 0 )
j = children.count();
}
return nullptr;
}
namespace
{
class InputGrabber final : public QQuickItem
@ -339,11 +272,11 @@ void QskPopup::grabFocus( bool on )
if ( on )
{
qskSetFocusInScope( this, true );
qskSetFocus( this, true );
}
else
{
QQuickItem* focusItem = nullptr;
QQuickItem* successor = nullptr;
if ( m_data->handoverFocus )
{
@ -352,13 +285,14 @@ void QskPopup::grabFocus( bool on )
when the active focus gets lost. For the situation of
a popup being closed we try to do it.
*/
focusItem = qskNextFocusItem( this );
successor = focusSuccessor();
}
if ( focusItem )
qskSetFocusInScope( focusItem, true );
else
qskSetFocusInScope( this, false );
if ( successor )
qskSetFocus( successor, true );
if ( hasFocus() )
qskSetFocus( this, false );
}
}
@ -388,6 +322,23 @@ bool QskPopup::event( QEvent* event )
return ok;
}
QQuickItem* QskPopup::focusSuccessor() const
{
if ( const auto scope = qskNearestFocusScope( this ) )
{
const auto children = qskPaintOrderChildItems( scope );
for ( auto it = children.crbegin(); it != children.crend(); ++it)
{
auto child = *it;
if ( child != this && child->isFocusScope() )
return child;
}
}
return nullptr;
}
void QskPopup::updateLayout()
{
if ( !m_data->isOpen )

View File

@ -43,6 +43,8 @@ protected:
virtual void itemChange( QQuickItem::ItemChange,
const QQuickItem::ItemChangeData& ) override;
virtual QQuickItem* focusSuccessor() const;
void grabFocus( bool );
private: