playground for gradient fix testing
This commit is contained in:
parent
9f9fafe9ef
commit
d9be53e7a1
|
@ -2,6 +2,7 @@ add_subdirectory(anchors)
|
||||||
add_subdirectory(dials)
|
add_subdirectory(dials)
|
||||||
add_subdirectory(dialogbuttons)
|
add_subdirectory(dialogbuttons)
|
||||||
add_subdirectory(gradients)
|
add_subdirectory(gradients)
|
||||||
|
add_subdirectory(gradient_fix)
|
||||||
add_subdirectory(invoker)
|
add_subdirectory(invoker)
|
||||||
add_subdirectory(shadows)
|
add_subdirectory(shadows)
|
||||||
add_subdirectory(shapes)
|
add_subdirectory(shapes)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
############################################################################
|
||||||
|
# QSkinny - Copyright (C) The authors
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
qsk_add_example(gradient_fix main.cpp)
|
|
@ -0,0 +1,285 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* QSkinny - Copyright (C) The authors
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <QskObjectCounter.h>
|
||||||
|
#include <QskTabBar.h>
|
||||||
|
#include <QskTabView.h>
|
||||||
|
#include <QskWindow.h>
|
||||||
|
#include <QskSkinlet.h>
|
||||||
|
#include <QskBoxNode.h>
|
||||||
|
#include <QskTextNode.h>
|
||||||
|
#include <QskSGNode.h>
|
||||||
|
#include <QskBoxShadowNode.h>
|
||||||
|
#include <QskBoxBorderColors.h>
|
||||||
|
#include <QskBoxBorderMetrics.h>
|
||||||
|
#include <QskGradientDirection.h>
|
||||||
|
#include <QskBoxShapeMetrics.h>
|
||||||
|
#include <QskLinearBox.h>
|
||||||
|
#include <QskPushButton.h>
|
||||||
|
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <SkinnyShortcut.h>
|
||||||
|
|
||||||
|
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"
|
Loading…
Reference in New Issue