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 ):
|
ButtonBox( QQuickItem* parent = nullptr ):
|
||||||
QskLinearBox( Qt::Horizontal, 2, parent )
|
QskLinearBox( Qt::Horizontal, 2, parent )
|
||||||
{
|
{
|
||||||
|
setObjectName( "ButtonBox" );
|
||||||
|
|
||||||
setMargins( 10 );
|
setMargins( 10 );
|
||||||
setSpacing( 5 );
|
setSpacing( 5 );
|
||||||
|
|
||||||
@ -130,9 +132,19 @@ int main( int argc, char* argv[] )
|
|||||||
qskDialog->setPolicy( QskDialog::EmbeddedBox );
|
qskDialog->setPolicy( QskDialog::EmbeddedBox );
|
||||||
|
|
||||||
ButtonBox* box = new ButtonBox();
|
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;
|
QskWindow window;
|
||||||
window.addItem( box );
|
window.addItem( box );
|
||||||
|
@ -93,6 +93,27 @@ bool qskIsTransparentForPositioner( const QQuickItem* item )
|
|||||||
return QQuickItemPrivate::get( item )->isTransparentForPositioner();
|
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 )
|
const QSGNode* qskItemNode( const QQuickItem* item )
|
||||||
{
|
{
|
||||||
if ( item == nullptr )
|
if ( item == nullptr )
|
||||||
|
@ -246,6 +246,8 @@ QSK_EXPORT bool qskIsTransparentForPositioner( const QQuickItem* );
|
|||||||
QSK_EXPORT bool qskIsTabFence( const QQuickItem* );
|
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 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* );
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ static void qskSetupGeometryConnections(
|
|||||||
QObject::connect( sender, SIGNAL( yChanged() ), receiver, method );
|
QObject::connect( sender, SIGNAL( yChanged() ), receiver, method );
|
||||||
QObject::connect( sender, SIGNAL( widthChanged() ), receiver, method );
|
QObject::connect( sender, SIGNAL( widthChanged() ), receiver, method );
|
||||||
QObject::connect( sender, SIGNAL( heightChanged() ), receiver, method );
|
QObject::connect( sender, SIGNAL( heightChanged() ), receiver, method );
|
||||||
|
QObject::connect( sender, SIGNAL( visibleChanged() ), receiver, method );
|
||||||
|
|
||||||
bool hasIndicatorSignal = ( qobject_cast< const QskControl* >( sender ) != nullptr );
|
bool hasIndicatorSignal = ( qobject_cast< const QskControl* >( sender ) != nullptr );
|
||||||
if ( !hasIndicatorSignal )
|
if ( !hasIndicatorSignal )
|
||||||
@ -143,7 +144,8 @@ QRectF QskFocusIndicator::focusRect() const
|
|||||||
if ( window() && parentItem() )
|
if ( window() && parentItem() )
|
||||||
{
|
{
|
||||||
const QQuickItem* focusItem = window()->activeFocusItem();
|
const QQuickItem* focusItem = window()->activeFocusItem();
|
||||||
if ( focusItem && ( focusItem != window()->contentItem() ) )
|
if ( focusItem && ( focusItem != this )
|
||||||
|
&& ( focusItem != window()->contentItem() ) )
|
||||||
{
|
{
|
||||||
const auto rect = qskFocusIndicatorRect( focusItem );
|
const auto rect = qskFocusIndicatorRect( focusItem );
|
||||||
return parentItem()->mapRectFromItem( focusItem, rect );
|
return parentItem()->mapRectFromItem( focusItem, rect );
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
#include "QskPopup.h"
|
#include "QskPopup.h"
|
||||||
#include "QskAspect.h"
|
#include "QskAspect.h"
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
#include <QPointer>
|
#include <QGuiApplication>
|
||||||
|
#include <QStyleHints>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
QSK_QT_PRIVATE_BEGIN
|
QSK_QT_PRIVATE_BEGIN
|
||||||
@ -16,25 +17,7 @@ QSK_QT_PRIVATE_END
|
|||||||
|
|
||||||
QSK_SUBCONTROL( QskPopup, Overlay )
|
QSK_SUBCONTROL( QskPopup, Overlay )
|
||||||
|
|
||||||
static inline QQuickItem* qskNearestFocusScope( const QQuickItem* item )
|
static void qskSetFocusInScope( QQuickItem* item, bool on )
|
||||||
{
|
|
||||||
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 )
|
|
||||||
{
|
{
|
||||||
if ( item->window() == nullptr )
|
if ( item->window() == nullptr )
|
||||||
return;
|
return;
|
||||||
@ -45,8 +28,7 @@ static void qskSetFocus( QQuickItem* item, bool on )
|
|||||||
QQuickWindowPrivate::setFocusInScope/clearFocusInScope directly,
|
QQuickWindowPrivate::setFocusInScope/clearFocusInScope directly,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const auto scope = qskNearestFocusScope( item );
|
if ( const auto scope = qskNearestFocusScope( item ) )
|
||||||
if ( scope )
|
|
||||||
{
|
{
|
||||||
auto dw = QQuickWindowPrivate::get( item->window() );
|
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
|
namespace
|
||||||
{
|
{
|
||||||
class InputGrabber final : public QQuickItem
|
class InputGrabber final : public QQuickItem
|
||||||
@ -194,16 +241,17 @@ public:
|
|||||||
inputGrabber( nullptr ),
|
inputGrabber( nullptr ),
|
||||||
isModal( false ),
|
isModal( false ),
|
||||||
isOpen( false ),
|
isOpen( false ),
|
||||||
autoGrabFocus( true )
|
autoGrabFocus( true ),
|
||||||
|
handoverFocus( true )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
InputGrabber* inputGrabber;
|
InputGrabber* inputGrabber;
|
||||||
QPointer< QQuickItem > initialFocusItem;
|
|
||||||
|
|
||||||
bool isModal : 1;
|
bool isModal : 1;
|
||||||
bool isOpen : 1;
|
bool isOpen : 1;
|
||||||
bool autoGrabFocus : 1;
|
bool autoGrabFocus : 1;
|
||||||
|
bool handoverFocus : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
QskPopup::QskPopup( QQuickItem* parent ):
|
QskPopup::QskPopup( QQuickItem* parent ):
|
||||||
@ -217,7 +265,7 @@ QskPopup::QskPopup( QQuickItem* parent ):
|
|||||||
|
|
||||||
setFlag( ItemIsFocusScope, true );
|
setFlag( ItemIsFocusScope, true );
|
||||||
setTabFence( true );
|
setTabFence( true );
|
||||||
setFocusPolicy( Qt::ClickFocus );
|
setFocusPolicy( Qt::StrongFocus );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskPopup::~QskPopup()
|
QskPopup::~QskPopup()
|
||||||
@ -286,35 +334,31 @@ bool QskPopup::hasOverlay() const
|
|||||||
|
|
||||||
void QskPopup::grabFocus( bool on )
|
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() )
|
if ( on == hasFocus() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( on )
|
if ( on )
|
||||||
{
|
{
|
||||||
if ( auto scope = qskNearestFocusScope( this ) )
|
qskSetFocusInScope( this, true );
|
||||||
{
|
|
||||||
m_data->initialFocusItem = scope->scopedFocusItem();
|
|
||||||
qskSetFocus( this, true );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QQuickItem* focusItem = m_data->initialFocusItem;
|
QQuickItem* focusItem = nullptr;
|
||||||
m_data->initialFocusItem = nullptr;
|
|
||||||
|
|
||||||
if ( focusItem == nullptr )
|
if ( m_data->handoverFocus )
|
||||||
focusItem = nextItemInFocusChain( false );
|
{
|
||||||
|
/*
|
||||||
|
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 )
|
if ( focusItem )
|
||||||
qskSetFocus( focusItem, true );
|
qskSetFocusInScope( focusItem, true );
|
||||||
else
|
else
|
||||||
qskSetFocus( this, false );
|
qskSetFocusInScope( this, false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,13 +428,6 @@ void QskPopup::itemChange( QQuickItem::ItemChange change,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QQuickItem::ItemActiveFocusHasChanged:
|
|
||||||
{
|
|
||||||
if ( !hasFocus() )
|
|
||||||
m_data->initialFocusItem = nullptr;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
@ -108,9 +108,6 @@ int QskTabView::insertTab( int index, QskTabButton* button, QQuickItem* item )
|
|||||||
index = m_data->tabBar->insertTab( index, button );
|
index = m_data->tabBar->insertTab( index, button );
|
||||||
m_data->stackBox->insertItem( index, item, Qt::Alignment() );
|
m_data->stackBox->insertItem( index, item, Qt::Alignment() );
|
||||||
|
|
||||||
if ( m_data->tabBar->count() == 1 )
|
|
||||||
button->setFocus( true );
|
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user