From 7cd4b940aa134369eb34a0a67d4f61d211cb8866 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 31 Jul 2020 16:57:22 +0200 Subject: [PATCH] QskValueBar added --- skins/material/QskMaterialSkin.cpp | 15 ++ skins/material/QskMaterialSkin.h | 1 + skins/squiek/QskSquiekSkin.cpp | 15 ++ skins/squiek/QskSquiekSkin.h | 1 + src/controls/QskSkin.cpp | 4 + src/controls/QskValueBar.cpp | 215 ++++++++++++++++++++++++++++ src/controls/QskValueBar.h | 81 +++++++++++ src/controls/QskValueBarSkinlet.cpp | 136 ++++++++++++++++++ src/controls/QskValueBarSkinlet.h | 43 ++++++ src/src.pro | 4 + 10 files changed, 515 insertions(+) create mode 100644 src/controls/QskValueBar.cpp create mode 100644 src/controls/QskValueBar.h create mode 100644 src/controls/QskValueBarSkinlet.cpp create mode 100644 src/controls/QskValueBarSkinlet.h diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index a43dd4f5..3986bebf 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -144,6 +145,7 @@ void QskMaterialSkin::initHints() initTabViewHints(); initTextLabelHints(); initTextInputHints(); + initValueBarHints(); } void QskMaterialSkin::resetColors( const QColor& accent ) @@ -234,6 +236,19 @@ void QskMaterialSkin::initTextInputHints() setGradient( Q::Panel, pal.baseColor ); } +void QskMaterialSkin::initValueBarHints() +{ + using namespace QskAspect; + using Q = QskValueBar; + + const ColorPalette& pal = m_data->palette; + + setGradient( Q::Groove, Qt::white ); + setMetric( Q::Groove | Size, 10 ); + + setGradient( Q::ValueFill, pal.accentColor ); +} + void QskMaterialSkin::initFocusIndicatorHints() { using namespace QskAspect; diff --git a/skins/material/QskMaterialSkin.h b/skins/material/QskMaterialSkin.h index 19b5f4cf..1b6ffe44 100644 --- a/skins/material/QskMaterialSkin.h +++ b/skins/material/QskMaterialSkin.h @@ -46,6 +46,7 @@ class QSK_MATERIAL_EXPORT QskMaterialSkin : public QskSkin void initTabViewHints(); void initTextInputHints(); void initTextLabelHints(); + void initValueBarHints(); class PrivateData; std::unique_ptr< PrivateData > m_data; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 5edc2d62..5e347be9 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -272,6 +273,7 @@ void QskSquiekSkin::initHints() initTabViewHints(); initTextLabelHints(); initTextInputHints(); + initValueBarInputHints(); } void QskSquiekSkin::resetColors( const QColor& accent ) @@ -380,6 +382,19 @@ void QskSquiekSkin::initTextInputHints() setAnimation( Q::Panel | Color, qskDuration ); } +void QskSquiekSkin::initValueBarInputHints() +{ + using namespace QskAspect; + using Q = QskValueBar; + + const ColorPalette& pal = m_data->palette; + + setGradient( Q::Groove, Qt::white ); + setMetric( Q::Groove | Size, 10 ); + + setGradient( Q::ValueFill, pal.highlighted ); +} + void QskSquiekSkin::initFocusIndicatorHints() { using namespace QskAspect; diff --git a/skins/squiek/QskSquiekSkin.h b/skins/squiek/QskSquiekSkin.h index 63747bbf..1f1e85cd 100644 --- a/skins/squiek/QskSquiekSkin.h +++ b/skins/squiek/QskSquiekSkin.h @@ -47,6 +47,7 @@ class QSK_SQUIEK_EXPORT QskSquiekSkin : public QskSkin void initTabViewHints(); void initTextLabelHints(); void initTextInputHints(); + void initValueBarInputHints(); enum PanelStyle { diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index b23d0439..a241d141 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -74,6 +74,9 @@ QSK_QT_PRIVATE_END #include "QskStatusIndicator.h" #include "QskStatusIndicatorSkinlet.h" +#include "QskValueBar.h" +#include "QskValueBarSkinlet.h" + static inline QskSkinlet* qskNewSkinlet( const QMetaObject* metaObject, QskSkin* skin ) { const QByteArray signature = metaObject->className() + QByteArrayLiteral( "(QskSkin*)" ); @@ -148,6 +151,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskTabView, QskTabViewSkinlet >(); declareSkinlet< QskTextLabel, QskTextLabelSkinlet >(); declareSkinlet< QskTextInput, QskTextInputSkinlet >(); + declareSkinlet< QskValueBar, QskValueBarSkinlet >(); const QFont font = QGuiApplication::font(); setupFonts( font.family(), font.weight(), font.italic() ); diff --git a/src/controls/QskValueBar.cpp b/src/controls/QskValueBar.cpp new file mode 100644 index 00000000..30231407 --- /dev/null +++ b/src/controls/QskValueBar.cpp @@ -0,0 +1,215 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskValueBar.h" + +#include "QskIntervalF.h" +#include "QskGradient.h" +#include "QskFunctions.h" +#include "QskAspect.h" + +QSK_SUBCONTROL( QskValueBar, Groove ) +QSK_SUBCONTROL( QskValueBar, ValueFill ) + +class QskValueBar::PrivateData +{ + public: + qreal value = 0.0; + + qreal origin = 0.0; + bool hasOrigin = false; + + Qt::Orientation orientation; +}; + +QskValueBar::QskValueBar( Qt::Orientation orientation, + qreal min, qreal max, QQuickItem* parent ) + : QskBoundedControl( min, max, parent ) + , m_data( new PrivateData ) +{ + m_data->orientation = orientation; + m_data->value = minimum(); + + if ( orientation == Qt::Horizontal ) + initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed ); + else + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::MinimumExpanding ); + + connect( this, &QskBoundedControl::boundariesChanged, + this, &QskValueBar::adjustValue ); +} + +QskValueBar::QskValueBar( Qt::Orientation orientation, QQuickItem* parent ) + : QskValueBar( orientation, 0.0, 100.0, parent ) +{ +} + +QskValueBar::QskValueBar( const QskIntervalF& boundaries, QQuickItem* parent ) + : QskValueBar( boundaries.lowerBound(), boundaries.upperBound(), parent ) +{ +} + +QskValueBar::QskValueBar( qreal min, qreal max, QQuickItem* parent ) + : QskValueBar( Qt::Horizontal, min, max, parent ) +{ +} + +QskValueBar::QskValueBar( QQuickItem* parent ) + : QskValueBar( Qt::Horizontal, parent ) +{ +} + +QskValueBar::~QskValueBar() +{ +} + +Qt::Orientation QskValueBar::orientation() const +{ + return m_data->orientation; +} + +void QskValueBar::setOrientation( Qt::Orientation orientation ) +{ + if ( orientation != m_data->orientation ) + { + m_data->orientation = orientation; + setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) ); + + resetImplicitSize(); + update(); + + Q_EMIT orientationChanged( m_data->orientation ); + } +} + +QskAspect::Placement QskValueBar::effectivePlacement() const +{ + // so you can define different hints depending on the orientation + return static_cast< QskAspect::Placement >( m_data->orientation ); +} + +void QskValueBar::setFillGradient( const QskGradient& gradient ) +{ + setGradientHint( QskValueBar::ValueFill, gradient ); +} + +QskGradient QskValueBar::fillGradient() const +{ + return gradientHint( QskValueBar::ValueFill ); +} + +void QskValueBar::setThickness( qreal thickness ) +{ + // effectiveSubcontrol( QskValueBar::Groove ) ??? + const auto aspect = QskValueBar::Groove | QskAspect::Size; + + if ( thickness != metric( aspect ) ) + { + setMetric( aspect, thickness ); + + resetImplicitSize(); + update(); + } +} + +qreal QskValueBar::thickness() const +{ + return metric( Groove | QskAspect::Size ); +} + +void QskValueBar::setOrigin( qreal origin ) +{ + if ( isComponentComplete() ) + origin = boundedValue( origin ); + + if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) ) + { + m_data->hasOrigin = true; + m_data->origin = origin; + + update(); + Q_EMIT originChanged( origin ); + } +} + +void QskValueBar::resetOrigin() +{ + if ( m_data->hasOrigin ) + { + m_data->hasOrigin = false; + update(); + Q_EMIT originChanged( origin() ); + } +} + +qreal QskValueBar::origin() const +{ + if ( m_data->hasOrigin ) + { + return boundedValue( m_data->origin ); + } + + return minimum(); +} + +void QskValueBar::setValue( qreal value ) +{ + if ( isComponentComplete() ) + value = boundedValue( value ); + + setValueInternal( value ); +} + +qreal QskValueBar::value() const +{ + return m_data->value; +} + +void QskValueBar::setValueAsRatio( qreal ratio ) +{ + ratio = qBound( 0.0, ratio, 1.0 ); + setValue( minimum() + ratio * boundaryLength() ); +} + +qreal QskValueBar::valueAsRatio() const +{ + return valueAsRatio( m_data->value ); +} + +QSizeF QskValueBar::contentsSizeHint( Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + if ( orientation() == Qt::Horizontal ) + return QSizeF( -1, thickness() ); + else + return QSizeF( thickness(), -1 ); +} + +void QskValueBar::componentComplete() +{ + Inherited::componentComplete(); + adjustValue(); +} + +void QskValueBar::adjustValue() +{ + if ( isComponentComplete() ) + setValueInternal( boundedValue( m_data->value ) ); +} + +void QskValueBar::setValueInternal( qreal value ) +{ + if ( !qskFuzzyCompare( value, m_data->value ) ) + { + m_data->value = value; + Q_EMIT valueChanged( value ); + + update(); + } +} + +#include "moc_QskValueBar.cpp" diff --git a/src/controls/QskValueBar.h b/src/controls/QskValueBar.h new file mode 100644 index 00000000..124b2d44 --- /dev/null +++ b/src/controls/QskValueBar.h @@ -0,0 +1,81 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_VALUE_BAR_H +#define QSK_VALUE_BAR_H + +#include "QskBoundedControl.h" + +class QskIntervalF; + +class QSK_EXPORT QskValueBar : public QskBoundedControl +{ + Q_OBJECT + + Q_PROPERTY( Qt::Orientation orientation READ orientation + WRITE setOrientation NOTIFY orientationChanged ) + + Q_PROPERTY( qreal origin READ origin + WRITE setOrigin RESET resetOrigin NOTIFY originChanged ) + + Q_PROPERTY( qreal value READ value WRITE setValue NOTIFY valueChanged ) + Q_PROPERTY( qreal valueAsRatio READ valueAsRatio + WRITE setValueAsRatio NOTIFY valueChanged ) + + using Inherited = QskBoundedControl; + + public: + QSK_SUBCONTROLS( Groove, ValueFill ) + + QskValueBar( Qt::Orientation, QQuickItem* parent = nullptr ); + QskValueBar( Qt::Orientation, qreal min, qreal max, QQuickItem* parent = nullptr ); + QskValueBar( const QskIntervalF&, QQuickItem* parent = nullptr ); + QskValueBar( qreal min, qreal max, QQuickItem* parent = nullptr ); + QskValueBar( QQuickItem* parent = nullptr ); + + ~QskValueBar() override; + + Qt::Orientation orientation() const; + void setOrientation( Qt::Orientation orientation ); + + QskAspect::Placement effectivePlacement() const override; + + void setFillGradient( const QskGradient& ); + QskGradient fillGradient() const; + + void setThickness( qreal ); + qreal thickness() const; + + void resetOrigin(); + qreal origin() const; + + qreal value() const; + qreal valueAsRatio() const; // [0.0, 1.0] + using QskBoundedControl::valueAsRatio; + + public Q_SLOTS: + void setValue( qreal ); + void setValueAsRatio( qreal ); + void setOrigin( qreal ); + + Q_SIGNALS: + void orientationChanged( Qt::Orientation ); + void valueChanged( qreal ); + void originChanged( qreal ); + + protected: + QSizeF contentsSizeHint( Qt::SizeHint, const QSizeF& ) const override; + void componentComplete() override; + + private: + void setValueInternal( qreal value ); + void adjustBoundaries( bool increasing ); + void adjustValue(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskValueBarSkinlet.cpp b/src/controls/QskValueBarSkinlet.cpp new file mode 100644 index 00000000..303245b8 --- /dev/null +++ b/src/controls/QskValueBarSkinlet.cpp @@ -0,0 +1,136 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskValueBarSkinlet.h" +#include "QskValueBar.h" + +#include + +QskValueBarSkinlet::QskValueBarSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { GrooveRole, ValueFillRole } ); +} + +QskValueBarSkinlet::~QskValueBarSkinlet() +{ +} + +QRectF QskValueBarSkinlet::subControlRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + const auto bar = static_cast< const QskValueBar* >( skinnable ); + + if( ( subControl == QskValueBar::Groove ) ) + { + const auto dim = bar->thickness(); + + auto rect = contentsRect; + if ( bar->orientation() == Qt::Horizontal ) + { + rect.setY( rect.y() + 0.5 * ( rect.height() - dim ) ); + rect.setHeight( dim ); + } + else + { + rect.setX( rect.x() + 0.5 * ( rect.width() - dim ) ); + rect.setWidth( dim ); + } + + return rect; + } + + if( subControl == QskValueBar::ValueFill ) + { + const auto bar = static_cast< const QskValueBar* >( skinnable ); + return fillRect( bar ); + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSGNode* QskValueBarSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + switch( nodeRole ) + { + case GrooveRole: + { + return updateBoxNode( skinnable, node, QskValueBar::Groove ); + } + + case ValueFillRole: + { + const auto bar = static_cast< const QskValueBar* >( skinnable ); + return updateFillNode( bar, node ); + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +QSGNode* QskValueBarSkinlet::updateFillNode( + const QskValueBar* bar, QSGNode* node ) const +{ + const auto subControl = QskValueBar::ValueFill; + + const auto rect = bar->subControlRect( subControl ); + if ( rect.isEmpty() ) + return nullptr; + + auto gradient = bar->gradientHint( subControl ); + if ( !gradient.isVisible() ) + return nullptr; + + gradient.setOrientation( bar->orientation() ); + + if ( !gradient.isMonochrome() ) + { + qreal pos1 = bar->valueAsRatio( bar->origin() ); + qreal pos2 = bar->valueAsRatio( bar->value() ); + + if ( pos2 < pos1 ) + std::swap( pos1, pos2 ); + + gradient = gradient.extracted( pos1, pos2 ); + + if ( bar->orientation() == Qt::Vertical ) + gradient.reverse(); + } + + return updateBoxNode( bar, node, rect, gradient, subControl ); +} + +QRectF QskValueBarSkinlet::fillRect( const QskValueBar* bar ) const +{ + auto rect = bar->subControlRect( QskValueBar::Groove ); + rect = bar->innerBox( QskValueBar::Groove, rect ); + + auto pos1 = bar->valueAsRatio( bar->origin() ); + auto pos2 = bar->valueAsRatio( bar->value() ); + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + if( bar->orientation() == Qt::Horizontal ) + { + const auto w = rect.width(); + + rect.setRight( rect.left() + pos2 * w ); + rect.setLeft( rect.left() + pos1 * w ); + } + else + { + const auto h = rect.height(); + + rect.setTop( rect.bottom() - h * pos2 ); + rect.setBottom( rect.bottom() - h * pos1 ); + } + + return rect; +} + +#include "moc_QskValueBarSkinlet.cpp" diff --git a/src/controls/QskValueBarSkinlet.h b/src/controls/QskValueBarSkinlet.h new file mode 100644 index 00000000..bf3f336e --- /dev/null +++ b/src/controls/QskValueBarSkinlet.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_VALUE_BAR_SKINLET_H +#define QSK_VALUE_BAR_SKINLET_H + +#include "QskSkinlet.h" + +class QskValueBar; + +class QSK_EXPORT QskValueBarSkinlet : public QskSkinlet +{ + Q_GADGET + + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + GrooveRole, + ValueFillRole, + + RoleCount, + }; + + Q_INVOKABLE QskValueBarSkinlet( QskSkin* = nullptr ); + ~QskValueBarSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + private: + QSGNode* updateFillNode( const QskValueBar*, QSGNode* ) const; + QRectF fillRect( const QskValueBar* ) const; +}; + +#endif diff --git a/src/src.pro b/src/src.pro index 180ef979..baa0e634 100644 --- a/src/src.pro +++ b/src/src.pro @@ -177,6 +177,8 @@ HEADERS += \ controls/QskTextInputSkinlet.h \ controls/QskTextLabel.h \ controls/QskTextLabelSkinlet.h \ + controls/QskValueBar.h \ + controls/QskValueBarSkinlet.h \ controls/QskVariantAnimator.h \ controls/QskWindow.h @@ -248,6 +250,8 @@ SOURCES += \ controls/QskTextInputSkinlet.cpp \ controls/QskTextLabel.cpp \ controls/QskTextLabelSkinlet.cpp \ + controls/QskValueBar.cpp \ + controls/QskValueBarSkinlet.cpp \ controls/QskVariantAnimator.cpp \ controls/QskWindow.cpp