From d9be53e7a189bbdd8de78e75c7948dc0f201674c Mon Sep 17 00:00:00 2001 From: "Vogel, Rick" Date: Thu, 25 Jan 2024 13:05:14 +0100 Subject: [PATCH] playground for gradient fix testing --- playground/CMakeLists.txt | 1 + playground/gradient_fix/CMakeLists.txt | 6 + playground/gradient_fix/main.cpp | 285 +++++++++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 playground/gradient_fix/CMakeLists.txt create mode 100644 playground/gradient_fix/main.cpp diff --git a/playground/CMakeLists.txt b/playground/CMakeLists.txt index 2e7b04ca..7f56d037 100644 --- a/playground/CMakeLists.txt +++ b/playground/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(anchors) add_subdirectory(dials) add_subdirectory(dialogbuttons) add_subdirectory(gradients) +add_subdirectory(gradient_fix) add_subdirectory(invoker) add_subdirectory(shadows) add_subdirectory(shapes) diff --git a/playground/gradient_fix/CMakeLists.txt b/playground/gradient_fix/CMakeLists.txt new file mode 100644 index 00000000..14fc1947 --- /dev/null +++ b/playground/gradient_fix/CMakeLists.txt @@ -0,0 +1,6 @@ +############################################################################ +# QSkinny - Copyright (C) The authors +# SPDX-License-Identifier: BSD-3-Clause +############################################################################ + +qsk_add_example(gradient_fix main.cpp) diff --git a/playground/gradient_fix/main.cpp b/playground/gradient_fix/main.cpp new file mode 100644 index 00000000..7c1bd0cd --- /dev/null +++ b/playground/gradient_fix/main.cpp @@ -0,0 +1,285 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +double qskMapValueRange( double value, double srcMin, double srcMax, double dstMin, double dstMax ) +{ + value = std::min( std::max( value, srcMin ), srcMax ); + double percentage = ( value - srcMin ) / ( srcMax - srcMin ); + double mappedValue = dstMin + percentage * ( dstMax - dstMin ); + return mappedValue; +} + +QColor extracted( const QskGradient& gradient, qreal ratio ) +{ + // Ensure the factor is within the [0, 1] range + ratio = qBound< qreal >( 0.0, ratio, 1.0 ); + + // Extract RGB components of the start and end colors + int startRed, startGreen, startBlue, startAlpha; + gradient.startColor().getRgb( &startRed, &startGreen, &startBlue, &startAlpha ); + + int endRed, endGreen, endBlue, endAlpha; + gradient.endColor().getRgb( &endRed, &endGreen, &endBlue, &endAlpha ); + + // Linearly interpolate each color component + int interpolatedRed = static_cast< int >( startRed + ratio * ( endRed - startRed ) ); + int interpolatedGreen = static_cast< int >( startGreen + ratio * ( endGreen - startGreen ) ); + int interpolatedBlue = static_cast< int >( startBlue + ratio * ( endBlue - startBlue ) ); + int interpolatedAlpha = static_cast< int >( startAlpha + ratio * ( endAlpha - startAlpha ) ); + + // Create and return the interpolated color + return QColor( interpolatedRed, interpolatedGreen, interpolatedBlue, interpolatedAlpha ); +} + +QskGradient extracted( const QskGradient& gradient, qreal from, qreal to ) +{ + const auto stops = gradient.stops(); + + if ( stops.count() == 0 ) + return {}; + if ( stops.count() == 1 ) + return stops[ 0 ].color(); + + from = qBound( 0.0, from, 1.0 ); + to = qBound( 0.0, to, 1.0 ); + + // returns the stop indexes that contain 'from' + const auto fromIndex = [ & ]() { + for ( int i = 0; i < stops.count() - 1; ++i ) + { + if ( stops[ i ].position() <= from && from <= stops[ i + 1 ].position() ) + { + return std::make_pair( i, i + 1 ); + } + } + return std::pair< int, int >{ -1, -1 }; + }(); + + // returns the stop indexes that contain 'to' + const auto toIndex = [ & ]() { + for ( int i = stops.count() - 1; i > 0; --i ) + { + if ( stops[ i - 1 ].position() <= to && to <= stops[ i ].position() ) + { + return std::make_pair( i - 1, i ); + } + } + return std::pair< int, int >{ -1, -1 }; + }(); + + Q_ASSERT( fromIndex.first < toIndex.second ); + + const auto fromColor = [ & ]() { + const auto p = qskMapValueRange( from, stops[ fromIndex.first ].position(), + stops[ fromIndex.second ].position(), 0.0, 1.0 ); + return extracted( + { stops[ fromIndex.first ].color(), stops[ fromIndex.second ].color() }, p ); + }(); + + const auto toColor = [ & ]() { + const auto p = qskMapValueRange( + to, stops[ toIndex.first ].position(), stops[ toIndex.second ].position(), 0.0, 1.0 ); + return extracted( { stops[ toIndex.first ].color(), stops[ toIndex.second ].color() }, p ); + }(); + + QskGradient newGradient; + newGradient.setLinearDirection( Qt::Horizontal ); + QskGradientStops newStops; + newStops << QskGradientStop{ 0.0, fromColor }; + + for ( int i = fromIndex.second; i < toIndex.second; ++i ) + { + const auto p = qskMapValueRange( stops[ i ].position(), from, to, 0.0, 1.0 ); + newStops << QskGradientStop{ p, stops[ i ].color() }; + } + + newStops << QskGradientStop{ 1.0, toColor }; + newGradient.setStops( newStops ); + + Q_ASSERT( newGradient.stops().count() <= gradient.stops().count() ); + + return newGradient; +} + +class Control : public QskControl +{ + public: + QSK_SUBCONTROLS( Gradient, GradientStart, GradientStop ) + + Control( QQuickItem* parent = nullptr ) + : QskControl( parent ) + { + setAcceptedMouseButtons( Qt::LeftButton | Qt::RightButton ); + setPositionHint( GradientStart, 0.0 ); + setPositionHint( GradientStop, 1.0 ); + } + + void mouseMoveEvent( QMouseEvent* event ) override + { + if ( event->buttons() == Qt::LeftButton ) + setPositionHint( GradientStart, event->pos().x() / width() ); + if ( event->buttons() == Qt::RightButton ) + setPositionHint( GradientStop, event->pos().x() / width() ); + } + + void mousePressEvent( QMouseEvent* event ) override + { + } + + void mouseReleaseEvent( QMouseEvent* event ) override + { + } +}; + +QSK_SUBCONTROL( Control, Gradient ) +QSK_SUBCONTROL( Control, GradientStart ) +QSK_SUBCONTROL( Control, GradientStop ) + +class Skinlet : public QskSkinlet +{ + public: + enum NodeRoles + { + A, + B, + C + }; + Skinlet( QskSkin* skin = nullptr ) + : QskSkinlet( skin ) + { + setNodeRoles( { A, B, C } ); + } + + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override + { + struct Node : QSGNode + { + Node() + { + appendChildNode( new QskBoxNode ); + appendChildNode( new QskTextNode ); + } + }; + + const auto r = static_cast< const QskControl* >( skinnable )->contentsRect(); + auto g = skinnable->gradientHint( Control::Gradient ); + g.setLinearDirection( Qt::Horizontal ); + + const auto from = skinnable->positionHint( Control::GradientStart ); + const auto to = skinnable->positionHint( Control::GradientStop ); + + const auto x1 = qskMapValueRange( from, 0.0, 1.0, r.left(), r.right() ); + const auto x2 = qskMapValueRange( to, 0.0, 1.0, r.left(), r.right() ); + const auto y1 = 0.0; + const auto y2 = r.height() / 3; + const auto rect = QRectF{ QPointF{ x1, y1 }, QPointF{ x2, y2 } }; + const auto dy = r.height() / 3; + + if ( nodeRole == A ) + { + const auto rect = QRectF{ r.topLeft(), QSizeF{ r.width(), r.height() / 3 } }; + auto* const n = QskSGNode::ensureNode< Node >( node ); + updateBoxNode( skinnable, n->firstChild(), rect, {}, {}, {}, g ); + updateTextNode( skinnable, n->lastChild(), rect, Qt::AlignTop | Qt::AlignLeft, + "Reference", QskControl::Background ); + return n; + } + if ( nodeRole == B ) + { + const auto rectB = rect.adjusted( 0, dy, 0, dy ); + auto* const n = QskSGNode::ensureNode< Node >( node ); + updateBoxNode( + skinnable, n->firstChild(), rectB, {}, {}, {}, extracted( g, from, to ) ); + updateTextNode( skinnable, n->lastChild(), rectB, Qt::AlignTop | Qt::AlignLeft, + "Expected", QskControl::Background ); + return n; + } + if ( nodeRole == C ) + { + const auto rectC = rect.adjusted( 0, 2 * dy, 0, 2 * dy ); + auto* const n = QskSGNode::ensureNode< Node >( node ); + updateBoxNode( skinnable, n->firstChild(), rectC, {}, {}, {}, g.extracted( from, to ) ); + updateTextNode( skinnable, n->lastChild(), rectC, Qt::AlignTop | Qt::AlignLeft, + "Actual", QskControl::Background ); + return n; + } + + return nullptr; + } +}; + +int main( int argc, char* argv[] ) +{ +#ifdef ITEM_STATISTICS + QskObjectCounter counter( true ); +#endif + + QGuiApplication app( argc, argv ); + + SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); + + auto* const layout = new QskLinearBox(Qt::Vertical); + auto* const row = new QskLinearBox(layout); + auto* const control = new Control(layout); + auto* const skinlet = new Skinlet; + control->setSkinlet( skinlet ); + skinlet->setOwnedBySkinnable( true ); + + auto qskHGradient = [](Qt::Orientation orientation , QskGradient gradient ){ + gradient.setLinearDirection(orientation); + return gradient; + }; + + { + auto* const button = new QskPushButton( row ); + button->setGradientHint(QskPushButton::Panel, Qt::red); + QObject::connect(button, &QskPushButton::clicked, control, [control, button](){ + control->setGradientHint(Control::Gradient, button->gradientHint(QskPushButton::Panel)); + }); + } + { + auto* const button = new QskPushButton( row ); + button->setGradientHint(QskPushButton::Panel, qskHGradient(Qt::Horizontal, {Qt::red, Qt::green})); + QObject::connect(button, &QskPushButton::clicked, control, [control, button](){ + control->setGradientHint(Control::Gradient, button->gradientHint(QskPushButton::Panel)); + }); + } + { + auto* const button = new QskPushButton( row ); + button->setGradientHint(QskPushButton::Panel, qskHGradient(Qt::Horizontal,{{{0.0, Qt::red}, {0.5, Qt::green}, {1.0, Qt::blue}}})); + QObject::connect(button, &QskPushButton::clicked, control, [control, button](){ + control->setGradientHint(Control::Gradient, button->gradientHint(QskPushButton::Panel)); + }); + } + + QskWindow window; + window.addItem( layout ); + window.resize( 600, 600 ); + window.show(); + + return app.exec(); +} + +// #include "moc_MainWindow.cpp" \ No newline at end of file