From d06c2c6d8f1d8190295f358df33f69e0c11dd0e3 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 17 Feb 2023 13:32:27 +0100 Subject: [PATCH 1/6] gcc issues fixed --- src/controls/QskSpinBox.cpp | 4 +--- src/controls/QskSpinBoxSkinlet.cpp | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/controls/QskSpinBox.cpp b/src/controls/QskSpinBox.cpp index ce56b2ea..4776b4a6 100644 --- a/src/controls/QskSpinBox.cpp +++ b/src/controls/QskSpinBox.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -17,7 +16,6 @@ #include #include #include -#include QSK_SUBCONTROL(QskSpinBox, Inc) QSK_SUBCONTROL(QskSpinBox, Dec) @@ -193,7 +191,7 @@ void QskSpinBox::hoverEnterEvent(QHoverEvent* event) m_data->saveMousePosition( qskHoverPosition( event ) ); } -void QskSpinBox::hoverLeaveEvent(QHoverEvent* event) +void QskSpinBox::hoverLeaveEvent(QHoverEvent* ) { m_data->saveMousePosition( {} ); } diff --git a/src/controls/QskSpinBoxSkinlet.cpp b/src/controls/QskSpinBoxSkinlet.cpp index 1d280943..71a22b83 100644 --- a/src/controls/QskSpinBoxSkinlet.cpp +++ b/src/controls/QskSpinBoxSkinlet.cpp @@ -173,8 +173,6 @@ QRectF QskSpinBoxSkinlet::subControlRect(const QskSkinnable* const skinnable, co } else if(layout == (Qt::AlignBottom | Qt::AlignHCenter)) { - const auto dx = qMax(rects[Inc].width(), rects[Dec].width()); - const auto dy = qMax(rects[Inc].height(), rects[Dec].height()); rects[Txt].moveTopLeft({center.x() - rects[Txt].width() * 0.5, center.y() - rects[Txt].height() * 0.5}); rects[Dec].moveTopLeft({center.x() - spacing * 0.5 - rects[Dec].width() , rects[Txt].top() - spacing - rects[Dec].height() }); rects[Inc].moveTopLeft({center.x() + spacing * 0.5, rects[Txt].top() - spacing - rects[Inc].height() }); From eb5f3d3bcfc3616fcf5bbe34c18acb55cd8c369f Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 17 Feb 2023 13:36:08 +0100 Subject: [PATCH 2/6] Qt5 compiler issue fixed --- src/controls/QskSpinBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controls/QskSpinBox.cpp b/src/controls/QskSpinBox.cpp index 4776b4a6..eaec9825 100644 --- a/src/controls/QskSpinBox.cpp +++ b/src/controls/QskSpinBox.cpp @@ -172,7 +172,7 @@ using S = QskSpinBox; QskSpinBox::QskSpinBox(QQuickItem* const parent) : Inherited(parent) - , m_data(std::make_unique(this)) + , m_data( new PrivateData( this ) ) { setBoundaries(0.0,1.0); setAcceptHoverEvents(true); From 8df15517015dfbc848f9ab4c7aad1c9ac370a3fe Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 17 Feb 2023 14:05:05 +0100 Subject: [PATCH 3/6] formal adjustments --- src/controls/QskSpinBox.cpp | 25 +++++++------ src/controls/QskSpinBox.h | 57 ++++++++++++++--------------- src/controls/QskSpinBoxSkinlet.cpp | 7 ++-- src/controls/QskSpinBoxSkinlet.h | 58 ++++++++++++++++++++---------- 4 files changed, 86 insertions(+), 61 deletions(-) diff --git a/src/controls/QskSpinBox.cpp b/src/controls/QskSpinBox.cpp index eaec9825..186d4db6 100644 --- a/src/controls/QskSpinBox.cpp +++ b/src/controls/QskSpinBox.cpp @@ -1,21 +1,24 @@ /****************************************************************************** - * 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" -#include -#include -#include -#include -#include -#include -#include -#include +#include "QskLinearBox.h" +#include "QskGridBox.h" +#include "QskTextInput.h" +#include "QskBoxShapeMetrics.h" +#include "QskBoxBorderColors.h" +#include "QskBoxBorderMetrics.h" +#include "QskSkinlet.h" +#include "QskIntervalF.h" +#include "QskEvent.h" + #include #include #include -#include + +#include QSK_SUBCONTROL(QskSpinBox, Inc) QSK_SUBCONTROL(QskSpinBox, Dec) diff --git a/src/controls/QskSpinBox.h b/src/controls/QskSpinBox.h index 9222b6e2..0f4db5d6 100644 --- a/src/controls/QskSpinBox.h +++ b/src/controls/QskSpinBox.h @@ -1,48 +1,49 @@ /****************************************************************************** - * 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 -#include #include class QSK_EXPORT QskSpinBox : public QskBoundedInput { - Q_OBJECT - using Inherited = QskBoundedInput; + Q_OBJECT + Q_PROPERTY(qreal value READ value NOTIFY valueChanged) + + using Inherited = QskBoundedInput; public: - Q_PROPERTY(qreal value READ value NOTIFY valueChanged) - QSK_SUBCONTROLS(Inc, Dec, IncText, DecText, TextPanel, Text, Layout) - QSK_STATES( Pressed ) + QSK_SUBCONTROLS(Inc, Dec, IncText, DecText, TextPanel, Text, Layout) + QSK_STATES( Pressed ) - explicit QskSpinBox( QQuickItem* parent = nullptr ); - ~QskSpinBox() override; + explicit QskSpinBox( QQuickItem* parent = nullptr ); + ~QskSpinBox() override; - void increment( qreal offset ) override; - qreal value() const; + void increment( qreal offset ) override; + qreal value() const; Q_SIGNALS: - void valueChanged(qreal value); + void valueChanged( qreal ); + void focusIndexChanged( int ); private: - Q_SIGNAL void focusIndexChanged(int index); + void hoverEnterEvent( QHoverEvent* ) override; + void hoverLeaveEvent( QHoverEvent* ) override; + void hoverMoveEvent( QHoverEvent* ) override; - void hoverEnterEvent( QHoverEvent* event) override; - void hoverLeaveEvent( QHoverEvent* event) override; - void hoverMoveEvent( QHoverEvent* event) override; + void mouseReleaseEvent( QMouseEvent* ) override; + void mousePressEvent( QMouseEvent* ) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mousePressEvent(QMouseEvent* event) override; + void keyPressEvent( QKeyEvent* ) override; + void keyReleaseEvent( QKeyEvent* ) override; - void keyPressEvent( QKeyEvent* event ) override; - void keyReleaseEvent( QKeyEvent* event ) override; + void focusInEvent( QFocusEvent* ) override; + QRectF focusIndicatorRect() const override; - void focusInEvent(QFocusEvent* event) override; - QRectF focusIndicatorRect() const override; - - class PrivateData; - std::unique_ptr m_data; + class PrivateData; + std::unique_ptr m_data; }; + +#endif diff --git a/src/controls/QskSpinBoxSkinlet.cpp b/src/controls/QskSpinBoxSkinlet.cpp index 71a22b83..b0d9961f 100644 --- a/src/controls/QskSpinBoxSkinlet.cpp +++ b/src/controls/QskSpinBoxSkinlet.cpp @@ -1,11 +1,12 @@ /****************************************************************************** - * 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" #include "QskSpinBox.h" -#include + +#include const auto INC_TEXT = QStringLiteral("+"); const auto DEC_TEXT = QStringLiteral("-"); diff --git a/src/controls/QskSpinBoxSkinlet.h b/src/controls/QskSpinBoxSkinlet.h index ab6cbf65..7cbee6ec 100644 --- a/src/controls/QskSpinBoxSkinlet.h +++ b/src/controls/QskSpinBoxSkinlet.h @@ -1,28 +1,48 @@ /****************************************************************************** - * 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 class QSK_EXPORT QskSpinBoxSkinlet : public QskSkinlet { - Q_GADGET - using Inherited = QskSkinlet; -public: - enum NodeRole - { - IncPanel, IncText, DecPanel, DecText, TextPanel, TextText, RoleCount - }; - Q_INVOKABLE QskSpinBoxSkinlet( QskSkin* = nullptr ); -protected: - int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override; - QRectF sampleRect( const QskSkinnable*, const QRectF&, QskAspect::Subcontrol, int index ) const override; - QskAspect::States sampleStates(const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const override; - QSizeF sizeHint( const QskSkinnable*, Qt::SizeHint, const QSizeF& ) const override; - QRectF subControlRect( const QskSkinnable*, const QRectF&, QskAspect::Subcontrol ) const override; - QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; - QSGNode* updateSampleNode( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index, QSGNode* node ) const override; + Q_GADGET + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + IncPanel, + IncText, + DecPanel, + DecText, + TextPanel, + TextText, + RoleCount + }; + + Q_INVOKABLE QskSpinBoxSkinlet( QskSkin* = nullptr ); + + protected: + int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override; + + QRectF sampleRect( const QskSkinnable*, const QRectF&, + QskAspect::Subcontrol, int index ) const override; + + QskAspect::States sampleStates( const QskSkinnable*, + QskAspect::Subcontrol, int index ) const override; + + QSizeF sizeHint( const QskSkinnable*, Qt::SizeHint, const QSizeF& ) const override; + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; + QSGNode* updateSampleNode( const QskSkinnable*, + QskAspect::Subcontrol, int index, QSGNode* node ) const override; }; + +#endif From cf2e17def5c6260109d03530abe959f62dd9f558 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 14 Feb 2023 17:33:13 +0100 Subject: [PATCH 4/6] QskMenu: Add own subcontrol Overlay ... so we can style it differently from the one of the QskPopup --- skins/material3/QskMaterial3Skin.cpp | 2 ++ src/controls/QskMenu.cpp | 3 +++ src/controls/QskMenu.h | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 199d2cdf..7c23c6a5 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -324,6 +324,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 ); diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 39139210..95e56910 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -17,6 +17,7 @@ #include #include +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 ); } diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h index beb3261d..e669a532 100644 --- a/src/controls/QskMenu.h +++ b/src/controls/QskMenu.h @@ -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 ); From 9cdc1f635798fb581131d54f9dabb728db7d65b3 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 17 Feb 2023 14:32:24 +0100 Subject: [PATCH 5/6] material style: remove common hints Resolves #211 --- skins/material3/QskMaterial3Skin.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 7c23c6a5..ce01c2ee 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -126,8 +126,6 @@ namespace void setup(); private: - void setupControl(); - void setupBox(); void setupCheckBox(); void setupDialogButtonBox(); @@ -185,8 +183,6 @@ namespace void Editor::setup() { - setupControl(); - setupBox(); setupCheckBox(); setupDialogButtonBox(); @@ -213,16 +209,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 From a89d3def3be8d68cfa93ebb265a8b823494ce8cc Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 14 Feb 2023 08:58:37 +0100 Subject: [PATCH 6/6] Add new control QskComboBox Resolves #102 --- examples/gallery/selector/SelectorPage.cpp | 20 +- skins/material3/QskMaterial3Skin.cpp | 67 ++++- skins/material3/QskMaterial3Skin.h | 4 + skins/material3/icons.qrc | 2 + .../icons/combo-box-arrow-closed.svg | 4 + .../material3/icons/combo-box-arrow-open.svg | 4 + .../icons/qvg/combo-box-arrow-closed.qvg | Bin 0 -> 515 bytes .../icons/qvg/combo-box-arrow-open.qvg | Bin 0 -> 659 bytes src/controls/QskComboBox.cpp | 276 ++++++++++++++++++ src/controls/QskComboBox.h | 93 ++++++ src/controls/QskComboBoxSkinlet.cpp | 241 +++++++++++++++ src/controls/QskComboBoxSkinlet.h | 51 ++++ src/controls/QskMenu.cpp | 16 +- src/controls/QskSkin.cpp | 4 + src/graphic/QskStandardSymbol.cpp | 2 + src/graphic/QskStandardSymbol.h | 2 + src/src.pro | 4 + 17 files changed, 782 insertions(+), 8 deletions(-) create mode 100644 skins/material3/icons/combo-box-arrow-closed.svg create mode 100644 skins/material3/icons/combo-box-arrow-open.svg create mode 100644 skins/material3/icons/qvg/combo-box-arrow-closed.qvg create mode 100644 skins/material3/icons/qvg/combo-box-arrow-open.qvg create mode 100644 src/controls/QskComboBox.cpp create mode 100644 src/controls/QskComboBox.h create mode 100644 src/controls/QskComboBoxSkinlet.cpp create mode 100644 src/controls/QskComboBoxSkinlet.h diff --git a/examples/gallery/selector/SelectorPage.cpp b/examples/gallery/selector/SelectorPage.cpp index 81e90e85..d617641e 100644 --- a/examples/gallery/selector/SelectorPage.cpp +++ b/examples/gallery/selector/SelectorPage.cpp @@ -4,6 +4,8 @@ *****************************************************************************/ #include "SelectorPage.h" + +#include #include 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 ); diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index ce01c2ee..8b5f0f52 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,7 @@ namespace private: void setupBox(); void setupCheckBox(); + void setupComboBox(); void setupDialogButtonBox(); void setupFocusIndicator(); void setupInputPanel(); @@ -185,6 +187,7 @@ void Editor::setup() { setupBox(); setupCheckBox(); + setupComboBox(); setupDialogButtonBox(); setupFocusIndicator(); setupInputPanel(); @@ -282,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; @@ -332,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 ); @@ -1111,6 +1158,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 ) @@ -1144,11 +1193,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: @@ -1157,9 +1207,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 ); } @@ -1197,6 +1254,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 ); diff --git a/skins/material3/QskMaterial3Skin.h b/skins/material3/QskMaterial3Skin.h index 1c35b50b..bd03653a 100644 --- a/skins/material3/QskMaterial3Skin.h +++ b/skins/material3/QskMaterial3Skin.h @@ -8,6 +8,7 @@ #include "QskMaterial3Global.h" +#include #include #include #include @@ -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, }; diff --git a/skins/material3/icons.qrc b/skins/material3/icons.qrc index a2466aea..103698e5 100644 --- a/skins/material3/icons.qrc +++ b/skins/material3/icons.qrc @@ -1,6 +1,8 @@ icons/qvg/check_small.qvg + icons/qvg/combo-box-arrow-closed.qvg + icons/qvg/combo-box-arrow-open.qvg icons/qvg/segmented-button-check.qvg diff --git a/skins/material3/icons/combo-box-arrow-closed.svg b/skins/material3/icons/combo-box-arrow-closed.svg new file mode 100644 index 00000000..2937c960 --- /dev/null +++ b/skins/material3/icons/combo-box-arrow-closed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/skins/material3/icons/combo-box-arrow-open.svg b/skins/material3/icons/combo-box-arrow-open.svg new file mode 100644 index 00000000..fbc7b469 --- /dev/null +++ b/skins/material3/icons/combo-box-arrow-open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/skins/material3/icons/qvg/combo-box-arrow-closed.qvg b/skins/material3/icons/qvg/combo-box-arrow-closed.qvg new file mode 100644 index 0000000000000000000000000000000000000000..273f5a09423e31180beaded917f1f7e87fc5be79 GIT binary patch literal 515 zcmWFx_I77rU|?ouVrB?nu>SyL0|6uB|Njsc16&NoN2g(W(8XbVn7ji_0EuPSyL0|6uB|Njsc16&NoN2g(W(8XbVn7ji_0EuPNMQyK3uFcf@=O5Pl`&EP literal 0 HcmV?d00001 diff --git a/src/controls/QskComboBox.cpp b/src/controls/QskComboBox.cpp new file mode 100644 index 00000000..855cddcd --- /dev/null +++ b/src/controls/QskComboBox.cpp @@ -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 + +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" diff --git a/src/controls/QskComboBox.h b/src/controls/QskComboBox.h new file mode 100644 index 00000000..5a168a74 --- /dev/null +++ b/src/controls/QskComboBox.h @@ -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 diff --git a/src/controls/QskComboBoxSkinlet.cpp b/src/controls/QskComboBoxSkinlet.cpp new file mode 100644 index 00000000..a2c9cd6d --- /dev/null +++ b/src/controls/QskComboBoxSkinlet.cpp @@ -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" diff --git a/src/controls/QskComboBoxSkinlet.h b/src/controls/QskComboBoxSkinlet.h new file mode 100644 index 00000000..dbf25c67 --- /dev/null +++ b/src/controls/QskComboBoxSkinlet.h @@ -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 diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 95e56910..10d6c479 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -258,7 +258,12 @@ void QskMenu::keyPressEvent( QKeyEvent* event ) default: { - return; + const int steps = qskFocusChainIncrement( event ); + + if( steps != 0 ) + { + traverse( steps ); + } } } } @@ -356,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 ) diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index ee56230f..914b8c41 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -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 >(); diff --git a/src/graphic/QskStandardSymbol.cpp b/src/graphic/QskStandardSymbol.cpp index 3c9fd6ea..08176cfb 100644 --- a/src/graphic/QskStandardSymbol.cpp +++ b/src/graphic/QskStandardSymbol.cpp @@ -267,6 +267,8 @@ QskGraphic QskStandardSymbol::graphic( Type symbolType ) } case QskStandardSymbol::NoSymbol: case QskStandardSymbol::SymbolTypeCount: + case QskStandardSymbol::ComboBoxSymbolPopupClosed: + case QskStandardSymbol::ComboBoxSymbolPopupOpen: { break; } diff --git a/src/graphic/QskStandardSymbol.h b/src/graphic/QskStandardSymbol.h index 2b517319..78f5a11d 100644 --- a/src/graphic/QskStandardSymbol.h +++ b/src/graphic/QskStandardSymbol.h @@ -31,6 +31,8 @@ namespace QskStandardSymbol CrossMark, SegmentedBarCheckMark, + ComboBoxSymbolPopupClosed, + ComboBoxSymbolPopupOpen, SymbolTypeCount }; diff --git a/src/src.pro b/src/src.pro index 9250a6b3..7ba0bd8b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -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 \