diff --git a/examples/gallery/icons.qrc b/examples/gallery/icons.qrc index f6580c6a..001e16e2 100644 --- a/examples/gallery/icons.qrc +++ b/examples/gallery/icons.qrc @@ -1,5 +1,9 @@ + icons/qvg/airport_shuttle.qvg + icons/qvg/flight.qvg + icons/qvg/local_pizza.qvg icons/qvg/plus.qvg + icons/qvg/sports_soccer.qvg diff --git a/examples/gallery/icons/airport_shuttle.svg b/examples/gallery/icons/airport_shuttle.svg new file mode 100644 index 00000000..429b665d --- /dev/null +++ b/examples/gallery/icons/airport_shuttle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/gallery/icons/flight.svg b/examples/gallery/icons/flight.svg new file mode 100644 index 00000000..fac609e6 --- /dev/null +++ b/examples/gallery/icons/flight.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/gallery/icons/local_pizza.svg b/examples/gallery/icons/local_pizza.svg new file mode 100644 index 00000000..37f91bb0 --- /dev/null +++ b/examples/gallery/icons/local_pizza.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/gallery/icons/qvg/airport_shuttle.qvg b/examples/gallery/icons/qvg/airport_shuttle.qvg new file mode 100644 index 00000000..d6e0d203 Binary files /dev/null and b/examples/gallery/icons/qvg/airport_shuttle.qvg differ diff --git a/examples/gallery/icons/qvg/flight.qvg b/examples/gallery/icons/qvg/flight.qvg new file mode 100644 index 00000000..fc10d775 Binary files /dev/null and b/examples/gallery/icons/qvg/flight.qvg differ diff --git a/examples/gallery/icons/qvg/local_pizza.qvg b/examples/gallery/icons/qvg/local_pizza.qvg new file mode 100644 index 00000000..45232c20 Binary files /dev/null and b/examples/gallery/icons/qvg/local_pizza.qvg differ diff --git a/examples/gallery/icons/qvg/sports_soccer.qvg b/examples/gallery/icons/qvg/sports_soccer.qvg new file mode 100644 index 00000000..ff92937b Binary files /dev/null and b/examples/gallery/icons/qvg/sports_soccer.qvg differ diff --git a/examples/gallery/icons/sports_soccer.svg b/examples/gallery/icons/sports_soccer.svg new file mode 100644 index 00000000..0ee97398 --- /dev/null +++ b/examples/gallery/icons/sports_soccer.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/gallery/selector/SelectorPage.cpp b/examples/gallery/selector/SelectorPage.cpp index 483949a8..054577d4 100644 --- a/examples/gallery/selector/SelectorPage.cpp +++ b/examples/gallery/selector/SelectorPage.cpp @@ -19,31 +19,33 @@ namespace orientation = ( orientation == Qt::Horizontal ) ? Qt::Vertical : Qt::Horizontal; + const char* texts[] = + { + "airport", + "flight", + "pizza", + "soccer" + }; + { auto bar = new QskSegmentedBar( orientation, this ); - bar->addText( "Option 1" ); - bar->addText( "Option 2" ); - bar->addText( "Option 3" ); - bar->addText( "Option 4" ); + for ( const auto text: texts ) + bar->addText( text ); } { - const auto prefix = QStringLiteral( "image://shapes/" ); - const char* icons[] = { - "rectangle/crimson", - "triangleright/thistle", - "ellipse/khaki", - "ring/sandybrown", - "star/darkviolet", - "hexagon/darkslategray" + "airport_shuttle", + "flight", + "local_pizza", + "sports_soccer" }; auto bar = new QskSegmentedBar( orientation, this ); - for ( const auto icon : icons ) - bar->addGraphic( prefix + icon ); + for ( uint i = 0; i < sizeof( icons ) / sizeof( icons[ 0 ] ); ++i ) + bar->addGraphicAndText( QUrl( QString( icons[ i ] ) ), texts[ i ] ); } setExtraSpacingAt( Qt::LeftEdge | Qt::BottomEdge ); diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 9dd14c73..be4eeff0 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -433,13 +433,15 @@ void Editor::setupSegmentedBar() using A = QskAspect; using Q = QskSegmentedBar; - const QSize strutSize( -1, 40_dp ); + const QSize panelStrutSize( -1, 48_dp ); + const QSize segmentStrutSize( 48_dp, 40_dp ); { - // Panel + // Container + setGradient( Q::Panel, Qt::transparent ); // ### background setPadding( Q::Panel, 0 ); - setSpacing( Q::Panel, 0 ); + setSpacing( Q::Panel, 8_dp ); // ### messes up the cursor setBoxShape( Q::Panel, 100, Qt::RelativeSize ); @@ -447,22 +449,24 @@ void Editor::setupSegmentedBar() setBoxBorderColors( Q::Panel, m_pal.outline ); setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface12 ); - setStrutSize( Q::Panel | A::Horizontal, strutSize ); - setStrutSize( Q::Panel | A::Vertical, strutSize.transposed() ); + setStrutSize( Q::Panel | A::Horizontal, panelStrutSize ); + setStrutSize( Q::Panel | A::Vertical, panelStrutSize.transposed() ); } { // Segment - setGradient( Q::Segment, Qt::transparent ); + setStrutSize( Q::Segment | A::Horizontal, segmentStrutSize ); + setStrutSize( Q::Segment | A::Vertical, segmentStrutSize.transposed() ); + setGradient( Q::Segment, Qt::transparent ); // ### background setPadding( Q::Segment, 0 ); } { // Separator - setStrutSize( Q::Separator | A::Horizontal, 1_dp, strutSize.height() ); - setStrutSize( Q::Separator | A::Vertical, strutSize.height(), 1_dp ); + setStrutSize( Q::Separator | A::Horizontal, 1_dp, segmentStrutSize.height() ); + setStrutSize( Q::Separator | A::Vertical, segmentStrutSize.height(), 1_dp ); setPadding( Q::Separator, 0 ); setGradient( Q::Separator, m_pal.outline ); setColor( Q::Separator | Q::Disabled, m_pal.onSurface38 ); @@ -470,7 +474,6 @@ void Editor::setupSegmentedBar() { // Cursor - setMargin( Q::Cursor, 1_dp ); setBoxShape( Q::Cursor, 0 ); setBoxShape( Q::Cursor | Q::Minimum | A::Horizontal, @@ -489,7 +492,6 @@ void Editor::setupSegmentedBar() setGradient( Q::Cursor, m_pal.secondaryContainer ); setGradient( Q::Cursor | Q::Disabled, m_pal.onSurface12 ); - setAnimation( Q::Cursor | A::Metric | A::Position, 100 ); } @@ -509,8 +511,8 @@ void Editor::setupSegmentedBar() { // Graphic - setPadding( Q::Graphic, 10_dp ); - setMargin( Q::Graphic, 10_dp ); + setPadding( Q::Graphic, 0_dp ); + setStrutSize( Q::Graphic, { 18_dp, 18_dp } ); } } diff --git a/src/controls/QskSegmentedBar.cpp b/src/controls/QskSegmentedBar.cpp index 14275b92..a01314a9 100644 --- a/src/controls/QskSegmentedBar.cpp +++ b/src/controls/QskSegmentedBar.cpp @@ -149,6 +149,22 @@ int QskSegmentedBar::addGraphic( const QUrl& graphicSource ) return count() - 1; } +int QskSegmentedBar::addGraphicAndText( const QUrl& graphicSource, const QString& text ) +{ + m_data->addOption( this, Option( graphicSource, text ) ); + return count() - 1; +} + +QString QskSegmentedBar::textAt( int index ) const +{ + return m_data->options.at( index ).text; +} + +QskGraphic QskSegmentedBar::graphicAt( int index ) const +{ + return m_data->options.at( index ).graphic; +} + QVariant QskSegmentedBar::optionAt( int index ) const { const auto& options = m_data->options; diff --git a/src/controls/QskSegmentedBar.h b/src/controls/QskSegmentedBar.h index e2afa911..d7fb6b23 100644 --- a/src/controls/QskSegmentedBar.h +++ b/src/controls/QskSegmentedBar.h @@ -48,6 +48,10 @@ class QSK_EXPORT QskSegmentedBar : public QskControl int addText( const QString& ); int addGraphic( const QUrl& ); + int addGraphicAndText( const QUrl&, const QString& ); + + QString textAt( int ) const; + QskGraphic graphicAt( int ) const; void clear(); diff --git a/src/controls/QskSegmentedBarSkinlet.cpp b/src/controls/QskSegmentedBarSkinlet.cpp index 54f40dd8..45258bdc 100644 --- a/src/controls/QskSegmentedBarSkinlet.cpp +++ b/src/controls/QskSegmentedBarSkinlet.cpp @@ -8,18 +8,50 @@ #include "QskGraphic.h" #include "QskColorFilter.h" -#include "QskTextOptions.h" -#include "QskSGNode.h" #include "QskFunctions.h" +#include "QskSubcontrolLayoutEngine.h" #include #include +static inline Qt::Orientation qskOrientation( const QskSegmentedBar* bar ) +{ + // For the moment we only handle the orientation TODO ... + + const auto direction = bar->flagHint( + QskSegmentedBar::Panel | QskAspect::Direction, Qsk::LeftToRight ); + + if ( direction == Qsk::LeftToRight || direction == Qsk::RightToLeft ) + return Qt::Horizontal; + else + return Qt::Vertical; +} + +namespace +{ + class LayoutEngine : public QskSubcontrolLayoutEngine + { + public: + LayoutEngine( const QskSegmentedBar* bar, int index ) + : QskSubcontrolLayoutEngine( qskOrientation( bar ) ) + { + setSpacing( bar->spacingHint( QskSegmentedBar::Panel ) ); + + setGraphicTextElements( bar, + QskSegmentedBar::Text, bar->textAt( index ), + QskSegmentedBar::Graphic, bar->graphicAt( index ).defaultSize() ); + + const auto alignment = bar->alignmentHint( QskSegmentedBar::Panel, Qt::AlignCenter ); + setFixedContent( QskSegmentedBar::Text, Qt::Horizontal, alignment ); + } + }; +} + QskSegmentedBarSkinlet::QskSegmentedBarSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { PanelRole, SegmentRole, SeparatorRole, - CursorRole, TextRole, GraphicRole } ); + setNodeRoles( { CursorRole, PanelRole, SegmentRole, + SeparatorRole, TextRole, GraphicRole } ); } QskSegmentedBarSkinlet::~QskSegmentedBarSkinlet() = default; @@ -115,7 +147,7 @@ QRectF QskSegmentedBarSkinlet::separatorRect( if( bar->orientation() == Qt::Horizontal ) { - rect.setLeft( rect.right() ); + rect.setLeft( rect.right() ); // ### *0.5 or so? rect.setSize( { strutSize.width(), sh.height() } ); } else @@ -137,12 +169,12 @@ QSGNode* QskSegmentedBarSkinlet::updateSubNode( switch( nodeRole ) { - case PanelRole: - return updateBoxNode( skinnable, node, Q::Panel ); - case CursorRole: return updateBoxNode( skinnable, node, Q::Cursor ); + case PanelRole: + return updateBoxNode( skinnable, node, Q::Panel ); + case SegmentRole: return updateSeriesNode( skinnable, Q::Segment, node ); @@ -159,56 +191,29 @@ QSGNode* QskSegmentedBarSkinlet::updateSubNode( return nullptr; } -QSizeF QskSegmentedBarSkinlet::segmentSizeHint( const QskSegmentedBar* bar ) const +QSizeF QskSegmentedBarSkinlet::segmentSizeHint( const QskSegmentedBar* bar, Qt::SizeHint which ) const { - qreal widthMax = 0; - qreal graphicRatioMax = 0; + using Q = QskSegmentedBar; - const QFontMetricsF fm( bar->effectiveFont( QskSegmentedBar::Text ) ); + QSizeF sizeMax; for ( int i = 0; i < bar->count(); i++ ) { - const auto value = bar->optionAt( i ); + LayoutEngine layoutEngine( bar, i ); - if ( value.canConvert< QskGraphic >() ) + const auto size = layoutEngine.sizeHint( which, QSizeF() ); + + if( size.width() > sizeMax.width() ) { - const auto graphic = value.value< QskGraphic >(); - - if ( !graphic.isNull() ) - { - const auto sz = graphic.defaultSize(); - - if( sz.isValid() ) - { - const qreal ratio = sz.width() / sz.height(); - - if( graphicRatioMax < ratio ) - graphicRatioMax = ratio; - } - } - } - else if ( value.canConvert< QString >() ) - { - const auto text = value.value< QString >(); - if ( !text.isEmpty() ) - { - const auto sz = fm.size( Qt::TextShowMnemonic, text ); - - if( sz.width() > widthMax ) - widthMax = sz.width(); - } + sizeMax = size; } } - if( graphicRatioMax > 0 ) - { - const qreal w = fm.height() * graphicRatioMax; + sizeMax = bar->outerBoxSize( Q::Segment, sizeMax ); + sizeMax = sizeMax.expandedTo( bar->strutSizeHint( Q::Segment ) ); + sizeMax = sizeMax.grownBy( bar->marginHint( Q::Segment ) ); - if( w > widthMax ) - widthMax = w; - } - - return bar->outerBoxSize( QskSegmentedBar::Segment, QSizeF( widthMax, fm.height() ) ); + return sizeMax; } QSizeF QskSegmentedBarSkinlet::sizeHint( const QskSkinnable* skinnable, @@ -231,7 +236,7 @@ QSizeF QskSegmentedBarSkinlet::sizeHint( const QskSkinnable* skinnable, const qreal spacing = skinnable->spacingHint( Q::Panel ); const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); - const auto segmentSize = segmentSizeHint( bar ); + const auto segmentSize = segmentSizeHint( bar, which ); if( bar->orientation() == Qt::Horizontal ) { @@ -275,7 +280,10 @@ QRectF QskSegmentedBarSkinlet::sampleRect( const QskSkinnable* skinnable, if ( subControl == Q::Text || subControl == Q::Graphic ) { const auto rect = sampleRect( skinnable, contentsRect, Q::Segment, index ); - return rect; + + LayoutEngine layoutEngine( bar, index ); + layoutEngine.setGeometries( rect ); + return layoutEngine.subControlRect( subControl ); } return Inherited::sampleRect( skinnable, contentsRect, subControl, index ); @@ -324,11 +332,10 @@ QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable if ( subControl == Q::Text ) { - const auto value = bar->optionAt( index ); - if ( value.canConvert< QString >() ) - { - const auto text = value.value< QString >(); + const auto text = bar->textAt( index ); + if( !text.isEmpty() ) + { return QskSkinlet::updateTextNode( bar, node, rect, alignment, text, Q::Text ); } @@ -338,10 +345,10 @@ QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable if ( subControl == Q::Graphic ) { - const auto value = bar->optionAt( index ); - if ( value.canConvert< QskGraphic >() ) + const auto graphic = bar->graphicAt( index ); + + if( !graphic.isEmpty() ) { - const auto graphic = value.value< QskGraphic >(); const auto filter = bar->effectiveGraphicFilter( subControl ); const auto padding = bar->paddingHint( Q::Graphic ); const auto graphicRect = rect.marginsRemoved( padding ); diff --git a/src/controls/QskSegmentedBarSkinlet.h b/src/controls/QskSegmentedBarSkinlet.h index 8bb8faa8..a61a2318 100644 --- a/src/controls/QskSegmentedBarSkinlet.h +++ b/src/controls/QskSegmentedBarSkinlet.h @@ -55,7 +55,7 @@ class QSK_EXPORT QskSegmentedBarSkinlet : public QskSkinlet QskAspect::Subcontrol, int index, QSGNode* ) const override; private: - QSizeF segmentSizeHint( const QskSegmentedBar* ) const; + QSizeF segmentSizeHint(const QskSegmentedBar*, Qt::SizeHint ) const; QRectF segmentRect( const QskSegmentedBar*, const QRectF&, int index ) const; QRectF separatorRect( const QskSegmentedBar*, const QRectF&, int index ) const; diff --git a/src/layouts/QskSubcontrolLayoutEngine.cpp b/src/layouts/QskSubcontrolLayoutEngine.cpp index 3c594f1e..4c42b011 100644 --- a/src/layouts/QskSubcontrolLayoutEngine.cpp +++ b/src/layouts/QskSubcontrolLayoutEngine.cpp @@ -11,6 +11,7 @@ #include "QskMargins.h" #include "QskTextOptions.h" +#include #include #include #include