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 ); m_pal.surfaceVariant, m_pal.focusOpacity );
setGradient( Q::Panel | Q::Focused, focusColor ); 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 ); 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 ); setStrutSize( Q::Graphic, 24_dp, 24_dp );
setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface ); setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface );
@ -300,9 +300,9 @@ void Editor::setupComboBox()
setColor( Q::Text, m_pal.onSurface ); setColor( Q::Text, m_pal.onSurface );
setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium );
setStrutSize( Q::OpenMenuGraphic, 12_dp, 12_dp ); setStrutSize( Q::PopupIndicator, 12_dp, 12_dp );
setGraphicRole( Q::OpenMenuGraphic, QskMaterial3Skin::GraphicRoleOnSurface ); setGraphicRole( Q::PopupIndicator, QskMaterial3Skin::GraphicRoleOnSurface );
setAlignment( Q::OpenMenuGraphic, Qt::AlignRight | Qt::AlignVCenter ); setAlignment( Q::PopupIndicator, Qt::AlignRight | Qt::AlignVCenter );
const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); 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 ); 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::PopupIndicator, symbol( "combo-box-arrow-closed" ) );
setSymbol( Q::OpenMenuGraphic | Q::PopupOpen, symbol( "combo-box-arrow-open" ) ); setSymbol( Q::PopupIndicator | Q::PopupOpen, symbol( "combo-box-arrow-open" ) );
} }
void Editor::setupBox() void Editor::setupBox()

View File

@ -382,14 +382,14 @@ void Editor::setupComboBox()
setStrutSize( Q::Graphic, 24_dp, 24_dp ); setStrutSize( Q::Graphic, 24_dp, 24_dp );
setGraphicRole( Q::Graphic | Q::Disabled, DisabledSymbol ); setGraphicRole( Q::Graphic | Q::Disabled, DisabledSymbol );
setStrutSize( Q::OpenMenuGraphic, 15_dp, 15_dp ); setStrutSize( Q::PopupIndicator, 15_dp, 15_dp );
setGraphicRole( Q::OpenMenuGraphic | Q::Disabled, DisabledSymbol ); 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 ) ); QskStandardSymbol::graphic( QskStandardSymbol::TriangleDown ) );
setSymbol( Q::OpenMenuGraphic | Q::PopupOpen, setSymbol( Q::PopupIndicator | Q::PopupOpen,
QskStandardSymbol::graphic( QskStandardSymbol::TriangleUp ) ); QskStandardSymbol::graphic( QskStandardSymbol::TriangleUp ) );
} }

View File

@ -8,17 +8,45 @@
#include "QskGraphic.h" #include "QskGraphic.h"
#include "QskMenu.h" #include "QskMenu.h"
#include "QskTextOptions.h" #include "QskTextOptions.h"
#include "QskEvent.h"
#include <qquickwindow.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, Panel )
QSK_SUBCONTROL( QskComboBox, Graphic ) QSK_SUBCONTROL( QskComboBox, Graphic )
QSK_SUBCONTROL( QskComboBox, Text ) QSK_SUBCONTROL( QskComboBox, Text )
QSK_SUBCONTROL( QskComboBox, OpenMenuGraphic ) QSK_SUBCONTROL( QskComboBox, PopupIndicator )
QSK_SUBCONTROL( QskComboBox, Splash ) QSK_SUBCONTROL( QskComboBox, Splash )
QSK_SYSTEM_STATE( QskComboBox, Pressed, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskComboBox, PopupOpen, QskAspect::FirstSystemState << 1 )
QSK_SYSTEM_STATE( QskComboBox, PopupOpen, QskAspect::FirstSystemState << 2 )
#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 class QskComboBox::PrivateData
{ {
@ -56,63 +84,34 @@ QskComboBox::QskComboBox( QQuickItem* parent )
setAcceptHoverEvents( true ); setAcceptHoverEvents( true );
connect( m_data->menu, &QskMenu::currentIndexChanged, connect( m_data->menu, &QskMenu::triggered,
this, &QskComboBox::currentIndexChanged ); this, &QskComboBox::activated );
connect( m_data->menu, &QskMenu::currentIndexChanged, connect( m_data->menu, &QskMenu::currentIndexChanged,
this, &QQuickItem::update ); this, &QskComboBox::showOption );
connect( m_data->menu, &QskMenu::countChanged, connect( m_data->menu, &QskMenu::countChanged,
this, &QskComboBox::countChanged ); this, &QskComboBox::countChanged );
connect( this, &QskComboBox::currentIndexChanged,
this, &QskControl::focusIndicatorRectChanged );
connect( m_data->menu, &QskMenu::closed, this, connect( m_data->menu, &QskMenu::closed, this,
[ this ]() { setPopupOpen( false ); setFocus( true ); } ); [ this ]() { setPopupOpen( false ); setFocus( true ); } );
connect( this, &QskComboBox::pressed, this, &QskComboBox::togglePopup );
} }
QskComboBox::~QskComboBox() 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 ) void QskComboBox::setPopupOpen( bool on )
{ {
if ( on == isPopupOpen() ) if ( on == isPopupOpen() )
return; return;
if( on )
{
openPopup();
}
else
{
closePopup();
}
setSkinStateFlag( PopupOpen, on ); setSkinStateFlag( PopupOpen, on );
Q_EMIT popupOpenChanged( on );
if( on )
openPopup();
else
closePopup();
} }
bool QskComboBox::isPopupOpen() const bool QskComboBox::isPopupOpen() const
@ -172,7 +171,7 @@ void QskComboBox::setPlaceholderText( const QString& text )
Q_EMIT placeholderTextChanged( text ); Q_EMIT placeholderTextChanged( text );
} }
QString QskComboBox::text() const QString QskComboBox::currentText() const
{ {
const int index = currentIndex(); const int index = currentIndex();
if( index >= 0 ) if( index >= 0 )
@ -184,11 +183,6 @@ QString QskComboBox::text() const
return placeholderText(); return placeholderText();
} }
void QskComboBox::togglePopup()
{
setPopupOpen( !isPopupOpen() );
}
void QskComboBox::openPopup() void QskComboBox::openPopup()
{ {
const auto cr = contentsRect(); const auto cr = contentsRect();
@ -209,39 +203,64 @@ void QskComboBox::closePopup()
void QskComboBox::mousePressEvent( QMouseEvent* ) void QskComboBox::mousePressEvent( QMouseEvent* )
{ {
setPressed( true ); setPopupOpen( true );
}
void QskComboBox::mouseUngrabEvent()
{
setPressed( false );
} }
void QskComboBox::mouseReleaseEvent( QMouseEvent* ) void QskComboBox::mouseReleaseEvent( QMouseEvent* )
{ {
releaseButton();
} }
void QskComboBox::keyPressEvent( QKeyEvent* event ) void QskComboBox::keyPressEvent( QKeyEvent* event )
{ {
switch ( event->key() ) if ( qskButtonPressKeys().contains( event->key() ) )
{ {
case Qt::Key_Select: if ( !event->isAutoRepeat() )
case Qt::Key_Space:
{ {
if ( !event->isAutoRepeat() ) setPopupOpen( true );
{
setPressed( true );
// calling release button here, because
// we will never get the key release event
// when the menu is opened:
releaseButton();
}
return; 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 ); Inherited::keyPressEvent( event );
} }
@ -250,6 +269,11 @@ void QskComboBox::keyReleaseEvent( QKeyEvent* event )
Inherited::keyReleaseEvent( event ); Inherited::keyReleaseEvent( event );
} }
void QskComboBox::wheelEvent( QWheelEvent* event )
{
increment( -qRound( qskWheelSteps( event ) ) );
}
void QskComboBox::clear() void QskComboBox::clear()
{ {
m_data->menu->clear(); m_data->menu->clear();
@ -271,13 +295,29 @@ int QskComboBox::count() const
return m_data->menu->count(); 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; return;
setPressed( false ); if ( currentIndex() == -1 && steps < 0 )
Q_EMIT clicked(); 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" #include "moc_QskComboBox.cpp"

View File

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

View File

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

View File

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