Merge branch 'master' into qskspinbox

This commit is contained in:
uwerat 2023-02-19 10:25:04 +01:00 committed by GitHub
commit cf449f174f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 806 additions and 33 deletions

View File

@ -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 );

View File

@ -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 );

View File

@ -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,
};

View File

@ -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>

View 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

View 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

Binary file not shown.

Binary file not shown.

View 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"

View 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

View 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"

View 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

View File

@ -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 )

View File

@ -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 );

View File

@ -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 >();

View File

@ -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 )

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -267,6 +267,8 @@ QskGraphic QskStandardSymbol::graphic( Type symbolType )
}
case QskStandardSymbol::NoSymbol:
case QskStandardSymbol::SymbolTypeCount:
case QskStandardSymbol::ComboBoxSymbolPopupClosed:
case QskStandardSymbol::ComboBoxSymbolPopupOpen:
{
break;
}

View File

@ -31,6 +31,8 @@ namespace QskStandardSymbol
CrossMark,
SegmentedBarCheckMark,
ComboBoxSymbolPopupClosed,
ComboBoxSymbolPopupOpen,
SymbolTypeCount
};

View File

@ -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 \