better focus handling for popups
This commit is contained in:
parent
ee7a404ae1
commit
33efb2d17a
@ -6,6 +6,7 @@
|
|||||||
#include "QskPopup.h"
|
#include "QskPopup.h"
|
||||||
#include "QskAspect.h"
|
#include "QskAspect.h"
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
#include <QPointer>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
QSK_QT_PRIVATE_BEGIN
|
QSK_QT_PRIVATE_BEGIN
|
||||||
@ -14,6 +15,18 @@ QSK_QT_PRIVATE_END
|
|||||||
|
|
||||||
QSK_SUBCONTROL( QskPopup, Overlay )
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class InputGrabber final : public QQuickItem
|
class InputGrabber final : public QQuickItem
|
||||||
@ -149,12 +162,18 @@ class QskPopup::PrivateData
|
|||||||
public:
|
public:
|
||||||
PrivateData():
|
PrivateData():
|
||||||
inputGrabber( nullptr ),
|
inputGrabber( nullptr ),
|
||||||
isModal( false )
|
isModal( false ),
|
||||||
|
isOpen( false ),
|
||||||
|
autoGrabFocus( true )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
InputGrabber* inputGrabber;
|
InputGrabber* inputGrabber;
|
||||||
|
QPointer< QQuickItem > initialFocusItem;
|
||||||
|
|
||||||
bool isModal : 1;
|
bool isModal : 1;
|
||||||
|
bool isOpen : 1;
|
||||||
|
bool autoGrabFocus : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
QskPopup::QskPopup( QQuickItem* parent ):
|
QskPopup::QskPopup( QQuickItem* parent ):
|
||||||
@ -168,6 +187,7 @@ QskPopup::QskPopup( QQuickItem* parent ):
|
|||||||
|
|
||||||
setFlag( ItemIsFocusScope );
|
setFlag( ItemIsFocusScope );
|
||||||
setTabFence( true );
|
setTabFence( true );
|
||||||
|
setFocusPolicy( Qt::ClickFocus );
|
||||||
}
|
}
|
||||||
|
|
||||||
QskPopup::~QskPopup()
|
QskPopup::~QskPopup()
|
||||||
@ -234,6 +254,50 @@ bool QskPopup::hasOverlay() const
|
|||||||
return flagHint< bool >( QskPopup::Overlay | QskAspect::Style, true );
|
return flagHint< bool >( QskPopup::Overlay | QskAspect::Style, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
For unknown reasons Qt::PopupFocusReason is blocked inside of
|
||||||
|
QQuickItem::setFocus. Nontheless there is specific code dealing
|
||||||
|
with it f.e in qquicktextinput.cpp. If this becomes a problem
|
||||||
|
we will have to bypass QQuickItem::setFocus by calling
|
||||||
|
QQuickWindowPrivate::setFocusInScope/clearFocusInScope directly,
|
||||||
|
but for the moment we use Qt::OtherFocusReason instead. TODO ...
|
||||||
|
*/
|
||||||
|
const auto reason = Qt::OtherFocusReason;
|
||||||
|
|
||||||
|
if ( on )
|
||||||
|
{
|
||||||
|
if ( auto scope = qskNearestFocusScope( this ) )
|
||||||
|
{
|
||||||
|
m_data->initialFocusItem = scope->scopedFocusItem();
|
||||||
|
setFocus( true, reason );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QQuickItem* focusItem = m_data->initialFocusItem;
|
||||||
|
m_data->initialFocusItem = nullptr;
|
||||||
|
|
||||||
|
if ( focusItem == nullptr )
|
||||||
|
focusItem = nextItemInFocusChain( false );
|
||||||
|
|
||||||
|
if ( focusItem )
|
||||||
|
focusItem->setFocus( true, reason );
|
||||||
|
else
|
||||||
|
setFocus( false, reason );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool QskPopup::event( QEvent* event )
|
bool QskPopup::event( QEvent* event )
|
||||||
{
|
{
|
||||||
bool ok = Inherited::event( event );
|
bool ok = Inherited::event( event );
|
||||||
@ -260,6 +324,19 @@ bool QskPopup::event( QEvent* event )
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QskPopup::updateLayout()
|
||||||
|
{
|
||||||
|
if ( !m_data->isOpen )
|
||||||
|
{
|
||||||
|
if ( m_data->autoGrabFocus )
|
||||||
|
grabFocus( true );
|
||||||
|
|
||||||
|
m_data->isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inherited::updateLayout();
|
||||||
|
}
|
||||||
|
|
||||||
void QskPopup::itemChange( QQuickItem::ItemChange change,
|
void QskPopup::itemChange( QQuickItem::ItemChange change,
|
||||||
const QQuickItem::ItemChangeData& value )
|
const QQuickItem::ItemChangeData& value )
|
||||||
{
|
{
|
||||||
@ -270,6 +347,12 @@ void QskPopup::itemChange( QQuickItem::ItemChange change,
|
|||||||
case QQuickItem::ItemVisibleHasChanged:
|
case QQuickItem::ItemVisibleHasChanged:
|
||||||
{
|
{
|
||||||
updateInputGrabber();
|
updateInputGrabber();
|
||||||
|
if ( !value.boolValue )
|
||||||
|
{
|
||||||
|
m_data->isOpen = false;
|
||||||
|
grabFocus( false );
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QQuickItem::ItemParentHasChanged:
|
case QQuickItem::ItemParentHasChanged:
|
||||||
@ -281,6 +364,13 @@ void QskPopup::itemChange( QQuickItem::ItemChange change,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case QQuickItem::ItemActiveFocusHasChanged:
|
||||||
|
{
|
||||||
|
if ( !hasFocus() )
|
||||||
|
m_data->initialFocusItem = nullptr;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
@ -37,11 +37,14 @@ Q_SIGNALS:
|
|||||||
void overlayChanged();
|
void overlayChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void updateLayout() override;
|
||||||
virtual bool event( QEvent* ) override;
|
virtual bool event( QEvent* ) override;
|
||||||
|
|
||||||
virtual void itemChange( QQuickItem::ItemChange,
|
virtual void itemChange( QQuickItem::ItemChange,
|
||||||
const QQuickItem::ItemChangeData& ) override;
|
const QQuickItem::ItemChangeData& ) override;
|
||||||
|
|
||||||
|
void grabFocus( bool );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateInputGrabber();
|
void updateInputGrabber();
|
||||||
|
|
||||||
|
@ -136,14 +136,6 @@ QRectF QskSubWindow::layoutRect() const
|
|||||||
return innerBox( Panel, rect );
|
return innerBox( Panel, rect );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskSubWindow::updateLayout()
|
|
||||||
{
|
|
||||||
if ( !isInitiallyPainted() )
|
|
||||||
setFocus( true );
|
|
||||||
|
|
||||||
Inherited::updateLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
QSizeF QskSubWindow::contentsSizeHint() const
|
QSizeF QskSubWindow::contentsSizeHint() const
|
||||||
{
|
{
|
||||||
qreal w = -1;
|
qreal w = -1;
|
||||||
|
@ -62,7 +62,6 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool event( QEvent* ) override;
|
virtual bool event( QEvent* ) override;
|
||||||
virtual void updateLayout() override;
|
|
||||||
|
|
||||||
virtual void itemChange( QQuickItem::ItemChange,
|
virtual void itemChange( QQuickItem::ItemChange,
|
||||||
const QQuickItem::ItemChangeData& ) override;
|
const QQuickItem::ItemChangeData& ) override;
|
||||||
|
@ -26,8 +26,7 @@ class QSK_EXPORT QskWindow : public QQuickWindow
|
|||||||
WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL )
|
WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL )
|
||||||
|
|
||||||
Q_PROPERTY( FramebufferMode framebufferMode READ framebufferMode
|
Q_PROPERTY( FramebufferMode framebufferMode READ framebufferMode
|
||||||
WRITE setFramebufferMode
|
WRITE setFramebufferMode NOTIFY framebufferModeChanged FINAL )
|
||||||
NOTIFY framebufferModeChanged FINAL )
|
|
||||||
|
|
||||||
using Inherited = QQuickWindow;
|
using Inherited = QQuickWindow;
|
||||||
|
|
||||||
|
@ -55,8 +55,6 @@ QskDialog::DialogCode QskDialogSubWindow::exec()
|
|||||||
mouseGrabber->ungrabMouse();
|
mouseGrabber->ungrabMouse();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer< QQuickItem > focusItem = window()->activeFocusItem();
|
|
||||||
|
|
||||||
show();
|
show();
|
||||||
|
|
||||||
QEventLoop eventLoop;
|
QEventLoop eventLoop;
|
||||||
@ -64,9 +62,6 @@ QskDialog::DialogCode QskDialogSubWindow::exec()
|
|||||||
connect( this, &QskDialogSubWindow::finished, &eventLoop, &QEventLoop::quit );
|
connect( this, &QskDialogSubWindow::finished, &eventLoop, &QEventLoop::quit );
|
||||||
( void ) eventLoop.exec( QEventLoop::DialogExec );
|
( void ) eventLoop.exec( QEventLoop::DialogExec );
|
||||||
|
|
||||||
if ( focusItem )
|
|
||||||
focusItem->setFocus( true );
|
|
||||||
|
|
||||||
return m_result;
|
return m_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user