segmented bar: Allow icon and text

This commit is contained in:
Peter Hartmann 2023-01-21 16:53:58 +01:00 committed by uwerat
parent 329531777a
commit 9d1b2568e5
16 changed files with 135 additions and 83 deletions

View File

@ -1,5 +1,9 @@
<RCC>
<qresource>
<file>icons/qvg/airport_shuttle.qvg</file>
<file>icons/qvg/flight.qvg</file>
<file>icons/qvg/local_pizza.qvg</file>
<file>icons/qvg/plus.qvg</file>
<file>icons/qvg/sports_soccer.qvg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,4 @@
<svg width="23" height="15" viewBox="0 0 23 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.5 14.5C4.66667 14.5 3.95833 14.2083 3.375 13.625C2.79167 13.0417 2.5 12.3333 2.5 11.5H0.5V2.5C0.5 1.95 0.696 1.47933 1.088 1.088C1.47933 0.696 1.95 0.5 2.5 0.5H16.5L22.5 6.5V11.5H20.5C20.5 12.3333 20.2083 13.0417 19.625 13.625C19.0417 14.2083 18.3333 14.5 17.5 14.5C16.6667 14.5 15.9583 14.2083 15.375 13.625C14.7917 13.0417 14.5 12.3333 14.5 11.5H8.5C8.5 12.3333 8.20833 13.0417 7.625 13.625C7.04167 14.2083 6.33333 14.5 5.5 14.5ZM14.5 5.5H18.5L15.5 2.5H14.5V5.5ZM8.5 5.5H12.5V2.5H8.5V5.5ZM2.5 5.5H6.5V2.5H2.5V5.5ZM5.5 12.75C5.85 12.75 6.146 12.629 6.388 12.387C6.62933 12.1457 6.75 11.85 6.75 11.5C6.75 11.15 6.62933 10.8543 6.388 10.613C6.146 10.371 5.85 10.25 5.5 10.25C5.15 10.25 4.85433 10.371 4.613 10.613C4.371 10.8543 4.25 11.15 4.25 11.5C4.25 11.85 4.371 12.1457 4.613 12.387C4.85433 12.629 5.15 12.75 5.5 12.75ZM17.5 12.75C17.85 12.75 18.146 12.629 18.388 12.387C18.6293 12.1457 18.75 11.85 18.75 11.5C18.75 11.15 18.6293 10.8543 18.388 10.613C18.146 10.371 17.85 10.25 17.5 10.25C17.15 10.25 16.8543 10.371 16.613 10.613C16.371 10.8543 16.25 11.15 16.25 11.5C16.25 11.85 16.371 12.1457 16.613 12.387C16.8543 12.629 17.15 12.75 17.5 12.75ZM2.5 9.5H3.3C3.58333 9.2 3.90833 8.95833 4.275 8.775C4.64167 8.59167 5.05 8.5 5.5 8.5C5.95 8.5 6.35833 8.59167 6.725 8.775C7.09167 8.95833 7.41667 9.2 7.7 9.5H15.3C15.5833 9.2 15.9083 8.95833 16.275 8.775C16.6417 8.59167 17.05 8.5 17.5 8.5C17.95 8.5 18.3583 8.59167 18.725 8.775C19.0917 8.95833 19.4167 9.2 19.7 9.5H20.5V7.5H2.5V9.5Z" fill="#1C1B1F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,4 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 20.5V19L9 17.5V12L0.5 14.5V12.5L9 7.5V2C9 1.58333 9.146 1.22933 9.438 0.938C9.72933 0.646 10.0833 0.5 10.5 0.5C10.9167 0.5 11.2707 0.646 11.562 0.938C11.854 1.22933 12 1.58333 12 2V7.5L20.5 12.5V14.5L12 12V17.5L14 19V20.5L10.5 19.5L7 20.5Z" fill="#1C1B1F"/>
</svg>

After

Width:  |  Height:  |  Size: 374 B

View File

@ -0,0 +1,4 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5037 19.5037L0.503679 4.50368C1.92035 3.30368 3.47468 2.33701 5.16668 1.60368C6.85801 0.870346 8.63701 0.503679 10.5037 0.503679C12.3703 0.503679 14.1493 0.866346 15.8407 1.59168C17.5327 2.31635 19.087 3.28701 20.5037 4.50368L10.5037 19.5037ZM10.5037 15.9037L17.8037 4.95368C16.7203 4.20368 15.562 3.60768 14.3287 3.16568C13.0953 2.72435 11.8203 2.50368 10.5037 2.50368C9.18701 2.50368 7.91635 2.72435 6.69168 3.16568C5.46635 3.60768 4.30368 4.20368 3.20368 4.95368L10.5037 15.9037ZM8.00368 7.50368C8.42035 7.50368 8.77435 7.35768 9.06568 7.06568C9.35768 6.77435 9.50368 6.42035 9.50368 6.00368C9.50368 5.58701 9.35768 5.23301 9.06568 4.94168C8.77435 4.64968 8.42035 4.50368 8.00368 4.50368C7.58701 4.50368 7.23301 4.64968 6.94168 4.94168C6.64968 5.23301 6.50368 5.58701 6.50368 6.00368C6.50368 6.42035 6.64968 6.77435 6.94168 7.06568C7.23301 7.35768 7.58701 7.50368 8.00368 7.50368ZM10.5037 12.5037C10.9203 12.5037 11.2743 12.3577 11.5657 12.0657C11.8577 11.7743 12.0037 11.4203 12.0037 11.0037C12.0037 10.587 11.8577 10.233 11.5657 9.94168C11.2743 9.64968 10.9203 9.50368 10.5037 9.50368C10.087 9.50368 9.73301 9.64968 9.44168 9.94168C9.14968 10.233 9.00368 10.587 9.00368 11.0037C9.00368 11.4203 9.14968 11.7743 9.44168 12.0657C9.73301 12.3577 10.087 12.5037 10.5037 12.5037Z" fill="#1C1B1F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,4 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 20.5C9.11667 20.5 7.81667 20.2373 6.6 19.712C5.38333 19.1873 4.325 18.475 3.425 17.575C2.525 16.675 1.81267 15.6167 1.288 14.4C0.762667 13.1833 0.5 11.8833 0.5 10.5C0.5 9.11667 0.762667 7.81667 1.288 6.6C1.81267 5.38333 2.525 4.325 3.425 3.425C4.325 2.525 5.38333 1.81233 6.6 1.287C7.81667 0.762333 9.11667 0.5 10.5 0.5C11.8833 0.5 13.1833 0.762333 14.4 1.287C15.6167 1.81233 16.675 2.525 17.575 3.425C18.475 4.325 19.1873 5.38333 19.712 6.6C20.2373 7.81667 20.5 9.11667 20.5 10.5C20.5 11.8833 20.2373 13.1833 19.712 14.4C19.1873 15.6167 18.475 16.675 17.575 17.575C16.675 18.475 15.6167 19.1873 14.4 19.712C13.1833 20.2373 11.8833 20.5 10.5 20.5ZM15.5 8L16.85 7.55L17.25 6.2C16.7167 5.4 16.075 4.71233 15.325 4.137C14.575 3.56233 13.75 3.13333 12.85 2.85L11.5 3.8V5.2L15.5 8ZM5.5 8L9.5 5.2V3.8L8.15 2.85C7.25 3.13333 6.425 3.56233 5.675 4.137C4.925 4.71233 4.28333 5.4 3.75 6.2L4.15 7.55L5.5 8ZM4.45 15.7L5.6 15.6L6.35 14.25L4.9 9.9L3.5 9.4L2.5 10.15C2.5 11.2333 2.65 12.2207 2.95 13.112C3.25 14.004 3.75 14.8667 4.45 15.7ZM10.5 18.5C10.9333 18.5 11.3583 18.4667 11.775 18.4C12.1917 18.3333 12.6 18.2333 13 18.1L13.7 16.6L13.05 15.5H7.95L7.3 16.6L8 18.1C8.4 18.2333 8.80833 18.3333 9.225 18.4C9.64167 18.4667 10.0667 18.5 10.5 18.5ZM8.25 13.5H12.75L14.15 9.5L10.5 6.95L6.9 9.5L8.25 13.5ZM16.55 15.7C17.25 14.8667 17.75 14.004 18.05 13.112C18.35 12.2207 18.5 11.2333 18.5 10.15L17.5 9.45L16.1 9.9L14.65 14.25L15.4 15.6L16.55 15.7Z" fill="#1C1B1F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

View File

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

View File

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

View File

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

View File

@ -8,18 +8,50 @@
#include "QskGraphic.h"
#include "QskColorFilter.h"
#include "QskTextOptions.h"
#include "QskSGNode.h"
#include "QskFunctions.h"
#include "QskSubcontrolLayoutEngine.h"
#include <qfontmetrics.h>
#include <qmath.h>
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 );

View File

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

View File

@ -11,6 +11,7 @@
#include "QskMargins.h"
#include "QskTextOptions.h"
#include <qdebug.h>
#include <qfont.h>
#include <qfontmetrics.h>
#include <qmath.h>