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; return nullptr;
} }
QList<QQuickItem *> qskPaintOrderChildItems( const QQuickItem* item )
{
if ( item )
return QQuickItemPrivate::get( item )->paintOrderChildItems();
return QList<QQuickItem *>();
}
const QSGNode* qskItemNode( const QQuickItem* item ) const QSGNode* qskItemNode( const QQuickItem* item )
{ {
if ( item == nullptr ) if ( item == nullptr )

View File

@ -247,6 +247,7 @@ QSK_EXPORT bool qskIsTabFence( const QQuickItem* );
QSK_EXPORT bool qskIsShortcutScope( const QQuickItem* ); QSK_EXPORT bool qskIsShortcutScope( const QQuickItem* );
QSK_EXPORT QQuickItem* qskNearestFocusScope( 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* qskItemNode( const QQuickItem* );
QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* ); QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* );

View File

@ -6,8 +6,6 @@
#include "QskPopup.h" #include "QskPopup.h"
#include "QskAspect.h" #include "QskAspect.h"
#include <QQuickWindow> #include <QQuickWindow>
#include <QGuiApplication>
#include <QStyleHints>
#include <QtMath> #include <QtMath>
QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_BEGIN
@ -17,7 +15,7 @@ QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskPopup, Overlay ) QSK_SUBCONTROL( QskPopup, Overlay )
static void qskSetFocusInScope( QQuickItem* item, bool on ) static void qskSetFocus( QQuickItem* item, bool on )
{ {
if ( item->window() == nullptr ) if ( item->window() == nullptr )
return; 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 namespace
{ {
class InputGrabber final : public QQuickItem class InputGrabber final : public QQuickItem
@ -339,11 +272,11 @@ void QskPopup::grabFocus( bool on )
if ( on ) if ( on )
{ {
qskSetFocusInScope( this, true ); qskSetFocus( this, true );
} }
else else
{ {
QQuickItem* focusItem = nullptr; QQuickItem* successor = nullptr;
if ( m_data->handoverFocus ) if ( m_data->handoverFocus )
{ {
@ -352,13 +285,14 @@ void QskPopup::grabFocus( bool on )
when the active focus gets lost. For the situation of when the active focus gets lost. For the situation of
a popup being closed we try to do it. a popup being closed we try to do it.
*/ */
focusItem = qskNextFocusItem( this ); successor = focusSuccessor();
} }
if ( focusItem ) if ( successor )
qskSetFocusInScope( focusItem, true ); qskSetFocus( successor, true );
else
qskSetFocusInScope( this, false ); if ( hasFocus() )
qskSetFocus( this, false );
} }
} }
@ -388,6 +322,23 @@ bool QskPopup::event( QEvent* event )
return ok; 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() void QskPopup::updateLayout()
{ {
if ( !m_data->isOpen ) if ( !m_data->isOpen )

View File

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