2024-01-25 12:05:14 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) The authors
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include <QskBoxBorderColors.h>
|
|
|
|
#include <QskBoxBorderMetrics.h>
|
2024-01-29 13:43:25 +00:00
|
|
|
#include <QskBoxNode.h>
|
|
|
|
#include <QskBoxShadowNode.h>
|
2024-01-25 12:05:14 +00:00
|
|
|
#include <QskBoxShapeMetrics.h>
|
2024-01-29 13:43:25 +00:00
|
|
|
#include <QskGradientDirection.h>
|
2024-01-25 12:05:14 +00:00
|
|
|
#include <QskLinearBox.h>
|
2024-01-29 13:43:25 +00:00
|
|
|
#include <QskObjectCounter.h>
|
2024-01-25 12:05:14 +00:00
|
|
|
#include <QskPushButton.h>
|
2024-01-29 13:43:25 +00:00
|
|
|
#include <QskSGNode.h>
|
|
|
|
#include <QskSkinlet.h>
|
|
|
|
#include <QskTabBar.h>
|
|
|
|
#include <QskTabView.h>
|
|
|
|
#include <QskTextNode.h>
|
|
|
|
#include <QskWindow.h>
|
2024-01-25 12:05:14 +00:00
|
|
|
|
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <SkinnyShortcut.h>
|
2024-01-29 10:29:34 +00:00
|
|
|
#include <qnamespace.h>
|
2024-01-25 12:05:14 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-01-29 13:43:25 +00:00
|
|
|
QColor extracted( const QColor& from, const QColor& to, qreal ratio )
|
2024-01-25 12:05:14 +00:00
|
|
|
{
|
|
|
|
// 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;
|
2024-01-29 13:43:25 +00:00
|
|
|
from.getRgb( &startRed, &startGreen, &startBlue, &startAlpha );
|
2024-01-25 12:05:14 +00:00
|
|
|
|
|
|
|
int endRed, endGreen, endBlue, endAlpha;
|
2024-01-29 13:43:25 +00:00
|
|
|
to.getRgb( &endRed, &endGreen, &endBlue, &endAlpha );
|
2024-01-25 12:05:14 +00:00
|
|
|
|
|
|
|
// 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 )
|
|
|
|
{
|
2024-01-29 15:47:19 +00:00
|
|
|
const auto& stops = gradient.stops();
|
2024-01-25 12:05:14 +00:00
|
|
|
|
|
|
|
if ( stops.count() == 0 )
|
2024-01-29 13:43:25 +00:00
|
|
|
{
|
2024-01-25 12:05:14 +00:00
|
|
|
return {};
|
2024-01-29 13:43:25 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 12:05:14 +00:00
|
|
|
if ( stops.count() == 1 )
|
2024-01-29 13:43:25 +00:00
|
|
|
{
|
2024-01-25 12:05:14 +00:00
|
|
|
return stops[ 0 ].color();
|
2024-01-29 13:43:25 +00:00
|
|
|
}
|
2024-01-25 12:05:14 +00:00
|
|
|
|
|
|
|
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 );
|
2024-01-29 13:43:25 +00:00
|
|
|
return extracted( stops[ fromIndex.first ].color(), stops[ fromIndex.second ].color(), p );
|
2024-01-25 12:05:14 +00:00
|
|
|
}();
|
|
|
|
|
|
|
|
const auto toColor = [ & ]() {
|
|
|
|
const auto p = qskMapValueRange(
|
|
|
|
to, stops[ toIndex.first ].position(), stops[ toIndex.second ].position(), 0.0, 1.0 );
|
2024-01-29 13:43:25 +00:00
|
|
|
return extracted( stops[ toIndex.first ].color(), stops[ toIndex.second ].color(), p );
|
2024-01-25 12:05:14 +00:00
|
|
|
}();
|
|
|
|
|
2024-01-29 15:47:19 +00:00
|
|
|
QskGradient newGradient = gradient;
|
2024-01-25 12:05:14 +00:00
|
|
|
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 );
|
|
|
|
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const layout = new QskLinearBox( Qt::Vertical );
|
|
|
|
auto* const row = new QskLinearBox( Qt::Vertical, layout );
|
|
|
|
auto* const control = new Control( layout );
|
2024-01-25 12:05:14 +00:00
|
|
|
auto* const skinlet = new Skinlet;
|
|
|
|
control->setSkinlet( skinlet );
|
|
|
|
skinlet->setOwnedBySkinnable( true );
|
|
|
|
|
2024-01-29 13:43:25 +00:00
|
|
|
auto qskGradient = []( Qt::Orientation orientation, QskGradient gradient ) {
|
|
|
|
gradient.setLinearDirection( orientation );
|
2024-01-25 12:05:14 +00:00
|
|
|
return gradient;
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const button = new QskPushButton( "Click", row );
|
|
|
|
button->setGradientHint( QskPushButton::Panel, {} );
|
|
|
|
QObject::connect( button, &QskPushButton::clicked, control, [ control, button ]() {
|
|
|
|
control->setGradientHint(
|
|
|
|
Control::Gradient, button->gradientHint( QskPushButton::Panel ) );
|
|
|
|
} );
|
2024-01-29 10:29:34 +00:00
|
|
|
}
|
|
|
|
{
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const button = new QskPushButton( "Click", row );
|
|
|
|
button->setGradientHint( QskPushButton::Panel, Qt::red );
|
|
|
|
QObject::connect( button, &QskPushButton::clicked, control, [ control, button ]() {
|
|
|
|
control->setGradientHint(
|
|
|
|
Control::Gradient, button->gradientHint( QskPushButton::Panel ) );
|
|
|
|
} );
|
2024-01-25 12:05:14 +00:00
|
|
|
}
|
|
|
|
{
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const button = new QskPushButton( "Click", row );
|
|
|
|
button->setGradientHint(
|
|
|
|
QskPushButton::Panel, qskGradient( Qt::Horizontal, { Qt::red, Qt::green } ) );
|
|
|
|
QObject::connect( button, &QskPushButton::clicked, control, [ control, button ]() {
|
|
|
|
control->setGradientHint(
|
|
|
|
Control::Gradient, button->gradientHint( QskPushButton::Panel ) );
|
|
|
|
} );
|
2024-01-29 10:29:34 +00:00
|
|
|
}
|
|
|
|
{
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const button = new QskPushButton( "Click", row );
|
|
|
|
button->setGradientHint( QskPushButton::Panel,
|
|
|
|
qskGradient(
|
|
|
|
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 ) );
|
|
|
|
} );
|
2024-01-29 10:29:34 +00:00
|
|
|
}
|
|
|
|
{
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const button = new QskPushButton( "Click", row );
|
|
|
|
button->setGradientHint( QskPushButton::Panel,
|
|
|
|
qskGradient( Qt::Horizontal, { { { 0.0, Qt::red }, { 0.5, Qt::red }, { 0.5, Qt::blue },
|
|
|
|
{ 1.0, Qt::blue } } } ) );
|
|
|
|
QObject::connect( button, &QskPushButton::clicked, control, [ control, button ]() {
|
|
|
|
control->setGradientHint(
|
|
|
|
Control::Gradient, button->gradientHint( QskPushButton::Panel ) );
|
|
|
|
} );
|
2024-01-25 12:05:14 +00:00
|
|
|
}
|
|
|
|
{
|
2024-01-29 13:43:25 +00:00
|
|
|
auto* const button = new QskPushButton( "Click", row );
|
|
|
|
button->setGradientHint(
|
|
|
|
QskPushButton::Panel, qskGradient( Qt::Horizontal,
|
|
|
|
{ { { 0.0, Qt::red }, { 0.25, Qt::red }, { 0.25, Qt::green },
|
|
|
|
{ 0.5, Qt::green }, { 0.5, Qt::blue }, { 0.75, Qt::blue },
|
|
|
|
{ 0.75, Qt::yellow }, { 1.0, Qt::yellow } } } ) );
|
|
|
|
QObject::connect( button, &QskPushButton::clicked, control, [ control, button ]() {
|
|
|
|
control->setGradientHint(
|
|
|
|
Control::Gradient, button->gradientHint( QskPushButton::Panel ) );
|
|
|
|
} );
|
2024-01-25 12:05:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QskWindow window;
|
|
|
|
window.addItem( layout );
|
|
|
|
window.resize( 600, 600 );
|
|
|
|
window.show();
|
|
|
|
|
|
|
|
return app.exec();
|
|
|
|
}
|
|
|
|
|
|
|
|
// #include "moc_MainWindow.cpp"
|