Merge branch 'master' into qskspinbox
This commit is contained in:
commit
cf449f174f
@ -4,6 +4,8 @@
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SelectorPage.h"
|
||||
|
||||
#include <QskComboBox.h>
|
||||
#include <QskSegmentedBar.h>
|
||||
|
||||
namespace
|
||||
@ -61,10 +63,24 @@ SelectorPage::SelectorPage( QQuickItem* parent )
|
||||
|
||||
void SelectorPage::populate()
|
||||
{
|
||||
setSpacing( 20 );
|
||||
setSpacing( 40 );
|
||||
|
||||
new Box( Qt::Horizontal, this );
|
||||
new Box( Qt::Vertical, this );
|
||||
auto* horizontalButtonsBox = new Box( Qt::Vertical, this );
|
||||
|
||||
auto* comboBoxBox = new QskLinearBox( Qt::Horizontal, horizontalButtonsBox );
|
||||
auto* comboBox1 = new QskComboBox( comboBoxBox );
|
||||
comboBox1->setLabel( "label" );
|
||||
comboBox1->addOption( {}, "airport" );
|
||||
comboBox1->addOption( {}, "flight" );
|
||||
comboBox1->addOption( {}, "pizza" );
|
||||
comboBox1->addOption( {}, "soccer" );
|
||||
|
||||
auto* comboBox2 = new QskComboBox( comboBoxBox );
|
||||
comboBox2->addOption( { "airport_shuttle" }, "airport" );
|
||||
comboBox2->addOption( { "flight" }, "flight" );
|
||||
comboBox2->addOption( { "local_pizza" }, "pizza" );
|
||||
comboBox2->addOption( { "sports_soccer" }, "soccer" );
|
||||
|
||||
setStretchFactor( 0, 0 );
|
||||
setStretchFactor( 1, 10 );
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <QskBox.h>
|
||||
#include <QskCheckBox.h>
|
||||
#include <QskComboBox.h>
|
||||
#include <QskColorFilter.h>
|
||||
#include <QskDialogButtonBox.h>
|
||||
#include <QskFocusIndicator.h>
|
||||
@ -126,10 +127,9 @@ namespace
|
||||
void setup();
|
||||
|
||||
private:
|
||||
void setupControl();
|
||||
|
||||
void setupBox();
|
||||
void setupCheckBox();
|
||||
void setupComboBox();
|
||||
void setupDialogButtonBox();
|
||||
void setupFocusIndicator();
|
||||
void setupInputPanel();
|
||||
@ -185,10 +185,9 @@ namespace
|
||||
|
||||
void Editor::setup()
|
||||
{
|
||||
setupControl();
|
||||
|
||||
setupBox();
|
||||
setupCheckBox();
|
||||
setupComboBox();
|
||||
setupDialogButtonBox();
|
||||
setupFocusIndicator();
|
||||
setupInputPanel();
|
||||
@ -213,16 +212,6 @@ void Editor::setup()
|
||||
setupTextInput();
|
||||
}
|
||||
|
||||
void Editor::setupControl()
|
||||
{
|
||||
using A = QskAspect;
|
||||
|
||||
setPadding( A::NoSubcontrol, 11_dp );
|
||||
|
||||
setGradient( A::NoSubcontrol, m_pal.background );
|
||||
setColor( A::NoSubcontrol | A::StyleColor, m_pal.onBackground );
|
||||
}
|
||||
|
||||
void Editor::setupCheckBox()
|
||||
{
|
||||
// skin hints are ordered according to
|
||||
@ -296,6 +285,49 @@ void Editor::setupCheckBox()
|
||||
setGradient( Q::Ripple | Q::Error | Q::Pressed | Q::Checked, m_pal.error12 );
|
||||
}
|
||||
|
||||
void Editor::setupComboBox()
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
setStrutSize( Q::Panel, { -1, 56_dp } );
|
||||
setPadding( Q::Panel, { 12_dp, 8_dp, 12_dp, 8_dp } );
|
||||
setGradient( Q::Panel, m_pal.surfaceVariant );
|
||||
setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop );
|
||||
setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_dp } );
|
||||
setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant );
|
||||
setSpacing( Q::Panel, 8_dp );
|
||||
|
||||
const auto hoverColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.hoverOpacity );
|
||||
setGradient( Q::Panel | Q::Hovered, hoverColor );
|
||||
|
||||
const auto focusColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.focusOpacity );
|
||||
setGradient( Q::Panel | Q::Focused, focusColor );
|
||||
|
||||
const auto pressedColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.pressedOpacity );
|
||||
setGradient( Q::Panel | Q::Pressed, pressedColor );
|
||||
|
||||
setStrutSize( Q::Graphic, { 24_dp, 24_dp } );
|
||||
setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface );
|
||||
|
||||
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 );
|
||||
|
||||
|
||||
const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 );
|
||||
setGradient( Q::Panel | Q::Disabled, disabledPanelColor );
|
||||
setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 );
|
||||
|
||||
setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface38 );
|
||||
|
||||
setColor( Q::Text | Q::Disabled, m_pal.onSurface38 );
|
||||
|
||||
setGraphicRole( Q::OpenMenuGraphic, QskMaterial3Skin::GraphicRoleOnSurface38 );
|
||||
}
|
||||
|
||||
void Editor::setupBox()
|
||||
{
|
||||
using Q = QskBox;
|
||||
@ -324,6 +356,8 @@ void Editor::setupMenu()
|
||||
setBoxBorderMetrics( Q::Panel, 0 );
|
||||
setPadding( Q::Panel, 0 );
|
||||
|
||||
setGradient( Q::Overlay, Qt::transparent );
|
||||
|
||||
// The color here is primary with an opacity of 8% - we blend that
|
||||
// with the background, because we don't want the menu to have transparency:
|
||||
const auto panel = flattenedColor( m_pal.primary, m_pal.background, 0.08 );
|
||||
@ -344,7 +378,8 @@ void Editor::setupMenu()
|
||||
setGradient( Q::Cursor, m_pal.primary12 );
|
||||
|
||||
setPadding( Q::Graphic, 7_dp );
|
||||
setStrutSize( Q::Graphic, { 46_dp, -1 } );
|
||||
setStrutSize( Q::Graphic, { 24_dp, 24_dp } );
|
||||
setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface );
|
||||
|
||||
setColor( Q::Text, m_pal.onSurface );
|
||||
setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium );
|
||||
@ -1122,6 +1157,8 @@ QskMaterial3Theme::QskMaterial3Theme( Lightness lightness,
|
||||
elevationLight1 = QskShadowMetrics( -3, 5, { 0, 2 } );
|
||||
elevationLight2 = QskShadowMetrics( -2, 8, { 0, 2 } );
|
||||
elevationLight3 = QskShadowMetrics( -1, 11, { 0, 2 } );
|
||||
|
||||
shapeExtraSmallTop = QskBoxShapeMetrics( 4_dp, 4_dp, 0, 0 );
|
||||
}
|
||||
|
||||
QskMaterial3GraphicProvder::QskMaterial3GraphicProvder( QObject* parent )
|
||||
@ -1155,11 +1192,12 @@ QskMaterial3Skin::~QskMaterial3Skin()
|
||||
|
||||
QskGraphic QskMaterial3Skin::symbol( int symbolType ) const
|
||||
{
|
||||
const auto* provider = graphicProvider( {} );
|
||||
|
||||
switch ( symbolType )
|
||||
{
|
||||
case QskStandardSymbol::CheckMark:
|
||||
{
|
||||
const auto* provider = graphicProvider( {} );
|
||||
return *( provider->requestGraphic( "check_small" ) );
|
||||
}
|
||||
case QskStandardSymbol::CrossMark:
|
||||
@ -1168,9 +1206,16 @@ QskGraphic QskMaterial3Skin::symbol( int symbolType ) const
|
||||
}
|
||||
case QskStandardSymbol::SegmentedBarCheckMark:
|
||||
{
|
||||
const auto* provider = graphicProvider( {} );
|
||||
return *( provider->requestGraphic( "segmented-button-check" ) );
|
||||
}
|
||||
case QskStandardSymbol::ComboBoxSymbolPopupClosed:
|
||||
{
|
||||
return *( provider->requestGraphic( "combo-box-arrow-closed" ) );
|
||||
}
|
||||
case QskStandardSymbol::ComboBoxSymbolPopupOpen:
|
||||
{
|
||||
return *( provider->requestGraphic( "combo-box-arrow-open" ) );
|
||||
}
|
||||
default:
|
||||
return Inherited::symbol( symbolType );
|
||||
}
|
||||
@ -1208,6 +1253,10 @@ void QskMaterial3Skin::setupGraphicFilters( const QskMaterial3Theme& palette )
|
||||
onSurfaceFilter38.addColorSubstitution( Qt::white, palette.onSurface38 );
|
||||
setGraphicFilter( GraphicRoleOnSurface38, onSurfaceFilter38 );
|
||||
|
||||
QskColorFilter onSurfaceVariantFilter;
|
||||
onSurfaceVariantFilter.addColorSubstitution( Qt::white, palette.onSurfaceVariant );
|
||||
setGraphicFilter( GraphicRoleOnSurfaceVariant, onSurfaceVariantFilter );
|
||||
|
||||
QskColorFilter surfaceFilter;
|
||||
surfaceFilter.addColorSubstitution( Qt::white, palette.surface );
|
||||
setGraphicFilter( GraphicRoleSurface, surfaceFilter );
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "QskMaterial3Global.h"
|
||||
|
||||
#include <QskBoxShapeMetrics.h>
|
||||
#include <QskGraphicProvider.h>
|
||||
#include <QskHctColor.h>
|
||||
#include <QskSkin.h>
|
||||
@ -93,6 +94,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme
|
||||
const qreal pressedOpacity = 0.12;
|
||||
const qreal draggedOpacity = 0.16;
|
||||
|
||||
QskBoxShapeMetrics shapeExtraSmallTop;
|
||||
|
||||
private:
|
||||
std::array< QskHctColor, NumPaletteTypes > m_palettes;
|
||||
};
|
||||
@ -129,6 +132,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin
|
||||
GraphicRoleOnSecondaryContainer,
|
||||
GraphicRoleOnSurface,
|
||||
GraphicRoleOnSurface38,
|
||||
GraphicRoleOnSurfaceVariant,
|
||||
GraphicRoleSurface,
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file>icons/qvg/check_small.qvg</file>
|
||||
<file>icons/qvg/combo-box-arrow-closed.qvg</file>
|
||||
<file>icons/qvg/combo-box-arrow-open.qvg</file>
|
||||
<file>icons/qvg/segmented-button-check.qvg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
4
skins/material3/icons/combo-box-arrow-closed.svg
Normal file
4
skins/material3/icons/combo-box-arrow-closed.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.25 0.5L4 4.25L7.75 0.5H0.25Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 157 B |
4
skins/material3/icons/combo-box-arrow-open.svg
Normal file
4
skins/material3/icons/combo-box-arrow-open.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="8" height="5" viewBox="0 0 8 5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.25 0.5L4 4.25L7.75 0.5H0.25Z" transform="rotate(180 4 2.5)" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 187 B |
BIN
skins/material3/icons/qvg/combo-box-arrow-closed.qvg
Normal file
BIN
skins/material3/icons/qvg/combo-box-arrow-closed.qvg
Normal file
Binary file not shown.
BIN
skins/material3/icons/qvg/combo-box-arrow-open.qvg
Normal file
BIN
skins/material3/icons/qvg/combo-box-arrow-open.qvg
Normal file
Binary file not shown.
276
src/controls/QskComboBox.cpp
Normal file
276
src/controls/QskComboBox.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2023 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskComboBox.h"
|
||||
|
||||
#include "QskGraphic.h"
|
||||
#include "QskMenu.h"
|
||||
#include "QskTextOptions.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
QSK_SUBCONTROL( QskComboBox, Panel )
|
||||
QSK_SUBCONTROL( QskComboBox, Graphic )
|
||||
QSK_SUBCONTROL( QskComboBox, Text )
|
||||
QSK_SUBCONTROL( QskComboBox, OpenMenuGraphic )
|
||||
QSK_SUBCONTROL( QskComboBox, Ripple )
|
||||
|
||||
QSK_SYSTEM_STATE( QskComboBox, Pressed, QskAspect::FirstSystemState << 1 )
|
||||
QSK_SYSTEM_STATE( QskComboBox, PopupOpen, QskAspect::FirstSystemState << 2 )
|
||||
|
||||
class QskComboBox::PrivateData
|
||||
{
|
||||
public:
|
||||
PrivateData( QskComboBox* const box )
|
||||
: menu( new QskMenu( box ) )
|
||||
{
|
||||
menu->setPopupFlag( QskPopup::DeleteOnClose, false );
|
||||
}
|
||||
|
||||
QskMenu* const menu;
|
||||
QString label;
|
||||
};
|
||||
|
||||
QskComboBox::QskComboBox( QQuickItem* parent )
|
||||
: Inherited( parent )
|
||||
, m_data( new PrivateData( this ) )
|
||||
{
|
||||
initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
|
||||
|
||||
setPolishOnResize( true );
|
||||
|
||||
setAcceptedMouseButtons( Qt::LeftButton );
|
||||
setWheelEnabled( true );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
|
||||
setAcceptHoverEvents( true );
|
||||
|
||||
connect( m_data->menu, &QskMenu::currentIndexChanged, this, &QskComboBox::currentIndexChanged );
|
||||
connect( m_data->menu, &QskMenu::currentIndexChanged, this, &QQuickItem::update );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
bool QskComboBox::isPopupOpen() const
|
||||
{
|
||||
return hasSkinState( PopupOpen );
|
||||
}
|
||||
|
||||
QskGraphic QskComboBox::graphic() const
|
||||
{
|
||||
const int index = m_data->menu->currentIndex();
|
||||
|
||||
if( index >= 0 )
|
||||
{
|
||||
const auto option = m_data->menu->optionAt( index );
|
||||
return option.at( 0 ).value< QskGraphic >();
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void QskComboBox::setTextOptions( const QskTextOptions& textOptions )
|
||||
{
|
||||
setTextOptionsHint( Text, textOptions );
|
||||
}
|
||||
|
||||
QskTextOptions QskComboBox::textOptions() const
|
||||
{
|
||||
return textOptionsHint( Text );
|
||||
}
|
||||
|
||||
void QskComboBox::addOption( const QUrl& graphicSource, const QString& text )
|
||||
{
|
||||
m_data->menu->addOption( graphicSource, text );
|
||||
}
|
||||
|
||||
QVariantList QskComboBox::optionAt( int index ) const
|
||||
{
|
||||
return m_data->menu->optionAt( index );
|
||||
}
|
||||
|
||||
QString QskComboBox::label() const
|
||||
{
|
||||
return m_data->label;
|
||||
}
|
||||
|
||||
void QskComboBox::setLabel( const QString& label )
|
||||
{
|
||||
m_data->label = label;
|
||||
}
|
||||
|
||||
QString QskComboBox::text() const
|
||||
{
|
||||
const int index = m_data->menu->currentIndex();
|
||||
|
||||
if( index >= 0 )
|
||||
{
|
||||
const auto option = m_data->menu->optionAt( index );
|
||||
return option.at( 1 ).toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return label();
|
||||
}
|
||||
}
|
||||
|
||||
void QskComboBox::togglePopup()
|
||||
{
|
||||
setPopupOpen( !isPopupOpen() );
|
||||
}
|
||||
|
||||
void QskComboBox::openPopup()
|
||||
{
|
||||
m_data->menu->open();
|
||||
}
|
||||
|
||||
void QskComboBox::closePopup()
|
||||
{
|
||||
m_data->menu->close();
|
||||
}
|
||||
|
||||
void QskComboBox::updateLayout()
|
||||
{
|
||||
Inherited::updateLayout();
|
||||
|
||||
auto origin = contentsRect().bottomLeft();
|
||||
m_data->menu->setOrigin( origin );
|
||||
|
||||
m_data->menu->setFixedWidth( contentsRect().width() );
|
||||
}
|
||||
|
||||
void QskComboBox::mousePressEvent( QMouseEvent* event )
|
||||
{
|
||||
Q_UNUSED( event )
|
||||
setPressed( true );
|
||||
}
|
||||
|
||||
void QskComboBox::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
Q_UNUSED( event )
|
||||
releaseButton();
|
||||
}
|
||||
|
||||
void QskComboBox::keyPressEvent( QKeyEvent* event )
|
||||
{
|
||||
switch ( event->key() )
|
||||
{
|
||||
case Qt::Key_Select:
|
||||
case Qt::Key_Space:
|
||||
{
|
||||
if ( !event->isAutoRepeat() )
|
||||
{
|
||||
setPressed( true );
|
||||
// calling release button here, because
|
||||
// we will never get the key release event
|
||||
// when the menu is opened:
|
||||
releaseButton();
|
||||
}
|
||||
|
||||
// always accepting
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::keyPressEvent( event );
|
||||
}
|
||||
|
||||
void QskComboBox::keyReleaseEvent( QKeyEvent* event )
|
||||
{
|
||||
Inherited::keyReleaseEvent( event );
|
||||
}
|
||||
|
||||
void QskComboBox::clear()
|
||||
{
|
||||
m_data->menu->clear();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void QskComboBox::click()
|
||||
{
|
||||
setPressed( true );
|
||||
releaseButton();
|
||||
}
|
||||
|
||||
void QskComboBox::setCurrentIndex( int index )
|
||||
{
|
||||
m_data->menu->setCurrentIndex( index );
|
||||
}
|
||||
|
||||
int QskComboBox::currentIndex() const
|
||||
{
|
||||
return m_data->menu->currentIndex();
|
||||
}
|
||||
|
||||
int QskComboBox::count() const
|
||||
{
|
||||
return m_data->menu->count();
|
||||
}
|
||||
|
||||
void QskComboBox::releaseButton()
|
||||
{
|
||||
if ( !isPressed() )
|
||||
return;
|
||||
|
||||
setPressed( false );
|
||||
Q_EMIT clicked();
|
||||
}
|
||||
|
||||
#include "moc_QskComboBox.cpp"
|
93
src/controls/QskComboBox.h
Normal file
93
src/controls/QskComboBox.h
Normal file
@ -0,0 +1,93 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2023 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_COMBO_BOX_H
|
||||
#define QSK_COMBO_BOX_H
|
||||
|
||||
#include "QskControl.h"
|
||||
|
||||
class QskGraphic;
|
||||
|
||||
class QSK_EXPORT QskComboBox : public QskControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( int currentIndex READ currentIndex
|
||||
WRITE setCurrentIndex NOTIFY currentIndexChanged )
|
||||
|
||||
Q_PROPERTY( int count READ count NOTIFY countChanged )
|
||||
|
||||
using Inherited = QskControl;
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel, Graphic, Text, OpenMenuGraphic, Ripple )
|
||||
QSK_STATES( Pressed, PopupOpen )
|
||||
|
||||
QskComboBox( QQuickItem* parent = nullptr );
|
||||
|
||||
~QskComboBox() override;
|
||||
|
||||
void setPressed( bool on );
|
||||
bool isPressed() const;
|
||||
|
||||
void setPopupOpen( bool on );
|
||||
bool isPopupOpen() const;
|
||||
|
||||
QskGraphic graphic() const;
|
||||
|
||||
void setTextOptions( const QskTextOptions& );
|
||||
QskTextOptions textOptions() const;
|
||||
|
||||
void addOption( const QUrl&, const QString& );
|
||||
|
||||
void clear();
|
||||
|
||||
int currentIndex() const;
|
||||
|
||||
int count() const;
|
||||
|
||||
QVariantList optionAt( int ) const;
|
||||
|
||||
QString label() const;
|
||||
void setLabel( const QString& );
|
||||
|
||||
QString text() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void togglePopup();
|
||||
virtual void openPopup();
|
||||
virtual void closePopup();
|
||||
|
||||
void click();
|
||||
void setCurrentIndex( int index );
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentIndexChanged( int );
|
||||
void countChanged();
|
||||
|
||||
void pressed();
|
||||
void released();
|
||||
void clicked();
|
||||
|
||||
void pressedChanged( bool );
|
||||
void popupOpenChanged( bool );
|
||||
|
||||
protected:
|
||||
virtual void updateLayout() override;
|
||||
|
||||
void mousePressEvent( QMouseEvent* ) override;
|
||||
void mouseReleaseEvent( QMouseEvent* ) override;
|
||||
|
||||
void keyPressEvent( QKeyEvent* ) override;
|
||||
void keyReleaseEvent( QKeyEvent* ) override;
|
||||
|
||||
private:
|
||||
void releaseButton();
|
||||
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
241
src/controls/QskComboBoxSkinlet.cpp
Normal file
241
src/controls/QskComboBoxSkinlet.cpp
Normal file
@ -0,0 +1,241 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2023 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskComboBoxSkinlet.h"
|
||||
#include "QskComboBox.h"
|
||||
|
||||
#include "QskGraphic.h"
|
||||
#include "QskSkin.h"
|
||||
#include "QskSGNode.h"
|
||||
#include "QskStandardSymbol.h"
|
||||
#include "QskSubcontrolLayoutEngine.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
#if 1 // unify with the implementation from QskMenu
|
||||
template< class T >
|
||||
static inline QVariant qskSampleAt( const QskComboBox* box )
|
||||
{
|
||||
if( std::is_same< T, QString >() )
|
||||
{
|
||||
return box->text();
|
||||
}
|
||||
|
||||
const int index = box->currentIndex();
|
||||
|
||||
if( index < 0 )
|
||||
return QVariant::fromValue( T() );
|
||||
|
||||
const auto list = box->optionAt( index );
|
||||
for ( const auto& value : list )
|
||||
{
|
||||
if ( value.canConvert< T >() )
|
||||
return value;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static inline T qskValueAt( const QskComboBox* box )
|
||||
{
|
||||
const auto sample = qskSampleAt< T >( box );
|
||||
return sample.template value< T >();
|
||||
}
|
||||
#endif
|
||||
|
||||
class LayoutEngine : public QskSubcontrolLayoutEngine
|
||||
{
|
||||
public:
|
||||
LayoutEngine( const QskComboBox* box )
|
||||
: QskSubcontrolLayoutEngine( Qt::Horizontal )
|
||||
{
|
||||
setSpacing( box->spacingHint( QskComboBox::Panel ) );
|
||||
|
||||
setGraphicTextElements( box,
|
||||
QskComboBox::Text, qskValueAt< QString >( box ),
|
||||
QskComboBox::Graphic, qskValueAt< QskGraphic >( box ).defaultSize() );
|
||||
|
||||
const auto alignment = box->alignmentHint( QskComboBox::Panel, Qt::AlignLeft );
|
||||
setFixedContent( QskComboBox::Text, Qt::Horizontal, alignment );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QskComboBoxSkinlet::QskComboBoxSkinlet( QskSkin* skin )
|
||||
: Inherited( skin )
|
||||
{
|
||||
setNodeRoles( { PanelRole, GraphicRole, TextRole, OpenMenuGraphicRole, RippleRole } );
|
||||
}
|
||||
|
||||
QskComboBoxSkinlet::~QskComboBoxSkinlet() = default;
|
||||
|
||||
QRectF QskComboBoxSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
const auto* box = static_cast< const QskComboBox* >( skinnable );
|
||||
|
||||
if ( subControl == Q::Text || subControl == Q::Graphic )
|
||||
{
|
||||
const auto r = box->subControlContentsRect( contentsRect, Q::Panel );
|
||||
|
||||
LayoutEngine layoutEngine( box );
|
||||
layoutEngine.setGeometries( r );
|
||||
|
||||
return layoutEngine.subControlRect( subControl );
|
||||
}
|
||||
|
||||
if( subControl == Q::OpenMenuGraphic )
|
||||
{
|
||||
auto rect = box->innerBox( Q::Panel, contentsRect );
|
||||
const auto size = box->strutSizeHint( Q::OpenMenuGraphic );
|
||||
rect.setLeft( rect.right() - size.width() );
|
||||
return rect;
|
||||
}
|
||||
|
||||
if ( subControl == Q::Panel )
|
||||
{
|
||||
return contentsRect;
|
||||
}
|
||||
|
||||
if ( subControl == Q::Ripple )
|
||||
{
|
||||
return rippleRect( box, contentsRect );
|
||||
}
|
||||
|
||||
return Inherited::subControlRect( skinnable, contentsRect, subControl );
|
||||
}
|
||||
|
||||
QSGNode* QskComboBoxSkinlet::updateSubNode(
|
||||
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
const auto bar = static_cast< const QskComboBox* >( skinnable );
|
||||
|
||||
switch ( nodeRole )
|
||||
{
|
||||
case PanelRole:
|
||||
{
|
||||
return updateBoxNode( bar, node, Q::Panel );
|
||||
}
|
||||
|
||||
case GraphicRole:
|
||||
{
|
||||
return updateGraphicNode( bar, node, bar->graphic(), Q::Graphic );
|
||||
}
|
||||
|
||||
case TextRole:
|
||||
{
|
||||
return updateTextNode( bar, node );
|
||||
}
|
||||
|
||||
case OpenMenuGraphicRole:
|
||||
{
|
||||
const auto symbol = bar->isPopupOpen() ? QskStandardSymbol::ComboBoxSymbolPopupOpen
|
||||
: QskStandardSymbol::ComboBoxSymbolPopupClosed;
|
||||
const auto graphic = bar->effectiveSkin()->symbol( symbol );
|
||||
return updateGraphicNode( bar, node, graphic, Q::OpenMenuGraphic );
|
||||
}
|
||||
}
|
||||
|
||||
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
||||
}
|
||||
|
||||
QRectF QskComboBoxSkinlet::rippleRect(
|
||||
const QskComboBox* bar, const QRectF& contentsRect ) const
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
QRectF rect;
|
||||
|
||||
const auto ratio = bar->metric( Q::Ripple | QskAspect::Size );
|
||||
if ( ratio > 0.0 )
|
||||
{
|
||||
const auto pos = bar->effectiveSkinHint(
|
||||
Q::Ripple | QskAspect::Metric | QskAspect::Position ).toPointF();
|
||||
|
||||
const auto panelRect = subControlRect( bar, contentsRect, Q::Panel );
|
||||
|
||||
rect.setSize( 2.0 * panelRect.size() * ratio );
|
||||
rect.moveCenter( pos );
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
QSGNode* QskComboBoxSkinlet::updateTextNode(
|
||||
const QskComboBox* box, QSGNode* node ) const
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
const auto rect = box->subControlRect( Q::Text ).toAlignedRect();
|
||||
|
||||
const auto textHeight = box->effectiveFontHeight( Q::Text );
|
||||
if ( !box->clip() && ( rect.height() < textHeight ) )
|
||||
return nullptr;
|
||||
|
||||
const auto alignment = box->alignmentHint( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
|
||||
|
||||
return QskSkinlet::updateTextNode( box, node, rect,
|
||||
alignment, box->text(), Q::Text );
|
||||
}
|
||||
|
||||
QSGNode* QskComboBoxSkinlet::updateRippleNode(
|
||||
const QskComboBox* button, QSGNode* node ) const
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
const auto rippleRect = button->subControlRect( Q::Ripple );
|
||||
if ( rippleRect.isEmpty() )
|
||||
return nullptr;
|
||||
|
||||
auto clipNode = updateBoxClipNode( button, node,
|
||||
button->subControlRect( Q::Panel ), Q::Panel );
|
||||
|
||||
if ( clipNode )
|
||||
{
|
||||
auto boxNode = QskSGNode::findChildNode( clipNode, RippleRole );
|
||||
boxNode = updateBoxNode( button, boxNode, rippleRect, Q::Ripple );
|
||||
|
||||
if ( boxNode == nullptr )
|
||||
return nullptr;
|
||||
|
||||
QskSGNode::setNodeRole( boxNode, RippleRole );
|
||||
if ( boxNode->parent() != clipNode )
|
||||
clipNode->appendChildNode( boxNode );
|
||||
}
|
||||
|
||||
return clipNode;
|
||||
}
|
||||
|
||||
QSizeF QskComboBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||
Qt::SizeHint which, const QSizeF& ) const
|
||||
{
|
||||
using Q = QskComboBox;
|
||||
|
||||
if ( which != Qt::PreferredSize )
|
||||
return QSizeF();
|
||||
|
||||
const auto box = static_cast< const QskComboBox* >( skinnable );
|
||||
|
||||
LayoutEngine layoutEngine( box );
|
||||
auto size = layoutEngine.sizeHint( which, QSizeF() );
|
||||
|
||||
const auto spacingHint = box->spacingHint( Q::Panel );
|
||||
const auto menuGraphicHint = box->strutSizeHint( Q::OpenMenuGraphic );
|
||||
|
||||
size.rwidth() += spacingHint + menuGraphicHint.width();
|
||||
|
||||
size = box->outerBoxSize( Q::Panel, size );
|
||||
size = size.expandedTo( box->strutSizeHint( Q::Panel ) );
|
||||
size = size.grownBy( skinnable->marginHint( Q::Panel ) );
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#include "moc_QskComboBoxSkinlet.cpp"
|
51
src/controls/QskComboBoxSkinlet.h
Normal file
51
src/controls/QskComboBoxSkinlet.h
Normal file
@ -0,0 +1,51 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2023 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_COMBO_BOX_SKINLET_H
|
||||
#define QSK_COMBO_BOX_SKINLET_H
|
||||
|
||||
#include "QskSkinlet.h"
|
||||
|
||||
class QskComboBox;
|
||||
|
||||
class QSK_EXPORT QskComboBoxSkinlet : public QskSkinlet
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
using Inherited = QskSkinlet;
|
||||
|
||||
public:
|
||||
enum NodeRole
|
||||
{
|
||||
PanelRole,
|
||||
GraphicRole,
|
||||
TextRole,
|
||||
OpenMenuGraphicRole,
|
||||
RippleRole,
|
||||
|
||||
RoleCount
|
||||
};
|
||||
|
||||
Q_INVOKABLE QskComboBoxSkinlet( QskSkin* = nullptr );
|
||||
~QskComboBoxSkinlet() override;
|
||||
|
||||
QRectF subControlRect( const QskSkinnable*,
|
||||
const QRectF&, QskAspect::Subcontrol ) const override;
|
||||
|
||||
QSizeF sizeHint( const QskSkinnable*,
|
||||
Qt::SizeHint, const QSizeF& ) const override;
|
||||
|
||||
protected:
|
||||
QSGNode* updateSubNode( const QskSkinnable*,
|
||||
quint8 nodeRole, QSGNode* ) const override;
|
||||
|
||||
private:
|
||||
QRectF rippleRect( const QskComboBox*, const QRectF& ) const;
|
||||
|
||||
QSGNode* updateTextNode( const QskComboBox*, QSGNode* ) const;
|
||||
QSGNode* updateRippleNode( const QskComboBox*, QSGNode* ) const;
|
||||
};
|
||||
|
||||
#endif
|
@ -17,6 +17,7 @@
|
||||
#include <qvariant.h>
|
||||
#include <qeventloop.h>
|
||||
|
||||
QSK_SUBCONTROL( QskMenu, Overlay )
|
||||
QSK_SUBCONTROL( QskMenu, Panel )
|
||||
QSK_SUBCONTROL( QskMenu, Segment )
|
||||
QSK_SUBCONTROL( QskMenu, Cursor )
|
||||
@ -81,6 +82,8 @@ QskMenu::QskMenu( QQuickItem* parent )
|
||||
setPopupFlag( QskPopup::CloseOnPressOutside, true );
|
||||
setPopupFlag( QskPopup::DeleteOnClose, true );
|
||||
|
||||
setSubcontrolProxy( Inherited::Overlay, Overlay );
|
||||
|
||||
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
|
||||
}
|
||||
|
||||
@ -255,7 +258,12 @@ void QskMenu::keyPressEvent( QKeyEvent* event )
|
||||
|
||||
default:
|
||||
{
|
||||
return;
|
||||
const int steps = qskFocusChainIncrement( event );
|
||||
|
||||
if( steps != 0 )
|
||||
{
|
||||
traverse( steps );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,8 +361,13 @@ void QskMenu::aboutToShow()
|
||||
|
||||
QRectF QskMenu::focusIndicatorRect() const
|
||||
{
|
||||
// highlighting the item is good enough
|
||||
return QRectF();
|
||||
if( currentIndex() >= 0 )
|
||||
{
|
||||
return effectiveSkinlet()->sampleRect( this,
|
||||
contentsRect(), Segment, currentIndex() );
|
||||
}
|
||||
|
||||
return Inherited::focusIndicatorRect();
|
||||
}
|
||||
|
||||
void QskMenu::setSelectedIndex( int index )
|
||||
|
@ -32,7 +32,7 @@ class QSK_EXPORT QskMenu : public QskPopup
|
||||
using Inherited = QskPopup;
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Graphic, Separator )
|
||||
QSK_SUBCONTROLS( Overlay, Panel, Segment, Cursor, Text, Graphic, Separator )
|
||||
QSK_STATES( Selected )
|
||||
|
||||
QskMenu( QQuickItem* parentItem = nullptr );
|
||||
|
@ -32,6 +32,9 @@ QSK_QT_PRIVATE_END
|
||||
#include "QskCheckBox.h"
|
||||
#include "QskCheckBoxSkinlet.h"
|
||||
|
||||
#include "QskComboBox.h"
|
||||
#include "QskComboBoxSkinlet.h"
|
||||
|
||||
#include "QskFocusIndicator.h"
|
||||
#include "QskFocusIndicatorSkinlet.h"
|
||||
|
||||
@ -153,6 +156,7 @@ QskSkin::QskSkin( QObject* parent )
|
||||
|
||||
declareSkinlet< QskBox, QskBoxSkinlet >();
|
||||
declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >();
|
||||
declareSkinlet< QskComboBox, QskComboBoxSkinlet >();
|
||||
declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >();
|
||||
declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >();
|
||||
declareSkinlet< QskListView, QskListViewSkinlet >();
|
||||
|
@ -1,6 +1,6 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2023 Edelhirsch Software GmbH
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskSpinBox.h"
|
||||
@ -17,6 +17,7 @@
|
||||
#include <QskSkinlet.h>
|
||||
#include <QskTextInput.h>
|
||||
#include <QtMath>
|
||||
|
||||
#include <array>
|
||||
|
||||
QSK_SUBCONTROL( QskSpinBox, IncrementPanel )
|
||||
|
@ -1,12 +1,14 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2023 Edelhirsch Software GmbH
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#ifndef QSK_SPIN_BOX_H
|
||||
#define QSK_SPIN_BOX_H
|
||||
|
||||
#include <QskBoundedValueInput.h>
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief
|
||||
/// This control allows the user to increment and decrement a floating point value.
|
||||
@ -86,3 +88,5 @@ class QSK_EXPORT QskSpinBox : public QskBoundedValueInput
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2023 Edelhirsch Software GmbH
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskSpinBoxSkinlet.h"
|
||||
|
@ -1,9 +1,10 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2023 Edelhirsch Software GmbH
|
||||
* This file may be used under the terms of the 3-clause BSD License
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#ifndef QSK_SPIN_BOX_SKINLET_H
|
||||
#define QSK_SPIN_BOX_SKINLET_H
|
||||
|
||||
#include <QskSkinlet.h>
|
||||
|
||||
@ -109,3 +110,5 @@ class QSK_EXPORT QskSpinBoxSkinlet : public QskSkinlet
|
||||
QSGNode* updateSampleNode( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl,
|
||||
int index, QSGNode* node ) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -267,6 +267,8 @@ QskGraphic QskStandardSymbol::graphic( Type symbolType )
|
||||
}
|
||||
case QskStandardSymbol::NoSymbol:
|
||||
case QskStandardSymbol::SymbolTypeCount:
|
||||
case QskStandardSymbol::ComboBoxSymbolPopupClosed:
|
||||
case QskStandardSymbol::ComboBoxSymbolPopupOpen:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ namespace QskStandardSymbol
|
||||
CrossMark,
|
||||
|
||||
SegmentedBarCheckMark,
|
||||
ComboBoxSymbolPopupClosed,
|
||||
ComboBoxSymbolPopupOpen,
|
||||
|
||||
SymbolTypeCount
|
||||
};
|
||||
|
@ -173,6 +173,8 @@ HEADERS += \
|
||||
controls/QskBoxSkinlet.h \
|
||||
controls/QskCheckBox.h \
|
||||
controls/QskCheckBoxSkinlet.h \
|
||||
controls/QskComboBox.h \
|
||||
controls/QskComboBoxSkinlet.h \
|
||||
controls/QskControl.h \
|
||||
controls/QskControlPrivate.h \
|
||||
controls/QskDirtyItemFilter.h \
|
||||
@ -260,6 +262,8 @@ SOURCES += \
|
||||
controls/QskBoxSkinlet.cpp \
|
||||
controls/QskCheckBox.cpp \
|
||||
controls/QskCheckBoxSkinlet.cpp \
|
||||
controls/QskComboBox.cpp \
|
||||
controls/QskComboBoxSkinlet.cpp \
|
||||
controls/QskControl.cpp \
|
||||
controls/QskControlPrivate.cpp \
|
||||
controls/QskDirtyItemFilter.cpp \
|
||||
|
Loading…
x
Reference in New Issue
Block a user