internal layoutBox replaced by a plain layout. A bit more lightweight

and we do not end up with focus changes when rearranging the buttons
This commit is contained in:
Uwe Rathmann 2021-10-27 14:03:39 +02:00
parent ff8a5fbe31
commit 4938156ada
2 changed files with 135 additions and 58 deletions

View File

@ -8,6 +8,8 @@
#include "QskLinearBox.h" #include "QskLinearBox.h"
#include "QskSkin.h" #include "QskSkin.h"
#include "QskLinearLayoutEngine.h"
#include <qevent.h> #include <qevent.h>
#include <qpointer.h> #include <qpointer.h>
#include <qvector.h> #include <qvector.h>
@ -19,6 +21,8 @@ QSK_QT_PRIVATE_BEGIN
#include <private/qguiapplication_p.h> #include <private/qguiapplication_p.h>
QSK_QT_PRIVATE_END QSK_QT_PRIVATE_END
#include <limits>
QSK_SUBCONTROL( QskDialogButtonBox, Panel ) QSK_SUBCONTROL( QskDialogButtonBox, Panel )
static void qskSendEventTo( QObject* object, QEvent::Type type ) static void qskSendEventTo( QObject* object, QEvent::Type type )
@ -35,38 +39,53 @@ static inline QskDialog::ActionRole qskActionRole( QskDialog::Action action )
return static_cast< QskDialog::ActionRole >( role ); return static_cast< QskDialog::ActionRole >( role );
} }
static void qskAddToLayout( const QVector< QskPushButton* >& buttons, namespace
bool reverse, QskLinearBox* layoutBox ) {
class LayoutEngine : public QskLinearLayoutEngine
{
public:
LayoutEngine( Qt::Orientation orientation )
: QskLinearLayoutEngine( orientation, std::numeric_limits< uint >::max() )
{
}
void addStretch()
{
const auto index = insertSpacerAt( count(), 0 );
setStretchFactorAt( index, 1 );
}
void addButtons( const QVector< QskPushButton* >& buttons, bool reverse )
{ {
if ( reverse ) if ( reverse )
{ {
for ( int i = buttons.count() - 1; i >= 0; i-- ) for ( int i = buttons.count() - 1; i >= 0; i-- )
layoutBox->addItem( buttons[ i ] ); addItem( buttons[ i ] );
} }
else else
{ {
for ( int i = 0; i < buttons.count(); i++ ) for ( int i = 0; i < buttons.count(); i++ )
layoutBox->addItem( buttons[ i ] ); addItem( buttons[ i ] );
} }
} }
};
}
class QskDialogButtonBox::PrivateData class QskDialogButtonBox::PrivateData
{ {
public: public:
PrivateData() PrivateData( Qt::Orientation orientation )
: centeredButtons( false ) : layoutEngine( orientation )
, dirtyLayout( false )
{ {
} }
QskLinearBox* layoutBox = nullptr; LayoutEngine layoutEngine;
QVector< QskPushButton* > buttons[ QskDialog::NActionRoles ]; QVector< QskPushButton* > buttons[ QskDialog::NActionRoles ];
QPointer< QskPushButton > defaultButton; QPointer< QskPushButton > defaultButton;
QskDialog::Action clickedAction = QskDialog::NoAction; QskDialog::Action clickedAction = QskDialog::NoAction;
bool centeredButtons = false;
bool centeredButtons : 1;
bool dirtyLayout : 1;
}; };
QskDialogButtonBox::QskDialogButtonBox( QQuickItem* parent ) QskDialogButtonBox::QskDialogButtonBox( QQuickItem* parent )
@ -76,10 +95,14 @@ QskDialogButtonBox::QskDialogButtonBox( QQuickItem* parent )
QskDialogButtonBox::QskDialogButtonBox( Qt::Orientation orientation, QQuickItem* parent ) QskDialogButtonBox::QskDialogButtonBox( Qt::Orientation orientation, QQuickItem* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData() ) , m_data( new PrivateData( orientation ) )
{ {
setPolishOnResize( true ); setPolishOnResize( true );
setOrientation( orientation );
if ( orientation == Qt::Horizontal )
initSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Fixed );
else
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred );
} }
QskDialogButtonBox::~QskDialogButtonBox() QskDialogButtonBox::~QskDialogButtonBox()
@ -88,13 +111,10 @@ QskDialogButtonBox::~QskDialogButtonBox()
void QskDialogButtonBox::setOrientation( Qt::Orientation orientation ) void QskDialogButtonBox::setOrientation( Qt::Orientation orientation )
{ {
if ( m_data->layoutBox && m_data->layoutBox->orientation() == orientation ) if ( m_data->layoutEngine.orientation() == orientation )
return; return;
delete m_data->layoutBox; m_data->layoutEngine.setOrientation( orientation );
m_data->layoutBox = new QskLinearBox( orientation, this );
m_data->layoutBox->setObjectName( QStringLiteral( "DialogButtonBoxLayout" ) );
if ( orientation == Qt::Horizontal ) if ( orientation == Qt::Horizontal )
setSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Fixed ); setSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Fixed );
@ -108,7 +128,7 @@ void QskDialogButtonBox::setOrientation( Qt::Orientation orientation )
Qt::Orientation QskDialogButtonBox::orientation() const Qt::Orientation QskDialogButtonBox::orientation() const
{ {
return m_data->layoutBox->orientation(); return m_data->layoutEngine.orientation();
} }
QskAspect::Subcontrol QskDialogButtonBox::substitutedSubcontrol( QskAspect::Subcontrol QskDialogButtonBox::substitutedSubcontrol(
@ -123,35 +143,38 @@ QskAspect::Subcontrol QskDialogButtonBox::substitutedSubcontrol(
QSizeF QskDialogButtonBox::layoutSizeHint( QSizeF QskDialogButtonBox::layoutSizeHint(
Qt::SizeHint which, const QSizeF& constraint ) const Qt::SizeHint which, const QSizeF& constraint ) const
{ {
if ( m_data->dirtyLayout ) if ( which == Qt::MaximumSize )
return QSizeF(); // unlimited
if ( ( m_data->layoutEngine.count() == 0 ) && hasChildItems() )
{ {
const_cast< QskDialogButtonBox* >( this )->rearrangeButtons(); const_cast< QskDialogButtonBox* >( this )->rearrangeButtons();
m_data->dirtyLayout = false;
} }
return m_data->layoutBox->effectiveSizeHint( which, constraint ); return m_data->layoutEngine.sizeHint( which, constraint );
} }
void QskDialogButtonBox::invalidateLayout() void QskDialogButtonBox::invalidateLayout()
{ {
m_data->dirtyLayout = true; m_data->layoutEngine.clear();
resetImplicitSize(); resetImplicitSize();
polish(); polish();
} }
void QskDialogButtonBox::updateLayout() void QskDialogButtonBox::updateLayout()
{ {
if ( m_data->dirtyLayout ) auto& layoutEngine = m_data->layoutEngine;
if ( ( layoutEngine.count() == 0 ) && hasChildItems() )
{ {
rearrangeButtons(); rearrangeButtons();
m_data->dirtyLayout = false;
if ( parentItem() ) if ( parentItem() && ( layoutEngine.count() > 0 ) )
qskSendEventTo( parentItem(), QEvent::LayoutRequest ); qskSendEventTo( parentItem(), QEvent::LayoutRequest );
} }
m_data->layoutBox->setGeometry( layoutRect() ); if ( !maybeUnresized() )
layoutEngine.setGeometries( layoutRect() );
} }
void QskDialogButtonBox::rearrangeButtons() void QskDialogButtonBox::rearrangeButtons()
@ -159,14 +182,13 @@ void QskDialogButtonBox::rearrangeButtons()
// Result differs from QDialogButtonBox. Needs more // Result differs from QDialogButtonBox. Needs more
// investigation - TODO ... // investigation - TODO ...
auto layoutBox = m_data->layoutBox; auto& layoutEngine = m_data->layoutEngine;
layoutEngine.clear();
layoutBox->clear();
const int* currentLayout = effectiveSkin()->dialogButtonLayout( orientation() ); const int* currentLayout = effectiveSkin()->dialogButtonLayout( orientation() );
if ( m_data->centeredButtons ) if ( m_data->centeredButtons )
layoutBox->addStretch( 1 ); layoutEngine.addStretch();
while ( *currentLayout != QPlatformDialogHelper::EOL ) while ( *currentLayout != QPlatformDialogHelper::EOL )
{ {
@ -178,7 +200,7 @@ void QskDialogButtonBox::rearrangeButtons()
case QPlatformDialogHelper::Stretch: case QPlatformDialogHelper::Stretch:
{ {
if ( !m_data->centeredButtons ) if ( !m_data->centeredButtons )
layoutBox->addStretch( 1 ); layoutEngine.addStretch();
break; break;
} }
@ -187,7 +209,7 @@ void QskDialogButtonBox::rearrangeButtons()
const auto& buttons = m_data->buttons[ role ]; const auto& buttons = m_data->buttons[ role ];
if ( !buttons.isEmpty() ) if ( !buttons.isEmpty() )
layoutBox->addItem( buttons.first() ); layoutEngine.addItem( buttons.first() );
break; break;
} }
@ -196,7 +218,7 @@ void QskDialogButtonBox::rearrangeButtons()
const auto& buttons = m_data->buttons[ QskDialog::AcceptRole ]; const auto& buttons = m_data->buttons[ QskDialog::AcceptRole ];
if ( buttons.size() > 1 ) if ( buttons.size() > 1 )
qskAddToLayout( buttons.mid( 1 ), reverse, layoutBox ); layoutEngine.addButtons( buttons.mid( 1 ), reverse );
break; break;
} }
@ -212,7 +234,7 @@ void QskDialogButtonBox::rearrangeButtons()
const auto& buttons = m_data->buttons[ role ]; const auto& buttons = m_data->buttons[ role ];
if ( !buttons.isEmpty() ) if ( !buttons.isEmpty() )
qskAddToLayout( buttons, reverse, layoutBox ); layoutEngine.addButtons( buttons, reverse );
break; break;
} }
@ -222,9 +244,29 @@ void QskDialogButtonBox::rearrangeButtons()
} }
if ( m_data->centeredButtons ) if ( m_data->centeredButtons )
layoutBox->addStretch( 1 ); layoutEngine.addStretch();
// reorganizing the tab chain ??? updateTabFocusChain();
}
void QskDialogButtonBox::updateTabFocusChain()
{
if ( childItems().count() <= 1 )
return;
QQuickItem* lastItem = nullptr;
const auto& layoutEngine = m_data->layoutEngine;
for ( int i = 0; i < layoutEngine.count(); i++ )
{
if ( auto item = layoutEngine.itemAt( i ) )
{
if ( lastItem )
item->stackAfter( lastItem );
lastItem = item;
}
}
} }
void QskDialogButtonBox::setCenteredButtons( bool centered ) void QskDialogButtonBox::setCenteredButtons( bool centered )
@ -255,14 +297,18 @@ void QskDialogButtonBox::addButton(
button->setParent( this ); button->setParent( this );
/* /*
To have a proper ownership. Inserting the buttons Order of the children according to the layout rules
according to the layout rules will be done later will be done later in updateTabOrder
*/ */
button->setParentItem( m_data->layoutBox ); button->setParentItem( this );
connect( button, &QskPushButton::clicked, this, connect( button, &QskPushButton::clicked,
&QskDialogButtonBox::onButtonClicked ); this, &QskDialogButtonBox::onButtonClicked );
connect( button, &QskPushButton::visibleChanged,
this, &QskDialogButtonBox::invalidateLayout );
m_data->buttons[ role ].removeOne( button );
m_data->buttons[ role ] += button; m_data->buttons[ role ] += button;
invalidateLayout(); invalidateLayout();
} }
@ -277,20 +323,21 @@ void QskDialogButtonBox::addAction( QskDialog::Action action )
void QskDialogButtonBox::removeButton( QskPushButton* button ) void QskDialogButtonBox::removeButton( QskPushButton* button )
{ {
// ChildRemove Events !!!
if ( button == nullptr ) if ( button == nullptr )
return; return;
for ( int i = 0; i < QskDialog::NActionRoles; i++ ) for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{ {
auto& buttons = m_data->buttons[ i ]; if ( m_data->buttons[ i ].removeOne( button ) )
if ( buttons.removeOne( button ) )
{ {
disconnect( button, &QskPushButton::clicked, disconnect( button, &QskPushButton::clicked,
this, &QskDialogButtonBox::onButtonClicked ); this, &QskDialogButtonBox::onButtonClicked );
disconnect( button, &QskPushButton::visibleChanged,
this, &QskDialogButtonBox::invalidateLayout );
invalidateLayout(); invalidateLayout();
return; return;
} }
} }
@ -467,15 +514,43 @@ QskDialog::Action QskDialogButtonBox::clickedAction() const
bool QskDialogButtonBox::event( QEvent* event ) bool QskDialogButtonBox::event( QEvent* event )
{ {
if ( event->type() == QEvent::LayoutRequest ) switch ( static_cast< int >( event->type() ) )
{ {
if ( !m_data->dirtyLayout ) case QEvent::LayoutRequest:
resetImplicitSize(); {
invalidateLayout();
break;
}
case QEvent::LayoutDirectionChange:
{
m_data->layoutEngine.setVisualDirection(
layoutMirroring() ? Qt::RightToLeft : Qt::LeftToRight );
break;
}
case QEvent::ContentsRectChange:
{
polish();
break;
}
} }
return Inherited::event( event ); return Inherited::event( event );
} }
void QskDialogButtonBox::itemChange(
QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& value )
{
Inherited::itemChange( change, value );
if ( change == ItemChildRemovedChange )
{
if ( auto button = qobject_cast< QskPushButton* >( value.item ) )
removeButton( button );
}
}
bool QskDialogButtonBox::isDefaultButtonKeyEvent( const QKeyEvent* event ) bool QskDialogButtonBox::isDefaultButtonKeyEvent( const QKeyEvent* event )
{ {
if ( event->modifiers() & Qt::KeypadModifier && event->key() == Qt::Key_Enter ) if ( event->modifiers() & Qt::KeypadModifier && event->key() == Qt::Key_Enter )

View File

@ -72,7 +72,8 @@ class QSK_EXPORT QskDialogButtonBox : public QskBox
void orientationChanged(); void orientationChanged();
protected: protected:
bool event( QEvent* event ) override; bool event( QEvent* ) override;
void itemChange( ItemChange, const ItemChangeData& ) override;
void updateLayout() override; void updateLayout() override;
QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override; QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override;
@ -86,6 +87,7 @@ class QSK_EXPORT QskDialogButtonBox : public QskBox
private: private:
void onButtonClicked(); void onButtonClicked();
void rearrangeButtons(); void rearrangeButtons();
void updateTabFocusChain();
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;