Name | |
---|---|
void | qskSendEventTo(QObject * object, QEvent::Type type) |
QskDialog::ActionRole | qskActionRole(QskDialog::Action action) |
static void qskSendEventTo(
QObject * object,
QEvent::Type type
)
static inline QskDialog::ActionRole qskActionRole(
QskDialog::Action action
)
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskDialogButtonBox.h"
#include "QskDialogButton.h"
#include "QskLinearBox.h"
#include "QskSkin.h"
#include "QskLinearLayoutEngine.h"
#include <qevent.h>
#include <qpointer.h>
#include <qvector.h>
#include <qpa/qplatformdialoghelper.h>
#include <qpa/qplatformtheme.h>
#include <private/qguiapplication_p.h>
#include <limits>
QSK_SUBCONTROL( QskDialogButtonBox, Panel )
static void qskSendEventTo( QObject* object, QEvent::Type type )
{
QEvent event( type );
QCoreApplication::sendEvent( object, &event );
}
static inline QskDialog::ActionRole qskActionRole( QskDialog::Action action )
{
const auto role = QPlatformDialogHelper::buttonRole(
static_cast< QPlatformDialogHelper::StandardButton >( action ) );
return static_cast< QskDialog::ActionRole >( role );
}
namespace
{
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 )
{
for ( int i = buttons.count() - 1; i >= 0; i-- )
addItem( buttons[ i ] );
}
else
{
for ( int i = 0; i < buttons.count(); i++ )
addItem( buttons[ i ] );
}
}
};
}
class QskDialogButtonBox::PrivateData
{
public:
PrivateData( Qt::Orientation orientation )
: layoutEngine( orientation )
{
}
static inline void connectButton( QskDialogButtonBox* box,
QskPushButton* button, bool on )
{
if ( on )
{
connect( button, &QskPushButton::clicked,
box, &QskDialogButtonBox::onButtonClicked,
Qt::UniqueConnection );
connect( button, &QskPushButton::visibleChanged,
box, &QskDialogButtonBox::invalidateLayout,
Qt::UniqueConnection );
}
else
{
disconnect( button, &QskPushButton::clicked,
box, &QskDialogButtonBox::onButtonClicked );
disconnect( button, &QskPushButton::visibleChanged,
box, &QskDialogButtonBox::invalidateLayout );
}
}
LayoutEngine layoutEngine;
QVector< QskPushButton* > buttons[ QskDialog::NActionRoles ];
QPointer< QskPushButton > defaultButton;
QskDialog::Action clickedAction = QskDialog::NoAction;
bool centeredButtons = false;
};
QskDialogButtonBox::QskDialogButtonBox( QQuickItem* parent )
: QskDialogButtonBox( Qt::Horizontal, parent )
{
}
QskDialogButtonBox::QskDialogButtonBox( Qt::Orientation orientation, QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData( orientation ) )
{
setPolishOnResize( true );
if ( orientation == Qt::Horizontal )
initSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Fixed );
else
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred );
}
QskDialogButtonBox::~QskDialogButtonBox()
{
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
for ( auto button : qskAsConst( m_data->buttons[ i ] ) )
{
/*
The destructor of QQuickItem sets the parentItem of
all children to nullptr, what leads to visibleChanged
signals. So we better disconnect first.
*/
PrivateData::connectButton( this, button, false );
}
}
}
void QskDialogButtonBox::setOrientation( Qt::Orientation orientation )
{
if ( m_data->layoutEngine.orientation() == orientation )
return;
m_data->layoutEngine.setOrientation( orientation );
if ( orientation == Qt::Horizontal )
setSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Fixed );
else
setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred );
invalidateLayout();
Q_EMIT orientationChanged();
}
Qt::Orientation QskDialogButtonBox::orientation() const
{
return m_data->layoutEngine.orientation();
}
QskAspect::Subcontrol QskDialogButtonBox::substitutedSubcontrol(
QskAspect::Subcontrol subControl ) const
{
if ( subControl == QskBox::Panel )
return QskDialogButtonBox::Panel;
return Inherited::substitutedSubcontrol( subControl );
}
QSizeF QskDialogButtonBox::layoutSizeHint(
Qt::SizeHint which, const QSizeF& constraint ) const
{
if ( which == Qt::MaximumSize )
return QSizeF(); // unlimited
if ( ( m_data->layoutEngine.count() == 0 ) && hasChildItems() )
{
const_cast< QskDialogButtonBox* >( this )->rearrangeButtons();
}
return m_data->layoutEngine.sizeHint( which, constraint );
}
void QskDialogButtonBox::invalidateLayout()
{
m_data->layoutEngine.clear();
resetImplicitSize();
polish();
}
void QskDialogButtonBox::updateLayout()
{
auto& layoutEngine = m_data->layoutEngine;
if ( ( layoutEngine.count() == 0 ) && hasChildItems() )
{
rearrangeButtons();
if ( parentItem() && ( layoutEngine.count() > 0 ) )
qskSendEventTo( parentItem(), QEvent::LayoutRequest );
}
if ( !maybeUnresized() )
layoutEngine.setGeometries( layoutRect() );
}
void QskDialogButtonBox::rearrangeButtons()
{
// Result differs from QDialogButtonBox. Needs more
// investigation - TODO ...
auto& layoutEngine = m_data->layoutEngine;
layoutEngine.clear();
const int* currentLayout = effectiveSkin()->dialogButtonLayout( orientation() );
if ( m_data->centeredButtons )
layoutEngine.addStretch();
while ( *currentLayout != QPlatformDialogHelper::EOL )
{
const int role = ( *currentLayout & ~QPlatformDialogHelper::Reverse );
const bool reverse = ( *currentLayout & QPlatformDialogHelper::Reverse );
switch ( role )
{
case QPlatformDialogHelper::Stretch:
{
if ( !m_data->centeredButtons )
layoutEngine.addStretch();
break;
}
case QPlatformDialogHelper::AcceptRole:
{
const auto& buttons = m_data->buttons[ role ];
if ( !buttons.isEmpty() )
layoutEngine.addItem( buttons.first() );
break;
}
case QPlatformDialogHelper::AlternateRole:
{
const auto& buttons = m_data->buttons[ QskDialog::AcceptRole ];
if ( buttons.size() > 1 )
layoutEngine.addButtons( buttons.mid( 1 ), reverse );
break;
}
case QPlatformDialogHelper::DestructiveRole:
case QPlatformDialogHelper::RejectRole:
case QPlatformDialogHelper::ActionRole:
case QPlatformDialogHelper::HelpRole:
case QPlatformDialogHelper::YesRole:
case QPlatformDialogHelper::NoRole:
case QPlatformDialogHelper::ApplyRole:
case QPlatformDialogHelper::ResetRole:
{
const auto& buttons = m_data->buttons[ role ];
if ( !buttons.isEmpty() )
layoutEngine.addButtons( buttons, reverse );
break;
}
}
++currentLayout;
}
if ( m_data->centeredButtons )
layoutEngine.addStretch();
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 )
{
if ( centered != m_data->centeredButtons )
{
m_data->centeredButtons = centered;
invalidateLayout();
Q_EMIT centeredButtonsChanged();
}
}
bool QskDialogButtonBox::centeredButtons() const
{
return m_data->centeredButtons;
}
void QskDialogButtonBox::addButton(
QskPushButton* button, QskDialog::ActionRole role )
{
if ( role < 0 || role >= QskDialog::NActionRoles )
return;
if ( button )
{
if ( button->parent() == nullptr )
button->setParent( this );
/*
Order of the children according to the layout rules
will be done later in updateTabOrder
*/
button->setParentItem( this );
PrivateData::connectButton( this, button, true );
m_data->buttons[ role ].removeOne( button );
m_data->buttons[ role ] += button;
invalidateLayout();
}
}
void QskDialogButtonBox::addAction( QskDialog::Action action )
{
QskPushButton* button = createButton( action );
if ( button )
addButton( button, qskActionRole( action ) );
}
void QskDialogButtonBox::removeButton( QskPushButton* button )
{
if ( button == nullptr )
return;
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
if ( m_data->buttons[ i ].removeOne( button ) )
{
PrivateData::connectButton( this, button, false );
invalidateLayout();
return;
}
}
}
QskPushButton* QskDialogButtonBox::createButton(
QskDialog::Action action ) const
{
return new QskDialogButton( action );
}
void QskDialogButtonBox::clear()
{
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
qDeleteAll( m_data->buttons[ i ] );
m_data->buttons[ i ].clear();
}
invalidateLayout();
}
void QskDialogButtonBox::setDefaultButton( QskPushButton* button )
{
m_data->defaultButton = button;
}
QskPushButton* QskDialogButtonBox::defaultButton() const
{
return m_data->defaultButton;
}
void QskDialogButtonBox::setActions( QskDialog::Actions actions )
{
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
qDeleteAll( m_data->buttons[ i ] );
m_data->buttons[ i ].clear();
}
for ( int i = QskDialog::Ok; i <= QskDialog::RestoreDefaults; i <<= 1 )
{
const auto action = static_cast< QskDialog::Action >( i );
if ( action & actions )
addAction( action );
}
invalidateLayout();
}
QVector< QskPushButton* > QskDialogButtonBox::buttons() const
{
QVector< QskPushButton* > buttons;
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
buttons += m_data->buttons[ i ];
return buttons;
}
QVector< QskPushButton* > QskDialogButtonBox::buttons(
QskDialog::ActionRole role ) const
{
if ( role < 0 || role >= QskDialog::NActionRoles )
return QVector< QskPushButton* >();
return m_data->buttons[ role ];
}
QskDialog::ActionRole QskDialogButtonBox::actionRole(
const QskPushButton* button ) const
{
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
const auto& buttons = m_data->buttons[ i ];
for ( const auto btn : buttons )
{
if ( button == btn )
return static_cast< QskDialog::ActionRole >( i );
}
}
return QskDialog::InvalidRole;
}
void QskDialogButtonBox::onButtonClicked()
{
auto button = qobject_cast< QskPushButton* >( sender() );
if ( button == nullptr )
return;
switch ( actionRole( button ) )
{
case QskDialog::AcceptRole:
case QskDialog::YesRole:
{
m_data->clickedAction = action( button );
Q_EMIT clicked( button );
Q_EMIT accepted();
break;
}
case QskDialog::RejectRole:
case QskDialog::NoRole:
case QskDialog::DestructiveRole:
{
m_data->clickedAction = action( button );
Q_EMIT clicked( button );
Q_EMIT rejected();
break;
}
default:
{
m_data->clickedAction = QskDialog::NoAction;
Q_EMIT clicked( button );
break;
}
}
}
QskDialog::Actions QskDialogButtonBox::actions() const
{
QskDialog::Actions actions;
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
const auto& buttons = m_data->buttons[ i ];
for ( const auto btn : buttons )
actions |= action( btn );
}
return actions;
}
QskDialog::Action QskDialogButtonBox::action( const QskPushButton* button ) const
{
if ( button )
{
if ( auto dialogButton = qobject_cast< const QskDialogButton* >( button ) )
return dialogButton->action();
const QVariant value = button->property( "standardButton" );
if ( value.canConvert< int >() )
return static_cast< QskDialog::Action >( value.toInt() );
}
return QskDialog::NoAction;
}
QskPushButton* QskDialogButtonBox::button( QskDialog::Action action ) const
{
for ( int i = 0; i < QskDialog::NActionRoles; i++ )
{
const auto& buttons = m_data->buttons[ i ];
for ( auto btn : buttons )
{
if ( this->action( btn ) == action )
return btn;
}
}
return nullptr;
}
QskDialog::Action QskDialogButtonBox::clickedAction() const
{
return m_data->clickedAction;
}
bool QskDialogButtonBox::event( QEvent* event )
{
switch ( static_cast< int >( event->type() ) )
{
case QEvent::LayoutRequest:
{
invalidateLayout();
break;
}
case QEvent::LayoutDirectionChange:
{
m_data->layoutEngine.setVisualDirection(
layoutMirroring() ? Qt::RightToLeft : Qt::LeftToRight );
break;
}
case QEvent::ContentsRectChange:
{
polish();
break;
}
}
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 )
{
if ( event->modifiers() & Qt::KeypadModifier && event->key() == Qt::Key_Enter )
{
return ( event->modifiers() & Qt::KeypadModifier )
&& ( event->key() == Qt::Key_Enter );
}
else
{
return ( event->key() == Qt::Key_Enter ) ||
( event->key() == Qt::Key_Return );
}
}
QString QskDialogButtonBox::buttonText( QskDialog::Action action )
{
// should be redirected through the skin !
const QPlatformTheme* theme = QGuiApplicationPrivate::platformTheme();
QString text = theme->standardButtonText( action );
#if QT_VERSION < QT_VERSION_CHECK( 5, 7, 0 )
text.remove( '&' );
#else
text = QPlatformTheme::removeMnemonics( text );
#endif
return text;
}
#include "moc_QskDialogButtonBox.cpp"
Updated on 28 July 2023 at 14:02:30 CEST