better focus handover after closing a popup
This commit is contained in:
parent
2cdabf34d6
commit
8cb65fefa6
@ -36,6 +36,8 @@ public:
|
||||
ButtonBox( QQuickItem* parent = nullptr ):
|
||||
QskLinearBox( Qt::Horizontal, 2, parent )
|
||||
{
|
||||
setObjectName( "ButtonBox" );
|
||||
|
||||
setMargins( 10 );
|
||||
setSpacing( 5 );
|
||||
|
||||
@ -130,9 +132,19 @@ int main( int argc, char* argv[] )
|
||||
qskDialog->setPolicy( QskDialog::EmbeddedBox );
|
||||
|
||||
ButtonBox* box = new ButtonBox();
|
||||
box->itemAtIndex( 0 )->setFocus( true );
|
||||
|
||||
box->setObjectName( "ButtonBox" );
|
||||
/*
|
||||
To avoid losing the focus, when a message box is executed
|
||||
we have to define the "main window" ( here a ButtonBox ) to
|
||||
be a focusScope.
|
||||
*/
|
||||
box->setFlag( QQuickItem::ItemIsFocusScope, true );
|
||||
box->setTabFence( true );
|
||||
box->setFocusPolicy( Qt::TabFocus );
|
||||
|
||||
// setting the initial focus
|
||||
box->itemAtIndex( 0 )->setFocus( true );
|
||||
box->setFocus( true );
|
||||
|
||||
QskWindow window;
|
||||
window.addItem( box );
|
||||
|
@ -93,6 +93,27 @@ bool qskIsTransparentForPositioner( const QQuickItem* item )
|
||||
return QQuickItemPrivate::get( item )->isTransparentForPositioner();
|
||||
}
|
||||
|
||||
QQuickItem* qskNearestFocusScope( const QQuickItem* item )
|
||||
{
|
||||
if ( item )
|
||||
{
|
||||
for ( QQuickItem* scope = item->parentItem();
|
||||
scope != nullptr; scope = scope->parentItem() )
|
||||
{
|
||||
if ( scope->isFocusScope() )
|
||||
return scope;
|
||||
}
|
||||
|
||||
/*
|
||||
As the default setting of the root item is to be a focus scope
|
||||
we usually never get here - beside the flag has been explicitely
|
||||
disabled in application code.
|
||||
*/
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const QSGNode* qskItemNode( const QQuickItem* item )
|
||||
{
|
||||
if ( item == nullptr )
|
||||
|
@ -246,6 +246,8 @@ QSK_EXPORT bool qskIsTransparentForPositioner( const QQuickItem* );
|
||||
QSK_EXPORT bool qskIsTabFence( const QQuickItem* );
|
||||
QSK_EXPORT bool qskIsShortcutScope( const QQuickItem* );
|
||||
|
||||
QSK_EXPORT QQuickItem* qskNearestFocusScope( const QQuickItem* );
|
||||
|
||||
QSK_EXPORT const QSGNode* qskItemNode( const QQuickItem* );
|
||||
QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* );
|
||||
|
||||
|
@ -19,6 +19,7 @@ static void qskSetupGeometryConnections(
|
||||
QObject::connect( sender, SIGNAL( yChanged() ), receiver, method );
|
||||
QObject::connect( sender, SIGNAL( widthChanged() ), receiver, method );
|
||||
QObject::connect( sender, SIGNAL( heightChanged() ), receiver, method );
|
||||
QObject::connect( sender, SIGNAL( visibleChanged() ), receiver, method );
|
||||
|
||||
bool hasIndicatorSignal = ( qobject_cast< const QskControl* >( sender ) != nullptr );
|
||||
if ( !hasIndicatorSignal )
|
||||
@ -143,7 +144,8 @@ QRectF QskFocusIndicator::focusRect() const
|
||||
if ( window() && parentItem() )
|
||||
{
|
||||
const QQuickItem* focusItem = window()->activeFocusItem();
|
||||
if ( focusItem && ( focusItem != window()->contentItem() ) )
|
||||
if ( focusItem && ( focusItem != this )
|
||||
&& ( focusItem != window()->contentItem() ) )
|
||||
{
|
||||
const auto rect = qskFocusIndicatorRect( focusItem );
|
||||
return parentItem()->mapRectFromItem( focusItem, rect );
|
||||
|
@ -6,7 +6,8 @@
|
||||
#include "QskPopup.h"
|
||||
#include "QskAspect.h"
|
||||
#include <QQuickWindow>
|
||||
#include <QPointer>
|
||||
#include <QGuiApplication>
|
||||
#include <QStyleHints>
|
||||
#include <QtMath>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
@ -16,25 +17,7 @@ QSK_QT_PRIVATE_END
|
||||
|
||||
QSK_SUBCONTROL( QskPopup, Overlay )
|
||||
|
||||
static inline QQuickItem* qskNearestFocusScope( const QQuickItem* item )
|
||||
{
|
||||
for ( QQuickItem* scope = item->parentItem();
|
||||
scope != nullptr; scope = scope->parentItem() )
|
||||
{
|
||||
if ( scope->isFocusScope() )
|
||||
return scope;
|
||||
}
|
||||
|
||||
/*
|
||||
As the default setting of the root item is to be a focus scope
|
||||
we usually never get here - beside the flag has been explicitely
|
||||
disabled in application code.
|
||||
*/
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void qskSetFocus( QQuickItem* item, bool on )
|
||||
static void qskSetFocusInScope( QQuickItem* item, bool on )
|
||||
{
|
||||
if ( item->window() == nullptr )
|
||||
return;
|
||||
@ -45,8 +28,7 @@ static void qskSetFocus( QQuickItem* item, bool on )
|
||||
QQuickWindowPrivate::setFocusInScope/clearFocusInScope directly,
|
||||
*/
|
||||
|
||||
const auto scope = qskNearestFocusScope( item );
|
||||
if ( scope )
|
||||
if ( const auto scope = qskNearestFocusScope( item ) )
|
||||
{
|
||||
auto dw = QQuickWindowPrivate::get( item->window() );
|
||||
|
||||
@ -57,6 +39,71 @@ static void qskSetFocus( 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
|
||||
@ -194,16 +241,17 @@ public:
|
||||
inputGrabber( nullptr ),
|
||||
isModal( false ),
|
||||
isOpen( false ),
|
||||
autoGrabFocus( true )
|
||||
autoGrabFocus( true ),
|
||||
handoverFocus( true )
|
||||
{
|
||||
}
|
||||
|
||||
InputGrabber* inputGrabber;
|
||||
QPointer< QQuickItem > initialFocusItem;
|
||||
|
||||
bool isModal : 1;
|
||||
bool isOpen : 1;
|
||||
bool autoGrabFocus : 1;
|
||||
bool handoverFocus : 1;
|
||||
};
|
||||
|
||||
QskPopup::QskPopup( QQuickItem* parent ):
|
||||
@ -217,7 +265,7 @@ QskPopup::QskPopup( QQuickItem* parent ):
|
||||
|
||||
setFlag( ItemIsFocusScope, true );
|
||||
setTabFence( true );
|
||||
setFocusPolicy( Qt::ClickFocus );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
}
|
||||
|
||||
QskPopup::~QskPopup()
|
||||
@ -286,35 +334,31 @@ bool QskPopup::hasOverlay() const
|
||||
|
||||
void QskPopup::grabFocus( bool on )
|
||||
{
|
||||
/*
|
||||
Note, that we are grabbing the local focus, what only
|
||||
has an effect on the active focus, when all surrounding
|
||||
focus scopes already have the focus.
|
||||
*/
|
||||
|
||||
if ( on == hasFocus() )
|
||||
return;
|
||||
|
||||
if ( on )
|
||||
{
|
||||
if ( auto scope = qskNearestFocusScope( this ) )
|
||||
{
|
||||
m_data->initialFocusItem = scope->scopedFocusItem();
|
||||
qskSetFocus( this, true );
|
||||
}
|
||||
qskSetFocusInScope( this, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
QQuickItem* focusItem = m_data->initialFocusItem;
|
||||
m_data->initialFocusItem = nullptr;
|
||||
QQuickItem* focusItem = nullptr;
|
||||
|
||||
if ( focusItem == nullptr )
|
||||
focusItem = nextItemInFocusChain( false );
|
||||
if ( m_data->handoverFocus )
|
||||
{
|
||||
/*
|
||||
Qt/Quick does not handover the focus to another item,
|
||||
when the active focus gets lost. For the situation of
|
||||
a popup being closed we try to do it.
|
||||
*/
|
||||
focusItem = qskNextFocusItem( this );
|
||||
}
|
||||
|
||||
if ( focusItem )
|
||||
qskSetFocus( focusItem, true );
|
||||
qskSetFocusInScope( focusItem, true );
|
||||
else
|
||||
qskSetFocus( this, false );
|
||||
qskSetFocusInScope( this, false );
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,13 +428,6 @@ void QskPopup::itemChange( QQuickItem::ItemChange change,
|
||||
|
||||
break;
|
||||
}
|
||||
case QQuickItem::ItemActiveFocusHasChanged:
|
||||
{
|
||||
if ( !hasFocus() )
|
||||
m_data->initialFocusItem = nullptr;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
;
|
||||
|
@ -108,9 +108,6 @@ int QskTabView::insertTab( int index, QskTabButton* button, QQuickItem* item )
|
||||
index = m_data->tabBar->insertTab( index, button );
|
||||
m_data->stackBox->insertItem( index, item, Qt::Alignment() );
|
||||
|
||||
if ( m_data->tabBar->count() == 1 )
|
||||
button->setFocus( true );
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user