combobox input handling improved ( wheel, more keys )

This commit is contained in:
Uwe Rathmann 2023-03-06 10:44:00 +01:00
parent 04c50fc301
commit dae0cd7b1b
6 changed files with 150 additions and 115 deletions

View File

@ -290,9 +290,9 @@ void Editor::setupComboBox()
m_pal.surfaceVariant, m_pal.focusOpacity );
setGradient( Q::Panel | Q::Focused, focusColor );
const auto pressedColor = flattenedColor( m_pal.onSurfaceVariant,
const auto activeColor = flattenedColor( m_pal.onSurfaceVariant,
m_pal.surfaceVariant, m_pal.pressedOpacity );
setGradient( Q::Panel | Q::Pressed, pressedColor );
setGradient( Q::Panel | Q::PopupOpen, activeColor );
setStrutSize( Q::Graphic, 24_dp, 24_dp );
setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface );
@ -300,9 +300,9 @@ void Editor::setupComboBox()
setColor( Q::Text, m_pal.onSurface );
setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium );
setStrutSize( Q::OpenMenuGraphic, 12_dp, 12_dp );
setGraphicRole( Q::OpenMenuGraphic, QskMaterial3Skin::GraphicRoleOnSurface );
setAlignment( Q::OpenMenuGraphic, Qt::AlignRight | Qt::AlignVCenter );
setStrutSize( Q::PopupIndicator, 12_dp, 12_dp );
setGraphicRole( Q::PopupIndicator, QskMaterial3Skin::GraphicRoleOnSurface );
setAlignment( Q::PopupIndicator, Qt::AlignRight | Qt::AlignVCenter );
const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 );
@ -313,10 +313,10 @@ void Editor::setupComboBox()
setColor( Q::Text | Q::Disabled, m_pal.onSurface38 );
setGraphicRole( Q::OpenMenuGraphic, QskMaterial3Skin::GraphicRoleOnSurface38 );
setGraphicRole( Q::PopupIndicator, QskMaterial3Skin::GraphicRoleOnSurface38 );
setSymbol( Q::OpenMenuGraphic, symbol( "combo-box-arrow-closed" ) );
setSymbol( Q::OpenMenuGraphic | Q::PopupOpen, symbol( "combo-box-arrow-open" ) );
setSymbol( Q::PopupIndicator, symbol( "combo-box-arrow-closed" ) );
setSymbol( Q::PopupIndicator | Q::PopupOpen, symbol( "combo-box-arrow-open" ) );
}
void Editor::setupBox()

View File

@ -382,14 +382,14 @@ void Editor::setupComboBox()
setStrutSize( Q::Graphic, 24_dp, 24_dp );
setGraphicRole( Q::Graphic | Q::Disabled, DisabledSymbol );
setStrutSize( Q::OpenMenuGraphic, 15_dp, 15_dp );
setGraphicRole( Q::OpenMenuGraphic | Q::Disabled, DisabledSymbol );
setStrutSize( Q::PopupIndicator, 15_dp, 15_dp );
setGraphicRole( Q::PopupIndicator | Q::Disabled, DisabledSymbol );
setAlignment( Q::OpenMenuGraphic, Qt::AlignRight | Qt::AlignVCenter );
setAlignment( Q::PopupIndicator, Qt::AlignRight | Qt::AlignVCenter );
setSymbol( Q::OpenMenuGraphic,
setSymbol( Q::PopupIndicator,
QskStandardSymbol::graphic( QskStandardSymbol::TriangleDown ) );
setSymbol( Q::OpenMenuGraphic | Q::PopupOpen,
setSymbol( Q::PopupIndicator | Q::PopupOpen,
QskStandardSymbol::graphic( QskStandardSymbol::TriangleUp ) );
}

View File

@ -8,17 +8,45 @@
#include "QskGraphic.h"
#include "QskMenu.h"
#include "QskTextOptions.h"
#include "QskEvent.h"
#include <qquickwindow.h>
QSK_QT_PRIVATE_BEGIN
#include <private/qguiapplication_p.h>
QSK_QT_PRIVATE_END
#include <qpa/qplatformtheme.h>
QSK_SUBCONTROL( QskComboBox, Panel )
QSK_SUBCONTROL( QskComboBox, Graphic )
QSK_SUBCONTROL( QskComboBox, Text )
QSK_SUBCONTROL( QskComboBox, OpenMenuGraphic )
QSK_SUBCONTROL( QskComboBox, PopupIndicator )
QSK_SUBCONTROL( QskComboBox, Splash )
QSK_SYSTEM_STATE( QskComboBox, Pressed, QskAspect::FirstSystemState << 1 )
QSK_SYSTEM_STATE( QskComboBox, PopupOpen, QskAspect::FirstSystemState << 2 )
QSK_SYSTEM_STATE( QskComboBox, PopupOpen, QskAspect::FirstSystemState << 1 )
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
static inline QList< Qt::Key > qskButtonPressKeys()
{
const auto hint = QGuiApplicationPrivate::platformTheme()->themeHint(
QPlatformTheme::ButtonPressKeys );
return hint.value< QList< Qt::Key > >();
}
#else
static inline QList< Qt::Key > qskButtonPressKeys()
{
static const QList< Qt::Key > keys =
{ Qt::Key_Space, Qt::Key_Enter, Qt::Key_Return, Qt::Key_Select };
return keys;
}
#endif
class QskComboBox::PrivateData
{
@ -56,63 +84,34 @@ QskComboBox::QskComboBox( QQuickItem* parent )
setAcceptHoverEvents( true );
connect( m_data->menu, &QskMenu::currentIndexChanged,
this, &QskComboBox::currentIndexChanged );
connect( m_data->menu, &QskMenu::triggered,
this, &QskComboBox::activated );
connect( m_data->menu, &QskMenu::currentIndexChanged,
this, &QQuickItem::update );
this, &QskComboBox::showOption );
connect( m_data->menu, &QskMenu::countChanged,
this, &QskComboBox::countChanged );
connect( this, &QskComboBox::currentIndexChanged,
this, &QskControl::focusIndicatorRectChanged );
connect( m_data->menu, &QskMenu::closed, this,
[ this ]() { setPopupOpen( false ); setFocus( true ); } );
connect( this, &QskComboBox::pressed, this, &QskComboBox::togglePopup );
}
QskComboBox::~QskComboBox()
{
}
void QskComboBox::setPressed( bool on )
{
if ( on == isPressed() )
return;
setSkinStateFlag( Pressed, on );
Q_EMIT pressedChanged( on );
if ( on )
Q_EMIT pressed();
else
Q_EMIT released();
}
bool QskComboBox::isPressed() const
{
return hasSkinState( Pressed );
}
void QskComboBox::setPopupOpen( bool on )
{
if ( on == isPopupOpen() )
return;
if( on )
{
openPopup();
}
else
{
closePopup();
}
setSkinStateFlag( PopupOpen, on );
Q_EMIT popupOpenChanged( on );
if( on )
openPopup();
else
closePopup();
}
bool QskComboBox::isPopupOpen() const
@ -172,7 +171,7 @@ void QskComboBox::setPlaceholderText( const QString& text )
Q_EMIT placeholderTextChanged( text );
}
QString QskComboBox::text() const
QString QskComboBox::currentText() const
{
const int index = currentIndex();
if( index >= 0 )
@ -184,11 +183,6 @@ QString QskComboBox::text() const
return placeholderText();
}
void QskComboBox::togglePopup()
{
setPopupOpen( !isPopupOpen() );
}
void QskComboBox::openPopup()
{
const auto cr = contentsRect();
@ -209,39 +203,64 @@ void QskComboBox::closePopup()
void QskComboBox::mousePressEvent( QMouseEvent* )
{
setPressed( true );
}
void QskComboBox::mouseUngrabEvent()
{
setPressed( false );
setPopupOpen( true );
}
void QskComboBox::mouseReleaseEvent( QMouseEvent* )
{
releaseButton();
}
void QskComboBox::keyPressEvent( QKeyEvent* event )
{
switch ( event->key() )
if ( qskButtonPressKeys().contains( event->key() ) )
{
case Qt::Key_Select:
case Qt::Key_Space:
if ( !event->isAutoRepeat() )
{
if ( !event->isAutoRepeat() )
{
setPressed( true );
// calling release button here, because
// we will never get the key release event
// when the menu is opened:
releaseButton();
}
setPopupOpen( true );
return;
}
}
switch( event->key() )
{
#if 0
case Qt::Key_F4:
{
// QComboBox does this ???
setPopupOpen( true );
return;
}
#endif
case Qt::Key_Up:
case Qt::Key_PageUp:
{
increment( -1 );
return;
}
case Qt::Key_Down:
case Qt::Key_PageDown:
{
increment( 1 );
return;
}
case Qt::Key_Home:
{
if ( count() > 0 )
setCurrentIndex( 0 );
return;
}
case Qt::Key_End:
{
if ( count() > 0 )
setCurrentIndex( count() - 1 );
return;
}
default:
// searching option by key TODO ...
break;
}
Inherited::keyPressEvent( event );
}
@ -250,6 +269,11 @@ void QskComboBox::keyReleaseEvent( QKeyEvent* event )
Inherited::keyReleaseEvent( event );
}
void QskComboBox::wheelEvent( QWheelEvent* event )
{
increment( -qRound( qskWheelSteps( event ) ) );
}
void QskComboBox::clear()
{
m_data->menu->clear();
@ -271,13 +295,29 @@ int QskComboBox::count() const
return m_data->menu->count();
}
void QskComboBox::releaseButton()
void QskComboBox::showOption( int index )
{
if ( !isPressed() )
update();
Q_EMIT currentIndexChanged( index );
}
void QskComboBox::increment( int steps )
{
if ( count() == 0 )
return;
setPressed( false );
Q_EMIT clicked();
if ( currentIndex() == -1 && steps < 0 )
steps++;
int nextIndex = ( currentIndex() + steps ) % count();
if ( nextIndex < 0 )
nextIndex += count();
if ( nextIndex != currentIndex() )
{
m_data->menu->setCurrentIndex( nextIndex );
Q_EMIT activated( nextIndex );
}
}
#include "moc_QskComboBox.cpp"

View File

@ -17,6 +17,8 @@ class QSK_EXPORT QskComboBox : public QskControl
Q_PROPERTY( int currentIndex READ currentIndex
WRITE setCurrentIndex NOTIFY currentIndexChanged )
Q_PROPERTY( QString currentText READ currentText )
Q_PROPERTY( int count READ count NOTIFY countChanged )
Q_PROPERTY( QString placeholderText READ placeholderText
@ -25,17 +27,14 @@ class QSK_EXPORT QskComboBox : public QskControl
using Inherited = QskControl;
public:
QSK_SUBCONTROLS( Panel, Graphic, Text, OpenMenuGraphic, Splash )
QSK_STATES( Pressed, PopupOpen )
QSK_SUBCONTROLS( Panel, Graphic, Text, PopupIndicator, Splash )
QSK_STATES( PopupOpen )
QskComboBox( QQuickItem* parent = nullptr );
~QskComboBox() override;
void setPressed( bool on );
bool isPressed() const;
void setPopupOpen( bool on );
void setPopupOpen( bool );
bool isPopupOpen() const;
QskGraphic graphic() const;
@ -48,46 +47,40 @@ class QSK_EXPORT QskComboBox : public QskControl
void clear();
int currentIndex() const;
QString currentText() const;
int count() const;
QVariantList optionAt( int ) const;
QString placeholderText() const;
void setPlaceholderText( const QString& );
QString text() const;
public Q_SLOTS:
void togglePopup();
virtual void openPopup();
virtual void closePopup();
void setCurrentIndex( int );
Q_SIGNALS:
void activated( int );
void currentIndexChanged( int );
void countChanged();
void pressed();
void released();
void clicked();
void pressedChanged( bool );
void popupOpenChanged( bool );
void placeholderTextChanged( const QString& );
protected:
void mousePressEvent( QMouseEvent* ) override;
void mouseUngrabEvent() override;
void mouseReleaseEvent( QMouseEvent* ) override;
void keyPressEvent( QKeyEvent* ) override;
void keyReleaseEvent( QKeyEvent* ) override;
void wheelEvent( QWheelEvent* ) override;
virtual void openPopup();
virtual void closePopup();
private:
void showOption( int );
void releaseButton();
void increment( int );
class PrivateData;
std::unique_ptr< PrivateData > m_data;

View File

@ -18,7 +18,7 @@ namespace
{
if( std::is_same< T, QString >() )
{
return box->text();
return box->currentText();
}
const int index = box->currentIndex();
@ -56,7 +56,8 @@ namespace
QskComboBox::Text, qskValueAt< QString >( box ),
QskComboBox::Graphic, qskValueAt< QskGraphic >( box ).defaultSize() );
const auto alignment = box->alignmentHint( QskComboBox::Panel, Qt::AlignLeft );
const auto alignment = box->alignmentHint(
QskComboBox::Panel, Qt::AlignLeft );
setFixedContent( QskComboBox::Text, Qt::Horizontal, alignment );
}
};
@ -65,7 +66,8 @@ namespace
QskComboBoxSkinlet::QskComboBoxSkinlet( QskSkin* skin )
: Inherited( skin )
{
setNodeRoles( { PanelRole, SplashRole, GraphicRole, TextRole, OpenMenuGraphicRole } );
setNodeRoles( { PanelRole, SplashRole,
GraphicRole, TextRole, PopupIndicatorRole } );
}
QskComboBoxSkinlet::~QskComboBoxSkinlet() = default;
@ -93,10 +95,10 @@ QRectF QskComboBoxSkinlet::subControlRect( const QskSkinnable* skinnable,
return layoutEngine.subControlRect( subControl );
}
if( subControl == Q::OpenMenuGraphic )
if( subControl == Q::PopupIndicator )
{
auto rect = box->innerBox( Q::Panel, contentsRect );
const auto size = box->strutSizeHint( Q::OpenMenuGraphic );
const auto size = box->strutSizeHint( Q::PopupIndicator );
rect.setLeft( rect.right() - size.width() );
return rect;
}
@ -122,8 +124,8 @@ QSGNode* QskComboBoxSkinlet::updateSubNode(
case TextRole:
return updateTextNode( box, node );
case OpenMenuGraphicRole:
return updateSymbolNode( box, node, Q::OpenMenuGraphic );
case PopupIndicatorRole:
return updateSymbolNode( box, node, Q::PopupIndicator );
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
@ -144,7 +146,7 @@ QRectF QskComboBoxSkinlet::splashRect(
const auto pos = box->positionHint( Q::Splash );
const qreal w = 2.0 * rect.width() * ratio;
rect.setX( pos - 0.5 * w );
rect.setX( pos - 0.5 * w );
rect.setWidth( w );
}
@ -165,7 +167,7 @@ QSGNode* QskComboBoxSkinlet::updateTextNode(
const auto alignment = box->alignmentHint( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
return QskSkinlet::updateTextNode( box, node, rect,
alignment, box->text(), Q::Text );
alignment, box->currentText(), Q::Text );
}
QSGNode* QskComboBoxSkinlet::updateSplashNode(
@ -210,7 +212,7 @@ QSizeF QskComboBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
auto size = layoutEngine.sizeHint( which, QSizeF() );
const auto spacingHint = box->spacingHint( Q::Panel );
const auto menuGraphicHint = box->strutSizeHint( Q::OpenMenuGraphic );
const auto menuGraphicHint = box->strutSizeHint( Q::PopupIndicator );
size.rwidth() += spacingHint + menuGraphicHint.width();

View File

@ -22,7 +22,7 @@ class QSK_EXPORT QskComboBoxSkinlet : public QskSkinlet
PanelRole,
GraphicRole,
TextRole,
OpenMenuGraphicRole,
PopupIndicatorRole,
SplashRole,
RoleCount